From bd8d12b7f68ea8aa05a86cc9ed65dd660a6b075d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 4 May 2011 14:37:50 +0200 Subject: move platform specific files out of the way Keep the files as reference to implement the lighthouse plugins, but move them away from the main directory hierachy as they won't be part of Qt5 in any case. --- src/gui/guikernel/qclipboard_mac.cpp | 634 -- src/gui/guikernel/qclipboard_s60.cpp | 331 -- src/gui/guikernel/qclipboard_win.cpp | 398 -- src/gui/guikernel/qclipboard_x11.cpp | 1539 ----- src/gui/guikernel/qcursor_mac.mm | 689 --- src/gui/guikernel/qcursor_s60.cpp | 533 -- src/gui/guikernel/qcursor_win.cpp | 492 -- src/gui/guikernel/qcursor_x11.cpp | 637 -- src/gui/guikernel/qdnd_mac.mm | 753 --- src/gui/guikernel/qdnd_s60.cpp | 359 -- src/gui/guikernel/qdnd_win.cpp | 1027 ---- src/gui/guikernel/qdnd_x11.cpp | 2072 ------- src/gui/guikernel/qeventdispatcher_mac.mm | 1200 ---- src/gui/guikernel/qeventdispatcher_mac_p.h | 224 - src/gui/guikernel/qeventdispatcher_s60.cpp | 196 - src/gui/guikernel/qeventdispatcher_s60_p.h | 127 - src/gui/guikernel/qeventdispatcher_x11.cpp | 191 - src/gui/guikernel/qeventdispatcher_x11_p.h | 86 - src/gui/guikernel/qkeymapper_mac.cpp | 1023 ---- src/gui/guikernel/qkeymapper_s60.cpp | 258 - src/gui/guikernel/qkeymapper_win.cpp | 1207 ---- src/gui/guikernel/qkeymapper_x11.cpp | 1869 ------ src/gui/guikernel/qkeymapper_x11_p.cpp | 489 -- src/gui/guikernel/qmime_mac.cpp | 1310 ---- src/gui/guikernel/qmime_win.cpp | 1556 ----- src/gui/guikernel/qmotifdnd_x11.cpp | 1031 ---- src/gui/guikernel/qole_win.cpp | 255 - src/gui/guikernel/qwindowdefs_win.h | 132 - src/gui/image/qpixmap_mac.cpp | 1195 ---- src/gui/image/qpixmap_mac_p.h | 134 - src/gui/image/qpixmap_s60.cpp | 1040 ---- src/gui/image/qpixmap_s60_p.h | 141 - src/gui/image/qpixmap_win.cpp | 477 -- src/gui/image/qpixmap_x11.cpp | 2419 -------- src/gui/image/qpixmap_x11_p.h | 156 - src/gui/kernel/qapplication_mac.mm | 3134 ---------- src/gui/kernel/qapplication_s60.cpp | 2712 --------- src/gui/kernel/qapplication_win.cpp | 4243 ------------- src/gui/kernel/qapplication_x11.cpp | 6239 -------------------- src/gui/kernel/qcocoaapplication_mac.mm | 222 - src/gui/kernel/qcocoaapplication_mac_p.h | 117 - src/gui/kernel/qcocoaapplicationdelegate_mac.mm | 354 -- src/gui/kernel/qcocoaapplicationdelegate_mac_p.h | 128 - src/gui/kernel/qcocoaintrospection_mac.mm | 125 - src/gui/kernel/qcocoaintrospection_p.h | 84 - src/gui/kernel/qcocoamenuloader_mac.mm | 264 - src/gui/kernel/qcocoamenuloader_mac_p.h | 95 - src/gui/kernel/qcocoapanel_mac.mm | 70 - src/gui/kernel/qcocoapanel_mac_p.h | 83 - src/gui/kernel/qcocoasharedwindowmethods_mac_p.h | 610 -- src/gui/kernel/qcocoaview_mac.mm | 1388 ----- src/gui/kernel/qcocoaview_mac_p.h | 87 - src/gui/kernel/qcocoawindow_mac.mm | 90 - src/gui/kernel/qcocoawindow_mac_p.h | 97 - src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm | 62 - .../kernel/qcocoawindowcustomthemeframe_mac_p.h | 61 - src/gui/kernel/qcocoawindowdelegate_mac.mm | 439 -- src/gui/kernel/qcocoawindowdelegate_mac_p.h | 110 - src/gui/kernel/qdesktopwidget_mac.mm | 257 - src/gui/kernel/qdesktopwidget_mac_p.h | 75 - src/gui/kernel/qdesktopwidget_s60.cpp | 316 - src/gui/kernel/qdesktopwidget_win.cpp | 387 -- src/gui/kernel/qdesktopwidget_x11.cpp | 406 -- src/gui/kernel/qguifunctions_wince.cpp | 408 -- src/gui/kernel/qguifunctions_wince.h | 151 - src/gui/kernel/qkde.cpp | 176 - src/gui/kernel/qkde_p.h | 81 - src/gui/kernel/qmacdefines_mac.h | 180 - src/gui/kernel/qmacgesturerecognizer_mac.mm | 272 - src/gui/kernel/qmacgesturerecognizer_mac_p.h | 106 - src/gui/kernel/qmultitouch_mac.mm | 218 - src/gui/kernel/qmultitouch_mac_p.h | 102 - src/gui/kernel/qnsframeview_mac_p.h | 154 - src/gui/kernel/qnsthemeframe_mac_p.h | 246 - src/gui/kernel/qnstitledframe_mac_p.h | 205 - src/gui/kernel/qsoftkeymanager_s60.cpp | 440 -- src/gui/kernel/qsoftkeymanager_s60_p.h | 113 - src/gui/kernel/qsound_mac.mm | 190 - src/gui/kernel/qsound_s60.cpp | 224 - src/gui/kernel/qsound_win.cpp | 205 - src/gui/kernel/qsound_x11.cpp | 296 - src/gui/kernel/qt_cocoa_helpers_mac.mm | 1824 ------ src/gui/kernel/qt_cocoa_helpers_mac_p.h | 340 -- src/gui/kernel/qt_mac.cpp | 174 - src/gui/kernel/qt_mac_p.h | 286 - src/gui/kernel/qt_s60_p.h | 625 -- src/gui/kernel/qt_x11_p.h | 757 --- src/gui/kernel/qwidget_mac.mm | 5420 ----------------- src/gui/kernel/qwidget_s60.cpp | 1450 ----- src/gui/kernel/qwidget_win.cpp | 2139 ------- src/gui/kernel/qwidget_wince.cpp | 675 --- src/gui/kernel/qwidget_x11.cpp | 3146 ---------- src/gui/kernel/qwidgetcreate_x11.cpp | 79 - .../kernel/qwinnativepangesturerecognizer_win.cpp | 133 - .../kernel/qwinnativepangesturerecognizer_win_p.h | 80 - src/gui/kernel/qx11embed_x11.cpp | 1808 ------ src/gui/kernel/qx11embed_x11.h | 132 - src/gui/kernel/qx11info_x11.cpp | 543 -- src/gui/kernel/qx11info_x11.h | 123 - src/gui/painting/qcolormap_mac.cpp | 111 - src/gui/painting/qcolormap_s60.cpp | 107 - src/gui/painting/qcolormap_win.cpp | 201 - src/gui/painting/qcolormap_x11.cpp | 670 --- src/gui/painting/qpaintdevice_mac.cpp | 152 - src/gui/painting/qpaintdevice_win.cpp | 62 - src/gui/painting/qpaintdevice_x11.cpp | 84 - src/gui/painting/qpaintengine_mac.cpp | 1751 ------ src/gui/painting/qpaintengine_mac_p.h | 254 - src/gui/painting/qpaintengine_s60.cpp | 145 - src/gui/painting/qpaintengine_s60_p.h | 84 - src/gui/painting/qpaintengine_x11.cpp | 2507 -------- src/gui/painting/qpaintengine_x11_p.h | 246 - src/gui/painting/qprintengine_mac.mm | 911 --- src/gui/painting/qprintengine_mac_p.h | 166 - src/gui/painting/qprintengine_win.cpp | 1776 ------ src/gui/painting/qprintengine_win_p.h | 261 - src/gui/painting/qprinterinfo_mac.cpp | 120 - src/gui/painting/qprinterinfo_win.cpp | 122 - src/gui/painting/qregion_mac.cpp | 286 - src/gui/painting/qregion_s60.cpp | 52 - src/gui/painting/qregion_win.cpp | 149 - src/gui/painting/qregion_x11.cpp | 92 - src/gui/platforms/mac/qapplication_mac.mm | 3134 ++++++++++ src/gui/platforms/mac/qclipboard_mac.cpp | 634 ++ src/gui/platforms/mac/qcocoaapplication_mac.mm | 222 + src/gui/platforms/mac/qcocoaapplication_mac_p.h | 117 + .../platforms/mac/qcocoaapplicationdelegate_mac.mm | 354 ++ .../mac/qcocoaapplicationdelegate_mac_p.h | 128 + src/gui/platforms/mac/qcocoaintrospection_mac.mm | 125 + src/gui/platforms/mac/qcocoaintrospection_p.h | 84 + src/gui/platforms/mac/qcocoamenuloader_mac.mm | 264 + src/gui/platforms/mac/qcocoamenuloader_mac_p.h | 95 + src/gui/platforms/mac/qcocoapanel_mac.mm | 70 + src/gui/platforms/mac/qcocoapanel_mac_p.h | 83 + .../mac/qcocoasharedwindowmethods_mac_p.h | 610 ++ src/gui/platforms/mac/qcocoaview_mac.mm | 1388 +++++ src/gui/platforms/mac/qcocoaview_mac_p.h | 87 + src/gui/platforms/mac/qcocoawindow_mac.mm | 90 + src/gui/platforms/mac/qcocoawindow_mac_p.h | 97 + .../mac/qcocoawindowcustomthemeframe_mac.mm | 62 + .../mac/qcocoawindowcustomthemeframe_mac_p.h | 61 + src/gui/platforms/mac/qcocoawindowdelegate_mac.mm | 439 ++ src/gui/platforms/mac/qcocoawindowdelegate_mac_p.h | 110 + src/gui/platforms/mac/qcolormap_mac.cpp | 111 + src/gui/platforms/mac/qcursor_mac.mm | 689 +++ src/gui/platforms/mac/qdesktopwidget_mac.mm | 257 + src/gui/platforms/mac/qdesktopwidget_mac_p.h | 75 + src/gui/platforms/mac/qdnd_mac.mm | 753 +++ src/gui/platforms/mac/qeventdispatcher_mac.mm | 1200 ++++ src/gui/platforms/mac/qeventdispatcher_mac_p.h | 224 + src/gui/platforms/mac/qfont_mac.cpp | 165 + src/gui/platforms/mac/qfontdatabase_mac.cpp | 466 ++ src/gui/platforms/mac/qfontengine_coretext.mm | 880 +++ src/gui/platforms/mac/qfontengine_coretext_p.h | 144 + src/gui/platforms/mac/qfontengine_mac.mm | 1236 ++++ src/gui/platforms/mac/qfontengine_mac_p.h | 165 + src/gui/platforms/mac/qkeymapper_mac.cpp | 1023 ++++ src/gui/platforms/mac/qmacdefines_mac.h | 180 + src/gui/platforms/mac/qmacgesturerecognizer_mac.mm | 272 + .../platforms/mac/qmacgesturerecognizer_mac_p.h | 106 + src/gui/platforms/mac/qmime_mac.cpp | 1310 ++++ src/gui/platforms/mac/qmultitouch_mac.mm | 218 + src/gui/platforms/mac/qmultitouch_mac_p.h | 102 + src/gui/platforms/mac/qnsframeview_mac_p.h | 154 + src/gui/platforms/mac/qnsthemeframe_mac_p.h | 246 + src/gui/platforms/mac/qnstitledframe_mac_p.h | 205 + src/gui/platforms/mac/qpaintdevice_mac.cpp | 152 + src/gui/platforms/mac/qpaintengine_mac.cpp | 1751 ++++++ src/gui/platforms/mac/qpaintengine_mac_p.h | 254 + src/gui/platforms/mac/qpixmap_mac.cpp | 1195 ++++ src/gui/platforms/mac/qpixmap_mac_p.h | 134 + src/gui/platforms/mac/qprintengine_mac.mm | 911 +++ src/gui/platforms/mac/qprintengine_mac_p.h | 166 + src/gui/platforms/mac/qprinterinfo_mac.cpp | 120 + src/gui/platforms/mac/qrawfont_mac.cpp | 83 + src/gui/platforms/mac/qregion_mac.cpp | 286 + src/gui/platforms/mac/qsound_mac.mm | 190 + src/gui/platforms/mac/qt_cocoa_helpers_mac.mm | 1824 ++++++ src/gui/platforms/mac/qt_cocoa_helpers_mac_p.h | 340 ++ src/gui/platforms/mac/qt_mac.cpp | 174 + src/gui/platforms/mac/qt_mac_p.h | 286 + src/gui/platforms/mac/qtextengine_mac.cpp | 656 ++ src/gui/platforms/mac/qwidget_mac.mm | 5420 +++++++++++++++++ src/gui/platforms/s60/qapplication_s60.cpp | 2712 +++++++++ src/gui/platforms/s60/qclipboard_s60.cpp | 331 ++ src/gui/platforms/s60/qcolormap_s60.cpp | 107 + src/gui/platforms/s60/qcursor_s60.cpp | 533 ++ src/gui/platforms/s60/qdesktopwidget_s60.cpp | 316 + src/gui/platforms/s60/qdnd_s60.cpp | 359 ++ src/gui/platforms/s60/qeventdispatcher_s60.cpp | 196 + src/gui/platforms/s60/qeventdispatcher_s60_p.h | 127 + src/gui/platforms/s60/qfont_s60.cpp | 136 + src/gui/platforms/s60/qfontdatabase_s60.cpp | 1091 ++++ src/gui/platforms/s60/qfontengine_s60.cpp | 569 ++ src/gui/platforms/s60/qfontengine_s60_p.h | 167 + src/gui/platforms/s60/qkeymapper_s60.cpp | 258 + src/gui/platforms/s60/qpaintengine_s60.cpp | 145 + src/gui/platforms/s60/qpaintengine_s60_p.h | 84 + src/gui/platforms/s60/qpixmap_s60.cpp | 1040 ++++ src/gui/platforms/s60/qpixmap_s60_p.h | 141 + src/gui/platforms/s60/qregion_s60.cpp | 52 + src/gui/platforms/s60/qsoftkeymanager_s60.cpp | 440 ++ src/gui/platforms/s60/qsoftkeymanager_s60_p.h | 113 + src/gui/platforms/s60/qsound_s60.cpp | 224 + src/gui/platforms/s60/qt_s60_p.h | 625 ++ src/gui/platforms/s60/qwidget_s60.cpp | 1450 +++++ src/gui/platforms/win/qapplication_win.cpp | 4243 +++++++++++++ src/gui/platforms/win/qclipboard_win.cpp | 398 ++ src/gui/platforms/win/qcolormap_win.cpp | 201 + src/gui/platforms/win/qcursor_win.cpp | 492 ++ src/gui/platforms/win/qdesktopwidget_win.cpp | 387 ++ src/gui/platforms/win/qdnd_win.cpp | 1027 ++++ src/gui/platforms/win/qfont_win.cpp | 166 + src/gui/platforms/win/qfontdatabase_win.cpp | 1348 +++++ src/gui/platforms/win/qfontengine_win.cpp | 1339 +++++ src/gui/platforms/win/qfontengine_win_p.h | 164 + src/gui/platforms/win/qguifunctions_wince.cpp | 408 ++ src/gui/platforms/win/qguifunctions_wince.h | 151 + src/gui/platforms/win/qkeymapper_win.cpp | 1207 ++++ src/gui/platforms/win/qmime_win.cpp | 1556 +++++ src/gui/platforms/win/qole_win.cpp | 255 + src/gui/platforms/win/qpaintdevice_win.cpp | 62 + src/gui/platforms/win/qpixmap_win.cpp | 477 ++ src/gui/platforms/win/qprintengine_win.cpp | 1776 ++++++ src/gui/platforms/win/qprintengine_win_p.h | 261 + src/gui/platforms/win/qprinterinfo_win.cpp | 122 + src/gui/platforms/win/qrawfont_win.cpp | 707 +++ src/gui/platforms/win/qregion_win.cpp | 149 + src/gui/platforms/win/qsound_win.cpp | 205 + src/gui/platforms/win/qwidget_win.cpp | 2139 +++++++ src/gui/platforms/win/qwidget_wince.cpp | 675 +++ src/gui/platforms/win/qwindowdefs_win.h | 132 + .../win/qwinnativepangesturerecognizer_win.cpp | 133 + .../win/qwinnativepangesturerecognizer_win_p.h | 80 + src/gui/platforms/x11/qapplication_x11.cpp | 6239 ++++++++++++++++++++ src/gui/platforms/x11/qclipboard_x11.cpp | 1539 +++++ src/gui/platforms/x11/qcolormap_x11.cpp | 670 +++ src/gui/platforms/x11/qcursor_x11.cpp | 637 ++ src/gui/platforms/x11/qdesktopwidget_x11.cpp | 406 ++ src/gui/platforms/x11/qdnd_x11.cpp | 2072 +++++++ src/gui/platforms/x11/qeventdispatcher_x11.cpp | 191 + src/gui/platforms/x11/qeventdispatcher_x11_p.h | 86 + src/gui/platforms/x11/qfont_x11.cpp | 368 ++ src/gui/platforms/x11/qfontdatabase_x11.cpp | 2146 +++++++ src/gui/platforms/x11/qfontengine_x11.cpp | 1215 ++++ src/gui/platforms/x11/qfontengine_x11_p.h | 180 + src/gui/platforms/x11/qkde.cpp | 176 + src/gui/platforms/x11/qkde_p.h | 81 + src/gui/platforms/x11/qkeymapper_x11.cpp | 1869 ++++++ src/gui/platforms/x11/qkeymapper_x11_p.cpp | 489 ++ src/gui/platforms/x11/qmotifdnd_x11.cpp | 1031 ++++ src/gui/platforms/x11/qpaintdevice_x11.cpp | 84 + src/gui/platforms/x11/qpaintengine_x11.cpp | 2507 ++++++++ src/gui/platforms/x11/qpaintengine_x11_p.h | 246 + src/gui/platforms/x11/qpixmap_x11.cpp | 2419 ++++++++ src/gui/platforms/x11/qpixmap_x11_p.h | 156 + src/gui/platforms/x11/qregion_x11.cpp | 92 + src/gui/platforms/x11/qsound_x11.cpp | 296 + src/gui/platforms/x11/qt_x11_p.h | 757 +++ src/gui/platforms/x11/qwidget_x11.cpp | 3146 ++++++++++ src/gui/platforms/x11/qwidgetcreate_x11.cpp | 79 + src/gui/platforms/x11/qx11embed_x11.cpp | 1808 ++++++ src/gui/platforms/x11/qx11embed_x11.h | 132 + src/gui/platforms/x11/qx11info_x11.cpp | 543 ++ src/gui/platforms/x11/qx11info_x11.h | 123 + src/gui/text/qfont_mac.cpp | 165 - src/gui/text/qfont_s60.cpp | 136 - src/gui/text/qfont_win.cpp | 166 - src/gui/text/qfont_x11.cpp | 368 -- src/gui/text/qfontdatabase_mac.cpp | 466 -- src/gui/text/qfontdatabase_s60.cpp | 1091 ---- src/gui/text/qfontdatabase_win.cpp | 1348 ----- src/gui/text/qfontdatabase_x11.cpp | 2146 ------- src/gui/text/qfontengine_coretext.mm | 880 --- src/gui/text/qfontengine_coretext_p.h | 144 - src/gui/text/qfontengine_mac.mm | 1236 ---- src/gui/text/qfontengine_mac_p.h | 165 - src/gui/text/qfontengine_s60.cpp | 569 -- src/gui/text/qfontengine_s60_p.h | 167 - src/gui/text/qfontengine_win.cpp | 1339 ----- src/gui/text/qfontengine_win_p.h | 164 - src/gui/text/qfontengine_x11.cpp | 1215 ---- src/gui/text/qfontengine_x11_p.h | 180 - src/gui/text/qrawfont_mac.cpp | 83 - src/gui/text/qrawfont_win.cpp | 707 --- src/gui/text/qtextengine_mac.cpp | 656 -- 286 files changed, 95636 insertions(+), 95636 deletions(-) delete mode 100644 src/gui/guikernel/qclipboard_mac.cpp delete mode 100644 src/gui/guikernel/qclipboard_s60.cpp delete mode 100644 src/gui/guikernel/qclipboard_win.cpp delete mode 100644 src/gui/guikernel/qclipboard_x11.cpp delete mode 100644 src/gui/guikernel/qcursor_mac.mm delete mode 100644 src/gui/guikernel/qcursor_s60.cpp delete mode 100644 src/gui/guikernel/qcursor_win.cpp delete mode 100644 src/gui/guikernel/qcursor_x11.cpp delete mode 100644 src/gui/guikernel/qdnd_mac.mm delete mode 100644 src/gui/guikernel/qdnd_s60.cpp delete mode 100644 src/gui/guikernel/qdnd_win.cpp delete mode 100644 src/gui/guikernel/qdnd_x11.cpp delete mode 100644 src/gui/guikernel/qeventdispatcher_mac.mm delete mode 100644 src/gui/guikernel/qeventdispatcher_mac_p.h delete mode 100644 src/gui/guikernel/qeventdispatcher_s60.cpp delete mode 100644 src/gui/guikernel/qeventdispatcher_s60_p.h delete mode 100644 src/gui/guikernel/qeventdispatcher_x11.cpp delete mode 100644 src/gui/guikernel/qeventdispatcher_x11_p.h delete mode 100644 src/gui/guikernel/qkeymapper_mac.cpp delete mode 100644 src/gui/guikernel/qkeymapper_s60.cpp delete mode 100644 src/gui/guikernel/qkeymapper_win.cpp delete mode 100644 src/gui/guikernel/qkeymapper_x11.cpp delete mode 100644 src/gui/guikernel/qkeymapper_x11_p.cpp delete mode 100644 src/gui/guikernel/qmime_mac.cpp delete mode 100644 src/gui/guikernel/qmime_win.cpp delete mode 100644 src/gui/guikernel/qmotifdnd_x11.cpp delete mode 100644 src/gui/guikernel/qole_win.cpp delete mode 100644 src/gui/guikernel/qwindowdefs_win.h delete mode 100644 src/gui/image/qpixmap_mac.cpp delete mode 100644 src/gui/image/qpixmap_mac_p.h delete mode 100644 src/gui/image/qpixmap_s60.cpp delete mode 100644 src/gui/image/qpixmap_s60_p.h delete mode 100644 src/gui/image/qpixmap_win.cpp delete mode 100644 src/gui/image/qpixmap_x11.cpp delete mode 100644 src/gui/image/qpixmap_x11_p.h delete mode 100644 src/gui/kernel/qapplication_mac.mm delete mode 100644 src/gui/kernel/qapplication_s60.cpp delete mode 100644 src/gui/kernel/qapplication_win.cpp delete mode 100644 src/gui/kernel/qapplication_x11.cpp delete mode 100644 src/gui/kernel/qcocoaapplication_mac.mm delete mode 100644 src/gui/kernel/qcocoaapplication_mac_p.h delete mode 100644 src/gui/kernel/qcocoaapplicationdelegate_mac.mm delete mode 100644 src/gui/kernel/qcocoaapplicationdelegate_mac_p.h delete mode 100644 src/gui/kernel/qcocoaintrospection_mac.mm delete mode 100644 src/gui/kernel/qcocoaintrospection_p.h delete mode 100644 src/gui/kernel/qcocoamenuloader_mac.mm delete mode 100644 src/gui/kernel/qcocoamenuloader_mac_p.h delete mode 100644 src/gui/kernel/qcocoapanel_mac.mm delete mode 100644 src/gui/kernel/qcocoapanel_mac_p.h delete mode 100644 src/gui/kernel/qcocoasharedwindowmethods_mac_p.h delete mode 100644 src/gui/kernel/qcocoaview_mac.mm delete mode 100644 src/gui/kernel/qcocoaview_mac_p.h delete mode 100644 src/gui/kernel/qcocoawindow_mac.mm delete mode 100644 src/gui/kernel/qcocoawindow_mac_p.h delete mode 100644 src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm delete mode 100644 src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h delete mode 100644 src/gui/kernel/qcocoawindowdelegate_mac.mm delete mode 100644 src/gui/kernel/qcocoawindowdelegate_mac_p.h delete mode 100644 src/gui/kernel/qdesktopwidget_mac.mm delete mode 100644 src/gui/kernel/qdesktopwidget_mac_p.h delete mode 100644 src/gui/kernel/qdesktopwidget_s60.cpp delete mode 100644 src/gui/kernel/qdesktopwidget_win.cpp delete mode 100644 src/gui/kernel/qdesktopwidget_x11.cpp delete mode 100644 src/gui/kernel/qguifunctions_wince.cpp delete mode 100644 src/gui/kernel/qguifunctions_wince.h delete mode 100644 src/gui/kernel/qkde.cpp delete mode 100644 src/gui/kernel/qkde_p.h delete mode 100644 src/gui/kernel/qmacdefines_mac.h delete mode 100644 src/gui/kernel/qmacgesturerecognizer_mac.mm delete mode 100644 src/gui/kernel/qmacgesturerecognizer_mac_p.h delete mode 100644 src/gui/kernel/qmultitouch_mac.mm delete mode 100644 src/gui/kernel/qmultitouch_mac_p.h delete mode 100644 src/gui/kernel/qnsframeview_mac_p.h delete mode 100644 src/gui/kernel/qnsthemeframe_mac_p.h delete mode 100644 src/gui/kernel/qnstitledframe_mac_p.h delete mode 100644 src/gui/kernel/qsoftkeymanager_s60.cpp delete mode 100644 src/gui/kernel/qsoftkeymanager_s60_p.h delete mode 100644 src/gui/kernel/qsound_mac.mm delete mode 100644 src/gui/kernel/qsound_s60.cpp delete mode 100644 src/gui/kernel/qsound_win.cpp delete mode 100644 src/gui/kernel/qsound_x11.cpp delete mode 100644 src/gui/kernel/qt_cocoa_helpers_mac.mm delete mode 100644 src/gui/kernel/qt_cocoa_helpers_mac_p.h delete mode 100644 src/gui/kernel/qt_mac.cpp delete mode 100644 src/gui/kernel/qt_mac_p.h delete mode 100644 src/gui/kernel/qt_s60_p.h delete mode 100644 src/gui/kernel/qt_x11_p.h delete mode 100644 src/gui/kernel/qwidget_mac.mm delete mode 100644 src/gui/kernel/qwidget_s60.cpp delete mode 100644 src/gui/kernel/qwidget_win.cpp delete mode 100644 src/gui/kernel/qwidget_wince.cpp delete mode 100644 src/gui/kernel/qwidget_x11.cpp delete mode 100644 src/gui/kernel/qwidgetcreate_x11.cpp delete mode 100644 src/gui/kernel/qwinnativepangesturerecognizer_win.cpp delete mode 100644 src/gui/kernel/qwinnativepangesturerecognizer_win_p.h delete mode 100644 src/gui/kernel/qx11embed_x11.cpp delete mode 100644 src/gui/kernel/qx11embed_x11.h delete mode 100644 src/gui/kernel/qx11info_x11.cpp delete mode 100644 src/gui/kernel/qx11info_x11.h delete mode 100644 src/gui/painting/qcolormap_mac.cpp delete mode 100644 src/gui/painting/qcolormap_s60.cpp delete mode 100644 src/gui/painting/qcolormap_win.cpp delete mode 100644 src/gui/painting/qcolormap_x11.cpp delete mode 100644 src/gui/painting/qpaintdevice_mac.cpp delete mode 100644 src/gui/painting/qpaintdevice_win.cpp delete mode 100644 src/gui/painting/qpaintdevice_x11.cpp delete mode 100644 src/gui/painting/qpaintengine_mac.cpp delete mode 100644 src/gui/painting/qpaintengine_mac_p.h delete mode 100644 src/gui/painting/qpaintengine_s60.cpp delete mode 100644 src/gui/painting/qpaintengine_s60_p.h delete mode 100644 src/gui/painting/qpaintengine_x11.cpp delete mode 100644 src/gui/painting/qpaintengine_x11_p.h delete mode 100644 src/gui/painting/qprintengine_mac.mm delete mode 100644 src/gui/painting/qprintengine_mac_p.h delete mode 100644 src/gui/painting/qprintengine_win.cpp delete mode 100644 src/gui/painting/qprintengine_win_p.h delete mode 100644 src/gui/painting/qprinterinfo_mac.cpp delete mode 100644 src/gui/painting/qprinterinfo_win.cpp delete mode 100644 src/gui/painting/qregion_mac.cpp delete mode 100644 src/gui/painting/qregion_s60.cpp delete mode 100644 src/gui/painting/qregion_win.cpp delete mode 100644 src/gui/painting/qregion_x11.cpp create mode 100644 src/gui/platforms/mac/qapplication_mac.mm create mode 100644 src/gui/platforms/mac/qclipboard_mac.cpp create mode 100644 src/gui/platforms/mac/qcocoaapplication_mac.mm create mode 100644 src/gui/platforms/mac/qcocoaapplication_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoaapplicationdelegate_mac.mm create mode 100644 src/gui/platforms/mac/qcocoaapplicationdelegate_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoaintrospection_mac.mm create mode 100644 src/gui/platforms/mac/qcocoaintrospection_p.h create mode 100644 src/gui/platforms/mac/qcocoamenuloader_mac.mm create mode 100644 src/gui/platforms/mac/qcocoamenuloader_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoapanel_mac.mm create mode 100644 src/gui/platforms/mac/qcocoapanel_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoasharedwindowmethods_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoaview_mac.mm create mode 100644 src/gui/platforms/mac/qcocoaview_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoawindow_mac.mm create mode 100644 src/gui/platforms/mac/qcocoawindow_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac.mm create mode 100644 src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h create mode 100644 src/gui/platforms/mac/qcocoawindowdelegate_mac.mm create mode 100644 src/gui/platforms/mac/qcocoawindowdelegate_mac_p.h create mode 100644 src/gui/platforms/mac/qcolormap_mac.cpp create mode 100644 src/gui/platforms/mac/qcursor_mac.mm create mode 100644 src/gui/platforms/mac/qdesktopwidget_mac.mm create mode 100644 src/gui/platforms/mac/qdesktopwidget_mac_p.h create mode 100644 src/gui/platforms/mac/qdnd_mac.mm create mode 100644 src/gui/platforms/mac/qeventdispatcher_mac.mm create mode 100644 src/gui/platforms/mac/qeventdispatcher_mac_p.h create mode 100644 src/gui/platforms/mac/qfont_mac.cpp create mode 100644 src/gui/platforms/mac/qfontdatabase_mac.cpp create mode 100644 src/gui/platforms/mac/qfontengine_coretext.mm create mode 100644 src/gui/platforms/mac/qfontengine_coretext_p.h create mode 100644 src/gui/platforms/mac/qfontengine_mac.mm create mode 100644 src/gui/platforms/mac/qfontengine_mac_p.h create mode 100644 src/gui/platforms/mac/qkeymapper_mac.cpp create mode 100644 src/gui/platforms/mac/qmacdefines_mac.h create mode 100644 src/gui/platforms/mac/qmacgesturerecognizer_mac.mm create mode 100644 src/gui/platforms/mac/qmacgesturerecognizer_mac_p.h create mode 100644 src/gui/platforms/mac/qmime_mac.cpp create mode 100644 src/gui/platforms/mac/qmultitouch_mac.mm create mode 100644 src/gui/platforms/mac/qmultitouch_mac_p.h create mode 100644 src/gui/platforms/mac/qnsframeview_mac_p.h create mode 100644 src/gui/platforms/mac/qnsthemeframe_mac_p.h create mode 100644 src/gui/platforms/mac/qnstitledframe_mac_p.h create mode 100644 src/gui/platforms/mac/qpaintdevice_mac.cpp create mode 100644 src/gui/platforms/mac/qpaintengine_mac.cpp create mode 100644 src/gui/platforms/mac/qpaintengine_mac_p.h create mode 100644 src/gui/platforms/mac/qpixmap_mac.cpp create mode 100644 src/gui/platforms/mac/qpixmap_mac_p.h create mode 100644 src/gui/platforms/mac/qprintengine_mac.mm create mode 100644 src/gui/platforms/mac/qprintengine_mac_p.h create mode 100644 src/gui/platforms/mac/qprinterinfo_mac.cpp create mode 100644 src/gui/platforms/mac/qrawfont_mac.cpp create mode 100644 src/gui/platforms/mac/qregion_mac.cpp create mode 100644 src/gui/platforms/mac/qsound_mac.mm create mode 100644 src/gui/platforms/mac/qt_cocoa_helpers_mac.mm create mode 100644 src/gui/platforms/mac/qt_cocoa_helpers_mac_p.h create mode 100644 src/gui/platforms/mac/qt_mac.cpp create mode 100644 src/gui/platforms/mac/qt_mac_p.h create mode 100644 src/gui/platforms/mac/qtextengine_mac.cpp create mode 100644 src/gui/platforms/mac/qwidget_mac.mm create mode 100644 src/gui/platforms/s60/qapplication_s60.cpp create mode 100644 src/gui/platforms/s60/qclipboard_s60.cpp create mode 100644 src/gui/platforms/s60/qcolormap_s60.cpp create mode 100644 src/gui/platforms/s60/qcursor_s60.cpp create mode 100644 src/gui/platforms/s60/qdesktopwidget_s60.cpp create mode 100644 src/gui/platforms/s60/qdnd_s60.cpp create mode 100644 src/gui/platforms/s60/qeventdispatcher_s60.cpp create mode 100644 src/gui/platforms/s60/qeventdispatcher_s60_p.h create mode 100644 src/gui/platforms/s60/qfont_s60.cpp create mode 100644 src/gui/platforms/s60/qfontdatabase_s60.cpp create mode 100644 src/gui/platforms/s60/qfontengine_s60.cpp create mode 100644 src/gui/platforms/s60/qfontengine_s60_p.h create mode 100644 src/gui/platforms/s60/qkeymapper_s60.cpp create mode 100644 src/gui/platforms/s60/qpaintengine_s60.cpp create mode 100644 src/gui/platforms/s60/qpaintengine_s60_p.h create mode 100644 src/gui/platforms/s60/qpixmap_s60.cpp create mode 100644 src/gui/platforms/s60/qpixmap_s60_p.h create mode 100644 src/gui/platforms/s60/qregion_s60.cpp create mode 100644 src/gui/platforms/s60/qsoftkeymanager_s60.cpp create mode 100644 src/gui/platforms/s60/qsoftkeymanager_s60_p.h create mode 100644 src/gui/platforms/s60/qsound_s60.cpp create mode 100644 src/gui/platforms/s60/qt_s60_p.h create mode 100644 src/gui/platforms/s60/qwidget_s60.cpp create mode 100644 src/gui/platforms/win/qapplication_win.cpp create mode 100644 src/gui/platforms/win/qclipboard_win.cpp create mode 100644 src/gui/platforms/win/qcolormap_win.cpp create mode 100644 src/gui/platforms/win/qcursor_win.cpp create mode 100644 src/gui/platforms/win/qdesktopwidget_win.cpp create mode 100644 src/gui/platforms/win/qdnd_win.cpp create mode 100644 src/gui/platforms/win/qfont_win.cpp create mode 100644 src/gui/platforms/win/qfontdatabase_win.cpp create mode 100644 src/gui/platforms/win/qfontengine_win.cpp create mode 100644 src/gui/platforms/win/qfontengine_win_p.h create mode 100644 src/gui/platforms/win/qguifunctions_wince.cpp create mode 100644 src/gui/platforms/win/qguifunctions_wince.h create mode 100644 src/gui/platforms/win/qkeymapper_win.cpp create mode 100644 src/gui/platforms/win/qmime_win.cpp create mode 100644 src/gui/platforms/win/qole_win.cpp create mode 100644 src/gui/platforms/win/qpaintdevice_win.cpp create mode 100644 src/gui/platforms/win/qpixmap_win.cpp create mode 100644 src/gui/platforms/win/qprintengine_win.cpp create mode 100644 src/gui/platforms/win/qprintengine_win_p.h create mode 100644 src/gui/platforms/win/qprinterinfo_win.cpp create mode 100644 src/gui/platforms/win/qrawfont_win.cpp create mode 100644 src/gui/platforms/win/qregion_win.cpp create mode 100644 src/gui/platforms/win/qsound_win.cpp create mode 100644 src/gui/platforms/win/qwidget_win.cpp create mode 100644 src/gui/platforms/win/qwidget_wince.cpp create mode 100644 src/gui/platforms/win/qwindowdefs_win.h create mode 100644 src/gui/platforms/win/qwinnativepangesturerecognizer_win.cpp create mode 100644 src/gui/platforms/win/qwinnativepangesturerecognizer_win_p.h create mode 100644 src/gui/platforms/x11/qapplication_x11.cpp create mode 100644 src/gui/platforms/x11/qclipboard_x11.cpp create mode 100644 src/gui/platforms/x11/qcolormap_x11.cpp create mode 100644 src/gui/platforms/x11/qcursor_x11.cpp create mode 100644 src/gui/platforms/x11/qdesktopwidget_x11.cpp create mode 100644 src/gui/platforms/x11/qdnd_x11.cpp create mode 100644 src/gui/platforms/x11/qeventdispatcher_x11.cpp create mode 100644 src/gui/platforms/x11/qeventdispatcher_x11_p.h create mode 100644 src/gui/platforms/x11/qfont_x11.cpp create mode 100644 src/gui/platforms/x11/qfontdatabase_x11.cpp create mode 100644 src/gui/platforms/x11/qfontengine_x11.cpp create mode 100644 src/gui/platforms/x11/qfontengine_x11_p.h create mode 100644 src/gui/platforms/x11/qkde.cpp create mode 100644 src/gui/platforms/x11/qkde_p.h create mode 100644 src/gui/platforms/x11/qkeymapper_x11.cpp create mode 100644 src/gui/platforms/x11/qkeymapper_x11_p.cpp create mode 100644 src/gui/platforms/x11/qmotifdnd_x11.cpp create mode 100644 src/gui/platforms/x11/qpaintdevice_x11.cpp create mode 100644 src/gui/platforms/x11/qpaintengine_x11.cpp create mode 100644 src/gui/platforms/x11/qpaintengine_x11_p.h create mode 100644 src/gui/platforms/x11/qpixmap_x11.cpp create mode 100644 src/gui/platforms/x11/qpixmap_x11_p.h create mode 100644 src/gui/platforms/x11/qregion_x11.cpp create mode 100644 src/gui/platforms/x11/qsound_x11.cpp create mode 100644 src/gui/platforms/x11/qt_x11_p.h create mode 100644 src/gui/platforms/x11/qwidget_x11.cpp create mode 100644 src/gui/platforms/x11/qwidgetcreate_x11.cpp create mode 100644 src/gui/platforms/x11/qx11embed_x11.cpp create mode 100644 src/gui/platforms/x11/qx11embed_x11.h create mode 100644 src/gui/platforms/x11/qx11info_x11.cpp create mode 100644 src/gui/platforms/x11/qx11info_x11.h delete mode 100644 src/gui/text/qfont_mac.cpp delete mode 100644 src/gui/text/qfont_s60.cpp delete mode 100644 src/gui/text/qfont_win.cpp delete mode 100644 src/gui/text/qfont_x11.cpp delete mode 100644 src/gui/text/qfontdatabase_mac.cpp delete mode 100644 src/gui/text/qfontdatabase_s60.cpp delete mode 100644 src/gui/text/qfontdatabase_win.cpp delete mode 100644 src/gui/text/qfontdatabase_x11.cpp delete mode 100644 src/gui/text/qfontengine_coretext.mm delete mode 100644 src/gui/text/qfontengine_coretext_p.h delete mode 100644 src/gui/text/qfontengine_mac.mm delete mode 100644 src/gui/text/qfontengine_mac_p.h delete mode 100644 src/gui/text/qfontengine_s60.cpp delete mode 100644 src/gui/text/qfontengine_s60_p.h delete mode 100644 src/gui/text/qfontengine_win.cpp delete mode 100644 src/gui/text/qfontengine_win_p.h delete mode 100644 src/gui/text/qfontengine_x11.cpp delete mode 100644 src/gui/text/qfontengine_x11_p.h delete mode 100644 src/gui/text/qrawfont_mac.cpp delete mode 100644 src/gui/text/qrawfont_win.cpp delete mode 100644 src/gui/text/qtextengine_mac.cpp diff --git a/src/gui/guikernel/qclipboard_mac.cpp b/src/gui/guikernel/qclipboard_mac.cpp deleted file mode 100644 index 4a8bc56e41..0000000000 --- a/src/gui/guikernel/qclipboard_mac.cpp +++ /dev/null @@ -1,634 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qclipboard.h" -#include "qapplication.h" -#include "qbitmap.h" -#include "qdatetime.h" -#include "qdebug.h" -#include "qapplication_p.h" -#include -#include "qevent.h" -#include "qurl.h" -#include -#include -#include "qt_cocoa_helpers_mac_p.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 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 md = promise.convertor->convertFromMime(promise.mime, promise.data, flavorAsQString); - if (md.size() <= promise.offset) - return cantGetFlavorErr; - const QByteArray &ba = md[promise.offset]; - QCFType 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 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 availableConverters = QMacPasteboardMime::all(mime_type); - if (mime != 0) { - clear_helper(); - QStringList formats = mime_src->formats(); - -#ifdef QT_MAC_USE_COCOA - // QMimeData sub classes reimplementing the formats() might not expose the - // temporary "application/x-qt-mime-type-name" mimetype. So check the existence - // of this mime type while doing drag and drop. - QString dummyMimeType(QLatin1String("application/x-qt-mime-type-name")); - if (!formats.contains(dummyMimeType)) { - QByteArray dummyType = mime_src->data(dummyMimeType); - if (!dummyType.isEmpty()) { - formats.append(dummyMimeType); - } - } -#endif - for(int f = 0; f < formats.size(); ++f) { - QString mimeType = formats.at(f); - for (QList::Iterator it = availableConverters.begin(); it != availableConverters.end(); ++it) { - QMacPasteboardMime *c = (*it); - QString flavor(c->flavorFor(mimeType)); - if(!flavor.isEmpty()) { - QVariant mimeData = static_cast(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 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 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 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. - bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") - || c_flavor == QLatin1String("public.utf8-plain-text")); - if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) { - // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped - // correctly (as '\n') in this data. The 'public.utf16-plain-text' type - // usually maps newlines to '\r' instead. - QString str = qt_mac_get_pasteboardString(paste); - if (!str.isEmpty()) - return str; - } - if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text"))) - c_flavor = QLatin1String("public.utf16-plain-text"); - - QVariant ret; - QList retList; - for(uint index = 1; index <= cnt; ++index) { - PasteboardItemID id; - if(PasteboardGetItemIdentifier(paste, index, &id) != noErr) - continue; - - QCFType 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(CFArrayGetValueAtIndex(types, i)); - if(c_flavor == QCFString::toQString(flavor)) { - QCFType 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(this)->setMimeData(0); - -#ifdef DEBUG_PASTEBOARD - if(fromGlobal) - qDebug("Pasteboard: Synchronize!"); -#endif - return fromGlobal; -} - - - - -QT_END_NAMESPACE diff --git a/src/gui/guikernel/qclipboard_s60.cpp b/src/gui/guikernel/qclipboard_s60.cpp deleted file mode 100644 index 0dafae0996..0000000000 --- a/src/gui/guikernel/qclipboard_s60.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 "private/qcore_symbian_p.h" -#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS -#include "txtclipboard.h" -#endif -#include "txtetext.h" -#include - -// Symbian's clipboard -#include -QT_BEGIN_NAMESPACE - -const TUid KQtCbDataStream = {0x2001B2DD}; -const TInt KPlainTextBegin = 0; - -class QClipboardData -{ -public: - QClipboardData(); - ~QClipboardData(); - - void setSource(QMimeData* s) - { - if (s == src) - return; - delete src; - src = s; - } - QMimeData* source() - { return src; } - bool connected() - { return connection; } - void clear(); - -private: - QMimeData* src; - bool connection; -}; - -QClipboardData::QClipboardData():src(0),connection(true) -{ - clear(); -} - -QClipboardData::~QClipboardData() -{ - connection = false; - delete src; -} - -void QClipboardData::clear() -{ - QMimeData* newSrc = new QMimeData; - delete src; - src = newSrc; -} - -static QClipboardData *internalCbData = 0; - -static void cleanupClipboardData() -{ - delete internalCbData; - internalCbData = 0; -} - -static QClipboardData *clipboardData() -{ - if (internalCbData == 0) { - internalCbData = new QClipboardData; - if (internalCbData) - { - if (!internalCbData->connected()) - { - delete internalCbData; - internalCbData = 0; - } - else - { - qAddPostRoutine(cleanupClipboardData); - } - } - } - return internalCbData; -} - -void writeToStreamLX(const QMimeData* aData, RWriteStream& aStream) -{ - // This function both leaves and throws exceptions. There must be no destructor - // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. - QStringList headers = aData->formats(); - aStream << TCardinality(headers.count()); - for (QStringList::const_iterator iter= headers.constBegin();iter != headers.constEnd();iter++) - { - HBufC* stringData = TPtrC(reinterpret_cast((*iter).utf16())).AllocLC(); - QByteArray ba = aData->data((*iter)); - // mime type - aStream << TCardinality(stringData->Size()); - aStream << *(stringData); - // mime data - aStream << TCardinality(ba.size()); - aStream.WriteL(reinterpret_cast(ba.constData()),ba.size()); - CleanupStack::PopAndDestroy(stringData); - } -} - -void writeToSymbianStoreLX(const QMimeData* aData, CClipboard* clipboard) -{ - // This function both leaves and throws exceptions. There must be no destructor - // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. - if (aData->hasText()) { - CPlainText* text = CPlainText::NewL(); - CleanupStack::PushL(text); - - TPtrC textPtr(qt_QString2TPtrC(aData->text())); - text->InsertL(KPlainTextBegin, textPtr); - text->CopyToStoreL(clipboard->Store(), clipboard->StreamDictionary(), - KPlainTextBegin, textPtr.Length()); - CleanupStack::PopAndDestroy(text); - } -} - -void readSymbianStoreLX(QMimeData* aData, CClipboard* clipboard) -{ - // This function both leaves and throws exceptions. There must be no destructor - // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. - CPlainText* text = CPlainText::NewL(); - CleanupStack::PushL(text); - TInt dataLength = text->PasteFromStoreL(clipboard->Store(), clipboard->StreamDictionary(), - KPlainTextBegin); - if (dataLength == 0) { - User::Leave(KErrNotFound); - } - HBufC* hBuf = HBufC::NewL(dataLength); - TPtr buf = hBuf->Des(); - text->Extract(buf, KPlainTextBegin, dataLength); - - QString string = qt_TDesC2QString(buf); - CleanupStack::PopAndDestroy(text); - - aData->setText(string); -} - -void readFromStreamLX(QMimeData* aData,RReadStream& aStream) -{ - // This function both leaves and throws exceptions. There must be no destructor - // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. - TCardinality mimeTypeCount; - aStream >> mimeTypeCount; - for (int i = 0; i< mimeTypeCount;i++) - { - // mime type - TCardinality mimeTypeSize; - aStream >> mimeTypeSize; - HBufC* mimeTypeBuf = HBufC::NewLC(aStream,mimeTypeSize); - QString mimeType = QString(reinterpret_cast(mimeTypeBuf->Des().Ptr()), - mimeTypeBuf->Length()); - CleanupStack::PopAndDestroy(mimeTypeBuf); - // mime data - TCardinality dataSize; - aStream >> dataSize; - QByteArray ba; - ba.reserve(dataSize); - aStream.ReadL(reinterpret_cast(ba.data_ptr()->data),dataSize); - ba.data_ptr()->size = dataSize; - aData->setData(mimeType,ba); - } -} - - -/***************************************************************************** - QClipboard member functions - *****************************************************************************/ - -void QClipboard::clear(Mode mode) -{ - setText(QString(), mode); -} -const QMimeData* QClipboard::mimeData(Mode mode) const -{ - if (mode != Clipboard) return 0; - QClipboardData *d = clipboardData(); - bool dataExists(false); - if (d) - { - TRAPD(err,{ - RFs fs = qt_s60GetRFs(); - CClipboard* cb = CClipboard::NewForReadingLC(fs); - Q_ASSERT(cb); - //stream for qt - RStoreReadStream stream; - TStreamId stid = (cb->StreamDictionary()).At(KQtCbDataStream); - if (stid != 0) { - stream.OpenLC(cb->Store(),stid); - QT_TRYCATCH_LEAVING(readFromStreamLX(d->source(),stream)); - CleanupStack::PopAndDestroy(&stream); - dataExists = true; - } - else { - //symbian clipboard - RStoreReadStream symbianStream; - TStreamId symbianStId = (cb->StreamDictionary()).At(KClipboardUidTypePlainText); - if (symbianStId != 0) { - symbianStream.OpenLC(cb->Store(), symbianStId); - QT_TRYCATCH_LEAVING(readSymbianStoreLX(d->source(), cb)); - CleanupStack::PopAndDestroy(&symbianStream); - dataExists = true; - } - } - CleanupStack::PopAndDestroy(cb); - }); - if (err != KErrNone){ - qDebug()<< "clipboard is empty/err: " << err; - } - - if (dataExists) { - return d->source(); - } - } - return 0; -} - - -void QClipboard::setMimeData(QMimeData* src, Mode mode) -{ - if (mode != Clipboard) return; - QClipboardData *d = clipboardData(); - if (d) - { - TRAPD(err,{ - RFs fs = qt_s60GetRFs(); - CClipboard* cb = CClipboard::NewForWritingLC(fs); - //stream for qt - RStoreWriteStream stream; - TStreamId stid = stream.CreateLC(cb->Store()); - QT_TRYCATCH_LEAVING(writeToStreamLX(src,stream)); - d->setSource(src); - stream.CommitL(); - (cb->StreamDictionary()).AssignL(KQtCbDataStream,stid); - cb->CommitL(); - - //stream for symbian - RStoreWriteStream symbianStream; - TStreamId symbianStId = symbianStream.CreateLC(cb->Store()); - QT_TRYCATCH_LEAVING(writeToSymbianStoreLX(src, cb)); - (cb->StreamDictionary()).AssignL(KClipboardUidTypePlainText, symbianStId); - cb->CommitL(); - CleanupStack::PopAndDestroy(3,cb); - }); - if (err != KErrNone){ - qDebug()<< "clipboard write err :" << err; - } - } - 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; -} - -bool QClipboard::event(QEvent * /* e */) -{ - return true; -} - -void QClipboard::connectNotify( const char * ) -{ -} - -void QClipboard::ownerDestroyed() -{ -} -QT_END_NAMESPACE -#endif // QT_NO_CLIPBOARD diff --git a/src/gui/guikernel/qclipboard_win.cpp b/src/gui/guikernel/qclipboard_win.cpp deleted file mode 100644 index ea41165b9c..0000000000 --- a/src/gui/guikernel/qclipboard_win.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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" -#include -#include - -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 - -typedef BOOL (WINAPI *PtrIsHungAppWindow)(HWND); - -static PtrIsHungAppWindow ptrIsHungAppWindow = 0; - -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")); - // We don't need this internal widget to appear in QApplication::topLevelWidgets() - if (QWidgetPrivate::allWidgets) - QWidgetPrivate::allWidgets->remove(clipBoardViewer); - } - - ~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::fromWCharArray((wchar_t *)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(obj); - - const QMimeData* data = qobj->mimeData(); - if (data->hasText()) { - EmptyClipboard(); - result = SetClipboardData(CF_UNICODETEXT, wcsdup(reinterpret_cast (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) { - if (ptrIsHungAppWindow == 0) { - QSystemLibrary library(QLatin1String("User32")); - ptrIsHungAppWindow = (PtrIsHungAppWindow)library.resolve("IsHungAppWindow"); - } - if (ptrIsHungAppWindow && ptrIsHungAppWindow(d->nextClipboardViewer)) { - qWarning("%s: Cowardly refusing to send clipboard message to hung application...", Q_FUNC_INFO); - } else { - SendMessage(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/guikernel/qclipboard_x11.cpp b/src/gui/guikernel/qclipboard_x11.cpp deleted file mode 100644 index d566c86e04..0000000000 --- a/src/gui/guikernel/qclipboard_x11.cpp +++ /dev/null @@ -1,1539 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// #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 "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 "qelapsedtimer.h" -#include "qvariant.h" -#include "qdnd_p.h" -#include - -#ifndef QT_NO_XFIXES -#include -#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")); - // We don't need this internal widgets to appear in QApplication::topLevelWidgets() - if (QWidgetPrivate::allWidgets) { - QWidgetPrivate::allWidgets->remove(owner); - QWidgetPrivate::allWidgets->remove(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 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(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(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(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(); - -#ifndef QT_NO_XFIXES - if (X11->use_xfixes && X11->ptrXFixesSelectSelectionInput) { - const unsigned long eventMask = - XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask; - for (int i = 0; i < X11->screenCount; ++i) { - X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), - XA_PRIMARY, eventMask); - X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), - ATOM(CLIPBOARD), eventMask); - } - } -#endif // QT_NO_XFIXES - - 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(); - // We need this value just for completeness, we don't use it. - long 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(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) -{ - QElapsedTimer started; - started.start(); - QElapsedTimer 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.start(); - - 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.start(); - - 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) -{ - 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; - buffer->resize(newSize); - - bool ok = (buffer->size() == newSize); - VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size()); - - if (ok && newSize) { - // 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 = buffer_offset; - textprop.value = (unsigned char *) buffer->data(); - - char **list_ret = 0; - int count; - if (XmbTextPropertyToTextList(display, &textprop, &list_ret, - &count) == Success && count && list_ret) { - offset = buffer_offset = strlen(list_ret[0]); - buffer->resize(offset); - memcpy(buffer->data(), list_ret[0], offset); - } - if (list_ret) XFreeStringList(list_ret); - } - } - - // 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)) { - 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")); - // We don't need this internal widget to appear in QApplication::topLevelWidgets() - if (QWidgetPrivate::allWidgets) - QWidgetPrivate::allWidgets->remove(requestor); - - return QByteArray(); -} - -static Atom send_targets_selection(QClipboardData *d, Window window, Atom property) -{ - QVector types; - QStringList formats = QInternalMimeData::formatsHelper(d->source()); - for (int i = 0; i < formats.size(); ++i) { - QList 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)); - types.append(ATOM(SAVE_TARGETS)); - - 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()) { // 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 property; - } - - // 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) { - // That means application exits and we need to give clipboard - // content to the clipboard manager. - // First we check if there is a clipboard manager. - if (XGetSelectionOwner(X11->display, ATOM(CLIPBOARD_MANAGER)) == XNone - || !owner) - return true; - - Window ownerId = owner->internalWinId(); - Q_ASSERT(ownerId); - // we delete the property so the manager saves all TARGETS. - XDeleteProperty(X11->display, ownerId, ATOM(_QT_SELECTION)); - XConvertSelection(X11->display, ATOM(CLIPBOARD_MANAGER), ATOM(SAVE_TARGETS), - ATOM(_QT_SELECTION), ownerId, X11->time); - XSync(dpy, false); - - XEvent event; - // waiting until the clipboard manager fetches the content. - if (!X11->clipboardWaitForEvent(ownerId, SelectionNotify, &event, 10000)) { - qWarning("QClipboard: Unable to receive an event from the " - "clipboard manager in a reasonable time"); - } - - 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) - || 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 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)) { - 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/guikernel/qcursor_mac.mm b/src/gui/guikernel/qcursor_mac.mm deleted file mode 100644 index 0afa3ee4f0..0000000000 --- a/src/gui/guikernel/qcursor_mac.mm +++ /dev/null @@ -1,689 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 -extern QPointer qt_button_down; //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(); - */ - } - } -}; - -inline void *qt_mac_nsCursorForQCursor(const QCursor &c) -{ - c.d->update(); - return [[static_cast(c.d->curs.cp.nscursor) retain] autorelease]; -} - -static QCursorData *currentCursor = 0; //current cursor - -void qt_mac_set_cursor(const QCursor *c) -{ -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - [static_cast(qt_mac_nsCursorForQCursor(*c)) set]; -#else - if (!c) { - currentCursor = 0; - return; - } - c->handle(); //force the cursor to get loaded, if it's not - - if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor - && currentCursor->curs.tc.anim) - currentCursor->curs.tc.anim->stop(); - if(c->d->type == QCursorData::TYPE_ImageCursor) { - [static_cast(c->d->curs.cp.nscursor) set]; - } else if(c->d->type == QCursorData::TYPE_ThemeCursor) { - 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); - } - } - - currentCursor = c->d; -#endif -} - -static QPointer lastWidgetUnderMouse = 0; -static QPointer lastMouseCursorWidget = 0; -static bool qt_button_down_on_prev_call = false; -static QCursor *grabCursor = 0; - -void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) -{ - QCursor cursor(Qt::ArrowCursor); - if (qt_button_down) { - // The widget that is currently pressed - // grabs the mouse cursor: - widgetUnderMouse = qt_button_down; - qt_button_down_on_prev_call = true; - } else if (qt_button_down_on_prev_call) { - // Grab has been released, so do - // a full check: - qt_button_down_on_prev_call = false; - lastWidgetUnderMouse = 0; - lastMouseCursorWidget = 0; - } - - if (QApplication::overrideCursor()) { - cursor = *QApplication::overrideCursor(); - } else if (grabCursor) { - cursor = *grabCursor; - } else if (widgetUnderMouse) { - if (widgetUnderMouse == lastWidgetUnderMouse) { - // Optimization that should hit when the widget under - // the mouse does not change as the mouse moves: - if (lastMouseCursorWidget) - cursor = lastMouseCursorWidget->cursor(); - } else { - QWidget *w = widgetUnderMouse; - for (; w; w = w->parentWidget()) { - if (w->testAttribute(Qt::WA_SetCursor)) { - cursor = w->cursor(); - break; - } - if (w->isWindow()) - break; - } - // One final check in case we ran out of parents in the loop: - if (w && !w->testAttribute(Qt::WA_SetCursor)) - w = 0; - - lastWidgetUnderMouse = widgetUnderMouse; - lastMouseCursorWidget = w; - } - } - -#ifdef QT_MAC_USE_COCOA - cursor.d->update(); - NSCursor *nsCursor = static_cast(cursor.d->curs.cp.nscursor); - if ([NSCursor currentCursor] != nsCursor) { - QMacCocoaAutoReleasePool pool; - [nsCursor set]; - } -#else - qt_mac_set_cursor(&cursor); -#endif -} - -void qt_mac_update_cursor() -{ - // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse - // except that is clears the optimization cache, and finds the widget - // under mouse itself. Clearing the cache is useful in cases where the - // application has been deactivated/activated etc. - // NB: since we dont have any true native widget, the call to - // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. -#ifdef QT_MAC_USE_COCOA - lastWidgetUnderMouse = 0; - lastMouseCursorWidget = 0; - QWidget *widgetUnderMouse = 0; - - if (qt_button_down) { - widgetUnderMouse = qt_button_down; - } else { - QPoint localPoint; - QPoint globalPoint; - qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); - } - qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); -#else - qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); -#endif -} - -void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) -{ - if (grabCursor) { - delete grabCursor; - grabCursor = 0; - } - if (set) { - if (cursor) - grabCursor = new QCursor(*cursor); - else if (lastMouseCursorWidget) - grabCursor = new QCursor(lastMouseCursorWidget->cursor()); - else - grabCursor = new QCursor(Qt::ArrowCursor); - } - qt_mac_update_cursor(); -} - -#ifndef QT_MAC_USE_COCOA -void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) -{ - qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); -} -#endif - -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(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) -{ -#ifdef QT_MAC_USE_COCOA - CGPoint pos; - pos.x = x; - pos.y = y; - - CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); - CGEventPost(kCGHIDEventTap, e); - CFRelease(e); -#else - 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); - } -#endif -} - -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(bmi.scanLine(row)); - QRgb *bmmData = reinterpret_cast(bmmi.scanLine(row)); - QRgb *finalData = reinterpret_cast(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(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(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; - case Qt::DragCopyCursor: - type = QCursorData::TYPE_ThemeCursor; - if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) - curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; - break; - case Qt::DragMoveCursor: - type = QCursorData::TYPE_ThemeCursor; - curs.cp.nscursor = [NSCursor arrowCursor]; - break; - case Qt::DragLinkCursor: - type = QCursorData::TYPE_ThemeCursor; - if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) - curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; - 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; - case Qt::DragMoveCursor: - type = QCursorData::TYPE_ThemeCursor; - curs.tc.curs = kThemeArrowCursor; - break; - case Qt::DragCopyCursor: - type = QCursorData::TYPE_ThemeCursor; - curs.tc.curs = kThemeCopyArrowCursor; - break; - case Qt::DragLinkCursor: - type = QCursorData::TYPE_ThemeCursor; - curs.tc.curs = kThemeAliasArrowCursor; - 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/guikernel/qcursor_s60.cpp b/src/gui/guikernel/qcursor_s60.cpp deleted file mode 100644 index 8dfe87ef81..0000000000 --- a/src/gui/guikernel/qcursor_s60.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_CURSOR -static QCursor cursorSprite; -static int cursorSpriteVisible; -#endif - -//pos and setpos are required whether cursors are configured or not. -QPoint QCursor::pos() -{ - return S60->lastCursorPos; -} - -void QCursor::setPos(int x, int y) -{ - //clip to screen size (window server allows a sprite hotspot to be outside the screen) - if (x < 0) - x=0; - else if (x >= S60->screenWidthInPixels) - x = S60->screenWidthInPixels - 1; - if (y < 0) - y = 0; - else if (y >= S60->screenHeightInPixels) - y = S60->screenHeightInPixels - 1; - -#ifndef QT_NO_CURSOR -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors && cursorSpriteVisible) - cursorSprite.d->scurs.SetPosition(TPoint(x,y)); - else -#endif - S60->wsSession().SetPointerCursorPosition(TPoint(x, y)); -#endif - S60->lastCursorPos = QPoint(x, y); - //send a fake mouse move event, so that enter/leave events go to the widget hierarchy - QWidget *w = QApplication::topLevelAt(S60->lastCursorPos); - if (w) { - CCoeControl* ctrl = w->effectiveWinId(); - TPoint epos(x, y); - TPoint cpos = epos - ctrl->PositionRelativeToScreen(); - TPointerEvent fakeEvent; - fakeEvent.iType = TPointerEvent::EMove; - fakeEvent.iModifiers = 0U; - fakeEvent.iPosition = cpos; - fakeEvent.iParentPosition = epos; - ctrl->HandlePointerEventL(fakeEvent); - } -} - -#ifndef QT_NO_CURSOR -/* - * Request cursor to be turned on or off. - * Reference counted, so 2 on + 1 off = on, for example - */ -void qt_symbian_set_cursor_visible(bool visible) { - if (visible) - cursorSpriteVisible++; - else - cursorSpriteVisible--; - Q_ASSERT(cursorSpriteVisible >=0); - - if (cursorSpriteVisible && !S60->mouseInteractionEnabled) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_show_pointer_sprite(); - else -#endif - S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); - } else if (!cursorSpriteVisible && S60->mouseInteractionEnabled) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_hide_pointer_sprite(); - else -#endif - S60->wsSession().SetPointerCursorMode(EPointerCursorNone); - } - S60->mouseInteractionEnabled = ((cursorSpriteVisible > 0) ? true : false); -} - -/* - * Check if the cursor is on or off - */ -bool qt_symbian_is_cursor_visible() { - return S60->mouseInteractionEnabled; -} - -QCursorData::QCursorData(Qt::CursorShape s) : - cshape(s), bm(0), bmm(0), hx(0), hy(0), pcurs() -{ - ref = 1; -} - -QCursorData::~QCursorData() -{ - for(int i=0;iiBitmap; - delete nativeSpriteMembers[i]->iMaskBitmap; - } - nativeSpriteMembers.ResetAndDestroy(); - pcurs.Close(); - delete bm; - delete bmm; -} - -/* Create a bitmap cursor, this is called by public constructors in the - * generic QCursor code. - */ -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->hx = hotX >= 0 ? hotX : bitmap.width() / 2; - d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; - return d; -} - -/* - * returns an opaque native handle to a cursor. - * It happens to be the address of the native handle, as window server handles - * are not POD types. Note there is no QCursor(HANDLE) constructor on Symbian, - * Mac or QWS. - */ -Qt::HANDLE QCursor::handle() const -{ - if (d->pcurs.WsHandle()) - return reinterpret_cast (&(d->pcurs)); - -#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS - // don't construct shape cursors, QApplication_s60 will use the system cursor instead - if (!(d->bm)) - return 0; -#endif - - d->pcurs = RWsPointerCursor(S60->wsSession()); - d->pcurs.Construct(0); - d->constructCursorSprite(d->pcurs); - d->pcurs.Activate(); - - return reinterpret_cast (&(d->pcurs)); -} - -#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS -/* - * Loads a single cursor shape from resources and appends it to a native sprite. - * Animated cursors (e.g. the busy cursor) have multiple members. - */ -void QCursorData::loadShapeFromResource(RWsSpriteBase& target, QString resource, int hx, int hy, int interval) -{ - QPixmap pix; - CFbsBitmap* native; - QScopedPointer member(new TSpriteMember); - member->iInterval = interval; - member->iInvertMask = false; - member->iMaskBitmap = 0; // all shapes are RGBA - member->iDrawMode = CGraphicsContext::EDrawModePEN; - member->iOffset = TPoint(-hx, -hy); - QString res(QLatin1String(":/trolltech/symbian/cursors/images/%1.png")); - pix.load(res.arg(resource)); - native = pix.toSymbianCFbsBitmap(); - member->iBitmap = native; - qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); - target.AppendMember(*(member.take())); -} - -//TODO: after 4.6, connect with style & skins? -/* - * Constructs the native cursor from resources compiled into QtGui - * This is needed only when the platform doesn't have system cursors. - * - * System cursors are higher performance, since they are constructed once - * and shared by all applications by specifying the shape number. - * Due to symbian platform security considerations, and the fact most - * existing phones have a broken RWsPointerCursor, system cursors are not - * being used. - */ -void QCursorData::constructShapeSprite(RWsSpriteBase& target) -{ - int i; - switch (cshape) { - default: - qWarning("QCursorData::constructShapeSprite unknown shape %d", cshape); - //fall through and give arrow cursor - case Qt::ArrowCursor: - loadShapeFromResource(target, QLatin1String("pointer"), 1, 1); - break; - case Qt::UpArrowCursor: - loadShapeFromResource(target, QLatin1String("uparrow"), 4, 0); - break; - case Qt::CrossCursor: - loadShapeFromResource(target, QLatin1String("cross"), 7, 7); - break; - case Qt::WaitCursor: - for (i = 1; i <= 12; i++) { - loadShapeFromResource(target, QString(QLatin1String("wait%1")).arg(i), 7, 7, 1000000); - } - break; - case Qt::IBeamCursor: - loadShapeFromResource(target, QLatin1String("ibeam"), 3, 10); - break; - case Qt::SizeVerCursor: - loadShapeFromResource(target, QLatin1String("sizever"), 4, 8); - break; - case Qt::SizeHorCursor: - loadShapeFromResource(target, QLatin1String("sizehor"), 8, 4); - break; - case Qt::SizeBDiagCursor: - loadShapeFromResource(target, QLatin1String("sizebdiag"), 8, 8); - break; - case Qt::SizeFDiagCursor: - loadShapeFromResource(target, QLatin1String("sizefdiag"), 8, 8); - break; - case Qt::SizeAllCursor: - loadShapeFromResource(target, QLatin1String("sizeall"), 7, 7); - break; - case Qt::BlankCursor: - loadShapeFromResource(target, QLatin1String("blank"), 0, 0); - break; - case Qt::SplitVCursor: - loadShapeFromResource(target, QLatin1String("splitv"), 7, 7); - break; - case Qt::SplitHCursor: - loadShapeFromResource(target, QLatin1String("splith"), 7, 7); - break; - case Qt::PointingHandCursor: - loadShapeFromResource(target, QLatin1String("handpoint"), 5, 0); - break; - case Qt::ForbiddenCursor: - loadShapeFromResource(target, QLatin1String("forbidden"), 7, 7); - break; - case Qt::WhatsThisCursor: - loadShapeFromResource(target, QLatin1String("whatsthis"), 1, 1); - break; - case Qt::BusyCursor: - loadShapeFromResource(target, QLatin1String("busy3"), 1, 1, 1000000); - loadShapeFromResource(target, QLatin1String("busy6"), 1, 1, 1000000); - loadShapeFromResource(target, QLatin1String("busy9"), 1, 1, 1000000); - loadShapeFromResource(target, QLatin1String("busy12"), 1, 1, 1000000); - break; - case Qt::OpenHandCursor: - loadShapeFromResource(target, QLatin1String("openhand"), 7, 7); - break; - case Qt::ClosedHandCursor: - loadShapeFromResource(target, QLatin1String("closehand"), 7, 7); - break; - } -} -#endif - -/* - * Common code between the sprite workaround and standard modes of operation. - * RWsSpriteBase is the base class for both RWsSprite and RWsPointerCursor. - * It is called from both handle() and qt_s60_show_pointer_sprite() - */ -void QCursorData::constructCursorSprite(RWsSpriteBase& target) -{ - int count = nativeSpriteMembers.Count(); - if (count) { - // already constructed - for (int i = 0; i < count; i++) - target.AppendMember(*(nativeSpriteMembers[i])); - - return; - } - if (pixmap.isNull() && !bm) { -#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS - //shape cursor - constructShapeSprite(target); -#endif - return; - } - QScopedPointer member(new TSpriteMember); - if (pixmap.isNull()) { - //construct mono cursor - member->iBitmap = bm->toSymbianCFbsBitmap(); - member->iMaskBitmap = bmm->toSymbianCFbsBitmap(); - } - else { - //construct normal cursor - member->iBitmap = pixmap.toSymbianCFbsBitmap(); - if (pixmap.hasAlphaChannel()) { - member->iMaskBitmap = 0; //use alpha blending - } - else if (pixmap.hasAlpha()) { - member->iMaskBitmap = pixmap.mask().toSymbianCFbsBitmap(); - } - else { - member->iMaskBitmap = 0; //opaque rectangle cursor (due to EDrawModePEN) - } - } - - member->iDrawMode = CGraphicsContext::EDrawModePEN; - member->iInvertMask = EFalse; - member->iInterval = 0; - member->iOffset = TPoint(-(hx), -(hy)); //Symbian hotspot coordinates are negative - qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); - target.AppendMember(*(member.take())); -} - -/* - * shows the pointer sprite by constructing a native handle, and registering - * it with the window server. - * Only used when the sprite workaround is in use. - */ -void qt_symbian_show_pointer_sprite() -{ - if (cursorSprite.d) { - if (cursorSprite.d->scurs.WsHandle()) - cursorSprite.d->scurs.Close(); - } else { - cursorSprite = QCursor(Qt::ArrowCursor); - } - - cursorSprite.d->scurs = RWsSprite(S60->wsSession()); - QPoint pos = QCursor::pos(); - cursorSprite.d->scurs.Construct(S60->windowGroup(), TPoint(pos.x(), pos.y()), ESpriteNoChildClip | ESpriteNoShadows); - - cursorSprite.d->constructCursorSprite(cursorSprite.d->scurs); - cursorSprite.d->scurs.Activate(); -} - -/* - * hides the pointer sprite by closing the native handle. - * Only used when the sprite workaround is in use. - */ -void qt_symbian_hide_pointer_sprite() -{ - if (cursorSprite.d) { - cursorSprite.d->scurs.Close(); - } -} - -/* - * Changes the cursor sprite to the cursor specified. - * Only used when the sprite workaround is in use. - */ -void qt_symbian_set_pointer_sprite(const QCursor& cursor) -{ - if (S60->mouseInteractionEnabled) - qt_symbian_hide_pointer_sprite(); - cursorSprite = cursor; - if (S60->mouseInteractionEnabled) - qt_symbian_show_pointer_sprite(); -} - -/* - * When using sprites as a workaround on phones that have a broken - * RWsPointerCursor, this function is called in response to pointer events - * and when QCursor::setPos() is called. - * Performance is worse than a real pointer cursor, due to extra context - * switches vs. the window server moving the cursor by itself. - */ -void qt_symbian_move_cursor_sprite() -{ - if (S60->mouseInteractionEnabled) { - cursorSprite.d->scurs.SetPosition(TPoint(S60->lastCursorPos.x(), S60->lastCursorPos.y())); - } -} - -/* - * Translate from Qt::CursorShape to OS system pointer cursor list index. - * Currently we control the implementation of the system pointer cursor list, - * so this function is trivial. That may not always be the case. - */ -TInt qt_symbian_translate_cursor_shape(Qt::CursorShape shape) -{ - return (TInt) shape; -} - -/* - 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. -*/ -void qt_symbian_set_cursor(QWidget *w, bool force) -{ - static QPointer lastUnderMouse = 0; - if (force) { - lastUnderMouse = w; - } - else if (w->testAttribute(Qt::WA_WState_Created) && lastUnderMouse - && lastUnderMouse->effectiveWinId() == w->effectiveWinId()) { - w = lastUnderMouse; - } - - if (!S60->curWin && w && w->internalWinId()) - return; - QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(S60->curWin); - if (!cW || cW->window() != w->window() || !cW->isVisible() || !cW->underMouse() - || QApplication::overrideCursor()) - return; - -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_set_pointer_sprite(cW->cursor()); - else -#endif - qt_symbian_setWindowCursor(cW->cursor(), w->effectiveWinId()); -} - -/* - * Makes the specified cursor appear above a specific native window group - * Called from QSymbianControl and QApplication::restoreOverrideCursor - * - * Window server is needed for this, so there is no equivalent when using - * the sprite workaround. - */ -void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node) -{ - Qt::HANDLE handle = cursor.handle(); - if (handle) { - RWsPointerCursor *pcurs = reinterpret_cast (handle); - node.SetCustomPointerCursor(*pcurs); - } else -#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS - { - TInt shape = qt_symbian_translate_cursor_shape(cursor.shape()); - node.SetPointerCursor(shape); - } -#else - qWarning("qt_s60_setWindowGroupCursor - null handle"); -#endif -} - -/* - * Makes the specified cursor appear above a specific native window - * Called from QSymbianControl and QApplication::restoreOverrideCursor - * - * Window server is needed for this, so there is no equivalent when using - * the sprite workaround. - */ -void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid) -{ - //find the window for this control - while (!wid->OwnsWindow()) { - wid = wid->Parent(); - if (!wid) - return; - } - RWindowTreeNode *node = wid->DrawableWindow(); - qt_symbian_setWindowGroupCursor(cursor, *node); -} - -/* - * Makes the specified cursor appear everywhere. - * Called from QApplication::setOverrideCursor - */ -void qt_symbian_setGlobalCursor(const QCursor &cursor) -{ -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) { - qt_symbian_set_pointer_sprite(cursor); - } else -#endif - { - //because of the internals of window server, we need to force the cursor - //to be set in all child windows too, otherwise when the cursor is over - //the child window it may show a widget cursor or arrow cursor instead, - //depending on construction order. - QListIterator iter(QWidgetPrivate::mapper->uniqueKeys()); - while(iter.hasNext()) - { - CCoeControl *ctrl = iter.next(); - if(ctrl->OwnsWindow()) { - RWindowTreeNode *node = ctrl->DrawableWindow(); - qt_symbian_setWindowGroupCursor(cursor, *node); - } - } - } -} -QT_END_NAMESPACE -#endif // QT_NO_CURSOR diff --git a/src/gui/guikernel/qcursor_win.cpp b/src/gui/guikernel/qcursor_win.cpp deleted file mode 100644 index 8a9362ebfc..0000000000 --- a/src/gui/guikernel/qcursor_win.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include - -#ifndef QT_NO_CURSOR - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -/***************************************************************************** - 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_WS_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_WS_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.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); - invm = mbits.colorCount() > 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 - }; - - wchar_t *sh = 0; - switch (cshape) { // map to windows cursor - case Qt::ArrowCursor: - sh = IDC_ARROW; - break; - case Qt::UpArrowCursor: - sh = IDC_UPARROW; - break; - case Qt::CrossCursor: - sh = IDC_CROSS; - break; - case Qt::WaitCursor: - sh = IDC_WAIT; - break; - case Qt::IBeamCursor: - sh = IDC_IBEAM; - break; - case Qt::SizeVerCursor: - sh = IDC_SIZENS; - break; - case Qt::SizeHorCursor: - sh = IDC_SIZEWE; - break; - case Qt::SizeBDiagCursor: - sh = IDC_SIZENESW; - break; - case Qt::SizeFDiagCursor: - sh = IDC_SIZENWSE; - break; - case Qt::SizeAllCursor: - sh = IDC_SIZEALL; - break; - case Qt::ForbiddenCursor: - sh = IDC_NO; - break; - case Qt::WhatsThisCursor: - sh = IDC_HELP; - break; - case Qt::BusyCursor: - sh = IDC_APPSTARTING; - break; - case Qt::PointingHandCursor: - sh = IDC_HAND; - break; - 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.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); - invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); - } - int n = qMax(1, bbits.width() / 8); - int h = bbits.height(); -#if !defined(Q_WS_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_WS_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; - } - case Qt::DragCopyCursor: - case Qt::DragMoveCursor: - case Qt::DragLinkCursor: { - QPixmap pixmap = QApplicationPrivate::instance()->getPixmapCursor(cshape); - hcurs = create32BitCursor(pixmap, hx, hy); - } - default: - qWarning("QCursor::update: Invalid cursor shape %d", cshape); - return; - } -#ifdef Q_WS_WINCE - hcurs = LoadCursor(0, sh); -#else - hcurs = (HCURSOR)LoadImage(0, sh, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); -#endif -} - -QT_END_NAMESPACE -#endif // QT_NO_CURSOR diff --git a/src/gui/guikernel/qcursor_x11.cpp b/src/gui/guikernel/qcursor_x11.cpp deleted file mode 100644 index d0ed98e1fe..0000000000 --- a/src/gui/guikernel/qcursor_x11.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef QT_NO_XCURSOR -# include -#endif // QT_NO_XCURSOR - -#ifndef QT_NO_XFIXES -# include -#endif // QT_NO_XFIXES - -#include "qx11info_x11.h" -#include - -QT_BEGIN_NAMESPACE - -// Define QT_USE_APPROXIMATE_CURSORS when compiling if you REALLY want to -// use the ugly X11 cursors. - -/***************************************************************************** - 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", - "copy", - "move", - "link" - }; - -#ifndef QT_NO_XCURSOR - if (X11->ptrXcursorLibraryLoadCursor) { - // special case for non-standard dnd-* cursors - switch (cshape) { - case Qt::DragCopyCursor: - hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); - break; - case Qt::DragMoveCursor: - hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); - break; - case Qt::DragLinkCursor: - hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); - break; - default: - break; - } - if (!hcurs) - hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); - } - if (hcurs) - return; -#endif // QT_NO_XCURSOR - - 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 }; - - // Non-standard X11 cursors are created from bitmaps - -#ifndef QT_USE_APPROXIMATE_CURSORS - 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 *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 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 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}; - - static const uchar * 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 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}; - - 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_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, reinterpret_cast(cursor_bits16[i]), 16, 16); - pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast(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, reinterpret_cast(cursor_bits32[i]), 32, 32); - pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast(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, reinterpret_cast(cursor_bits20[i]), 20, 20); - pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast(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, reinterpret_cast(open ? openhand_bits : closedhand_bits), 16, 16); - pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast(open ? openhandm_bits : closedhandm_bits), 16, 16); - hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); - } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor - || cshape == Qt::DragLinkCursor) { - XColor bg, fg; - bg.red = 255 << 8; - bg.green = 255 << 8; - bg.blue = 255 << 8; - fg.red = 0; - fg.green = 0; - fg.blue = 0; - QImage image = QApplicationPrivate::instance()->getPixmapCursor(cshape).toImage(); - pm = QX11PixmapData::createBitmapFromImage(image); - pmm = QX11PixmapData::createBitmapFromImage(image.createAlphaMask().convertToFormat(QImage::Format_MonoLSB)); - 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; - case Qt::DragCopyCursor: - sh = XC_tcross; - break; - case Qt::DragLinkCursor: - sh = XC_center_ptr; - break; - case Qt::DragMoveCursor: - sh = XC_top_left_arrow; - 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/guikernel/qdnd_mac.mm b/src/gui/guikernel/qdnd_mac.mm deleted file mode 100644 index 3af2ba007c..0000000000 --- a/src/gui/guikernel/qdnd_mac.mm +++ /dev/null @@ -1,753 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 -#include -#ifndef QT_NO_ACCESSIBILITY -# include "qaccessible.h" -#endif - -#include -#include -#include -#include - -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, ¤tActions); - 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, ¤tAction); - 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 interested 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 sent immediately 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); //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); - } - - //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 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 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); - o->setMimeData(0); - o->deleteLater(); - return action; - } - } - - DragActions ret = kDragActionNothing; - GetDragDropAction(dragRef, &ret); - DisposeDrag(dragRef); //cleanup - o->setMimeData(0); - o->deleteLater(); - 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/guikernel/qdnd_s60.cpp b/src/gui/guikernel/qdnd_s60.cpp deleted file mode 100644 index a9847a98f8..0000000000 --- a/src/gui/guikernel/qdnd_s60.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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" -#include "qt_s60_p.h" - -#include -// pointer cursor -#include -#include -#include - -QT_BEGIN_NAMESPACE -//### artistic impression of Symbians default DnD cursor ? - -static QPixmap *defaultPm = 0; -static const int default_pm_hotx = -50; -static const int default_pm_hoty = -50; -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", -}; -//### actions need to be redefined for S60 -// Shift/Ctrl handling, and final drop status -static Qt::DropAction global_accepted_action = Qt::MoveAction; -static Qt::DropActions possible_actions = Qt::IgnoreAction; - - -// static variables in place of a proper cross-process solution -static QDrag *drag_object; -static bool qt_symbian_dnd_dragging = false; - - -static Qt::KeyboardModifiers oldstate; - -void QDragManager::updatePixmap() -{ - 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; - } -#ifndef QT_NO_CURSOR - QCursor cursor(pm, pm_hot.x(), pm_hot.y()); - overrideCursor = cursor; -#endif -} - -void QDragManager::timerEvent(QTimerEvent *) { } - -void QDragManager::move(const QPoint&) { -} - -void QDragManager::updateCursor() -{ -#ifndef QT_NO_CURSOR - QCursor cursor = willDrop ? overrideCursor : Qt::ForbiddenCursor; - if (!restoreCursor) { - QApplication::setOverrideCursor(cursor); - restoreCursor = true; - } - else { - QApplication::changeOverrideCursor(cursor); - } -#endif -} - - -bool QDragManager::eventFilter(QObject *o, QEvent *e) -{ - if (beingCancelled) { - return false; - } - if (!o->isWidgetType()) - return false; - - switch(e->type()) { - 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()); - // map the Coords relative to the window. - if (!cw) - return true; - - while (cw && !cw->acceptDrops() && !cw->isWindow()) - cw = cw->parentWidget(); - - bool oldWillDrop = willDrop; - if (object->target() != cw) { - if (object->target()) { - QDragLeaveEvent dle; - QApplication::sendEvent(object->target(), &dle); - willDrop = false; - global_accepted_action = Qt::IgnoreAction; - if (oldWillDrop != willDrop) - updateCursor(); - 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; - if (oldWillDrop != willDrop) - updateCursor(); - } - } 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; - if (oldWillDrop != willDrop) { - updatePixmap(); - updateCursor(); - } - } - if (global_accepted_action != prevAction) - emitActionChanged(global_accepted_action); - } - return true; // Eat all mouse events - } - - case QEvent::MouseButtonRelease: - { - qApp->removeEventFilter(this); -#ifndef QT_NO_CURSOR - if (restoreCursor) { - QApplication::restoreOverrideCursor(); - willDrop = false; - restoreCursor = false; - } -#endif - 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) -{ - Q_ASSERT(!qt_symbian_dnd_dragging); - if (object == o || !o || !o->source()) - return Qt::IgnoreAction; - - if (object) { - cancel(); - qApp->removeEventFilter(this); - beingCancelled = false; - } - - object = drag_object = o; - - oldstate = Qt::NoModifier; // #### Should use state that caused the drag - willDrop = false; - updatePixmap(); - updateCursor(); - -#ifndef QT_NO_CURSOR - qt_symbian_set_cursor_visible(true); //force cursor on even for touch phone -#endif - - object->d_func()->target = 0; - - qApp->installEventFilter(this); - - global_accepted_action = defaultAction(dragPrivate()->possible_actions, Qt::NoModifier); - qt_symbian_dnd_dragging = true; - - eventLoop = new QEventLoop; - // block - (void) eventLoop->exec(QEventLoop::AllEvents); - delete eventLoop; - eventLoop = 0; - -#ifndef QT_NO_CURSOR - qt_symbian_set_cursor_visible(false); - - overrideCursor = QCursor(); //deref the cursor data - qt_symbian_dnd_dragging = false; -#endif - - return global_accepted_action; -} - - -void QDragManager::cancel(bool deleteSource) -{ - beingCancelled = true; - - if (object->target()) { - QDragLeaveEvent dle; - QApplication::sendEvent(object->target(), &dle); - } - - if (drag_object) { - if (deleteSource) - object->deleteLater(); - drag_object = object = 0; - } - -#ifndef QT_NO_CURSOR - if (restoreCursor) { - QApplication::restoreOverrideCursor(); - restoreCursor = false; - } -#endif - - global_accepted_action = Qt::IgnoreAction; -} - - -void QDragManager::drop() -{ -#ifndef QT_NO_CURSOR - if (restoreCursor) { - QApplication::restoreOverrideCursor(); - restoreCursor = false; - } -#endif -} - -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(); -} - -QT_END_NAMESPACE -#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/guikernel/qdnd_win.cpp b/src/gui/guikernel/qdnd_win.cpp deleted file mode 100644 index 176e3cef7f..0000000000 --- a/src/gui/guikernel/qdnd_win.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 -#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 - wchar_t buf[256] = {0}; - GetClipboardFormatName(pformatetc->cfFormat, buf, 255); - qDebug("CF = %d : %s", pformatetc->cfFormat, QString::fromWCharArray(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 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 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 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) - // 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 -//--------------------------------------------------------------------- -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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 ((GetAsyncKeyState(VK_LBUTTON) == 0) - && (GetAsyncKeyState(VK_MBUTTON) == 0) - && (GetAsyncKeyState(VK_RBUTTON) == 0)) { - // grfKeyState is broken on CE & some Windows XP versions, - // therefore we need to check the state manually - return ResultFromScode(DRAGDROP_S_DROP); - } else { -#if !defined(Q_OS_WINCE) - 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 - QApplication::processEvents(); - return NOERROR; - } -} - -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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 -//--------------------------------------------------------------------- - -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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; - } - } - -} - -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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 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; -} - -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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) - -QT_ENSURE_STACK_ALIGNED_FOR_SSE 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/guikernel/qdnd_x11.cpp b/src/gui/guikernel/qdnd_x11.cpp deleted file mode 100644 index 9ff1543e51..0000000000 --- a/src/gui/guikernel/qdnd_x11.cpp +++ /dev/null @@ -1,2072 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" - -#include "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 "qiodevice.h" -#include "qpointer.h" -#include "qcursor.h" -#include "qelapsedtimer.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 "qapplication_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 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); - ret = true; - - 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(mozUri.utf16()), mozUri.length() * 2); - ret = true; - } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) { - QPixmap pm = qvariant_cast(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; - ret = true; - } - } else { - DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName)); - } - } - return ret && data != 0; -} - -//$$$ -QList QX11Data::xdndMimeAtomsForFormat(const QString &format) -{ - QList 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 - // 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::fromRawData((const QChar *)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()); - if (!xpm) - return QByteArray(); - QPixmap qpm = QPixmap::fromX11Pixmap(xpm); - QImageWriter imageWriter; - imageWriter.setFormat("PPMRAW"); - QImage imageToWrite = qpm.toImage(); - 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 = 0; - XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0, - qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval); - if (retval) { - Atom *data = (Atom *)retval; - for (; j<qt_xdnd_max_type && j < (int)n; j++) { - qt_xdnd_types[j] = data[j]; - } - 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; - - QDragManager *manager = QDragManager::self(); - - XClientMessageEvent leave; - leave.type = ClientMessage; - leave.window = qt_xdnd_current_target; - leave.format = 32; - leave.message_type = ATOM(XdndLeave); - leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); - 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 - 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; -} - -// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8. -static Qt::KeyboardModifiers currentKeyboardModifiers() -{ - Window root; - Window child; - int root_x, root_y, win_x, win_y; - uint keybstate; - for (int i = 0; i < ScreenCount(X11->display); ++i) { - if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, - &root_x, &root_y, &win_x, &win_y, &keybstate)) - return X11->translateModifiers(keybstate & 0x00ff); - } - return 0; -} - -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; - - // Drop coming from another app? Update keyboard modifiers. - if (!qt_xdnd_dragging) { - QApplicationPrivate::modifier_buttons = currentKeyboardModifiers(); - } - - 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::ShortcutOverride) { - // prevent accelerators from firing while dragging - e->accept(); - 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: - return true; - default: - return false; - } -} - -void QDragManager::updateCursor() -{ - if (!noDropCursor) { -#ifndef QT_NO_CURSOR - noDropCursor = new QCursor(Qt::ForbiddenCursor); - moveCursor = new QCursor(Qt::DragMoveCursor); - copyCursor = new QCursor(Qt::DragCopyCursor); - linkCursor = new QCursor(Qt::DragLinkCursor); -#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)) { - 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(); - - QElapsedTimer timer; - timer.start(); - do { - XEvent event; - if (XCheckTypedEvent(X11->display, ClientMessage, &event)) - qApp->x11ProcessEvent(&event); - - // 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 && timer.hasExpired(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/guikernel/qeventdispatcher_mac.mm b/src/gui/guikernel/qeventdispatcher_mac.mm deleted file mode 100644 index 677a7368b4..0000000000 --- a/src/gui/guikernel/qeventdispatcher_mac.mm +++ /dev/null @@ -1,1200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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" -#endif - -QT_BEGIN_NAMESPACE - -QT_USE_NAMESPACE - -/***************************************************************************** - 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. - - - if (blockSendPostedEvents) { - QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id)); - } else { - 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); - - // There is a race condition that happen where we disable the notifier and - // the kernel still has a notification to pass on. We then get this - // notification after we've successfully disabled the CFSocket, but our Qt - // notifier is now gone. The upshot is we have to check the notifier - // everytime. - if (callbackType == kCFSocketReadCallBack) { - if (socketInfo->readNotifier) - QApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); - } else if (callbackType == kCFSocketWriteCallBack) { - if (socketInfo->writeNotifier) - QApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); - } -} - -/* - 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: -#ifndef QT_NO_GESTURES -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - case NSEventTypeGesture: // touch events - case NSEventTypeMagnify: - case NSEventTypeSwipe: - case NSEventTypeRotate: - case NSEventTypeBeginGesture: - case NSEventTypeEndGesture: -#endif -#endif // QT_NO_GESTURES - result = true; - break; - - default: - break; - } - return result; -} -#endif - -static inline void qt_mac_waitForMoreEvents() -{ -#ifndef QT_MAC_USE_COCOA - while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ; -#else - // If no event exist in the cocoa event que, wait - // (and free up cpu time) until at least one event occur. - // This implementation is a bit on the edge, but seems to - // work fine: - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantFuture] - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event) - [NSApp postEvent:event atStart:YES]; -#endif -} - -#ifdef QT_MAC_USE_COCOA -static inline void qt_mac_waitForMoreModalSessionEvents() -{ - // If no event exist in the cocoa event que, wait - // (and free up cpu time) until at least one event occur. - // This implementation is a bit on the edge, but seems to - // work fine: - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:[NSDate distantFuture] - inMode:NSModalPanelRunLoopMode - dequeue:YES]; - if (event) - [NSApp postEvent:event atStart:YES]; -} -#endif - -bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) -{ - Q_D(QEventDispatcherMac); - d->interrupt = false; - -#ifdef QT_MAC_USE_COCOA - bool interruptLater = false; - QtMacInterruptDispatcherHelp::cancelInterruptLater(); -#endif - - // 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(); - - bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; - bool retVal = false; - forever { - if (d->interrupt) - break; - -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - NSEvent* event = 0; - - // First, send all previously excluded input events, if any: - if (!excludeUserEvents) { - while (!d->queuedUserInputEvents.isEmpty()) { - event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); - if (!filterEvent(event)) { - qt_mac_send_event(flags, event, 0); - retVal = true; - } - [event release]; - } - } - - // If Qt is used as a plugin, or as an extension in a native cocoa - // application, we should not run or stop NSApplication; This will be - // done from the application itself. And if processEvents is called - // manually (rather than from a QEventLoop), we cannot enter a tight - // loop and block this call, but instead we need to return after one flush. - // Finally, if we are to exclude user input events, we cannot call [NSApp run] - // as we then loose control over which events gets dispatched: - const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; - const bool canExec_Qt = !excludeUserEvents && - (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; - - if (canExec_Qt && canExec_3rdParty) { - // We can use exec-mode, meaning that we can stay in a tight loop until - // interrupted. This is mostly an optimization, but it allow us to use - // [NSApp run], which is the normal code path for cocoa applications. - if (NSModalSession session = d->currentModalSession()) { - QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); - while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt) - qt_mac_waitForMoreModalSessionEvents(); - - if (!d->interrupt && session == d->currentModalSessionCached) { - // Someone called [NSApp stopModal:] from outside the event - // dispatcher (e.g to stop a native dialog). But that call wrongly stopped - // 'session' as well. As a result, we need to restart all internal sessions: - d->temporarilyStopAllModalSessions(); - } - } else { - d->nsAppRunCalledByQt = true; - QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); - [NSApp run]; - } - retVal = true; - } else { - // We cannot block the thread (and run in a tight loop). - // Instead we will process all current pending events and return. - d->ensureNSAppInitialized(); - if (NSModalSession session = d->currentModalSession()) { - // INVARIANT: a modal window is executing. - if (!excludeUserEvents) { - // Since we can dispatch all kinds of events, we choose - // to use cocoa's native way of running modal sessions: - if (flags & QEventLoop::WaitForMoreEvents) - qt_mac_waitForMoreModalSessionEvents(); - NSInteger status = [NSApp runModalSession:session]; - if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { - // INVARIANT: Someone called [NSApp stopModal:] from outside the event - // dispatcher (e.g to stop a native dialog). But that call wrongly stopped - // 'session' as well. As a result, we need to restart all internal sessions: - d->temporarilyStopAllModalSessions(); - } - retVal = true; - } else do { - // Dispatch all non-user events (but que non-user events up for later). In - // this case, we need more control over which events gets dispatched, and - // cannot use [NSApp runModalSession:session]: - event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:nil - inMode:NSModalPanelRunLoopMode - dequeue: YES]; - - if (event) { - if (IsMouseOrKeyEvent(event)) { - [event retain]; - d->queuedUserInputEvents.append(event); - continue; - } - if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) - retVal = true; - } - } while (!d->interrupt && event != nil); - } else do { - // INVARIANT: No modal window is executing. - event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:nil - inMode:NSDefaultRunLoopMode - dequeue: YES]; - - if (event) { - if (flags & QEventLoop::ExcludeUserInputEvents) { - if (IsMouseOrKeyEvent(event)) { - [event retain]; - d->queuedUserInputEvents.append(event); - continue; - } - } - if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) - retVal = true; - } - } while (!d->interrupt && event != nil); - - // Be sure to flush the Qt posted events when not using exec mode - // (exec mode will always do this call from the event loop source): - if (!d->interrupt) - QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); - - // Since the window that holds modality might have changed while processing - // events, we we need to interrupt when we return back the previous process - // event recursion to ensure that we spin the correct modal session. - // We do the interruptLater at the end of the function to ensure that we don't - // disturb the 'wait for more events' below (as deleteLater will post an event): - interruptLater = true; - } -#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. - qt_mac_waitForMoreEvents(); - flags &= ~QEventLoop::WaitForMoreEvents; - } else { - // Done with event processing for now. - // Leave the function: - break; - } - } - - // If we're interrupted, we need to interrupt the _current_ - // recursion as well to check if it is still supposed to be - // executing. This way we wind down the stack until we land - // on a recursion that again calls processEvents (typically - // from QEventLoop), and set interrupt to false: - if (d->interrupt) - interrupt(); - -#ifdef QT_MAC_USE_COCOA - if (interruptLater) - QtMacInterruptDispatcherHelp::interruptLater(); -#endif - - 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; -bool QEventDispatcherMacPrivate::interrupt = false; - -#ifdef QT_MAC_USE_COCOA -QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; -bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; -bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; -bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false; -NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; - -void QEventDispatcherMacPrivate::ensureNSAppInitialized() -{ - // Some elements in Cocoa require NSApplication to be running before - // they get fully initialized, in particular the menu bar. This - // function is intended for cases where a dialog is told to execute before - // QApplication::exec is called, or the application spins the events loop - // manually rather than calling QApplication:exec. - // The function makes sure that NSApplication starts running, but stops - // it again as soon as the send posted events callback is called. That way - // we let Cocoa finish the initialization it seems to need. We'll only - // apply this trick at most once for any application, and we avoid doing it - // for the common case where main just starts QApplication::exec. - if (nsAppRunCalledByQt || [NSApp isRunning]) - return; - nsAppRunCalledByQt = true; - QBoolBlocker block1(interrupt, true); - QBoolBlocker block2(currentExecIsNSAppRun, true); - [NSApp run]; -} - -void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() -{ - // Flush, and Stop, all created modal session, and as - // such, make them pending again. The next call to - // currentModalSession will recreate them again. The - // reason to stop all session like this is that otherwise - // a call [NSApp stop] would not stop NSApp, but rather - // the current modal session. So if we need to stop NSApp - // we need to stop all the modal session first. To avoid changing - // the stacking order of the windows while doing so, we put - // up a block that is used in QCocoaWindow and QCocoaPanel: - int stackSize = cocoaModalSessionStack.size(); - for (int i=0; i<stackSize; ++i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.session) { - [NSApp endModalSession:info.session]; - info.session = 0; - } - } - currentModalSessionCached = 0; -} - -NSModalSession QEventDispatcherMacPrivate::currentModalSession() -{ - // If we have one or more modal windows, this function will create - // a session for each of those, and return the one for the top. - if (currentModalSessionCached) - return currentModalSessionCached; - - if (cocoaModalSessionStack.isEmpty()) - return 0; - - int sessionCount = cocoaModalSessionStack.size(); - for (int i=0; i<sessionCount; ++i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (!info.widget) - continue; - if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)) - continue; - if (!info.session) { - QMacCocoaAutoReleasePool pool; - NSWindow *window = qt_mac_window_for(info.widget); - if (!window) - continue; - - ensureNSAppInitialized(); - QBoolBlocker block1(blockSendPostedEvents, true); - info.nswindow = window; - [(NSWindow*) info.nswindow retain]; - int levelBeforeEnterModal = [window level]; - info.session = [NSApp beginModalSessionForWindow:window]; - // Make sure we don't stack the window lower that it was before - // entering modal, in case it e.g. had the stays-on-top flag set: - if (levelBeforeEnterModal > [window level]) - [window setLevel:levelBeforeEnterModal]; - } - currentModalSessionCached = info.session; - cleanupModalSessionsNeeded = false; - } - return currentModalSessionCached; -} - -static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal) -{ - // For NSPanels (but not NSWindows, sadly), we can set the flag - // worksWhenModal, so that they are active even when they are not modal. - 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:worksWhenModal]; - if (worksWhenModal && [window isVisible]){ - [window orderFront:window]; - } - } - } -} - -void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() -{ - // Make the dialog children of the widget - // active. And make the dialog children of - // the previous modal dialog unactive again: - QMacCocoaAutoReleasePool pool; - int size = cocoaModalSessionStack.size(); - if (size > 0){ - if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget) - setChildrenWorksWhenModal(prevModal, true); - if (size > 1){ - if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) - setChildrenWorksWhenModal(prevModal, false); - } - } -} - -void QEventDispatcherMacPrivate::cleanupModalSessions() -{ - // Go through the list of modal sessions, and end those - // that no longer has a widget assosiated; no widget means - // the the session has logically ended. The reason we wait like - // this to actually end the sessions for real (rather than at the - // point they were marked as stopped), is that ending a session - // when no other session runs below it on the stack will make cocoa - // drop some events on the floor. - QMacCocoaAutoReleasePool pool; - int stackSize = cocoaModalSessionStack.size(); - - for (int i=stackSize-1; i>=0; --i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.widget) { - // This session has a widget, and is therefore not marked - // as stopped. So just make it current. There might still be other - // stopped sessions on the stack, but those will be stopped on - // a later "cleanup" call. - currentModalSessionCached = info.session; - break; - } - cocoaModalSessionStack.remove(i); - currentModalSessionCached = 0; - if (info.session) { - [NSApp endModalSession:info.session]; - [(NSWindow *)info.nswindow release]; - } - } - - updateChildrenWorksWhenModal(); - cleanupModalSessionsNeeded = false; -} - -void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) -{ - // Add a new, empty (null), NSModalSession to the stack. - // It will become active the next time QEventDispatcher::processEvents is called. - // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer - // is non-zero, and the session pointer is zero (it will become active upon a call to - // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if - // the widget pointer is zero, and the session pointer is non-zero (it will be fully - // stopped in cleanupModalSessions()). - QCocoaModalSessionInfo info = {widget, 0, 0}; - cocoaModalSessionStack.push(info); - updateChildrenWorksWhenModal(); - currentModalSessionCached = 0; -} - -void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) -{ - // Mark all sessions attached to widget as pending to be stopped. We do this - // by setting the widget pointer to zero, but leave the session pointer. - // We don't tell cocoa to stop any sessions just yet, because cocoa only understands - // when we stop the _current_ modal session (which is the session on top of - // the stack, and might not belong to 'widget'). - int stackSize = cocoaModalSessionStack.size(); - for (int i=stackSize-1; i>=0; --i) { - QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; - if (info.widget == widget) { - info.widget = 0; - if (i == stackSize-1) { - // The top sessions ended. Interrupt the event dispatcher - // to start spinning the correct session immidiatly: - currentModalSessionCached = 0; - cleanupModalSessionsNeeded = true; - QEventDispatcherMac::instance()->interrupt(); - } - } - } -} - -#endif - -QEventDispatcherMacPrivate::QEventDispatcherMacPrivate() -{ -} - -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); - - /* The first cycle in the loop adds the source and the events of the source - are not processed. - We use an observer to process the posted events for the first - execution of the loop. */ - CFRunLoopObserverContext firstTimeObserverContext; - bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); - firstTimeObserverContext.info = d; - d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, - kCFRunLoopEntry, - /* repeats = */ false, - 0, - QEventDispatcherMacPrivate::firstLoopEntry, - &firstTimeObserverContext); - CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, 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; -} - -inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) -{ - if (blockSendPostedEvents) { - // We're told to not send posted events (because the event dispatcher - // is currently working on setting up the correct session to run). But - // we still need to make sure that we don't fall asleep until pending events - // are sendt, so we just signal this need, and return: - CFRunLoopSourceSignal(d->postedEventsSource); - return; - } - -#ifdef QT_MAC_USE_COCOA - if (d->cleanupModalSessionsNeeded) - d->cleanupModalSessions(); -#endif - - if (d->interrupt) { -#ifdef QT_MAC_USE_COCOA - if (d->currentExecIsNSAppRun) { - // The event dispatcher has been interrupted. But since - // [NSApplication run] is running the event loop, we - // delayed stopping it until now (to let cocoa process - // pending cocoa events first). - if (d->currentModalSessionCached) - d->temporarilyStopAllModalSessions(); - [NSApp stop:NSApp]; - d->cancelWaitForMoreEvents(); - } -#endif - return; - } - - if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { - d->lastSerial = d->serialNumber; - QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); - } -} - -void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, - CFRunLoopActivity activity, - void *info) -{ - Q_UNUSED(ref); - Q_UNUSED(activity); -#ifdef QT_MAC_USE_COCOA - QApplicationPrivate::qt_initAfterNSAppStarted(); -#endif - processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); -} - -void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) -{ - processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); -} - -#ifdef QT_MAC_USE_COCOA -void QEventDispatcherMacPrivate::cancelWaitForMoreEvents() -{ - // In case the event dispatcher is waiting for more - // events somewhere, we post a dummy event to wake it up: - QMacCocoaAutoReleasePool pool; - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint - modifierFlags:0 timestamp:0. windowNumber:0 context:0 - subtype:QtCocoaEventSubTypeWakeup 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 - // We do nothing more here than setting d->interrupt = true, and - // poke the event loop if it is sleeping. Actually stopping - // NSApp, or the current modal session, is done inside the send - // posted events callback. We do this to ensure that all current pending - // cocoa events gets delivered before we stop. Otherwise, if we now stop - // the last event loop recursion, cocoa will just drop pending posted - // events on the floor before we get a chance to reestablish a new session. - d->cancelWaitForMoreEvents(); -#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); - - CFRunLoopObserverInvalidate(d->firstTimeObserver); - CFRelease(d->firstTimeObserver); -} - -#ifdef QT_MAC_USE_COCOA - -QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; - -QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) -{ - // The whole point of this class is that we enable a way to interrupt - // the event dispatcher when returning back to a lower recursion level - // than where interruptLater was called. This is needed to detect if - // [NSApp run] should still be running at the recursion level it is at. - // Since the interrupt is canceled if processEvents is called before - // this object gets deleted, we also avoid interrupting unnecessary. - deleteLater(); -} - -QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() -{ - if (cancelled) - return; - instance = 0; - QEventDispatcherMac::instance()->interrupt(); -} - -void QtMacInterruptDispatcherHelp::cancelInterruptLater() -{ - if (!instance) - return; - instance->cancelled = true; - delete instance; - instance = 0; -} - -void QtMacInterruptDispatcherHelp::interruptLater() -{ - cancelInterruptLater(); - instance = new QtMacInterruptDispatcherHelp; -} - -#endif - -QT_END_NAMESPACE - diff --git a/src/gui/guikernel/qeventdispatcher_mac_p.h b/src/gui/guikernel/qeventdispatcher_mac_p.h deleted file mode 100644 index 12fcafbb01..0000000000 --- a/src/gui/guikernel/qeventdispatcher_mac_p.h +++ /dev/null @@ -1,224 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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; - void *nswindow; -} 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 currentExecIsNSAppRun; - static bool nsAppRunCalledByQt; - static bool cleanupModalSessionsNeeded; - static NSModalSession currentModalSessionCached; - static NSModalSession currentModalSession(); - static void updateChildrenWorksWhenModal(); - static void temporarilyStopAllModalSessions(); - static void beginModalSession(QWidget *widget); - static void endModalSession(QWidget *widget); - static void cancelWaitForMoreEvents(); - static void cleanupModalSessions(); - static void ensureNSAppInitialized(); -#endif - - MacSocketHash macSockets; - QList<void *> queuedUserInputEvents; // List of EventRef in Carbon, and NSEvent * in Cocoa - CFRunLoopSourceRef postedEventsSource; - CFRunLoopObserverRef waitingObserver; - CFRunLoopObserverRef firstTimeObserver; - QAtomicInt serialNumber; - int lastSerial; - static 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); - static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); -}; - -#ifdef QT_MAC_USE_COCOA -class QtMacInterruptDispatcherHelp : public QObject -{ - static QtMacInterruptDispatcherHelp *instance; - bool cancelled; - - QtMacInterruptDispatcherHelp(); - ~QtMacInterruptDispatcherHelp(); - - public: - static void interruptLater(); - static void cancelInterruptLater(); -}; -#endif - -QT_END_NAMESPACE - -#endif // QEVENTDISPATCHER_MAC_P_H diff --git a/src/gui/guikernel/qeventdispatcher_s60.cpp b/src/gui/guikernel/qeventdispatcher_s60.cpp deleted file mode 100644 index 2d92c89c07..0000000000 --- a/src/gui/guikernel/qeventdispatcher_s60.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <qwidget.h> - -#include "qeventdispatcher_s60_p.h" - -QT_BEGIN_NAMESPACE - -QtEikonEnv::QtEikonEnv() - : m_lastIterationCount(0) - , m_savedStatusCode(KRequestPending) - , m_hasAlreadyRun(false) -{ -} - -QtEikonEnv::~QtEikonEnv() -{ -} - -void QtEikonEnv::RunL() -{ - QEventDispatcherS60 *dispatcher = qobject_cast<QEventDispatcherS60 *>(QAbstractEventDispatcher::instance()); - if (!dispatcher) { - CEikonEnv::RunL(); - return; - } - - if (m_lastIterationCount != dispatcher->iterationCount()) { - m_hasAlreadyRun = false; - m_lastIterationCount = dispatcher->iterationCount(); - } - - if (m_hasAlreadyRun) { - // Fool the active scheduler into believing we are still waiting for events. - // The window server thinks we are not, however. - m_savedStatusCode = iStatus.Int(); - iStatus = KRequestPending; - SetActive(); - dispatcher->queueDeferredActiveObjectsCompletion(); - } else { - m_hasAlreadyRun = true; - CEikonEnv::RunL(); - } -} - -void QtEikonEnv::DoCancel() -{ - complete(); - - CEikonEnv::DoCancel(); -} - -void QtEikonEnv::complete() -{ - if (m_hasAlreadyRun) { - if (m_savedStatusCode != KRequestPending) { - TRequestStatus *status = &iStatus; - QEventDispatcherSymbian::RequestComplete(status, m_savedStatusCode); - m_savedStatusCode = KRequestPending; - } - m_hasAlreadyRun = false; - } -} - -QEventDispatcherS60::QEventDispatcherS60(QObject *parent) - : QEventDispatcherSymbian(parent), - m_noInputEvents(false) -{ -} - -QEventDispatcherS60::~QEventDispatcherS60() -{ - for (int c = 0; c < m_deferredInputEvents.size(); ++c) { - delete m_deferredInputEvents[c].event; - } -} - -bool QEventDispatcherS60::processEvents ( QEventLoop::ProcessEventsFlags flags ) -{ - bool ret = false; - - QT_TRY { - bool oldNoInputEventsValue = m_noInputEvents; - if (flags & QEventLoop::ExcludeUserInputEvents) { - m_noInputEvents = true; - } else { - m_noInputEvents = false; - ret = sendDeferredInputEvents() || ret; - } - - ret = QEventDispatcherSymbian::processEvents(flags) || ret; - - m_noInputEvents = oldNoInputEventsValue; - } QT_CATCH (const std::exception& ex) { -#ifndef QT_NO_EXCEPTIONS - CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex)); -#endif - } - - return ret; -} - -bool QEventDispatcherS60::hasPendingEvents() -{ - return !m_deferredInputEvents.isEmpty() || QEventDispatcherSymbian::hasPendingEvents(); -} - -void QEventDispatcherS60::saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event) -{ - DeferredInputEvent inputEvent = {control, widget, event}; - m_deferredInputEvents.append(inputEvent); - connect(widget, SIGNAL(destroyed(QObject*)), SLOT(removeInputEventsForWidget(QObject*))); -} - -bool QEventDispatcherS60::sendDeferredInputEvents() -{ - bool eventsSent = false; - while (!m_deferredInputEvents.isEmpty()) { - DeferredInputEvent inputEvent = m_deferredInputEvents.takeFirst(); -#ifndef QT_NO_EXCEPTIONS - try { -#endif - inputEvent.control->sendInputEvent(inputEvent.widget, inputEvent.event); -#ifndef QT_NO_EXCEPTIONS - } catch (...) { - delete inputEvent.event; - throw; - } -#endif - delete inputEvent.event; - eventsSent = true; - } - - return eventsSent; -} - -void QEventDispatcherS60::removeInputEventsForWidget(QObject *object) -{ - for (int c = 0; c < m_deferredInputEvents.size(); ++c) { - if (m_deferredInputEvents[c].widget == object) { - delete m_deferredInputEvents[c].event; - m_deferredInputEvents.removeAt(c--); - } - } -} - -// reimpl -void QEventDispatcherS60::reactivateDeferredActiveObjects() -{ - if (S60->qtOwnsS60Environment) { - static_cast<QtEikonEnv *>(CCoeEnv::Static())->complete(); - } - - QEventDispatcherSymbian::reactivateDeferredActiveObjects(); -} - -QT_END_NAMESPACE diff --git a/src/gui/guikernel/qeventdispatcher_s60_p.h b/src/gui/guikernel/qeventdispatcher_s60_p.h deleted file mode 100644 index 7c5a8d03d4..0000000000 --- a/src/gui/guikernel/qeventdispatcher_s60_p.h +++ /dev/null @@ -1,127 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QEVENTDISPATCHER_S60_P_H -#define QEVENTDISPATCHER_S60_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_symbian_p.h> -#include "qt_s60_p.h" - -#include <eikenv.h> - -QT_BEGIN_NAMESPACE - -class QEventDispatcherS60; - -class QtEikonEnv : public CEikonEnv -{ -public: - QtEikonEnv(); - ~QtEikonEnv(); - - // from CActive. - void RunL(); - void DoCancel(); - - void complete(); - -private: - // Workaround for a BC break from S60 3.2 -> 5.0, where the CEikonEnv override was removed. - // To avoid linking to that when we build against 3.2, define an empty body here. - // Reserved_*() have been verified to be empty in the S60 code. - void Reserved_1() {} - void Reserved_2() {} - -private: - int m_lastIterationCount; - TInt m_savedStatusCode; - bool m_hasAlreadyRun; -}; - -class Q_GUI_EXPORT QEventDispatcherS60 : public QEventDispatcherSymbian -{ - Q_OBJECT - -public: - QEventDispatcherS60(QObject *parent = 0); - ~QEventDispatcherS60(); - - bool processEvents ( QEventLoop::ProcessEventsFlags flags ); - bool hasPendingEvents(); - - bool excludeUserInputEvents() { return m_noInputEvents; } - - void saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event); - - void reactivateDeferredActiveObjects(); - -private: - bool sendDeferredInputEvents(); - -private Q_SLOTS: - void removeInputEventsForWidget(QObject *object); - -private: - bool m_noInputEvents; - - struct DeferredInputEvent - { - QSymbianControl *control; - QWidget *widget; - QInputEvent *event; - }; - QList<DeferredInputEvent> m_deferredInputEvents; -}; - -QT_END_NAMESPACE - -#endif // QEVENTDISPATCHER_S60_P_H diff --git a/src/gui/guikernel/qeventdispatcher_x11.cpp b/src/gui/guikernel/qeventdispatcher_x11.cpp deleted file mode 100644 index 110786a378..0000000000 --- a/src/gui/guikernel/qeventdispatcher_x11.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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/guikernel/qeventdispatcher_x11_p.h b/src/gui/guikernel/qeventdispatcher_x11_p.h deleted file mode 100644 index cfdd2a5fa6..0000000000 --- a/src/gui/guikernel/qeventdispatcher_x11_p.h +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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/guikernel/qkeymapper_mac.cpp b/src/gui/guikernel/qkeymapper_mac.cpp deleted file mode 100644 index d3bbf89711..0000000000 --- a/src/gui/guikernel/qkeymapper_mac.cpp +++ /dev/null @@ -1,1023 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/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> -#include <private/qmacinputcontext_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); - } - } - if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - Qt::KeyboardModifiers oldModifiers = ret; - ret &= ~(Qt::MetaModifier | Qt::ControlModifier); - if (oldModifiers & Qt::ControlModifier) - ret |= Qt::MetaModifier; - if (oldModifiers & Qt::MetaModifier) - ret |= Qt::ControlModifier; - } - 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; - } - } - - if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - int oldModifiers = ret; - ret &= ~(controlKeyBit | cmdKeyBit); - if (oldModifiers & controlKeyBit) - ret |= cmdKeyBit; - if (oldModifiers & cmdKeyBit) - ret |= controlKeyBit; - } - 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 qt_mac_enum_mapper qt_mac_private_unicode[] = { - { 0xF700, QT_MAC_MAP_ENUM(Qt::Key_Up) }, //NSUpArrowFunctionKey - { 0xF701, QT_MAC_MAP_ENUM(Qt::Key_Down) }, //NSDownArrowFunctionKey - { 0xF702, QT_MAC_MAP_ENUM(Qt::Key_Left) }, //NSLeftArrowFunctionKey - { 0xF703, QT_MAC_MAP_ENUM(Qt::Key_Right) }, //NSRightArrowFunctionKey - { 0xF727, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertFunctionKey - { 0xF728, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteFunctionKey - { 0xF729, QT_MAC_MAP_ENUM(Qt::Key_Home) }, //NSHomeFunctionKey - { 0xF72B, QT_MAC_MAP_ENUM(Qt::Key_End) }, //NSEndFunctionKey - { 0xF72C, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, //NSPageUpFunctionKey - { 0xF72D, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, //NSPageDownFunctionKey - { 0xF72F, QT_MAC_MAP_ENUM(Qt::Key_ScrollLock) }, //NSScrollLockFunctionKey - { 0xF730, QT_MAC_MAP_ENUM(Qt::Key_Pause) }, //NSPauseFunctionKey - { 0xF731, QT_MAC_MAP_ENUM(Qt::Key_SysReq) }, //NSSysReqFunctionKey - { 0xF735, QT_MAC_MAP_ENUM(Qt::Key_Menu) }, //NSMenuFunctionKey - { 0xF738, QT_MAC_MAP_ENUM(Qt::Key_Print) }, //NSPrintFunctionKey - { 0xF73A, QT_MAC_MAP_ENUM(Qt::Key_Clear) }, //NSClearDisplayFunctionKey - { 0xF73D, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertCharFunctionKey - { 0xF73E, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteCharFunctionKey - { 0xF741, QT_MAC_MAP_ENUM(Qt::Key_Select) }, //NSSelectFunctionKey - { 0xF742, QT_MAC_MAP_ENUM(Qt::Key_Execute) }, //NSExecuteFunctionKey - { 0xF746, QT_MAC_MAP_ENUM(Qt::Key_Help) }, //NSHelpFunctionKey - { 0xF747, QT_MAC_MAP_ENUM(Qt::Key_Mode_switch) }, //NSModeSwitchFunctionKey - { 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; - } - } - - // check if they belong to key codes in private unicode range - if (key >= 0xf700 && key <= 0xf747) { - if (key >= 0xf704 && key <= 0xf726) { - return Qt::Key_F1 + (key.unicode() - 0xf704) ; - } - for (int i = 0; qt_mac_private_unicode[i].qt_code; i++) { - if (qt_mac_private_unicode[i].mac_code == key) { - return qt_mac_private_unicode[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; - if (er) - 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() -{ - deleteLayouts(); -} - -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)); - keyboardInputDirection = keyboardInputLocale.textDirection(); - } else { - keyboardInputLocale = QLocale::c(); - keyboardInputDirection = Qt::LeftToRight; - } - return true; -} - -void -QKeyMapperPrivate::deleteLayouts() -{ - keyboard_mode = NullMode; - for (int i = 0; i < 255; ++i) { - if (keyLayout[i]) { - delete keyLayout[i]; - keyLayout[i] = 0; - } - } -} - -void -QKeyMapperPrivate::clearMappings() -{ - deleteLayouts(); - 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; - } - - QInputContext *currentContext = qApp->inputContext(); - if (currentContext && currentContext->isComposing()) { - if (ekind == kEventRawKeyDown) { - QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); - if (context) - context->setLastKeydownEvent(event); - } - return false; - } - // Once we process the key down , we don't need to send the saved event again from - // kEventTextInputUnicodeForKeyEvent, so clear it. - if (currentContext && ekind == kEventRawKeyDown) { - QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); - if (context) - context->setLastKeydownEvent(0); - } - - //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 unfortunately it is the best that can be - done for now because of the Control/Meta mapping problems */ - if (modifiers & (Qt::ControlModifier | Qt::MetaModifier) - && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { - 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; - if (er) - 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); -#ifdef QT_MAC_USE_COCOA - // The unicode characters in the range 0xF700-0xF747 are reserved - // by Mac OS X for transient use as keyboard function keys. We - // wont send 'text' for such key events. This is done to match - // behavior on other platforms. - unsigned int *unicodeKey = (unsigned int*)info; - if (*unicodeKey >= 0xf700 && *unicodeKey <= 0xf747) - text = QString(); - bool isAccepted; -#endif - 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 - ,&isAccepted -#endif - ); -#ifdef QT_MAC_USE_COCOA - *unicodeKey = (unsigned int)isAccepted; -#endif - } - return handled_event; -} - -void -QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void * -#if defined(QT_MAC_USE_COCOA) - unicodeKey // unicode character from NSEvent (modifiers applied) -#endif - ) -{ - 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; - } -#ifndef Q_WS_MAC32 - else { - const QChar unicode(*((UniChar *)unicodeKey)); - int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); - if (qtkey == Qt::Key_unknown) - qtkey = unicode.unicode(); - keyLayout[macVirtualKey]->qtKey[i] = qtkey; - } -#endif -#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/guikernel/qkeymapper_s60.cpp b/src/gui/guikernel/qkeymapper_s60.cpp deleted file mode 100644 index 08cfae0d2d..0000000000 --- a/src/gui/guikernel/qkeymapper_s60.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "private/qkeymapper_p.h" -#include <private/qcore_symbian_p.h> -#include <e32keys.h> -#include <e32cmn.h> -#include <centralrepository.h> -#include <biditext.h> - -QT_BEGIN_NAMESPACE - -QKeyMapperPrivate::QKeyMapperPrivate() -{ -} - -QKeyMapperPrivate::~QKeyMapperPrivate() -{ -} - -QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent * /* e */) -{ - QList<int> result; - return result; -} - -void QKeyMapperPrivate::clearMappings() -{ - // stub -} - -QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */) -{ - if (keySym >= Qt::Key_Escape) { - switch (keySym) { - case Qt::Key_Tab: - return QString(QChar('\t')); - case Qt::Key_Return: // fall through - case Qt::Key_Enter: - return QString(QChar('\r')); - default: - return QString(); - } - } - - // Symbian doesn't actually use modifiers, but gives us the character code directly. - - return QString(QChar(keySym)); -} - -#include <e32keys.h> -struct KeyMapping{ - TKeyCode s60KeyCode; - TStdScanCode s60ScanCode; - Qt::Key qtKey; -}; - -using namespace Qt; - -static const KeyMapping keyMapping[] = { - {EKeyBackspace, EStdKeyBackspace, Key_Backspace}, - {EKeyTab, EStdKeyTab, Key_Tab}, - {EKeyEnter, EStdKeyEnter, Key_Enter}, - {EKeyEscape, EStdKeyEscape, Key_Escape}, - {EKeySpace, EStdKeySpace, Key_Space}, - {EKeyDelete, EStdKeyDelete, Key_Delete}, - {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq}, - {EKeyPause, EStdKeyPause, Key_Pause}, - {EKeyHome, EStdKeyHome, Key_Home}, - {EKeyEnd, EStdKeyEnd, Key_End}, - {EKeyPageUp, EStdKeyPageUp, Key_PageUp}, - {EKeyPageDown, EStdKeyPageDown, Key_PageDown}, - {EKeyInsert, EStdKeyInsert, Key_Insert}, - {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left}, - {EKeyRightArrow, EStdKeyRightArrow, Key_Right}, - {EKeyUpArrow, EStdKeyUpArrow, Key_Up}, - {EKeyDownArrow, EStdKeyDownArrow, Key_Down}, - {EKeyLeftShift, EStdKeyLeftShift, Key_Shift}, - {EKeyRightShift, EStdKeyRightShift, Key_Shift}, - {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt}, - {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr}, - {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control}, - {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control}, - {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L}, - {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R}, - {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock}, - {EKeyNumLock, EStdKeyNumLock, Key_NumLock}, - {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock}, - {EKeyF1, EStdKeyF1, Key_F1}, - {EKeyF2, EStdKeyF2, Key_F2}, - {EKeyF3, EStdKeyF3, Key_F3}, - {EKeyF4, EStdKeyF4, Key_F4}, - {EKeyF5, EStdKeyF5, Key_F5}, - {EKeyF6, EStdKeyF6, Key_F6}, - {EKeyF7, EStdKeyF7, Key_F7}, - {EKeyF8, EStdKeyF8, Key_F8}, - {EKeyF9, EStdKeyF9, Key_F9}, - {EKeyF10, EStdKeyF10, Key_F10}, - {EKeyF11, EStdKeyF11, Key_F11}, - {EKeyF12, EStdKeyF12, Key_F12}, - {EKeyF13, EStdKeyF13, Key_F13}, - {EKeyF14, EStdKeyF14, Key_F14}, - {EKeyF15, EStdKeyF15, Key_F15}, - {EKeyF16, EStdKeyF16, Key_F16}, - {EKeyF17, EStdKeyF17, Key_F17}, - {EKeyF18, EStdKeyF18, Key_F18}, - {EKeyF19, EStdKeyF19, Key_F19}, - {EKeyF20, EStdKeyF20, Key_F20}, - {EKeyF21, EStdKeyF21, Key_F21}, - {EKeyF22, EStdKeyF22, Key_F22}, - {EKeyF23, EStdKeyF23, Key_F23}, - {EKeyF24, EStdKeyF24, Key_F24}, - {EKeyOff, EStdKeyOff, Key_PowerOff}, -// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0 - {EKeyHelp, EStdKeyHelp, Key_Help}, - {EKeyDial, EStdKeyDial, Key_Call}, - {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp}, - {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown}, - {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing. - {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing. - {EKeyDevice3, EStdKeyDevice3, Key_Select}, - {EKeyDevice7, EStdKeyDevice7, Key_Camera}, - {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing. - {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing. - {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing. - {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing. - {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing. - {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing. - {EKeyApplication6, EStdKeyApplication6, Key_Launch6}, - {EKeyApplication7, EStdKeyApplication7, Key_Launch7}, - {EKeyApplication8, EStdKeyApplication8, Key_Launch8}, - {EKeyApplication9, EStdKeyApplication9, Key_Launch9}, - {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA}, - {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB}, - {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC}, - {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD}, - {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE}, - {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF}, - {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus}, - {EKeyYes, EStdKeyYes, Key_Yes}, - {EKeyNo, EStdKeyNo, Key_No}, - {TKeyCode(0), TStdScanCode(0), Qt::Key(0)} -}; - -int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key) -{ - int res = Qt::Key_unknown; - for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { - if (keyMapping[i].s60KeyCode == s60key) { - res = keyMapping[i].qtKey; - break; - } - } - return res; -} - -int QKeyMapperPrivate::mapS60ScanCodesToQt(TUint s60scanCode) -{ - int res = Qt::Key_unknown; - for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { - if (keyMapping[i].s60ScanCode == s60scanCode) { - res = keyMapping[i].qtKey; - break; - } - } - return res; -} - -int QKeyMapperPrivate::mapQtToS60Key(int qtKey) -{ - int res = KErrUnknown; - for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { - if (keyMapping[i].qtKey == qtKey) { - res = keyMapping[i].s60KeyCode; - break; - } - } - return res; -} - -int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey) -{ - int res = KErrUnknown; - for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { - if (keyMapping[i].qtKey == qtKey) { - res = keyMapping[i].s60ScanCode; - break; - } - } - return res; -} - -void QKeyMapperPrivate::updateInputLanguage() -{ -#ifdef Q_WS_S60 - TInt err; - CRepository *repo; - const TUid KCRUidAknFep = TUid::Uid(0x101F876D); - const TUint32 KAknFepInputTxtLang = 0x00000005; - TRAP(err, repo = CRepository::NewL(KCRUidAknFep)); - if (err != KErrNone) - return; - - TInt symbianLang; - err = repo->Get(KAknFepInputTxtLang, symbianLang); - delete repo; - if (err != KErrNone) - return; - - QString qtLang = QString::fromAscii(qt_symbianLocaleName(symbianLang)); - keyboardInputLocale = QLocale(qtLang); - keyboardInputDirection = (TBidiText::ScriptDirectionality(TLanguage(symbianLang)) == TBidiText::ERightToLeft) - ? Qt::RightToLeft : Qt::LeftToRight; -#else - keyboardInputLocale = QLocale(); - keyboardInputDirection = Qt::LeftToRight; -#endif -} - -QT_END_NAMESPACE diff --git a/src/gui/guikernel/qkeymapper_win.cpp b/src/gui/guikernel/qkeymapper_win.cpp deleted file mode 100644 index 92fa582617..0000000000 --- a/src/gui/guikernel/qkeymapper_win.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkeymapper_p.h" - -#include <qt_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 QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); - -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_* -}; - -/** - Remap return or action key to select key for windows mobile. -*/ -inline int winceKeyBend(int keyCode) -{ -#if defined(Q_OS_WINCE_WM) && defined(QT_KEYPAD_NAVIGATION) - // remap return or action key to select key for windows mobile. - // will be changed to a table remapping function in the next version (4.6/7). - if (keyCode == VK_RETURN && QApplication::keypadNavigationEnabled()) - return Qt::Key_Select; - else - return KeyTbl[keyCode]; -#else - return KeyTbl[keyCode]; -#endif -} - -#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 = 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 = winceKeyBend(vk); - - if (isDeadkey) - *isDeadkey = (res == -1); - - return code == Qt::Key_unknown ? 0 : code; -} - -Q_GUI_EXPORT int qt_translateKeyCode(int vk) -{ - int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : 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 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() -{ - deleteLayouts(); -} - -void QKeyMapperPrivate::deleteLayouts() -{ - for (int i = 0; i < 255; ++i) { - if (keyLayout[i]) { - delete keyLayout[i]; - keyLayout[i] = 0; - } - } -} - -void QKeyMapperPrivate::clearMappings() -{ - deleteLayouts(); - - /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout() - * returns a DWORD. */ - - LCID newLCID = MAKELCID((quintptr)GetKeyboardLayout(0), SORT_DEFAULT); -// keyboardInputLocale = qt_localeFromLCID(newLCID); - - bool bidi = false; - wchar_t LCIDFontSig[16]; - if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t)) - && (LCIDFontSig[7] & (wchar_t)0x0800)) - 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 = winceKeyBend(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) & 0x80000000) { - // 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 defined(Q_OS_WINCE) - 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); -#endif // Q_OS_WINCE - - 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) & 0x80000000; - - // A multi-character key not found by our look-ahead - if (msgType == WM_CHAR) { - QString s; - QChar ch = QChar((ushort)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 = QChar((ushort)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 keycodes (see bugs 127424, QTBUG-3630) - if (msg.wParam == 0 || 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 = winceKeyBend(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 (PeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) { - // Found a ?_CHAR - uch = QChar((ushort)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_t 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 = MapVirtualKey(msg.wParam, 2); - // If the high bit of the return value is set, it's a deadkey - if (!(map & 0x80000000)) - uch = QChar((ushort)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; - } -#else - Q_UNUSED(grab); -#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/guikernel/qkeymapper_x11.cpp b/src/gui/guikernel/qkeymapper_x11.cpp deleted file mode 100644 index 5383bfd456..0000000000 --- a/src/gui/guikernel/qkeymapper_x11.cpp +++ /dev/null @@ -1,1869 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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> - -#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 - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_XKB - -// bring in the auto-generated xkbLayoutData -#include "qkeymapper_x11_p.cpp" - -QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName) -{ - int i = 0; - while (xkbLayoutData[i].layout != 0) { - if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant) - return QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country); - ++i; - } - return QLocale::c(); -} -#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), xkb_currentGroup(0) -{ - memset(&coreDesc, 0, sizeof(coreDesc)); - -#ifndef QT_NO_XKB - if (X11->use_xkb) { - // get the current group - XkbStateRec xkbState; - if (XkbGetState(X11->display, XkbUseCoreKbd, &xkbState) == Success) - xkb_currentGroup = xkbState.group; - } -#endif -} - -QKeyMapperPrivate::~QKeyMapperPrivate() -{ - if (coreDesc.keysyms) - XFree(coreDesc.keysyms); -} - -QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event) -{ -#ifndef QT_NO_XKB - if (X11->use_xkb) - 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 == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { - // map shift+tab to shift+backtab - code = Qt::Key_Backtab; - text = QString(); - } - - 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 == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { - // map shift+tab to shift+backtab - code = Qt::Key_Backtab; - text = QString(); - } - - 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 (X11->use_xkb) { - // 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); - - // the layout names and variants are saved in the _XKB_RULES_NAMES property as a comma separated list - QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(','); - if (uint(xkb_currentGroup) < uint(layoutNames.count())) - layoutName = layoutNames.at(xkb_currentGroup); - QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(','); - if (uint(xkb_currentGroup) < uint(variantNames.count())) - variantName = variantNames.at(xkb_currentGroup); - } - - // ### ??? - // if (keyboardLayoutName.isEmpty()) - // qWarning("Qt: unable to determine keyboard layout, please talk to qt-bugs@trolltech.com"); ? - - keyboardInputLocale = q_getKeyboardLocale(layoutName, variantName); - keyboardInputDirection = keyboardInputLocale.textDirection(); - -#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 + 1, - &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 (X11->use_xkb) { - 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 on 10/2009 from X.org (X11/XF86keysym.h), defining some special -// multimedia keys. They are included here as not every system has them. -#define XF86XK_MonBrightnessUp 0x1008FF02 -#define XF86XK_MonBrightnessDown 0x1008FF03 -#define XF86XK_KbdLightOnOff 0x1008FF04 -#define XF86XK_KbdBrightnessUp 0x1008FF05 -#define XF86XK_KbdBrightnessDown 0x1008FF06 -#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_Mail 0x1008FF19 -#define XF86XK_Start 0x1008FF1A -#define XF86XK_Search 0x1008FF1B -#define XF86XK_AudioRecord 0x1008FF1C -#define XF86XK_Calculator 0x1008FF1D -#define XF86XK_Memo 0x1008FF1E -#define XF86XK_ToDoList 0x1008FF1F -#define XF86XK_Calendar 0x1008FF20 -#define XF86XK_PowerDown 0x1008FF21 -#define XF86XK_ContrastAdjust 0x1008FF22 -#define XF86XK_Back 0x1008FF26 -#define XF86XK_Forward 0x1008FF27 -#define XF86XK_Stop 0x1008FF28 -#define XF86XK_Refresh 0x1008FF29 -#define XF86XK_PowerOff 0x1008FF2A -#define XF86XK_WakeUp 0x1008FF2B -#define XF86XK_Eject 0x1008FF2C -#define XF86XK_ScreenSaver 0x1008FF2D -#define XF86XK_WWW 0x1008FF2E -#define XF86XK_Sleep 0x1008FF2F -#define XF86XK_Favorites 0x1008FF30 -#define XF86XK_AudioPause 0x1008FF31 -#define XF86XK_AudioMedia 0x1008FF32 -#define XF86XK_MyComputer 0x1008FF33 -#define XF86XK_LightBulb 0x1008FF35 -#define XF86XK_Shop 0x1008FF36 -#define XF86XK_History 0x1008FF37 -#define XF86XK_OpenURL 0x1008FF38 -#define XF86XK_AddFavorite 0x1008FF39 -#define XF86XK_HotLinks 0x1008FF3A -#define XF86XK_BrightnessAdjust 0x1008FF3B -#define XF86XK_Finance 0x1008FF3C -#define XF86XK_Community 0x1008FF3D -#define XF86XK_AudioRewind 0x1008FF3E -#define XF86XK_BackForward 0x1008FF3F -#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 -#define XF86XK_ApplicationLeft 0x1008FF50 -#define XF86XK_ApplicationRight 0x1008FF51 -#define XF86XK_Book 0x1008FF52 -#define XF86XK_CD 0x1008FF53 -#define XF86XK_Calculater 0x1008FF54 -#define XF86XK_Clear 0x1008FF55 -#define XF86XK_ClearGrab 0x1008FE21 -#define XF86XK_Close 0x1008FF56 -#define XF86XK_Copy 0x1008FF57 -#define XF86XK_Cut 0x1008FF58 -#define XF86XK_Display 0x1008FF59 -#define XF86XK_DOS 0x1008FF5A -#define XF86XK_Documents 0x1008FF5B -#define XF86XK_Excel 0x1008FF5C -#define XF86XK_Explorer 0x1008FF5D -#define XF86XK_Game 0x1008FF5E -#define XF86XK_Go 0x1008FF5F -#define XF86XK_iTouch 0x1008FF60 -#define XF86XK_LogOff 0x1008FF61 -#define XF86XK_Market 0x1008FF62 -#define XF86XK_Meeting 0x1008FF63 -#define XF86XK_MenuKB 0x1008FF65 -#define XF86XK_MenuPB 0x1008FF66 -#define XF86XK_MySites 0x1008FF67 -#define XF86XK_News 0x1008FF69 -#define XF86XK_OfficeHome 0x1008FF6A -#define XF86XK_Option 0x1008FF6C -#define XF86XK_Paste 0x1008FF6D -#define XF86XK_Phone 0x1008FF6E -#define XF86XK_Reply 0x1008FF72 -#define XF86XK_Reload 0x1008FF73 -#define XF86XK_RotateWindows 0x1008FF74 -#define XF86XK_RotationPB 0x1008FF75 -#define XF86XK_RotationKB 0x1008FF76 -#define XF86XK_Save 0x1008FF77 -#define XF86XK_Send 0x1008FF7B -#define XF86XK_Spell 0x1008FF7C -#define XF86XK_SplitScreen 0x1008FF7D -#define XF86XK_Support 0x1008FF7E -#define XF86XK_TaskPane 0x1008FF7F -#define XF86XK_Terminal 0x1008FF80 -#define XF86XK_Tools 0x1008FF81 -#define XF86XK_Travel 0x1008FF82 -#define XF86XK_Video 0x1008FF87 -#define XF86XK_Word 0x1008FF89 -#define XF86XK_Xfer 0x1008FF8A -#define XF86XK_ZoomIn 0x1008FF8B -#define XF86XK_ZoomOut 0x1008FF8C -#define XF86XK_Away 0x1008FF8D -#define XF86XK_Messenger 0x1008FF8E -#define XF86XK_WebCam 0x1008FF8F -#define XF86XK_MailForward 0x1008FF90 -#define XF86XK_Pictures 0x1008FF91 -#define XF86XK_Music 0x1008FF92 -#define XF86XK_Battery 0x1008FF93 -#define XF86XK_Bluetooth 0x1008FF94 -#define XF86XK_WLAN 0x1008FF95 -#define XF86XK_UWB 0x1008FF96 -#define XF86XK_AudioForward 0x1008FF97 -#define XF86XK_AudioRepeat 0x1008FF98 -#define XF86XK_AudioRandomPlay 0x1008FF99 -#define XF86XK_Subtitle 0x1008FF9A -#define XF86XK_AudioCycleTrack 0x1008FF9B -#define XF86XK_Time 0x1008FF9F -#define XF86XK_Select 0x1008FFA0 -#define XF86XK_View 0x1008FFA1 -#define XF86XK_TopMenu 0x1008FFA2 -#define XF86XK_Suspend 0x1008FFA7 -#define XF86XK_Hibernate 0x1008FFA8 - - -// 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 keys from X.org - This include multimedia keys, - // wireless/bluetooth/uwb keys, special launcher keys, etc. - 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, - 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, - XF86XK_Mail, Qt::Key_LaunchMail, - XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 5: remap properly - XF86XK_Calculator, Qt::Key_Launch1, - XF86XK_Memo, Qt::Key_Memo, - XF86XK_ToDoList, Qt::Key_ToDoList, - XF86XK_Calendar, Qt::Key_Calendar, - XF86XK_PowerDown, Qt::Key_PowerDown, - XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust, - XF86XK_Standby, Qt::Key_Standby, - XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp, - XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown, - XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff, - XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, - XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, - XF86XK_PowerOff, Qt::Key_PowerOff, - XF86XK_WakeUp, Qt::Key_WakeUp, - XF86XK_Eject, Qt::Key_Eject, - XF86XK_ScreenSaver, Qt::Key_ScreenSaver, - XF86XK_WWW, Qt::Key_WWW, - XF86XK_Sleep, Qt::Key_Sleep, - XF86XK_LightBulb, Qt::Key_LightBulb, - XF86XK_Shop, Qt::Key_Shop, - XF86XK_History, Qt::Key_History, - XF86XK_AddFavorite, Qt::Key_AddFavorite, - XF86XK_HotLinks, Qt::Key_HotLinks, - XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust, - XF86XK_Finance, Qt::Key_Finance, - XF86XK_Community, Qt::Key_Community, - XF86XK_AudioRewind, Qt::Key_AudioRewind, - XF86XK_BackForward, Qt::Key_BackForward, - XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft, - XF86XK_ApplicationRight, Qt::Key_ApplicationRight, - XF86XK_Book, Qt::Key_Book, - XF86XK_CD, Qt::Key_CD, - XF86XK_Calculater, Qt::Key_Calculator, - XF86XK_Clear, Qt::Key_Clear, - XF86XK_ClearGrab, Qt::Key_ClearGrab, - XF86XK_Close, Qt::Key_Close, - XF86XK_Copy, Qt::Key_Copy, - XF86XK_Cut, Qt::Key_Cut, - XF86XK_Display, Qt::Key_Display, - XF86XK_DOS, Qt::Key_DOS, - XF86XK_Documents, Qt::Key_Documents, - XF86XK_Excel, Qt::Key_Excel, - XF86XK_Explorer, Qt::Key_Explorer, - XF86XK_Game, Qt::Key_Game, - XF86XK_Go, Qt::Key_Go, - XF86XK_iTouch, Qt::Key_iTouch, - XF86XK_LogOff, Qt::Key_LogOff, - XF86XK_Market, Qt::Key_Market, - XF86XK_Meeting, Qt::Key_Meeting, - XF86XK_MenuKB, Qt::Key_MenuKB, - XF86XK_MenuPB, Qt::Key_MenuPB, - XF86XK_MySites, Qt::Key_MySites, - XF86XK_News, Qt::Key_News, - XF86XK_OfficeHome, Qt::Key_OfficeHome, - XF86XK_Option, Qt::Key_Option, - XF86XK_Paste, Qt::Key_Paste, - XF86XK_Phone, Qt::Key_Phone, - XF86XK_Reply, Qt::Key_Reply, - XF86XK_Reload, Qt::Key_Reload, - XF86XK_RotateWindows, Qt::Key_RotateWindows, - XF86XK_RotationPB, Qt::Key_RotationPB, - XF86XK_RotationKB, Qt::Key_RotationKB, - XF86XK_Save, Qt::Key_Save, - XF86XK_Send, Qt::Key_Send, - XF86XK_Spell, Qt::Key_Spell, - XF86XK_SplitScreen, Qt::Key_SplitScreen, - XF86XK_Support, Qt::Key_Support, - XF86XK_TaskPane, Qt::Key_TaskPane, - XF86XK_Terminal, Qt::Key_Terminal, - XF86XK_Tools, Qt::Key_Tools, - XF86XK_Travel, Qt::Key_Travel, - XF86XK_Video, Qt::Key_Video, - XF86XK_Word, Qt::Key_Word, - XF86XK_Xfer, Qt::Key_Xfer, - XF86XK_ZoomIn, Qt::Key_ZoomIn, - XF86XK_ZoomOut, Qt::Key_ZoomOut, - XF86XK_Away, Qt::Key_Away, - XF86XK_Messenger, Qt::Key_Messenger, - XF86XK_WebCam, Qt::Key_WebCam, - XF86XK_MailForward, Qt::Key_MailForward, - XF86XK_Pictures, Qt::Key_Pictures, - XF86XK_Music, Qt::Key_Music, - XF86XK_Battery, Qt::Key_Battery, - XF86XK_Bluetooth, Qt::Key_Bluetooth, - XF86XK_WLAN, Qt::Key_WLAN, - XF86XK_UWB, Qt::Key_UWB, - XF86XK_AudioForward, Qt::Key_AudioForward, - XF86XK_AudioRepeat, Qt::Key_AudioRepeat, - XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay, - XF86XK_Subtitle, Qt::Key_Subtitle, - XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack, - XF86XK_Time, Qt::Key_Time, - XF86XK_Select, Qt::Key_Select, - XF86XK_View, Qt::Key_View, - XF86XK_TopMenu, Qt::Key_TopMenu, - XF86XK_Bluetooth, Qt::Key_Bluetooth, - XF86XK_Suspend, Qt::Key_Suspend, - XF86XK_Hibernate, Qt::Key_Hibernate, - XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 5: remap properly - 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, - XF86XK_LaunchE, Qt::Key_LaunchG, - XF86XK_LaunchF, Qt::Key_LaunchH, - - // 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/guikernel/qkeymapper_x11_p.cpp b/src/gui/guikernel/qkeymapper_x11_p.cpp deleted file mode 100644 index 2dbe1e77a4..0000000000 --- a/src/gui/guikernel/qkeymapper_x11_p.cpp +++ /dev/null @@ -1,489 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// This file is auto-generated, do not edit! -// (Generated using util/xkbdatagen) - -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::RightToLeft, QLocale::Hebrew, QLocale::Israel }, - // name = il:lyx, description = Israel - { "il", "lyx", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, - // name = il:si1452, description = Israel - { "il", "si1452", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, - // name = il:phonetic, description = Israel - { "il", "phonetic", Qt::RightToLeft, 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::Syriac, QLocale::SyrianArabRepublic }, - // name = sy:syc, description = Syria - { "sy", "syc", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic }, - // name = sy:syc_phonetic, description = Syria - { "sy", "syc_phonetic", Qt::RightToLeft, QLocale::Syriac, 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/guikernel/qmime_mac.cpp b/src/gui/guikernel/qmime_mac.cpp deleted file mode 100644 index d6f6222c23..0000000000 --- a/src/gui/guikernel/qmime_mac.cpp +++ /dev/null @@ -1,1310 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 converts between a MIME type and a - \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform - Type Identifier (UTI)} format. - \since 4.2 - - \ingroup draganddrop - - Qt's drag and drop and clipboard facilities use the MIME - standard. On X11, this maps trivially to the Xdnd protocol. On - Mac, although some applications use MIME to describe clipboard - contents, it is more common to use Apple's UTI format. - - QMacPasteboardMime's role is to bridge the gap between MIME and UTI; - By subclasses this class, one can extend Qt's drag and drop - and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. - - A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. - - Qt has predefined support for the following UTIs: - \list - \i public.utf8-plain-text - converts to "text/plain" - \i public.utf16-plain-text - converts to "text/plain" - \i public.html - converts to "text/html" - \i public.url - converts to "text/uri-list" - \i public.file-url - converts to "text/uri-list" - \i public.tiff - converts to "application/x-qt-image" - \i public.vcard - converts to "text/plain" - \i com.apple.traditional-mac-plain-text - converts to "text/plain" - \i com.apple.pict - converts to "application/x-qt-image" - \endlist - - When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to - find an instance that can convert to, or from, a specific MIME type. It will do this by calling - canConvert() on each instance, starting with (and choosing) the last created instance first. - The actual conversions will be done by using convertToMime() and convertFromMime(). - - \note The API uses the term "flavor" in some cases. This is for backwards - compatibility reasons, and should now be understood as UTIs. -*/ - -/*! \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(QLatin1Char('/'), 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(QLatin1Char(';')); - 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(reinterpret_cast<const QChar *>(firstData.constData()), - firstData.size() / sizeof(QChar)); - } 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 - -// This can be removed once 10.6 is the minimum (or we have to require 64-bit) whichever comes first. - -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 //Q_WS_MAC32 - -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; - QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0, - reinterpret_cast<const UInt8 *>(a.constData()), - a.size(), kCFAllocatorNull); - QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0); - image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0); - - 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; -} - -class QMacPasteboardMimeUrl : public QMacPasteboardMime { -public: - QMacPasteboardMimeUrl() : 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 QMacPasteboardMimeUrl::convertorName() -{ - return QLatin1String("URL"); -} - -QString QMacPasteboardMimeUrl::flavorFor(const QString &mime) -{ - if(mime.startsWith(QLatin1String("text/uri-list"))) - return QLatin1String("public.url"); - return QString(); -} - -QString QMacPasteboardMimeUrl::mimeFor(QString flav) -{ - if(flav == QLatin1String("public.url")) - return QLatin1String("text/uri-list"); - return QString(); -} - -bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav) -{ - return flav == QLatin1String("public.url") - && mime == QLatin1String("text/uri-list"); -} - -QVariant QMacPasteboardMimeUrl::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> QMacPasteboardMimeUrl::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; -} - -class QMacPasteboardMimeVCard : public QMacPasteboardMime -{ -public: - QMacPasteboardMimeVCard() : 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 QMacPasteboardMimeVCard::convertorName() -{ - return QString("VCard"); -} - -bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav) -{ - return mimeFor(flav) == mime; -} - -QString QMacPasteboardMimeVCard::flavorFor(const QString &mime) -{ - if(mime.startsWith(QLatin1String("text/plain"))) - return QLatin1String("public.vcard"); - return QString(); -} - -QString QMacPasteboardMimeVCard::mimeFor(QString flav) -{ - if (flav == QLatin1String("public.vcard")) - return QLatin1String("text/plain"); - return QString(); -} - -QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString) -{ - QByteArray cards; - if (mime == QLatin1String("text/plain")) { - for (int i=0; i<data.size(); ++i) - cards += data[i]; - } - return QVariant(cards); -} - -QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) -{ - QList<QByteArray> ret; - if (mime == QLatin1String("text/plain")) - ret.append(data.toString().toUtf8()); - 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> ®istry, 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 - // 10.6 does automatic synthesis to and from PICT to standard image types (like TIFF), - // so don't bother doing it ourselves, especially since it's not available in 64-bit. - if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) - new QMacPasteboardMimePict; -#endif - new QMacPasteboardMimeUnicodeText; - new QMacPasteboardMimePlainText; - new QMacPasteboardMimeHTMLText; - new QMacPasteboardMimeFileUri; - new QMacPasteboardMimeUrl; - new QMacPasteboardMimeTypeName; - new QMacPasteboardMimeVCard; - //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/guikernel/qmime_win.cpp b/src/gui/guikernel/qmime_win.cpp deleted file mode 100644 index feb8b78eca..0000000000 --- a/src/gui/guikernel/qmime_win.cpp +++ /dev/null @@ -1,1556 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 draganddrop - - 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) -{ - int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); - 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()"); - wchar_t buf[256] = {0}; - GetClipboardFormatName(fmtetc.cfFormat, buf, 255); - qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(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() * sizeof(ushort); - 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::fromWCharArray((const wchar_t *)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()) { - size += sizeof(ushort) * (fn.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; - - d->fWide = true; - wchar_t* f = (wchar_t*)files; - for (int i=0; i<fileNames.size(); i++) { - int l = fileNames.at(i).length(); - memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort)); - f += l; - *f++ = 0; - } - *f = 0; - - return setData(result, pmedium); - } else if (getCf(formatetc) == CF_INETURL_W) { - QList<QUrl> urls = mimeData->urls(); - QByteArray result; - if (!urls.isEmpty()) { - QString url = urls.at(0).toString(); - result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort)); - } - result.append('\0'); - result.append('\0'); - return setData(result, pmedium); - } else if (getCf(formatetc) == CF_INETURL) { - QList<QUrl> urls = mimeData->urls(); - QByteArray result; - if (!urls.isEmpty()) - 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 wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles); - int i = 0; - while (filesw[i]) { - QString fileurl = QString::fromWCharArray(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::fromWCharArray((const wchar_t *)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() -{ - CF_PNG = RegisterClipboardFormat(L"PNG"); -} - -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 (img.format() > QImage::Format_ARGB32) - img = img.convertToFormat(QImage::Format_RGB32); - 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() * sizeof(ushort); - 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 Mozilla) - val = QString::fromWCharArray((const wchar_t *)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=\""; - -static bool isCustomMimeType(const QString &mimeType) -{ - return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); -} - -static QString customMimeType(const QString &mimeType) -{ - int len = sizeof(x_qt_windows_mime) - 1; - 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); - int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); - 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); - int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); - 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()) - return format; - - wchar_t buffer[256]; - int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); - - if (len) { - QString clipFormat = QString::fromWCharArray(buffer, 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/guikernel/qmotifdnd_x11.cpp b/src/gui/guikernel/qmotifdnd_x11.cpp deleted file mode 100644 index eef4cc470b..0000000000 --- a/src/gui/guikernel/qmotifdnd_x11.cpp +++ /dev/null @@ -1,1031 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* The following copyright notice pertains to the code as contributed -to Qt, not to Nokia'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; - - // squelch potential valgrind errors about uninitialized reads - memset(&receiver_prop, 0, sizeof(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)) { - } - } - - // 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/guikernel/qole_win.cpp b/src/gui/guikernel/qole_win.cpp deleted file mode 100644 index 24e2d5b292..0000000000 --- a/src/gui/guikernel/qole_win.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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/guikernel/qwindowdefs_win.h b/src/gui/guikernel/qwindowdefs_win.h deleted file mode 100644 index a4dd38410c..0000000000 --- a/src/gui/guikernel/qwindowdefs_win.h +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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_CORE_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/image/qpixmap_mac.cpp b/src/gui/image/qpixmap_mac.cpp deleted file mode 100644 index 72e2aa6e04..0000000000 --- a/src/gui/image/qpixmap_mac.cpp +++ /dev/null @@ -1,1195 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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> -#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.data())->pixels; -} - -Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix) -{ - return static_cast<QMacPixmapData*>(pix->data.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); - - uint cgflags = kCGImageAlphaPremultipliedFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#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), has_alpha(0), has_mask(0), - uninit(true), pixels(0), pixelsSize(0), pixelsToFree(0), - bytesPerRow(0), cg_data(0), cg_dataBeingReleased(0), cg_mask(0), - pengine(0) -{ -} - -QPixmapData *QMacPixmapData::createCompatiblePixmapData() const -{ - return new QMacPixmapData(pixelType()); -} - -#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; - is_null = (w <= 0 || h <= 0); - d = (pixelType() == BitmapType ? 1 : 32); - bool make_null = w <= 0 || h <= 0; // create null pixmap - if (make_null || d == 0) { - w = 0; - h = 0; - is_null = true; - 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(); - is_null = (w <= 0 || h <= 0); - 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.colorCount() == 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) = 0xFF000000; - else - *(drow+x) = 0xFFFFFFFF; - } - break; - } - case QImage::Format_Indexed8: { - int numColors = image.numColors(); - if (numColors > 0) { - for (int x = 0; x < w; ++x) { - int index = *(srow + x); - *(drow+x) = PREMUL(image.color(qMin(index, numColors))); - } - } - } 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.colorCount(); 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->colorCount();loopc++) { - if(qi->color(loopc)==mycol) - return loopc; - } - qi->setColorCount(qi->colorCount()+1); - qi->setColor(qi->colorCount(),mycol); - return qi->colorCount(); -} - -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.setColorCount(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; - } - } - - // If we had an alpha channel from before, don't - // switch it off. Only go from no alpha to alpha: - if (fillColor.alpha() != 255) - macSetHasAlpha(true); -} - -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.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.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); - 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; - macReleaseCGImageRef(); -} - -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); - uint cgflags = kCGImageAlphaPremultipliedFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#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, pixelsSize); - pixels = base_pixels; - pixelsSize = numBytes; -} - -#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 -} - -// 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); - QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap); - 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. - \warning As of Qt 4.6, this function \e{always} returns zero. -*/ - -Qt::HANDLE QPixmap::macQDHandle() const -{ - return 0; -} - -/*! \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. - \warning As of Qt 4.6, this function \e{always} returns zero. -*/ - -Qt::HANDLE QPixmap::macQDAlphaHandle() const -{ - 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 (isNull()) - return 0; - - if (data->classId() == QPixmapData::MacClass) { - QMacPixmapData *d = static_cast<QMacPixmapData *>(data.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.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.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; - - //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 - -/*! \internal */ -QPaintEngine* QMacPixmapData::paintEngine() const -{ - if (!pengine) { - QMacPixmapData *that = const_cast<QMacPixmapData*>(this); - that->pengine = new QCoreGraphicsPaintEngine(); - } - 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; -} - -bool QMacPixmapData::scroll(int dx, int dy, const QRect &rect) -{ - Q_UNUSED(dx); - Q_UNUSED(dy); - Q_UNUSED(rect); - return false; -} - -/*! - \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); - ret.fill(Qt::transparent); - 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 deleted file mode 100644 index 307e38aceb..0000000000 --- a/src/gui/image/qpixmap_mac_p.h +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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(); - - QPixmapData *createCompatiblePixmapData() const; - - void resize(int width, int height); - void fromImage(const QImage &image, Qt::ImageConversionFlags flags); - void copy(const QPixmapData *data, const QRect &rect); - bool scroll(int dx, int dy, 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: - - 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; - uint pixelsSize; - quint32 *pixelsToFree; - uint bytesPerRow; - QRectF cg_mask_rect; - CGImageRef cg_data, cg_dataBeingReleased, cg_mask; - 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_s60.cpp b/src/gui/image/qpixmap_s60.cpp deleted file mode 100644 index c8aa003ffa..0000000000 --- a/src/gui/image/qpixmap_s60.cpp +++ /dev/null @@ -1,1040 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <exception> -#include <w32std.h> -#include <fbs.h> - -#include <private/qapplication_p.h> -#include <private/qgraphicssystem_p.h> -#include <private/qt_s60_p.h> -#include <private/qpaintengine_s60_p.h> -#include <private/qfont_p.h> - -#include "qpixmap.h" -#include "qpixmap_raster_p.h" -#include <qwidget.h> -#include "qpixmap_s60_p.h" -#include "qnativeimage_p.h" -#include "qbitmap.h" -#include "qimage.h" -#include "qimage_p.h" - -#include <fbs.h> - -QT_BEGIN_NAMESPACE - -const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80 }; - -static bool cleanup_function_registered = false; -static QS60PixmapData *firstPixmap = 0; - -// static -void QS60PixmapData::qt_symbian_register_pixmap(QS60PixmapData *pd) -{ - if (!cleanup_function_registered) { - qAddPostRoutine(qt_symbian_release_pixmaps); - cleanup_function_registered = true; - } - - pd->next = firstPixmap; - pd->prev = 0; - if (firstPixmap) - firstPixmap->prev = pd; - firstPixmap = pd; -} - -// static -void QS60PixmapData::qt_symbian_unregister_pixmap(QS60PixmapData *pd) -{ - if (pd->next) - pd->next->prev = pd->prev; - if (pd->prev) - pd->prev->next = pd->next; - else - firstPixmap = pd->next; -} - -// static -void QS60PixmapData::qt_symbian_release_pixmaps() -{ - // Scan all QS60PixmapData objects in the system and destroy them. - QS60PixmapData *pd = firstPixmap; - while (pd != 0) { - pd->release(); - pd = pd->next; - } -} - -/* - \class QSymbianFbsClient - \since 4.6 - \internal - - Symbian Font And Bitmap server client that is - used to lock the global bitmap heap. Only used in - S60 v3.1 and S60 v3.2. -*/ -_LIT(KFBSERVLargeBitmapAccessName,"FbsLargeBitmapAccess"); -class QSymbianFbsClient -{ -public: - - QSymbianFbsClient() : heapLocked(false) - { - heapLock.OpenGlobal(KFBSERVLargeBitmapAccessName); - } - - ~QSymbianFbsClient() - { - heapLock.Close(); - } - - bool lockHeap() - { - bool wasLocked = heapLocked; - - if (heapLock.Handle() && !heapLocked) { - heapLock.Wait(); - heapLocked = true; - } - - return wasLocked; - } - - bool unlockHeap() - { - bool wasLocked = heapLocked; - - if (heapLock.Handle() && heapLocked) { - heapLock.Signal(); - heapLocked = false; - } - - return wasLocked; - } - - -private: - - RMutex heapLock; - bool heapLocked; -}; - -Q_GLOBAL_STATIC(QSymbianFbsClient, qt_symbianFbsClient); - - - -// QSymbianFbsHeapLock - -QSymbianFbsHeapLock::QSymbianFbsHeapLock(LockAction a) -: action(a), wasLocked(false) -{ - QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); - if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3) - wasLocked = qt_symbianFbsClient()->unlockHeap(); -} - -QSymbianFbsHeapLock::~QSymbianFbsHeapLock() -{ - // Do nothing -} - -void QSymbianFbsHeapLock::relock() -{ - QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); - if (wasLocked && (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3)) - qt_symbianFbsClient()->lockHeap(); -} - -/* - \class QSymbianBitmapDataAccess - \since 4.6 - \internal - - Data access class that is used to locks/unlocks pixel data - when drawing or modifying CFbsBitmap pixel data. -*/ -class QSymbianBitmapDataAccess -{ -public: - - static int heapRefCount; - QSysInfo::SymbianVersion symbianVersion; - - explicit QSymbianBitmapDataAccess() - { - symbianVersion = QSysInfo::symbianVersion(); - }; - - ~QSymbianBitmapDataAccess() {}; - - inline void beginDataAccess(CFbsBitmap *bitmap) - { - if (symbianVersion == QSysInfo::SV_9_2) { - if (heapRefCount == 0) - qt_symbianFbsClient()->lockHeap(); - } else { - bitmap->LockHeap(ETrue); - } - - heapRefCount++; - } - - inline void endDataAccess(CFbsBitmap *bitmap) - { - heapRefCount--; - - if (symbianVersion == QSysInfo::SV_9_2) { - if (heapRefCount == 0) - qt_symbianFbsClient()->unlockHeap(); - } else { - bitmap->UnlockHeap(ETrue); - } - } -}; - -int QSymbianBitmapDataAccess::heapRefCount = 0; - - -#define UPDATE_BUFFER() \ - { \ - beginDataAccess(); \ - endDataAccess(); \ -} - - -static CFbsBitmap* createSymbianCFbsBitmap(const TSize& size, TDisplayMode mode) -{ - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - - CFbsBitmap* bitmap = 0; - QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap); - - if (bitmap->Create(size, mode) != KErrNone) { - delete bitmap; - bitmap = 0; - } - - lock.relock(); - - return bitmap; -} - -static CFbsBitmap* uncompress(CFbsBitmap* bitmap) -{ - if(bitmap->IsCompressedInRAM()) { - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - - CFbsBitmap *uncompressed = 0; - QT_TRAP_THROWING(uncompressed = new (ELeave) CFbsBitmap); - - if (uncompressed->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) { - delete bitmap; - bitmap = 0; - lock.relock(); - - return bitmap; - } - - lock.relock(); - - CFbsBitmapDevice* bitmapDevice = 0; - CFbsBitGc *bitmapGc = 0; - QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed)); - QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); - bitmapGc->Activate(bitmapDevice); - - bitmapGc->BitBlt(TPoint(), bitmap); - - delete bitmapGc; - delete bitmapDevice; - - return uncompressed; - } else { - return bitmap; - } -} - -QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h) -{ - CWsScreenDevice* screenDevice = S60->screenDevice(); - TSize screenSize = screenDevice->SizeInPixels(); - - TSize srcSize; - // Find out if this is one of our windows. - QSymbianControl *sControl; - sControl = winId->MopGetObject(sControl); - if (sControl && sControl->widget()->windowType() == Qt::Desktop) { - // Grabbing desktop widget - srcSize = screenSize; - } else { - TPoint relativePos = winId->PositionRelativeToScreen(); - x += relativePos.iX; - y += relativePos.iY; - srcSize = winId->Size(); - } - - TRect srcRect(TPoint(x, y), srcSize); - // Clip to the screen - srcRect.Intersection(TRect(screenSize)); - - if (w > 0 && h > 0) { - TRect subRect(TPoint(x, y), TSize(w, h)); - // Clip to the subRect - srcRect.Intersection(subRect); - } - - if (srcRect.IsEmpty()) - return QPixmap(); - - CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode()); - - QPixmap pix; - - if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) { - pix = QPixmap::fromSymbianCFbsBitmap(temporary); - } - - delete temporary; - return pix; -} - -/*! - \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const - \since 4.6 - - Creates a \c CFbsBitmap that is equivalent to the QPixmap. Internally this - function will try to duplicate the handle instead of copying the data, - however in scenarios where this is not possible the data will be copied. - If the creation fails or the pixmap is null, then this function returns 0. - - It is the caller's responsibility to release the \c CFbsBitmap data - after use either by deleting the bitmap or calling \c Reset(). - - \warning On S60 3.1 and S60 3.2, semi-transparent pixmaps are always copied - and not duplicated. - \warning This function is only available on Symbian OS. - - \sa fromSymbianCFbsBitmap() -*/ -CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const -{ - QPixmapData *data = pixmapData(); - if (!data || data->isNull()) - return 0; - - return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPixmapData::FbsBitmap)); -} - -/*! - \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) - \since 4.6 - - Creates a QPixmap from a \c CFbsBitmap \a bitmap. Internally this function - will try to duplicate the bitmap handle instead of copying the data, however - in scenarios where this is not possible the data will be copied. - To be sure that QPixmap does not modify your original instance, you should - make a copy of your \c CFbsBitmap before calling this function. - If the CFbsBitmap is not valid this function will return a null QPixmap. - For performance reasons it is recommended to use a \a bitmap with a display - mode of EColor16MAP or EColor16MU whenever possible. - - \warning This function is only available on Symbian OS. - - \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} -*/ -QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) -{ - if (!bitmap) - return QPixmap(); - - QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType)); - data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap); - QPixmap pixmap(data.take()); - return pixmap; -} - -QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type), - symbianBitmapDataAccess(new QSymbianBitmapDataAccess), - cfbsBitmap(0), - pengine(0), - bytes(0), - formatLocked(false), - next(0), - prev(0) -{ - qt_symbian_register_pixmap(this); -} - -QS60PixmapData::~QS60PixmapData() -{ - release(); - delete symbianBitmapDataAccess; - qt_symbian_unregister_pixmap(this); -} - -void QS60PixmapData::resize(int width, int height) -{ - if (width <= 0 || height <= 0) { - w = width; - h = height; - is_null = true; - - release(); - return; - } else if (!cfbsBitmap) { - TDisplayMode mode; - if (pixelType() == BitmapType) - mode = EGray2; - else - mode = EColor16MU; - - CFbsBitmap* bitmap = createSymbianCFbsBitmap(TSize(width, height), mode); - fromSymbianBitmap(bitmap); - } else { - - TSize newSize(width, height); - - if(cfbsBitmap->SizeInPixels() != newSize) { - cfbsBitmap->Resize(TSize(width, height)); - if(pengine) { - delete pengine; - pengine = 0; - } - } - - UPDATE_BUFFER(); - } -} - -void QS60PixmapData::release() -{ - if (cfbsBitmap) { - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - delete cfbsBitmap; - lock.relock(); - } - - delete pengine; - image = QImage(); - cfbsBitmap = 0; - pengine = 0; - bytes = 0; -} - -/*! - * Takes ownership of bitmap. Used by window surface - */ -void QS60PixmapData::fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat) -{ - Q_ASSERT(bitmap); - - release(); - - cfbsBitmap = bitmap; - formatLocked = lockFormat; - - setSerialNumber(cfbsBitmap->Handle()); - - UPDATE_BUFFER(); - - // Create default palette if needed - if (cfbsBitmap->DisplayMode() == EGray2) { - image.setColorCount(2); - image.setColor(0, QColor(Qt::color0).rgba()); - image.setColor(1, QColor(Qt::color1).rgba()); - - //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid - //So invert mono bitmaps so that masks work correctly. - image.invertPixels(); - } else if (cfbsBitmap->DisplayMode() == EGray256) { - for (int i=0; i < 256; ++i) - image.setColor(i, qRgb(i, i, i)); - } else if (cfbsBitmap->DisplayMode() == EColor256) { - const TColor256Util *palette = TColor256Util::Default(); - for (int i=0; i < 256; ++i) - image.setColor(i, (QRgb)(palette->Color256(i).Value())); - } -} - -QImage QS60PixmapData::toImage(const QRect &r) const -{ - QS60PixmapData *that = const_cast<QS60PixmapData*>(this); - that->beginDataAccess(); - QImage copy = that->image.copy(r); - that->endDataAccess(); - - return copy; -} - -void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) -{ - release(); - - QImage sourceImage; - - if (pixelType() == BitmapType) { - sourceImage = img.convertToFormat(QImage::Format_MonoLSB); - } else { - if (img.depth() == 1) { - sourceImage = img.hasAlphaChannel() - ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) - : img.convertToFormat(QImage::Format_RGB32); - } else { - - QImage::Format opaqueFormat = QNativeImage::systemFormat(); - QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; - - if (!img.hasAlphaChannel() - || ((flags & Qt::NoOpaqueDetection) == 0 - && !const_cast<QImage &>(img).data_ptr()->checkForAlphaPixels())) { - sourceImage = img.convertToFormat(opaqueFormat); - } else { - sourceImage = img.convertToFormat(alphaFormat); - } - } - } - - - QImage::Format destFormat = sourceImage.format(); - TDisplayMode mode; - switch (destFormat) { - case QImage::Format_MonoLSB: - mode = EGray2; - break; - case QImage::Format_RGB32: - mode = EColor16MU; - break; - case QImage::Format_ARGB32_Premultiplied: - if (S60->supportsPremultipliedAlpha) { - mode = Q_SYMBIAN_ECOLOR16MAP; - break; - } else { - destFormat = QImage::Format_ARGB32; - } - // Fall through intended - case QImage::Format_ARGB32: - mode = EColor16MA; - break; - case QImage::Format_Invalid: - return; - default: - qWarning("Image format not supported: %d", image.format()); - return; - } - - cfbsBitmap = createSymbianCFbsBitmap(TSize(sourceImage.width(), sourceImage.height()), mode); - if (!cfbsBitmap) { - qWarning("Could not create CFbsBitmap"); - release(); - return; - } - - setSerialNumber(cfbsBitmap->Handle()); - - const uchar *sptr = const_cast<const QImage &>(sourceImage).bits(); - symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); - uchar *dptr = (uchar*)cfbsBitmap->DataAddress(); - Mem::Copy(dptr, sptr, sourceImage.byteCount()); - symbianBitmapDataAccess->endDataAccess(cfbsBitmap); - - UPDATE_BUFFER(); - - if (destFormat == QImage::Format_MonoLSB) { - image.setColorCount(2); - image.setColor(0, QColor(Qt::color0).rgba()); - image.setColor(1, QColor(Qt::color1).rgba()); - } else { - image.setColorTable(sourceImage.colorTable()); - } -} - -void QS60PixmapData::copy(const QPixmapData *data, const QRect &rect) -{ - const QS60PixmapData *s60Data = static_cast<const QS60PixmapData*>(data); - fromImage(s60Data->toImage(rect), Qt::AutoColor | Qt::OrderedAlphaDither); -} - -bool QS60PixmapData::scroll(int dx, int dy, const QRect &rect) -{ - beginDataAccess(); - bool res = QRasterPixmapData::scroll(dx, dy, rect); - endDataAccess(); - return res; -} - -int QS60PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const -{ - if (!cfbsBitmap) - return 0; - - switch (metric) { - case QPaintDevice::PdmWidth: - return cfbsBitmap->SizeInPixels().iWidth; - case QPaintDevice::PdmHeight: - return cfbsBitmap->SizeInPixels().iHeight; - case QPaintDevice::PdmWidthMM: - return qRound(cfbsBitmap->SizeInPixels().iWidth * 25.4 / qt_defaultDpiX()); - case QPaintDevice::PdmHeightMM: - return qRound(cfbsBitmap->SizeInPixels().iHeight * 25.4 / qt_defaultDpiY()); - case QPaintDevice::PdmNumColors: - return TDisplayModeUtils::NumDisplayModeColors(cfbsBitmap->DisplayMode()); - case QPaintDevice::PdmDpiX: - case QPaintDevice::PdmPhysicalDpiX: - return qt_defaultDpiX(); - case QPaintDevice::PdmDpiY: - case QPaintDevice::PdmPhysicalDpiY: - return qt_defaultDpiY(); - case QPaintDevice::PdmDepth: - return TDisplayModeUtils::NumDisplayModeBitsPerPixel(cfbsBitmap->DisplayMode()); - default: - qWarning("QPixmap::metric: Invalid metric command"); - } - return 0; - -} - -void QS60PixmapData::fill(const QColor &color) -{ - if (color.alpha() != 255) { - QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied); - im.fill(PREMUL(color.rgba())); - release(); - fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither); - } else { - beginDataAccess(); - QRasterPixmapData::fill(color); - endDataAccess(); - } -} - -void QS60PixmapData::setMask(const QBitmap &mask) -{ - if (mask.size().isEmpty()) { - if (image.depth() != 1) { - QImage newImage = image.convertToFormat(QImage::Format_RGB32); - release(); - fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither); - } - } else if (image.depth() == 1) { - beginDataAccess(); - QRasterPixmapData::setMask(mask); - endDataAccess(); - } else { - const int w = image.width(); - const int h = image.height(); - - const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB); - QImage newImage = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - for (int y = 0; y < h; ++y) { - const uchar *mscan = imageMask.scanLine(y); - QRgb *tscan = (QRgb *)newImage.scanLine(y); - for (int x = 0; x < w; ++x) { - if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7])) - tscan[x] = 0; - } - } - release(); - fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither); - } -} - -void QS60PixmapData::setAlphaChannel(const QPixmap &alphaChannel) -{ - QImage img(toImage()); - img.setAlphaChannel(alphaChannel.toImage()); - release(); - fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither); -} - -QImage QS60PixmapData::toImage() const -{ - return toImage(QRect()); -} - -QPaintEngine* QS60PixmapData::paintEngine() const -{ - if (!pengine) { - QS60PixmapData *that = const_cast<QS60PixmapData*>(this); - that->pengine = new QS60PaintEngine(&that->image, that); - } - return pengine; -} - -void QS60PixmapData::beginDataAccess() -{ - if(!cfbsBitmap) - return; - - symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); - - uchar* newBytes = (uchar*)cfbsBitmap->DataAddress(); - - TSize size = cfbsBitmap->SizeInPixels(); - - if (newBytes == bytes && image.width() == size.iWidth && image.height() == size.iHeight) - return; - - bytes = newBytes; - TDisplayMode mode = cfbsBitmap->DisplayMode(); - QImage::Format format = qt_TDisplayMode2Format(mode); - // On S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type. - // S60 window surface needs backing store pixmap for transparent window in ARGB32 format. - // In that case formatLocked is true. - if (!formatLocked && format == QImage::Format_ARGB32) - format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format - - QVector<QRgb> savedColorTable; - if (!image.isNull()) - savedColorTable = image.colorTable(); - - image = QImage(bytes, size.iWidth, size.iHeight, format); - - // Restore the palette or create a default - if (!savedColorTable.isEmpty()) { - image.setColorTable(savedColorTable); - } - - w = size.iWidth; - h = size.iHeight; - d = image.depth(); - is_null = (w <= 0 || h <= 0); - - if (pengine) { - QS60PaintEngine *engine = static_cast<QS60PaintEngine *>(pengine); - engine->prepare(&image); - } -} - -void QS60PixmapData::endDataAccess(bool readOnly) const -{ - Q_UNUSED(readOnly); - - if(!cfbsBitmap) - return; - - symbianBitmapDataAccess->endDataAccess(cfbsBitmap); -} - -/*! - \since 4.6 - - Returns a QPixmap that wraps given \a sgImage graphics resource. - The data should be valid even when original RSgImage handle has been - closed. - - \warning This function is only available on Symbian OS. - - \sa toSymbianRSgImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} -*/ - -QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage) -{ - // It is expected that RSgImage will - // CURRENTLY be used in conjuction with - // OpenVG graphics system - // - // Surely things might change in future - - if (!sgImage) - return QPixmap(); - - QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType)); - data->fromNativeType(reinterpret_cast<void*>(sgImage), QPixmapData::SgImage); - QPixmap pixmap(data.take()); - return pixmap; -} - -/*! -\since 4.6 - -Returns a \c RSgImage that is equivalent to the QPixmap by copying the data. - -It is the caller's responsibility to close/delete the \c RSgImage after use. - -\warning This function is only available on Symbian OS. - -\sa fromSymbianRSgImage() -*/ - -RSgImage *QPixmap::toSymbianRSgImage() const -{ - // It is expected that RSgImage will - // CURRENTLY be used in conjuction with - // OpenVG graphics system - // - // Surely things might change in future - - if (isNull()) - return 0; - - RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmapData()->toNativeType(QPixmapData::SgImage)); - - return sgImage; -} - -void* QS60PixmapData::toNativeType(NativeType type) -{ - if (type == QPixmapData::SgImage) { - return 0; - } else if (type == QPixmapData::FbsBitmap) { - - if (isNull() || !cfbsBitmap) - return 0; - - bool convertToArgb32 = false; - bool needsCopy = false; - - if (!(S60->supportsPremultipliedAlpha)) { - // Convert argb32_premultiplied to argb32 since Symbian 9.2 does - // not support premultipied format. - - if (image.format() == QImage::Format_ARGB32_Premultiplied) { - needsCopy = true; - convertToArgb32 = true; - } - } - - CFbsBitmap *bitmap = 0; - - TDisplayMode displayMode = cfbsBitmap->DisplayMode(); - - if(displayMode == EGray2) { - //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid - //So invert mono bitmaps so that masks work correctly. - beginDataAccess(); - image.invertPixels(); - endDataAccess(); - needsCopy = true; - } - - if (needsCopy) { - QImage source; - - if (convertToArgb32) { - beginDataAccess(); - source = image.convertToFormat(QImage::Format_ARGB32); - endDataAccess(); - displayMode = EColor16MA; - } else { - source = image; - } - - CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode); - const uchar *sptr = source.bits(); - symbianBitmapDataAccess->beginDataAccess(newBitmap); - - uchar *dptr = (uchar*)newBitmap->DataAddress(); - Mem::Copy(dptr, sptr, source.byteCount()); - - symbianBitmapDataAccess->endDataAccess(newBitmap); - - bitmap = newBitmap; - } else { - - QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap); - - TInt err = bitmap->Duplicate(cfbsBitmap->Handle()); - if (err != KErrNone) { - qWarning("Could not duplicate CFbsBitmap"); - delete bitmap; - bitmap = 0; - } - } - - if(displayMode == EGray2) { - // restore pixels - beginDataAccess(); - image.invertPixels(); - endDataAccess(); - } - - return reinterpret_cast<void*>(bitmap); - - } - - return 0; -} - -void QS60PixmapData::fromNativeType(void* pixmap, NativeType nativeType) -{ - if (nativeType == QPixmapData::SgImage) { - return; - } else if (nativeType == QPixmapData::FbsBitmap && pixmap) { - - CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap); - - bool deleteSourceBitmap = false; - bool needsCopy = false; - -#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE - - // Rasterize extended bitmaps - - TUid extendedBitmapType = bitmap->ExtendedBitmapType(); - if (extendedBitmapType != KNullUid) { - CFbsBitmap *rasterBitmap = createSymbianCFbsBitmap(bitmap->SizeInPixels(), EColor16MA); - - CFbsBitmapDevice *rasterBitmapDev = 0; - QT_TRAP_THROWING(rasterBitmapDev = CFbsBitmapDevice::NewL(rasterBitmap)); - - CFbsBitGc *rasterBitmapGc = 0; - TInt err = rasterBitmapDev->CreateContext(rasterBitmapGc); - if (err != KErrNone) { - delete rasterBitmap; - delete rasterBitmapDev; - rasterBitmapDev = 0; - return; - } - - rasterBitmapGc->BitBlt(TPoint( 0, 0), bitmap); - - bitmap = rasterBitmap; - - delete rasterBitmapDev; - delete rasterBitmapGc; - - rasterBitmapDev = 0; - rasterBitmapGc = 0; - - deleteSourceBitmap = true; - } -#endif - - - deleteSourceBitmap = bitmap->IsCompressedInRAM(); - CFbsBitmap *sourceBitmap = uncompress(bitmap); - - TDisplayMode displayMode = sourceBitmap->DisplayMode(); - QImage::Format format = qt_TDisplayMode2Format(displayMode); - - QImage::Format opaqueFormat = QNativeImage::systemFormat(); - QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; - - if (format != opaqueFormat && format != alphaFormat && format != QImage::Format_MonoLSB) - needsCopy = true; - - - type = (format != QImage::Format_MonoLSB) - ? QPixmapData::PixmapType - : QPixmapData::BitmapType; - - if (needsCopy) { - - TSize size = sourceBitmap->SizeInPixels(); - int bytesPerLine = sourceBitmap->ScanLineLength(size.iWidth, displayMode); - - QSymbianBitmapDataAccess da; - da.beginDataAccess(sourceBitmap); - uchar *bytes = (uchar*)sourceBitmap->DataAddress(); - QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format); - img = img.copy(); - da.endDataAccess(sourceBitmap); - - if(displayMode == EGray2) { - //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid - //So invert mono bitmaps so that masks work correctly. - img.invertPixels(); - } else if(displayMode == EColor16M) { - img = img.rgbSwapped(); // EColor16M is BGR - } - - fromImage(img, Qt::AutoColor); - - if(deleteSourceBitmap) - delete sourceBitmap; - } else { - CFbsBitmap* duplicate = 0; - QT_TRAP_THROWING(duplicate = new (ELeave) CFbsBitmap); - - TInt err = duplicate->Duplicate(sourceBitmap->Handle()); - if (err != KErrNone) { - qWarning("Could not duplicate CFbsBitmap"); - - if(deleteSourceBitmap) - delete sourceBitmap; - - delete duplicate; - return; - } - - fromSymbianBitmap(duplicate); - - if(deleteSourceBitmap) - delete sourceBitmap; - } - } -} - -void QS60PixmapData::convertToDisplayMode(int mode) -{ - const TDisplayMode displayMode = static_cast<TDisplayMode>(mode); - if (!cfbsBitmap || cfbsBitmap->DisplayMode() == displayMode) - return; - if (image.depth() != TDisplayModeUtils::NumDisplayModeBitsPerPixel(displayMode)) { - qWarning("Cannot convert display mode due to depth mismatch"); - return; - } - - const TSize size = cfbsBitmap->SizeInPixels(); - QScopedPointer<CFbsBitmap> newBitmap(createSymbianCFbsBitmap(size, displayMode)); - - const uchar *sptr = const_cast<const QImage &>(image).bits(); - symbianBitmapDataAccess->beginDataAccess(newBitmap.data()); - uchar *dptr = (uchar*)newBitmap->DataAddress(); - Mem::Copy(dptr, sptr, image.byteCount()); - symbianBitmapDataAccess->endDataAccess(newBitmap.data()); - - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - delete cfbsBitmap; - lock.relock(); - cfbsBitmap = newBitmap.take(); - setSerialNumber(cfbsBitmap->Handle()); - UPDATE_BUFFER(); -} - -QPixmapData *QS60PixmapData::createCompatiblePixmapData() const -{ - return new QS60PixmapData(pixelType()); -} - -QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap_s60_p.h b/src/gui/image/qpixmap_s60_p.h deleted file mode 100644 index c440bbc33a..0000000000 --- a/src/gui/image/qpixmap_s60_p.h +++ /dev/null @@ -1,141 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPIXMAPDATA_S60_P_H -#define QPIXMAPDATA_S60_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/qpixmap_raster_p.h> - -QT_BEGIN_NAMESPACE - -class CFbsBitmap; -class CFbsBitmapDevice; -class CFbsBitGc; - -class QSymbianBitmapDataAccess; - -class QSymbianFbsHeapLock -{ -public: - - enum LockAction { - Unlock - }; - - explicit QSymbianFbsHeapLock(LockAction a); - ~QSymbianFbsHeapLock(); - void relock(); - -private: - - LockAction action; - bool wasLocked; -}; - -class QS60PixmapData : public QRasterPixmapData -{ -public: - QS60PixmapData(PixelType type); - ~QS60PixmapData(); - - QPixmapData *createCompatiblePixmapData() const; - - void resize(int width, int height); - void fromImage(const QImage &image, Qt::ImageConversionFlags flags); - void copy(const QPixmapData *data, const QRect &rect); - bool scroll(int dx, int dy, const QRect &rect); - - int metric(QPaintDevice::PaintDeviceMetric metric) const; - void fill(const QColor &color); - void setMask(const QBitmap &mask); - void setAlphaChannel(const QPixmap &alphaChannel); - QImage toImage() const; - QPaintEngine* paintEngine() const; - - void beginDataAccess(); - void endDataAccess(bool readOnly=false) const; - - void* toNativeType(NativeType type); - void fromNativeType(void* pixmap, NativeType type); - - void convertToDisplayMode(int mode); - -private: - void release(); - void fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat=false); - QImage toImage(const QRect &r) const; - - QSymbianBitmapDataAccess *symbianBitmapDataAccess; - - CFbsBitmap *cfbsBitmap; - QPaintEngine *pengine; - uchar* bytes; - - bool formatLocked; - - QS60PixmapData *next; - QS60PixmapData *prev; - - static void qt_symbian_register_pixmap(QS60PixmapData *pd); - static void qt_symbian_unregister_pixmap(QS60PixmapData *pd); - static void qt_symbian_release_pixmaps(); - - friend class QPixmap; - friend class QS60WindowSurface; - friend class QS60PaintEngine; - friend class QS60Data; -}; - -QT_END_NAMESPACE - -#endif // QPIXMAPDATA_S60_P_H - diff --git a/src/gui/image/qpixmap_win.cpp b/src/gui/image/qpixmap_win.cpp deleted file mode 100644 index 9c14ac7726..0000000000 --- a/src/gui/image/qpixmap_win.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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_WS_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_WS_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_WS_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; -} - -HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const -{ - if (isNull()) - return 0; - - HBITMAP bitmap = 0; - if (data->classId() == QPixmapData::RasterClass) { - QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.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; -} - -QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format) -{ - // Verify size - BITMAP bitmap_info; - memset(&bitmap_info, 0, sizeof(BITMAP)); - - int res = GetObject(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); -} - -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 QPixmap::toWinHICON() const -{ - QBitmap maskBitmap = mask(); - if (maskBitmap.isNull()) { - maskBitmap= QBitmap(size()); - maskBitmap.fill(Qt::color1); - } - - ICONINFO ii; - ii.fIcon = true; - ii.hbmMask = qt_createIconMask(maskBitmap); - ii.hbmColor = toWinHBITMAP(QPixmap::Alpha); - ii.xHotspot = 0; - ii.yHotspot = 0; - - HICON hIcon = CreateIconIndirect(&ii); - - DeleteObject(ii.hbmColor); - DeleteObject(ii.hbmMask); - - return hIcon; -} - -#ifdef Q_WS_WIN -#ifndef Q_WS_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"); - } - qFree(data); - - return image; -} - -QPixmap QPixmap::fromWinHICON(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("QPixmap::fromWinHICON(), 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_WS_WINCE -QPixmap QPixmap::fromWinHICON(HICON icon) -{ - HDC screenDevice = GetDC(0); - HDC hdc = CreateCompatibleDC(screenDevice); - ReleaseDC(0, screenDevice); - - ICONINFO iconinfo; - bool result = GetIconInfo(icon, &iconinfo); - if (!result) - qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()"); - - int w = 0; - int h = 0; - if (!iconinfo.xHotspot || !iconinfo.yHotspot) { - // We could not retrieve the icon size via GetIconInfo, - // so we try again using the icon bitmap. - BITMAP bm; - int result = GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bm); - if (!result) result = GetObject(iconinfo.hbmMask, sizeof(BITMAP), &bm); - if (!result) { - qWarning("QPixmap::fromWinHICON(), failed to retrieve icon size"); - return QPixmap(); - } - w = bm.bmWidth; - h = bm.bmHeight; - } else { - // x and y Hotspot describes the icon center - w = iconinfo.xHotspot * 2; - h = iconinfo.yHotspot * 2; - } - const DWORD dwImageSize = w * h * 4; - - BITMAPINFO bmi; - memset(&bmi, 0, sizeof(bmi)); - bmi.bmiHeader.biSize = sizeof(BITMAPINFO); - bmi.bmiHeader.biWidth = w; - bmi.bmiHeader.biHeight = -h; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = dwImageSize; - - uchar* bits; - - HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0); - if (winBitmap ) - memset(bits, 0xff, dwImageSize); - if (!winBitmap) { - qWarning("QPixmap::fromWinHICON(), failed to CreateDIBSection()"); - return QPixmap(); - } - - HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); - if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_NORMAL)) - qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()"); - - uint mask = 0xff000000; - // Create image and copy data into image. - QImage image(w, h, QImage::Format_ARGB32); - - 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 *) (bits + y * bytes_per_line); - for (int x=0; x < w; ++x) { - dest[x] = src[x]; - } - } - } - if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK)) - qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()"); - 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 *) (bits + y * bytes_per_line); - for (int x=0; x < w; ++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_WS_WINCE -#endif //ifdef Q_WS_WIN - -QT_END_NAMESPACE diff --git a/src/gui/image/qpixmap_x11.cpp b/src/gui/image/qpixmap_x11.cpp deleted file mode 100644 index bc468cb7ec..0000000000 --- a/src/gui/image/qpixmap_x11.cpp +++ /dev/null @@ -1,2419 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// 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 <private/qimage_p.h> -#include <private/qimagepixmapcleanuphooks_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 - *****************************************************************************/ - -QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); -int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; - -QX11PixmapData::QX11PixmapData(PixelType type) - : QPixmapData(type, X11Class), gl_surface(0), hd(0), - flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0), - share_mode(QPixmap::ImplicitlyShared), pengine(0) -{ -} - -QPixmapData *QX11PixmapData::createCompatiblePixmapData() const -{ - return new QX11PixmapData(pixelType()); -} - -void QX11PixmapData::resize(int width, int height) -{ - setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); - - w = width; - h = height; - is_null = (w <= 0 || h <= 0); - - 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; - is_null = true; - 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 -} - -struct QX11AlphaDetector -{ - bool hasAlpha() const { - if (checked) - return has; - // Will implicitly also check format and return quickly for opaque types... - checked = true; - has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels(); - return has; - } - - bool hasXRenderAndAlpha() const { - if (!X11->use_xrender) - return false; - return hasAlpha(); - } - - QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags) - : image(i), checked(false), has(false) - { - if (flags & Qt::NoOpaqueDetection) { - checked = true; - has = image->hasAlphaChannel(); - } - } - - const QImage *image; - mutable bool checked; - mutable bool has; -}; - -void QX11PixmapData::fromImage(const QImage &img, - Qt::ImageConversionFlags flags) -{ - setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); - - w = img.width(); - h = img.height(); - d = img.depth(); - is_null = (w <= 0 || h <= 0); - - if (is_null) { - w = h = 0; - return; - } - - 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; - is_null = true; - return; - } - - QX11AlphaDetector alphaCheck(&img, flags); - int dd = alphaCheck.hasXRenderAndAlpha() ? 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.colorCount() == 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.byteCount(); - uchar *newbits= 0; - -#ifndef QT_NO_XRENDER - if (alphaCheck.hasXRenderAndAlpha()) { - 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.colorCount(); 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.colorCount() == 0) - image.setColorCount(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.colorCount(); i++) { // compute number of colors - if (pop[i] > 0) - ncols++; - } - for (int i = cimage.colorCount(); 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 (alphaCheck.hasAlpha()) { - QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags)); - setMask(m); - } -} - -Qt::HANDLE QX11PixmapData::createBitmapFromImage(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; - int w = img.width(); - int h = img.height(); - 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; - } - Qt::HANDLE hd = (Qt::HANDLE)XCreateBitmapFromData(X11->display, - QX11Info::appRootWindow(), - bits, w, h); - if (tmp_bits) // Avoid purify complaint - delete [] tmp_bits; - return hd; -} - -void QX11PixmapData::bitmapFromImage(const QImage &image) -{ - w = image.width(); - h = image.height(); - d = 1; - is_null = (w <= 0 || h <= 0); - hd = createBitmapFromImage(image); -#ifndef QT_NO_XRENDER - if (X11->use_xrender) - picture = XRenderCreatePicture(X11->display, hd, - XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); -#endif // QT_NO_XRENDER -} - -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() -{ - // Cleanup hooks have to be called before the handles are freed - if (is_cached) { - QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(this); - is_cached = false; - } - - release(); -} - -void QX11PixmapData::release() -{ - delete pengine; - pengine = 0; - - if (!X11) { - // At this point, the X server will already have freed our resources, - // so there is nothing to do. - 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 (!(flags & Readonly)) - XFreePixmap(xinfo.display(), hd); - hd = 0; - } -} - -QPixmap QX11PixmapData::alphaChannel() const -{ - if (!hasAlphaChannel()) { - QPixmap pm(w, h); - pm.fill(Qt::white); - return pm; - } - 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; - // the new QX11PixmapData object isn't referenced yet, so - // ref it - ref.ref(); - - // the below is to make sure the QX11PixmapData destructor - // doesn't delete our newly created render picture - 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; - } -} - -struct QXImageWrapper -{ - XImage *xi; -}; - -bool QX11PixmapData::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const -{ - XImage *xi = xiWrapper.xi; - - // ARGB32_Premultiplied - if (picture && depth() == 32) - return true; - - Visual *visual = (Visual *)xinfo.visual(); - - // RGB32 - if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000 - && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) - return true; - - // RGB16 - if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800 - && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) - return true; - - return false; -} - -QImage QX11PixmapData::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const -{ - XImage *xi = xiWrapper.xi; - - QImage::Format format = QImage::Format_ARGB32_Premultiplied; - if (depth() == 24) - format = QImage::Format_RGB32; - else if (depth() == 16) - format = QImage::Format_RGB16; - - QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format); - // take ownership - image.data_ptr()->own_data = true; - xi->data = 0; - - // we may have to swap the byte order - if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst) - || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst)) - { - for (int i=0; i < image.height(); i++) { - if (depth() == 16) { - ushort *p = (ushort*)image.scanLine(i); - ushort *end = p + image.width(); - while (p < end) { - *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); - p++; - } - } else { - uint *p = (uint*)image.scanLine(i); - uint *end = p + image.width(); - while (p < end) { - *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) - | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); - p++; - } - } - } - } - - // fix-up alpha channel - if (format == QImage::Format_RGB32) { - QRgb *p = (QRgb *)image.bits(); - for (int y = 0; y < xi->height; ++y) { - for (int x = 0; x < xi->width; ++x) - p[x] |= 0xff000000; - p += xi->bytes_per_line / 4; - } - } - - XDestroyImage(xi); - return image; -} - -QImage QX11PixmapData::toImage(const QRect &rect) const -{ - QXImageWrapper xiWrapper; - xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(), - AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap); - - Q_CHECK_PTR(xiWrapper.xi); - if (!xiWrapper.xi) - return QImage(); - - if (!x11_mask && canTakeQImageFromXImage(xiWrapper)) - return takeQImageFromXImage(xiWrapper); - - QImage image = toImage(xiWrapper, rect); - qSafeXDestroyImage(xiWrapper.xi); - return image; -} - -/*! - 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 -{ - return toImage(QRect(0, 0, w, h)); -} - -QImage QX11PixmapData::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const -{ - XImage *xi = xiWrapper.xi; - - 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; - } - - 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(xi->width, xi->height, format); - if (image.isNull()) // could not create image - return image; - - QImage alpha; - if (x11_mask) { - if (rect.contains(QRect(0, 0, w, h))) - alpha = mask().toImage(); - else - alpha = mask().toImage().copy(rect); - } - 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 < xi->height; ++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 < xi->width; 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 = xi->width; // leave loop - y = xi->height; - 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<xi->height; 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.setColorCount(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 < xi->height; i++) { - uchar* asrc = alpha.scanLine(i); - p = image.scanLine(i); - if (ale) { - for (int x = 0; x < xi->width; x++) { - if (asrc[x >> 3] & (1 << (x & 7))) - use[*p] = 1; - ++p; - } - } else { - for (int x = 0; x < xi->width; x++) { - if (asrc[x >> 3] & (0x80 >> (x & 7))) - use[*p] = 1; - ++p; - } - } - } - } else { - for (int i = 0; i < xi->height; 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 < xi->height; 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.setColorCount(ncols); // create color table - image.setColor(trans, 0x00000000); - } else { - image.setColorCount(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 < xi->height; i++) { - uchar* asrc = alpha.scanLine(i); - p = image.scanLine(i); - if (ale) { - for (int x = 0; x < xi->width; x++) { - if (!(asrc[x >> 3] & (1 << (x & 7)))) - *p = trans; - ++p; - } - } else { - for (int x = 0; x < xi->width; x++) { - if (!(asrc[x >> 3] & (1 << (7 -(x & 7))))) - *p = trans; - ++p; - } - } - } - } else { - image.setColorCount(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()); - } - } - - 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 - QX11PixmapData *x11Data = new QX11PixmapData(QPixmapData::PixmapType); - QPixmap pm(x11Data); - x11Data->flags &= ~QX11PixmapData::Uninitialized; - x11Data->xinfo = xinfo; - x11Data->d = d; - x11Data->w = w; - x11Data->h = h; - x11Data->is_null = (w <= 0 || h <= 0); - x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, - RootWindow(X11->display, xinfo.screen()), - w, h, d); - x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); - -#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; - } -} - -int QPixmap::x11SetDefaultScreen(int screen) -{ - int old = defaultScreen; - defaultScreen = screen; - return old; -} - -void QPixmap::x11SetScreen(int screen) -{ - if (paintingActive()) { - qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting"); - return; - } - - if (isNull()) - return; - - if (data->classId() != QPixmapData::X11Class) - return; - - if (screen < 0) - screen = QX11Info::appScreen(); - - QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data.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->flags &= ~QX11PixmapData::Uninitialized; - 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; -} - -const QX11Info &QPixmap::x11Info() const -{ - if (data && data->classId() == QPixmapData::X11Class) - return static_cast<QX11PixmapData*>(data.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 ((flags & Readonly) && 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->flags &= ~QX11PixmapData::Readonly; - } - - if (!that->pengine) - that->pengine = new QX11PaintEngine; - return that->pengine; -} - -Qt::HANDLE QPixmap::x11PictureHandle() const -{ -#ifndef QT_NO_XRENDER - if (data && data->classId() == QPixmapData::X11Class) - return static_cast<const QX11PixmapData*>(data.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.fetchAndAddRelaxed(1)); - - flags &= ~Uninitialized; - xinfo = x11Data->xinfo; - d = x11Data->d; - w = rect.width(); - h = rect.height(); - is_null = (w <= 0 || h <= 0); - 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); - } -} - -bool QX11PixmapData::scroll(int dx, int dy, const QRect &rect) -{ - GC gc = XCreateGC(X11->display, hd, 0, 0); - XCopyArea(X11->display, hd, hd, gc, - rect.left(), rect.top(), rect.width(), rect.height(), - rect.left() + dx, rect.top() + dy); - XFreeGC(X11->display, gc); - return true; -} - -#if !defined(QT_NO_XRENDER) -void QX11PixmapData::convertToARGB32(bool preserveContents) -{ - if (!X11->use_xrender) - return; - - // Q_ASSERT(count == 1); - if ((flags & Readonly) && 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 (!(flags & Readonly)) - XRenderFreePicture(X11->display, picture); - } - if (hd && !(flags & Readonly)) - 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.fetchAndAddRelaxed(1)); - data->flags = QX11PixmapData::Readonly; - data->share_mode = mode; - data->w = width; - data->h = height; - data->is_null = (width <= 0 || height <= 0); - 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 deleted file mode 100644 index eb8e5819ad..0000000000 --- a/src/gui/image/qpixmap_x11_p.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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; - -struct QXImageWrapper; - -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(); - - QPixmapData *createCompatiblePixmapData() const; - - void resize(int width, int height); - void fromImage(const QImage &image, Qt::ImageConversionFlags flags); - void copy(const QPixmapData *data, const QRect &rect); - bool scroll(int dx, int dy, 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; - QImage toImage(const QRect &rect) const; - QPaintEngine* paintEngine() const; - - Qt::HANDLE handle() const { return hd; } - Qt::HANDLE x11ConvertToDefaultDepth(); - - static Qt::HANDLE createBitmapFromImage(const QImage &image); - - void* gl_surface; -#ifndef QT_NO_XRENDER - void convertToARGB32(bool preserveContents = true); -#endif - -protected: - int metric(QPaintDevice::PaintDeviceMetric metric) const; - -private: - friend class QPixmap; - friend class QBitmap; - friend class QX11PaintEngine; - friend class QX11WindowSurface; - friend class QRasterWindowSurface; - friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags - friend class QEglContext; // Needs gl_surface - friend class QGLContext; // Needs gl_surface - friend class QX11GLPixmapData; // Needs gl_surface - friend class QMeeGoLivePixmapData; // Needs gl_surface and flags - friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface - - void release(); - - QImage toImage(const QXImageWrapper &xi, const QRect &rect) const; - - QBitmap mask_to_bitmap(int screen) const; - static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen); - void bitmapFromImage(const QImage &image); - - bool canTakeQImageFromXImage(const QXImageWrapper &xi) const; - QImage takeQImageFromXImage(const QXImageWrapper &xi) const; - - Qt::HANDLE hd; - - enum Flag { - NoFlags = 0x0, - Uninitialized = 0x1, - Readonly = 0x2, - InvertedWhenBoundToTexture = 0x4, - GlSurfaceCreatedWithAlpha = 0x8 - }; - uint flags; - - QX11Info xinfo; - Qt::HANDLE x11_mask; - Qt::HANDLE picture; - Qt::HANDLE mask_picture; - Qt::HANDLE hd2; // sorted in the default display depth - QPixmap::ShareMode share_mode; - - QX11PaintEngine *pengine; -}; - -QT_END_NAMESPACE - -#endif // QPIXMAPDATA_X11_P_H - diff --git a/src/gui/kernel/qapplication_mac.mm b/src/gui/kernel/qapplication_mac.mm deleted file mode 100644 index f607a72e92..0000000000 --- a/src/gui/kernel/qapplication_mac.mm +++ /dev/null @@ -1,3134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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 "qurl.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 - -#define kCMDeviceUnregisteredNotification CFSTR("CMDeviceUnregisteredNotification") -#define kCMDefaultDeviceNotification CFSTR("CMDefaultDeviceNotification") -#define kCMDeviceProfilesNotification CFSTR("CMDeviceProfilesNotification") -#define kCMDefaultDeviceProfileNotification CFSTR("CMDefaultDeviceProfileNotification") - -QT_BEGIN_NAMESPACE - -//for qt_mac.h -QPaintDevice *qt_mac_safe_pdev = 0; -QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0; -QPointer<QWidget> topLevelAt_cache = 0; - -/***************************************************************************** - 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 -QWidget * qt_button_down; // widget got last button-down -QPointer<QWidget> qt_last_mouse_receiver; -#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 -#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; -#endif -static AEEventHandlerUPP app_proc_ae_handlerUPP = NULL; -static EventHandlerRef tablet_proximity_handler = 0; -static EventHandlerUPP tablet_proximity_UPP = 0; -bool QApplicationPrivate::native_modal_dialog_active; - -Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; - -/***************************************************************************** - 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 *); //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 -extern void qt_mac_update_cursor(); // qcursor_mac.mm - -// Forward Decls -void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); -void onApplicationChangedActivation( bool activated ); - -static void qt_mac_read_fontsmoothing_settings() -{ - qt_applefontsmoothing_enabled = true; - int w = 10, h = 10; - QImage image(w, h, QImage::Format_RGB32); - image.fill(0xffffffff); - QPainter p(&image); - p.drawText(0, h, "X\\"); - p.end(); - - const int *bits = (const int *) ((const QImage &) image).bits(); - int bpl = image.bytesPerLine() / 4; - for (int y=0; y<w; ++y) { - for (int x=0; x<h; ++x) { - int r = qRed(bits[x]); - int g = qGreen(bits[x]); - int b = qBlue(bits[x]); - if (r != g || r != b) { - qt_applefontsmoothing_enabled = true; - return; - } - } - bits += bpl; - } - qt_applefontsmoothing_enabled = false; -} - -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", kThemeMenuItemFont), // 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; - FontHash *hash = qt_app_fonts_hash(); - if (!hash->isEmpty()) { - FontHash::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; - PaletteHash *phash = qt_app_palettes_hash(); - if (!phash->isEmpty()) { - PaletteHash::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) -{ - // 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 - } - } -} - -struct QMacAppleEventTypeSpec { - AEEventClass mac_class; - AEEventID mac_id; -} app_apple_events[] = { - { kCoreEventClass, kAEQuitApplication }, - { kCoreEventClass, kAEOpenDocuments }, - { kInternetEventClass, kAEGetURL }, -}; - -#ifndef QT_MAC_USE_COCOA - -#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) -enum -{ - kEventMouseScroll = 11, - kEventParamMouseWheelSmoothVerticalDelta = 'saxy', - kEventParamMouseWheelSmoothHorizontalDelta = 'saxx', -}; -#endif - -/* watched events */ -static EventTypeSpec app_events[] = { - { kEventClassQt, kEventQtRequestWindowChange }, - { kEventClassQt, kEventQtRequestShowSheet }, - { kEventClassQt, kEventQtRequestContext }, - { kEventClassQt, kEventQtRequestActivate }, - { kEventClassQt, kEventQtRequestMenubarUpdate }, - - { kEventClassWindow, kEventWindowActivated }, - { kEventClassWindow, kEventWindowDeactivated }, - - { kEventClassMouse, kEventMouseScroll }, - { 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 -} - -void qt_color_profile_changed(CFNotificationCenterRef, void *, CFStringRef, const void *, - CFDictionaryRef) -{ - QCoreGraphicsPaintEngine::cleanUpMacColorSpaces(); -} -/* platform specific implementations */ -void qt_init(QApplicationPrivate *priv, int) -{ - if (qt_is_gui_used) { - CGDisplayRegisterReconfigurationCallback(qt_mac_display_change_callbk, 0); - CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); - CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, - kCMDeviceUnregisteredNotification, 0, - CFNotificationSuspensionBehaviorDeliverImmediately); - CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, - kCMDefaultDeviceNotification, 0, - CFNotificationSuspensionBehaviorDeliverImmediately); - CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, - kCMDeviceProfilesNotification, 0, - CFNotificationSuspensionBehaviorDeliverImmediately); - CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, - kCMDefaultDeviceProfileNotification, 0, - CFNotificationSuspensionBehaviorDeliverImmediately); - 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 something) - // 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(); - } - -#endif - if (!app_proc_ae_handlerUPP && !QApplication::testAttribute(Qt::AA_MacPluginApplication)) { - app_proc_ae_handlerUPP = AEEventHandlerUPP(QApplicationPrivate::globalAppleEventProcessor); - for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i) { - // Install apple event handler, but avoid overwriting an already - // existing handler (it means a 3rd party application has installed one): - SRefCon refCon = 0; - AEEventHandlerUPP current_handler = NULL; - AEGetEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id, ¤t_handler, &refCon, false); - if (!current_handler) - AEInstallEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id, - app_proc_ae_handlerUPP, SRefCon(qApp), false); - } - } - - 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 = [QNSApplication sharedApplication]; - qt_redirectNSApplicationSendEvent(); - - QMacCocoaAutoReleasePool pool; - id 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 - // 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_mac_read_fontsmoothing_settings(); -} - -void qt_release_apple_event_handler() -{ - 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 = 0; - } -} - -/***************************************************************************** - qt_cleanup() - cleans up when the application is finished - *****************************************************************************/ - -void qt_cleanup() -{ - CGDisplayRemoveReconfigurationCallback(qt_mac_display_change_callbk, 0); - CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); - CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceUnregisteredNotification, 0); - CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceNotification, 0); - CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceProfilesNotification, 0); - CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceProfileNotification, 0); - -#ifndef QT_MAC_USE_COCOA - qt_release_app_proc_handler(); - if (app_proc_handlerUPP) { - DisposeEventHandlerUPP(app_proc_handlerUPP); - app_proc_handlerUPP = 0; - } -#endif - qt_release_apple_event_handler(); - 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); - -#ifdef QT_MAC_USE_COCOA - qt_mac_update_cursor(); -#else - if (qApp && qApp->activeWindow()) - qt_mac_set_cursor(&qApp->d_func()->cursor_list.first()); -#endif -} - -void QApplication::restoreOverrideCursor() -{ - if (qApp->d_func()->cursor_list.isEmpty()) - return; - qApp->d_func()->cursor_list.removeFirst(); - -#ifdef QT_MAC_USE_COCOA - qt_mac_update_cursor(); -#else - 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()); - } -#endif -} -#endif // QT_NO_CURSOR - -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 - // Use a cache to avoid iterate through the whole list of windows for all - // calls to to topLevelAt. We e.g. do this for each and every mouse - // move since we need to find the widget under mouse: - if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p)) - return topLevelAt_cache; - - // INVARIANT: Cache miss. Go through the list if windows instead: - QMacCocoaAutoReleasePool pool; - NSPoint cocoaPoint = flipPoint(p); - NSInteger windowCount; - NSCountWindows(&windowCount); - if (windowCount <= 0) - return 0; // There's no window to find! - - QVarLengthArray<NSInteger> windowList(windowCount); - NSWindowList(windowCount, windowList.data()); - int firstQtWindowFound = -1; - for (int i = 0; i < windowCount; ++i) { - NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]]; - if (window) { - QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; - if (candidateWindow && firstQtWindowFound == -1) - firstQtWindowFound = i; - - if (NSPointInRect(cocoaPoint, [window frame])) { - // 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; - else - return candidateWindow; - } else { - if (i == firstQtWindowFound) { - // The cache will only work when the window under mouse is - // top most (that is, not partially obscured by other windows. - // And we only set it if no mask is present to optimize for the common case: - topLevelAt_cache = candidateWindow; - } - return candidateWindow; - } - } - } - } - - topLevelAt_cache = 0; - return 0; -#endif -} - -/***************************************************************************** - 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_last_mouse_receiver); - qt_last_mouse_receiver = 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)) - QEventDispatcherMacPrivate::beginModalSession(widget); -#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_last_mouse_receiver); // send synthetic enter event - qt_last_mouse_receiver = w; - } -#ifdef QT_MAC_USE_COCOA - if (!qt_mac_is_macsheet(widget)) - QEventDispatcherMacPrivate::endModalSession(widget); -#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(); -} - -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; -} - -#ifndef QT_MAC_USE_COCOA -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; -} -#endif - -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 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 kEventMouseScroll: edesc = "MouseWheelScroll"; 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); - if (ekind != kEventMouseWheelMoved) - buttons = qt_mac_get_buttons(mac_buttons); - else - buttons = QApplication::mouseButtons(); - } - - int wheel_deltaX = 0; - int wheel_deltaY = 0; - static EventRef compatibilityEvent = 0; - - if (ekind == kEventMouseScroll) { - // kEventMouseScroll is the new way of dealing with mouse wheel - // events (kEventMouseWheelMoved was the old). kEventMouseScroll results - // in much smoother scrolling when using Mighty Mouse or TrackPad. For - // compatibility with older applications, carbon will also send us - // kEventMouseWheelMoved events if we dont eat this event - // (actually two events; one for horizontal and one for vertical). - // As a results of this, and to make sure we dont't receive duplicate events, - // we try to detect when this happend by checking the 'compatibilityEvent'. - // Since delta is delivered as pixels rather than degrees, we need to - // convert from pixels to degrees in a sensible manner. - // It looks like 1/4 degrees per pixel behaves most native. - // (NB: Qt expects the unit for delta to be 8 per degree): - const int pixelsToDegrees = 2; - SInt32 mdelt = 0; - GetEventParameter(event, kEventParamMouseWheelSmoothHorizontalDelta, typeSInt32, 0, - sizeof(mdelt), 0, &mdelt); - wheel_deltaX = mdelt * pixelsToDegrees; - mdelt = 0; - GetEventParameter(event, kEventParamMouseWheelSmoothVerticalDelta, typeSInt32, 0, - sizeof(mdelt), 0, &mdelt); - wheel_deltaY = mdelt * pixelsToDegrees; - GetEventParameter(event, kEventParamEventRef, typeEventRef, 0, - sizeof(compatibilityEvent), 0, &compatibilityEvent); - } else if (ekind == kEventMouseWheelMoved) { - if (event != compatibilityEvent) { - compatibilityEvent = 0; - int mdelt = 0; - GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0, - sizeof(mdelt), 0, &mdelt); - EventMouseWheelAxis axis; - GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, - sizeof(axis), 0, &axis); - - // Remove acceleration, and use either -120 or 120 as delta: - if (axis == kEventMouseWheelAxisX) - wheel_deltaX = qBound(-120, int(mdelt * 10000), 120); - else - wheel_deltaY = qBound(-120, int(mdelt * 10000), 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); - } - - //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) && t != QEvent::TabletRelease) { - // Never discard TabletRelease events as they may be delivered *after* TabletLeaveProximity events - qWarning("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 (t == QEvent::TabletPress) { - qt_button_down = widget; - } else if (t == QEvent::TabletRelease) { - qt_button_down = 0; - } -#if defined(DEBUG_MOUSE_MAPS) - qDebug("Bail out early due to tablet 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_last_mouse_receiver != 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_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none", - qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : ""); -#endif - - QWidget * const mouseGrabber = QWidget::mouseGrabber(); - - if (inPopupMode) { - QWidget *enter = enterLeaveWidget; - QWidget *leave = qt_last_mouse_receiver; - 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_last_mouse_receiver = enter; - } - } else { - QApplicationPrivate::dispatchEnterLeave(enter, leave); - qt_last_mouse_receiver = enter; - } - } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) { - QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver); - qt_last_mouse_receiver = 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_deltaX || wheel_deltaY) { -#ifndef QT_NO_WHEELEVENT - if (wheel_deltaX) { - QWheelEvent qwe(plocal, p, wheel_deltaX, buttons, modifiers, Qt::Horizontal); - QApplication::sendSpontaneousEvent(widget, &qwe); - if (!qwe.isAccepted() && QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget != widget) { - QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(p), p, - wheel_deltaX, buttons, modifiers, Qt::Horizontal); - QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - if (!qwe2.isAccepted()) - handled_event = false; - } - } - if (wheel_deltaY) { - QWheelEvent qwe(plocal, p, wheel_deltaY, buttons, modifiers, 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_deltaY, buttons, modifiers, Qt::Vertical); - QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); - if (!qwe2.isAccepted()) - handled_event = false; - } - } -#endif // QT_NO_WHEELEVENT - } 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_last_mouse_receiver = 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_deltaX); -#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, ©_cmd); - copy_cmd.menu.menuRef = GetApplicationDockTileMenu(); - SetEventParameter(copy, kEventParamDirectObject, typeHICommand, sizeof(copy_cmd), ©_cmd); - if(SendEventToMenu(copy, copy_cmd.menu.menuRef) == noErr) - handled_event = true; - } - if(!handled_event) { - if(cmd.commandID == kHICommandQuit) { - // Quitting the application is not Qt's responsibility if - // used in a plugin or just embedded into a native application. - // In that case, let the event pass down to the native apps event handler. - if (!QApplication::testAttribute(Qt::AA_MacPluginApplication)) { - 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 -} - -#ifdef QT_MAC_USE_COCOA -void QApplicationPrivate::qt_initAfterNSAppStarted() -{ - setupAppleEvents(); - qt_mac_update_cursor(); -} - -void QApplicationPrivate::setupAppleEvents() -{ - // This function is called from the event dispatcher when NSApplication has - // finished initialization, which appears to be just after [NSApplication run] has - // started to execute. By setting up our apple events handlers this late, we override - // the ones set up by NSApplication. - - // If Qt is used as a plugin, we let the 3rd party application handle events - // like quit and open file events. Otherwise, if we install our own handlers, we - // easily end up breaking functionallity the 3rd party application depend on: - if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) - return; - - QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; - NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; - [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:) - forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; - [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:) - forEventClass:kInternetEventClass andEventID:kAEGetURL]; -} -#endif - -// In Carbon this is your one stop for apple events. -// In Cocoa, it ISN'T. This is the catch-all Apple Event handler that exists -// for the time between instantiating the NSApplication, but before the -// NSApplication has installed it's OWN Apple Event handler. When Cocoa has -// that set up, we remove this. So, if you are debugging problems, you likely -// want to check out QCocoaApplicationDelegate instead. -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 (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; - } - } else if (aeClass == kInternetEventClass) { - switch (aeID) { - case kAEGetURL: { - char urlData[1024]; - Size actualSize; - if (AEGetParamPtr(ae, keyDirectObject, typeChar, 0, urlData, - sizeof(urlData) - 1, &actualSize) == noErr) { - urlData[actualSize] = 0; - QFileOpenEvent ev(QUrl(QString::fromUtf8(urlData))); - QApplication::sendSpontaneousEvent(app, &ev); - } - 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 used under Mac OS X, and behaves different - depending on if Qt is based on Carbon or Cocoa. - - For the Carbon port, 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. - - For the Cocoa port, If you create an application that inherits QApplication and reimplement - this function, you get direct access to all Cocoa Events that Qt receives - from Mac OS X with this function being called with the \a caller being 0 and - the \a event being an NSEvent pointer: - - NSEvent *e = reinterpret_cast<NSEvent *>(event); - - Return true if you want to stop the event from being processed. - Return false for normal event dispatching. The default - implementation returns false. - - \sa macEventFilter(void *nsevent) -*/ -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; -} - -#ifndef QT_NO_WHEELEVENT -void QApplication::setWheelScrollLines(int n) -{ - QApplicationPrivate::wheel_scroll_lines = n; -} - -int QApplication::wheelScrollLines() -{ - return QApplicationPrivate::wheel_scroll_lines; -} -#endif - -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); - -#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"); - - 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 - if (!widget) - return; - - if (activated) { - if (QApplicationPrivate::app_style) { - QEvent ev(QEvent::Style); - qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev); - } - qApp->setActiveWindow(widget); - } else { // deactivated - if (QApplicationPrivate::active_window == widget) - qApp->setActiveWindow(0); - } - - QMenuBar::macUpdateMenuBar(); - qt_mac_update_cursor(); -#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()) { - OSWindowRef wp = [NSApp keyWindow]; - if (QWidget *tmp_w = qt_mac_find_window(wp)) - app->setActiveWindow(tmp_w); - } - QMenuBar::macUpdateMenuBar(); - qt_mac_update_cursor(); - } 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 -} - -void QApplicationPrivate::initializeMultitouch_sys() -{ } -void QApplicationPrivate::cleanupMultitouch_sys() -{ } - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp deleted file mode 100644 index 408c3b5883..0000000000 --- a/src/gui/kernel/qapplication_s60.cpp +++ /dev/null @@ -1,2712 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qapplication_p.h" -#include "qsessionmanager.h" -#include "qevent.h" -#include "qsymbianevent.h" -#include "qeventdispatcher_s60_p.h" -#include "qwidget.h" -#include "qdesktopwidget.h" -#include "private/qbackingstore_p.h" -#include "qt_s60_p.h" -#include "private/qevent_p.h" -#include "qstring.h" -#include "qdebug.h" -#include "qimage.h" -#include "qcombobox.h" -#include "private/qkeymapper_p.h" -#include "private/qfont_p.h" -#ifndef QT_NO_STYLE_S60 -#include "private/qs60style_p.h" -#endif -#include "private/qwindowsurface_s60_p.h" -#include "qpaintengine.h" -#include "private/qmenubar_p.h" -#include "private/qsoftkeymanager_p.h" -#ifdef QT_GRAPHICSSYSTEM_RUNTIME -#include "private/qgraphicssystem_runtime_p.h" -#endif - -#include "apgwgnam.h" // For CApaWindowGroupName -#include <mdaaudiotoneplayer.h> // For CMdaAudioToneUtility - -#if defined(Q_OS_SYMBIAN) -# include <private/qs60mainapplication_p.h> -# include <centralrepository.h> -# include "qs60mainappui.h" -# include "qinputcontext.h" -#endif - -#if defined(Q_WS_S60) -# if !defined(QT_NO_IM) -# include <private/qcoefepinputcontext_p.h> -# endif -#endif - -#include "private/qstylesheetstyle_p.h" - -#include <hal.h> -#include <hal_data.h> - -#ifdef Q_SYMBIAN_TRANSITION_EFFECTS -#include <graphics/wstfxconst.h> -#endif - -QT_BEGIN_NAMESPACE - -// Goom Events through Window Server -static const int KGoomMemoryLowEvent = 0x10282DBF; -static const int KGoomMemoryGoodEvent = 0x20026790; -// Split view open/close events from AVKON -static const int KSplitViewOpenEvent = 0x2001E2C0; -static const int KSplitViewCloseEvent = 0x2001E2C1; - -#if defined(QT_DEBUG) -static bool appNoGrab = false; // Grabbing enabled -#endif -static bool app_do_modal = false; // modal mode -Q_GLOBAL_STATIC(QS60Data, qt_s60Data); - -extern bool qt_sendSpontaneousEvent(QObject*,QEvent*); -extern QWidgetList *qt_modal_stack; // stack of modal widgets -extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp - -QWidget *qt_button_down = 0; // widget got last button-down - -QSymbianControl *QSymbianControl::lastFocusedControl = 0; - -QS60Data* qGlobalS60Data() -{ - return qt_s60Data(); -} - -#ifdef Q_WS_S60 -void QS60Data::setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible) -{ - bool buttonGroupVisibilityChanged = false; - if (CEikButtonGroupContainer *const b = buttonGroupContainer()) { - buttonGroupVisibilityChanged = (b->IsVisible() != buttonGroupVisible); - b->MakeVisible(buttonGroupVisible); - } - bool statusPaneVisibilityChanged = false; - if (CEikStatusPane *const s = statusPane()) { - statusPaneVisibilityChanged = (s->IsVisible() != statusPaneVisible); - s->MakeVisible(statusPaneVisible); - } - if (buttonGroupVisibilityChanged || statusPaneVisibilityChanged) { - const QSize size = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()).size(); - const QSize oldSize; // note that QDesktopWidget::resizeEvent ignores the QResizeEvent contents - QResizeEvent event(size, oldSize); - QApplication::instance()->sendEvent(QApplication::desktop(), &event); - } - if (buttonGroupVisibilityChanged && !statusPaneVisibilityChanged && QApplication::activeWindow()) - // Ensure that control rectangle is updated - static_cast<QSymbianControl *>(QApplication::activeWindow()->winId())->handleClientAreaChange(); -} - -bool QS60Data::setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState) -{ - // Show statusbar: - // Topmost parent: Show unless fullscreen/minimized. - // Child windows: Follow topmost parent, unless fullscreen, in which case do not show statusbar - // Show CBA: - // Topmost parent: Show unless fullscreen/minimized. - // Exception: Show if fullscreen with Qt::WindowSoftkeysVisibleHint. - // Child windows: - // Minimized: Unclear if there is an use case for having focused minimized window at all. - // Always follow topmost parent just to be safe. - // Maximized and normal: follow topmost parent. - // Exception: If topmost parent is not showing CBA, show CBA if any softkey actions are - // defined. - // Fullscreen: Show only if Qt::WindowSoftkeysVisibleHint set. - - Qt::WindowStates comparisonState = newState; - QWidget *parentWindow = window->parentWidget(); - if (parentWindow) { - while (parentWindow->parentWidget()) - parentWindow = parentWindow->parentWidget(); - comparisonState = parentWindow->windowState(); - } else { - parentWindow = window; - } - - bool decorationsVisible = !(comparisonState & (Qt::WindowFullScreen | Qt::WindowMinimized)); - const bool parentIsFullscreen = comparisonState & Qt::WindowFullScreen; - const bool parentCbaVisibilityHint = parentWindow->windowFlags() & Qt::WindowSoftkeysVisibleHint; - bool buttonGroupVisibility = (decorationsVisible || (parentIsFullscreen && parentCbaVisibilityHint)); - - // Do extra checking for child windows - if (window->parentWidget()) { - if (newState & Qt::WindowFullScreen) { - decorationsVisible = false; - if (window->windowFlags() & Qt::WindowSoftkeysVisibleHint) - buttonGroupVisibility = true; - else - buttonGroupVisibility = false; - } else if (!(newState & Qt::WindowMinimized) && !buttonGroupVisibility) { - for (int i = 0; i < window->actions().size(); ++i) { - if (window->actions().at(i)->softKeyRole() != QAction::NoSoftKey) { - buttonGroupVisibility = true; - break; - } - } - } - } - - S60->setStatusPaneAndButtonGroupVisibility(decorationsVisible, buttonGroupVisibility); - - return decorationsVisible; -} -#endif - -void QS60Data::controlVisibilityChanged(CCoeControl *control, bool visible) -{ - if (QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control)) { - QWidget *const widget = QWidgetPrivate::mapper->value(control); - QWidget *const window = widget->window(); - if (QTLWExtra *topData = qt_widget_private(window)->maybeTopData()) { - QWidgetBackingStoreTracker &backingStore = topData->backingStore; - if (visible) { - if (backingStore.data()) { - backingStore.registerWidget(widget); - } else { - backingStore.create(window); - backingStore.registerWidget(widget); - qt_widget_private(widget)->invalidateBuffer(widget->rect()); - widget->repaint(); - } - } else { - // In certain special scenarios we may get an ENotVisible event - // without a previous EPartiallyVisible. The backingstore must - // still be destroyed, hence the registerWidget() call below. - if (backingStore.data() && widget->internalWinId() - && qt_widget_private(widget)->maybeBackingStore() == backingStore.data()) - backingStore.registerWidget(widget); - backingStore.unregisterWidget(widget); - // In order to ensure that any resources used by the window surface - // are immediately freed, we flush the WSERV command buffer. - S60->wsSession().Flush(); - } - } - } -} - -bool qt_nograb() // application no-grab option -{ -#if defined(QT_DEBUG) - return appNoGrab; -#else - return false; -#endif -} - -// Modified from http://www3.symbian.com/faq.nsf/0/0F1464EE96E737E780256D5E00503DD1?OpenDocument -class QS60Beep : public CBase, public MMdaAudioToneObserver -{ -public: - static QS60Beep* NewL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration); - void Play(); - ~QS60Beep(); -private: - void ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration); - void MatoPrepareComplete(TInt aError); - void MatoPlayComplete(TInt aError); -private: - typedef enum - { - EBeepNotPrepared, - EBeepPrepared, - EBeepPlaying - } TBeepState; -private: - CMdaAudioToneUtility* iToneUtil; - TBeepState iState; - TInt iFrequency; - TTimeIntervalMicroSeconds iDuration; -}; - -static QS60Beep* qt_S60Beep = 0; - -QS60Beep::~QS60Beep() -{ - if (iToneUtil) { - switch (iState) { - case EBeepPlaying: - iToneUtil->CancelPlay(); - break; - case EBeepNotPrepared: - iToneUtil->CancelPrepare(); - break; - } - } - delete iToneUtil; -} - -QS60Beep* QS60Beep::NewL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration) -{ - QS60Beep* self = new (ELeave) QS60Beep(); - CleanupStack::PushL(self); - self->ConstructL(aFrequency, aDuration); - CleanupStack::Pop(); - return self; -} - -void QS60Beep::ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration) -{ - iToneUtil = CMdaAudioToneUtility::NewL(*this); - iState = EBeepNotPrepared; - iFrequency = aFrequency; - iDuration = aDuration; - iToneUtil->PrepareToPlayTone(iFrequency, iDuration); -} - -void QS60Beep::Play() -{ - if (iState == EBeepPlaying) { - iToneUtil->CancelPlay(); - iState = EBeepPrepared; - } - - iToneUtil->Play(); - iState = EBeepPlaying; -} - -void QS60Beep::MatoPrepareComplete(TInt aError) -{ - if (aError == KErrNone) { - iState = EBeepPrepared; - Play(); - } -} - -void QS60Beep::MatoPlayComplete(TInt aError) -{ - Q_UNUSED(aError); - iState = EBeepPrepared; -} - - -static Qt::KeyboardModifiers mapToQtModifiers(TUint s60Modifiers) -{ - Qt::KeyboardModifiers result = Qt::NoModifier; - - if (s60Modifiers & EModifierKeypad) - result |= Qt::KeypadModifier; - if (s60Modifiers & EModifierShift || s60Modifiers & EModifierLeftShift - || s60Modifiers & EModifierRightShift) - result |= Qt::ShiftModifier; - if (s60Modifiers & EModifierCtrl || s60Modifiers & EModifierLeftCtrl - || s60Modifiers & EModifierRightCtrl) - result |= Qt::ControlModifier; - if (s60Modifiers & EModifierAlt || s60Modifiers & EModifierLeftAlt - || s60Modifiers & EModifierRightAlt) - result |= Qt::AltModifier; - - return result; -} - -static void mapS60MouseEventTypeToQt(QEvent::Type *type, Qt::MouseButton *button, const TPointerEvent *pEvent) -{ - switch (pEvent->iType) { - case TPointerEvent::EButton1Down: - *type = QEvent::MouseButtonPress; - *button = Qt::LeftButton; - break; - case TPointerEvent::EButton1Up: - *type = QEvent::MouseButtonRelease; - *button = Qt::LeftButton; - break; - case TPointerEvent::EButton2Down: - *type = QEvent::MouseButtonPress; - *button = Qt::MidButton; - break; - case TPointerEvent::EButton2Up: - *type = QEvent::MouseButtonRelease; - *button = Qt::MidButton; - break; - case TPointerEvent::EButton3Down: - *type = QEvent::MouseButtonPress; - *button = Qt::RightButton; - break; - case TPointerEvent::EButton3Up: - *type = QEvent::MouseButtonRelease; - *button = Qt::RightButton; - break; - case TPointerEvent::EDrag: - *type = QEvent::MouseMove; - *button = Qt::NoButton; - break; - case TPointerEvent::EMove: - // Qt makes no distinction between move and drag - *type = QEvent::MouseMove; - *button = Qt::NoButton; - break; - default: - *type = QEvent::None; - *button = Qt::NoButton; - break; - } - if (pEvent->iModifiers & EModifierDoubleClick){ - *type = QEvent::MouseButtonDblClick; - } - - if (*type == QEvent::MouseButtonPress || *type == QEvent::MouseButtonDblClick) - QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons | (*button); - else if (*type == QEvent::MouseButtonRelease) - QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons &(~(*button)); - - QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons & Qt::MouseButtonMask; -} - -//### Can be replaced with CAknLongTapDetector if animation is required. -//NOTE: if CAknLongTapDetector is used make sure it gets variated out of 3.1 and 3.2,. -//also MLongTapObserver needs to be changed to MAknLongTapDetectorCallBack if CAknLongTapDetector is used. -class QLongTapTimer : public CTimer -{ -public: - static QLongTapTimer* NewL(QAbstractLongTapObserver *observer); - QLongTapTimer(QAbstractLongTapObserver *observer); - void ConstructL(); -public: - void PointerEventL(const TPointerEvent &event); - void RunL(); -protected: -private: - QAbstractLongTapObserver *m_observer; - TPointerEvent m_event; - QPoint m_pressedCoordinates; - int m_dragDistance; -}; - -QLongTapTimer* QLongTapTimer::NewL(QAbstractLongTapObserver *observer) -{ - QLongTapTimer* self = new QLongTapTimer(observer); - self->ConstructL(); - return self; -} -void QLongTapTimer::ConstructL() -{ - CTimer::ConstructL(); -} - -QLongTapTimer::QLongTapTimer(QAbstractLongTapObserver *observer):CTimer(CActive::EPriorityHigh) -{ - m_observer = observer; - m_dragDistance = qApp->startDragDistance(); - CActiveScheduler::Add(this); -} - -void QLongTapTimer::PointerEventL(const TPointerEvent& event) -{ - if ( event.iType == TPointerEvent::EDrag || event.iType == TPointerEvent::EButtonRepeat) - { - QPoint diff(QPoint(event.iPosition.iX,event.iPosition.iY) - m_pressedCoordinates); - if (diff.manhattanLength() < m_dragDistance) - return; - } - Cancel(); - m_event = event; - if (event.iType == TPointerEvent::EButton1Down) - { - m_pressedCoordinates = QPoint(event.iPosition.iX,event.iPosition.iY); - // must be same as KLongTapDelay in aknlongtapdetector.h - After(800000); - } -} -void QLongTapTimer::RunL() -{ - if (m_observer) - m_observer->HandleLongTapEventL(m_event.iPosition, m_event.iParentPosition); -} - -QSymbianControl::QSymbianControl(QWidget *w) - : CCoeControl() - , qwidget(w) - , m_longTapDetector(0) - , m_ignoreFocusChanged(0) - , m_symbianPopupIsOpen(0) - , m_inExternalScreenOverride(false) - , m_lastStatusPaneVisibility(0) -{ -} - -void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) -{ - if (!desktop) - { - if (isWindowOwning || !qwidget->parentWidget() - || qwidget->parentWidget()->windowType() == Qt::Desktop) { - RWindowGroup &wg(S60->windowGroup(qwidget)); - CreateWindowL(wg); - } else { - /** - * TODO: in order to avoid creating windows for all ancestors of - * this widget up to the root window, the parameter passed to - * CreateWindowL should be - * qwidget->parentWidget()->effectiveWinId(). However, if we do - * this, then we need to take care of re-parenting when a window - * is created for a widget between this one and the root window. - */ - CreateWindowL(qwidget->parentWidget()->winId()); - } - - // Necessary in order to be able to track the activation status of - // the control's window - qwidget->d_func()->createExtra(); - - SetFocusing(true); - m_longTapDetector = QLongTapTimer::NewL(this); - m_doubleClickTimer.invalidate(); - - DrawableWindow()->SetPointerGrab(ETrue); - } - -#ifdef Q_SYMBIAN_TRANSITION_EFFECTS - if (OwnsWindow()) { - TTfxWindowPurpose windowPurpose(ETfxPurposeNone); - switch (qwidget->windowType()) { - case Qt::Dialog: - windowPurpose = ETfxPurposeDialogWindow; - break; - case Qt::Popup: - windowPurpose = ETfxPurposePopupWindow; - break; - case Qt::Tool: - windowPurpose = ETfxPurposeToolWindow; - break; - case Qt::ToolTip: - windowPurpose = ETfxPurposeToolTipWindow; - break; - case Qt::SplashScreen: - windowPurpose = ETfxPurposeSplashScreenWindow; - break; - default: - windowPurpose = (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop) - ? ETfxPurposeWindow : ETfxPurposeChildWindow; - break; - } - Window().SetPurpose(windowPurpose); - } -#endif -} - -QSymbianControl::~QSymbianControl() -{ - // Ensure backing store is deleted before the top-level - // window is destroyed - qt_widget_private(qwidget)->topData()->backingStore.destroy(); - - if (S60->curWin == this) - S60->curWin = 0; - if (!QApplicationPrivate::is_app_closing) { - QT_TRY { - setFocusSafely(false); - } QT_CATCH(const std::exception&) { - // ignore exceptions, nothing can be done - } - } - S60->appUi()->RemoveFromStack(this); - delete m_longTapDetector; -} - -void QSymbianControl::setWidget(QWidget *w) -{ - qwidget = w; -} - -QPoint QSymbianControl::translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const -{ - QPoint pos(pointerEventPos.iX, pointerEventPos.iY); - if (qwidget->d_func()->fixNativeOrientationCalled) { - QSize wsize = qwidget->size(); - TSize size = Size(); - if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) { - qreal x = pos.x(); - qreal y = pos.y(); - pos.setX(size.iHeight - y); - pos.setY(x); - } - } - return pos; -} - -TRect QSymbianControl::translateRectForFixedNativeOrientation(const TRect &controlRect) const -{ - TRect rect = controlRect; - if (qwidget->d_func()->fixNativeOrientationCalled) { - QPoint a = translatePointForFixedNativeOrientation(rect.iTl); - QPoint b = translatePointForFixedNativeOrientation(rect.iBr); - if (a.x() < b.x()) { - rect.iTl.iX = a.x(); - rect.iBr.iX = b.x(); - } else { - rect.iTl.iX = b.x(); - rect.iBr.iX = a.x(); - } - if (a.y() < b.y()) { - rect.iTl.iY = a.y(); - rect.iBr.iY = b.y(); - } else { - rect.iTl.iY = b.y(); - rect.iBr.iY = a.y(); - } - } - return rect; -} - -void QSymbianControl::HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ) -{ - QWidget *alienWidget; - QPoint widgetPos = translatePointForFixedNativeOrientation(aPenEventLocation); - QPoint globalPos = translatePointForFixedNativeOrientation(aPenEventScreenLocation); - alienWidget = qwidget->childAt(widgetPos); - if (!alienWidget) - alienWidget = qwidget; - -#if !defined(QT_NO_CONTEXTMENU) - QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, widgetPos, globalPos, Qt::NoModifier); - qt_sendSpontaneousEvent(alienWidget, &contextMenuEvent); -#endif -} - -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER -void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event) -{ - QApplicationPrivate *d = QApplicationPrivate::instance(); - QPointF screenPos = qwidget->mapToGlobal(translatePointForFixedNativeOrientation(event->iPosition)); - qreal pressure; - if(d->pressureSupported - && event->Pressure() > 0) //workaround for misconfigured HAL - pressure = event->Pressure() / qreal(d->maxTouchPressure); - else - pressure = qreal(1.0); - processTouchEvent(event->PointerNumber(), event->iType, screenPos, pressure); -} -#endif - -void QSymbianControl::processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure) -{ - QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget); - - QApplicationPrivate *d = QApplicationPrivate::instance(); - - QList<QTouchEvent::TouchPoint> points = d->appAllTouchPoints; - while (points.count() <= pointerNumber) - points.append(QTouchEvent::TouchPoint(points.count())); - - Qt::TouchPointStates allStates = 0; - for (int i = 0; i < points.count(); ++i) { - QTouchEvent::TouchPoint &touchPoint = points[i]; - - if (touchPoint.id() == pointerNumber) { - Qt::TouchPointStates state; - switch (type) { - case TPointerEvent::EButton1Down: -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - case TPointerEvent::EEnterHighPressure: -#endif - state = Qt::TouchPointPressed; - break; - case TPointerEvent::EButton1Up: -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - case TPointerEvent::EExitCloseProximity: -#endif - state = Qt::TouchPointReleased; - break; - case TPointerEvent::EDrag: - state = Qt::TouchPointMoved; - break; - default: - // how likely is this to happen? - state = Qt::TouchPointStationary; - break; - } - if (pointerNumber == 0) - state |= Qt::TouchPointPrimary; - touchPoint.setState(state); - - touchPoint.setScreenPos(screenPos); - touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(), - screenPos.y() / screenGeometry.height())); - - touchPoint.setPressure(pressure); - } else if (touchPoint.state() != Qt::TouchPointReleased) { - // all other active touch points should be marked as stationary - touchPoint.setState(Qt::TouchPointStationary); - } - - allStates |= touchPoint.state(); - } - - if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) { - // all touch points released - d->appAllTouchPoints.clear(); - } else { - d->appAllTouchPoints = points; - } - - QApplicationPrivate::translateRawTouchEvent(qwidget, - QTouchEvent::TouchScreen, - points); -} - -void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent) -{ -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - if (pEvent.IsAdvancedPointerEvent()) { - const TAdvancedPointerEvent *advancedPointerEvent = pEvent.AdvancedPointerEvent(); - translateAdvancedPointerEvent(advancedPointerEvent); - if (advancedPointerEvent->PointerNumber() != 0) { - // only send mouse events for the first touch point - return; - } - } -#endif - - m_longTapDetector->PointerEventL(pEvent); - QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent)); -} - -void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) -{ - QMouseEvent::Type type; - Qt::MouseButton button; - mapS60MouseEventTypeToQt(&type, &button, &pEvent); - Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); - - QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition); - TPoint controlScreenPos = PositionRelativeToScreen(); - QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos; - S60->lastCursorPos = globalPos; - S60->lastPointerEventPos = widgetPos; - - QWidget *mouseGrabber = QWidget::mouseGrabber(); - - QWidget *popupWidget = qApp->activePopupWidget(); - QWidget *popupReceiver = 0; - if (popupWidget) { - QWidget *popupChild = popupWidget->childAt(popupWidget->mapFromGlobal(globalPos)); - popupReceiver = popupChild ? popupChild : popupWidget; - } - - if (mouseGrabber) { - if (popupReceiver) { - sendMouseEvent(popupReceiver, type, globalPos, button, modifiers); - } else { - sendMouseEvent(mouseGrabber, type, globalPos, button, modifiers); - } - // No Enter/Leave events in grabbing mode. - return; - } - - QWidget *widgetUnderPointer = qwidget->childAt(widgetPos); - if (!widgetUnderPointer) - widgetUnderPointer = qwidget; - - QApplicationPrivate::dispatchEnterLeave(widgetUnderPointer, S60->lastPointerEventTarget); - S60->lastPointerEventTarget = widgetUnderPointer; - - QWidget *receiver; - if (!popupReceiver && S60->mousePressTarget && type != QEvent::MouseButtonPress) { - receiver = S60->mousePressTarget; - if (type == QEvent::MouseButtonRelease) - S60->mousePressTarget = 0; - } else { - receiver = popupReceiver ? popupReceiver : widgetUnderPointer; - if (type == QEvent::MouseButtonPress) - S60->mousePressTarget = receiver; - } - -#if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS) - if (S60->brokenPointerCursors) - qt_symbian_move_cursor_sprite(); -#endif - -//Generate single touch event for S60 5.0 (has touchscreen, does not have advanced pointers) -#ifndef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - if (S60->hasTouchscreen) { - processTouchEvent(0, pEvent.iType, QPointF(globalPos), 1.0); - } -#endif - - sendMouseEvent(receiver, type, globalPos, button, modifiers); -} - -#ifdef Q_WS_S60 -void QSymbianControl::HandleStatusPaneSizeChange() -{ - QS60MainAppUi *s60AppUi = static_cast<QS60MainAppUi *>(S60->appUi()); - s60AppUi->HandleStatusPaneSizeChange(); -} -#endif - -void QSymbianControl::sendMouseEvent( - QWidget *receiver, - QEvent::Type type, - const QPoint &globalPos, - Qt::MouseButton button, - Qt::KeyboardModifiers modifiers) -{ - Q_ASSERT(receiver); - QMouseEvent mEvent(type, receiver->mapFromGlobal(globalPos), globalPos, - button, QApplicationPrivate::mouse_buttons, modifiers); - QEventDispatcherS60 *dispatcher; - // It is theoretically possible for someone to install a different event dispatcher. - if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(receiver->d_func()->threadData->eventDispatcher)) != 0) { - if (dispatcher->excludeUserInputEvents()) { - dispatcher->saveInputEvent(this, receiver, new QMouseEvent(mEvent)); - return; - } - } - - sendMouseEvent(receiver, &mEvent); -} - -bool QSymbianControl::sendMouseEvent(QWidget *widget, QMouseEvent *mEvent) -{ - return qt_sendSpontaneousEvent(widget, mEvent); -} - -TKeyResponse QSymbianControl::OfferKeyEventL(const TKeyEvent& keyEvent, TEventCode type) -{ - TKeyResponse r = EKeyWasNotConsumed; - QT_TRYCATCH_LEAVING(r = OfferKeyEvent(keyEvent, type)); - return r; -} - -TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCode type) -{ - /* - S60 has a confusing way of delivering key events. There are three types of - events: EEventKey, EEventKeyDown and EEventKeyUp. When a key is pressed, - EEventKeyDown is first generated, followed by EEventKey. Then, when the key is - released, EEventKeyUp is generated. - However, it is possible that only the EEventKey is generated alone, typically - in relation to virtual keyboards. In that case we need to take care to - generate both press and release events in Qt, since applications expect that. - We do this by having three states for each used scan code, depending on the - events received. See the switch below for what happens in each state - transition. - */ - - if (type != EEventKeyDown) - if (handleVirtualMouse(keyEvent, type) == EKeyWasConsumed) - return EKeyWasConsumed; - - TKeyResponse ret = EKeyWasNotConsumed; -#define GET_RETURN(x) (ret = ((x) == EKeyWasConsumed) ? EKeyWasConsumed : ret) - - // This top level switch corresponds to the states, and the inner switches - // correspond to the transitions. - QS60Data::ScanCodeState &scanCodeState = S60->scanCodeStates[keyEvent.iScanCode]; - switch (scanCodeState) { - case QS60Data::Unpressed: - switch (type) { - case EEventKeyDown: - scanCodeState = QS60Data::KeyDown; - break; - case EEventKey: - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); - break; - case EEventKeyUp: - // No action. - break; - } - break; - case QS60Data::KeyDown: - switch (type) { - case EEventKeyDown: - // This should never happen, just stay in this state to be safe. - break; - case EEventKey: - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); - scanCodeState = QS60Data::KeyDownAndKey; - break; - case EEventKeyUp: - scanCodeState = QS60Data::Unpressed; - break; - } - break; - case QS60Data::KeyDownAndKey: - switch (type) { - case EEventKeyDown: - // This should never happen, just stay in this state to be safe. - break; - case EEventKey: - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); - break; - case EEventKeyUp: - GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); - scanCodeState = QS60Data::Unpressed; - break; - } - break; - } - return ret; - -#undef GET_RETURN -} - -TKeyResponse QSymbianControl::sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type) -{ - // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp - // events, we need to cache the keysyms from the EKeyEvent events. This is what - // resolveS60ScanCode does. - TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode, - keyEvent.iCode); - int keyCode; - if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used - keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode); - } else if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) { - // Normal characters keys. - keyCode = s60Keysym; - } else { - // Special S60 keys. - keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym); - } - - Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers); - QKeyEventEx qKeyEvent(type, keyCode, mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods), - (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers); - QWidget *widget; - widget = QWidget::keyboardGrabber(); - if (!widget) { - if (QApplicationPrivate::popupWidgets != 0) { - widget = QApplication::activePopupWidget()->focusWidget(); - if (!widget) { - widget = QApplication::activePopupWidget(); - } - } else { - widget = QApplicationPrivate::focus_widget; - if (!widget) { - widget = qwidget; - } - } - } - - QEventDispatcherS60 *dispatcher; - // It is theoretically possible for someone to install a different event dispatcher. - if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) { - if (dispatcher->excludeUserInputEvents()) { - dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent)); - return EKeyWasConsumed; - } - } - return sendKeyEvent(widget, &qKeyEvent); -} - -TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type) -{ -#ifndef QT_NO_CURSOR - if (S60->mouseInteractionEnabled && S60->virtualMouseRequired) { - //translate keys to pointer - if ((keyEvent.iScanCode >= EStdKeyLeftArrow && keyEvent.iScanCode <= EStdKeyDownArrow) || - (keyEvent.iScanCode >= EStdKeyDevice10 && keyEvent.iScanCode <= EStdKeyDevice13) || - keyEvent.iScanCode == EStdKeyDevice3) { - QPoint pos = QCursor::pos(); - TPointerEvent fakeEvent; - fakeEvent.iType = (TPointerEvent::TType)(-1); - fakeEvent.iModifiers = keyEvent.iModifiers; - TInt x = pos.x(); - TInt y = pos.y(); - if (type == EEventKeyUp) { - S60->virtualMouseAccelTimeout.start(); - switch (keyEvent.iScanCode) { - case EStdKeyLeftArrow: - S60->virtualMousePressedKeys &= ~QS60Data::Left; - break; - case EStdKeyRightArrow: - S60->virtualMousePressedKeys &= ~QS60Data::Right; - break; - case EStdKeyUpArrow: - S60->virtualMousePressedKeys &= ~QS60Data::Up; - break; - case EStdKeyDownArrow: - S60->virtualMousePressedKeys &= ~QS60Data::Down; - break; - // diagonal keys (named aliases don't exist in 3.1 SDK) - case EStdKeyDevice10: - S60->virtualMousePressedKeys &= ~QS60Data::LeftUp; - break; - case EStdKeyDevice11: - S60->virtualMousePressedKeys &= ~QS60Data::RightUp; - break; - case EStdKeyDevice12: - S60->virtualMousePressedKeys &= ~QS60Data::RightDown; - break; - case EStdKeyDevice13: - S60->virtualMousePressedKeys &= ~QS60Data::LeftDown; - break; - case EStdKeyDevice3: //select - if (S60->virtualMousePressedKeys & QS60Data::Select) - fakeEvent.iType = TPointerEvent::EButton1Up; - S60->virtualMousePressedKeys &= ~QS60Data::Select; - break; - } - } - else if (type == EEventKey) { - int dx = 0; - int dy = 0; - if (keyEvent.iScanCode != EStdKeyDevice3) { - m_doubleClickTimer.invalidate(); - //reset mouse accelleration after a short time with no moves - const int maxTimeBetweenKeyEventsMs = 500; - if (S60->virtualMouseAccelTimeout.isValid() && - S60->virtualMouseAccelTimeout.hasExpired(maxTimeBetweenKeyEventsMs)) { - S60->virtualMouseAccelDX = 0; - S60->virtualMouseAccelDY = 0; - } - S60->virtualMouseAccelTimeout.invalidate(); - } - switch (keyEvent.iScanCode) { - case EStdKeyLeftArrow: - S60->virtualMousePressedKeys |= QS60Data::Left; - dx = -1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyRightArrow: - S60->virtualMousePressedKeys |= QS60Data::Right; - dx = 1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyUpArrow: - S60->virtualMousePressedKeys |= QS60Data::Up; - dy = -1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDownArrow: - S60->virtualMousePressedKeys |= QS60Data::Down; - dy = 1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDevice10: - S60->virtualMousePressedKeys |= QS60Data::LeftUp; - dx = -1; - dy = -1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDevice11: - S60->virtualMousePressedKeys |= QS60Data::RightUp; - dx = 1; - dy = -1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDevice12: - S60->virtualMousePressedKeys |= QS60Data::RightDown; - dx = 1; - dy = 1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDevice13: - S60->virtualMousePressedKeys |= QS60Data::LeftDown; - dx = -1; - dy = 1; - fakeEvent.iType = TPointerEvent::EMove; - break; - case EStdKeyDevice3: - // Platform bug. If you start pressing several keys simultaneously (for - // example for drag'n'drop), Symbian starts producing spurious up and - // down messages for some keys. Therefore, make sure we have a clean slate - // of pressed keys before starting a new button press. - if (S60->virtualMousePressedKeys & QS60Data::Select) { - return EKeyWasConsumed; - } else { - S60->virtualMousePressedKeys |= QS60Data::Select; - fakeEvent.iType = TPointerEvent::EButton1Down; - if (m_doubleClickTimer.isValid() - && !m_doubleClickTimer.hasExpired(QApplication::doubleClickInterval())) { - fakeEvent.iModifiers |= EModifierDoubleClick; - m_doubleClickTimer.invalidate(); - } else { - m_doubleClickTimer.start(); - } - } - break; - } - if (dx) { - int cdx = S60->virtualMouseAccelDX; - //reset accel on change of sign, else double accel - if (dx * cdx <= 0) - cdx = dx; - else - cdx *= 4; - //cap accelleration - if (dx * cdx > S60->virtualMouseMaxAccel) - cdx = dx * S60->virtualMouseMaxAccel; - //move mouse position - x += cdx; - S60->virtualMouseAccelDX = cdx; - } - - if (dy) { - int cdy = S60->virtualMouseAccelDY; - if (dy * cdy <= 0) - cdy = dy; - else - cdy *= 4; - if (dy * cdy > S60->virtualMouseMaxAccel) - cdy = dy * S60->virtualMouseMaxAccel; - y += cdy; - S60->virtualMouseAccelDY = cdy; - } - } - //clip to screen size (window server allows a sprite hotspot to be outside the screen) - int screenNumber = S60->screenNumberForWidget(qwidget); - if (x < 0) - x = 0; - else if (x >= S60->screenWidthInPixelsForScreen[screenNumber]) - x = S60->screenWidthInPixelsForScreen[screenNumber] - 1; - if (y < 0) - y = 0; - else if (y >= S60->screenHeightInPixelsForScreen[screenNumber]) - y = S60->screenHeightInPixelsForScreen[screenNumber] - 1; - TPoint epos(x, y); - TPoint cpos = epos - PositionRelativeToScreen(); - fakeEvent.iPosition = cpos; - fakeEvent.iParentPosition = epos; - if(fakeEvent.iType != -1) - HandlePointerEvent(fakeEvent); - return EKeyWasConsumed; - } - } -#endif - - return EKeyWasNotConsumed; -} - -void QSymbianControl::sendInputEvent(QWidget *widget, QInputEvent *inputEvent) -{ - switch (inputEvent->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: - sendKeyEvent(widget, static_cast<QKeyEvent *>(inputEvent)); - break; - case QEvent::MouseButtonDblClick: - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseMove: - sendMouseEvent(widget, static_cast<QMouseEvent *>(inputEvent)); - break; - default: - // Shouldn't get here. - Q_ASSERT_X(0 == 1, "QSymbianControl::sendInputEvent()", "inputEvent->type() is unknown"); - break; - } -} - -TKeyResponse QSymbianControl::sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent) -{ -#if !defined(QT_NO_IM) && defined(Q_WS_S60) - if (widget && widget->isEnabled() && widget->testAttribute(Qt::WA_InputMethodEnabled)) { - QInputContext *qic = widget->inputContext(); - if (qic && qic->filterEvent(keyEvent)) - return EKeyWasConsumed; - } -#endif // !defined(QT_NO_IM) && defined(Q_OS_SYMBIAN) - - if (widget && qt_sendSpontaneousEvent(widget, keyEvent)) - if (keyEvent->isAccepted()) - return EKeyWasConsumed; - - return EKeyWasNotConsumed; -} - -#if !defined(QT_NO_IM) && defined(Q_WS_S60) -TCoeInputCapabilities QSymbianControl::InputCapabilities() const -{ - QWidget *w = 0; - - if (qwidget->hasFocus()) - w = qwidget; - else - w = qwidget->focusWidget(); - - QCoeFepInputContext *ic; - if (w && w->isEnabled() && w->testAttribute(Qt::WA_InputMethodEnabled) - && (ic = qobject_cast<QCoeFepInputContext *>(w->inputContext()))) { - return ic->inputCapabilities(); - } else { - return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0); - } -} -#endif - -void QSymbianControl::Draw(const TRect& controlRect) const -{ - // Set flag to avoid calling DrawNow in window surface - QWidget *window = qwidget->window(); - Q_ASSERT(window); - QTLWExtra *topExtra = window->d_func()->maybeTopData(); - Q_ASSERT(topExtra); - - TRect wcontrolRect = translateRectForFixedNativeOrientation(controlRect); - - if (!topExtra->inExpose) { - topExtra->inExpose = true; - if (!qwidget->isWindow()) { - // If we get here, then it means we have a native child window - // Since no content should ever be painted to these windows, we - // erase them with a transparent brush when they get an expose. - CWindowGc &gc = SystemGc(); - gc.SetBrushColor(TRgb(0, 0, 0, 0)); - gc.Clear(controlRect); - } - QRect exposeRect = qt_TRect2QRect(wcontrolRect); - qwidget->d_func()->syncBackingStore(exposeRect); - topExtra->inExpose = false; - } - - QWindowSurface *surface = qwidget->windowSurface(); - QPaintEngine *engine = surface ? surface->paintDevice()->paintEngine() : NULL; - - if (!engine) - return; - - const bool sendNativePaintEvents = qwidget->d_func()->extraData()->receiveNativePaintEvents; - if (sendNativePaintEvents) { - const QRect r = qt_TRect2QRect(wcontrolRect); - QMetaObject::invokeMethod(qwidget, "beginNativePaintEvent", Qt::DirectConnection, Q_ARG(QRect, r)); - } - - // Map source rectangle into coordinates of the backing store. - const QPoint controlBase(controlRect.iTl.iX, controlRect.iTl.iY); - const QPoint backingStoreBase = qwidget->mapTo(qwidget->window(), controlBase); - const TRect backingStoreRect(TPoint(backingStoreBase.x(), backingStoreBase.y()), controlRect.Size()); - - if (engine->type() == QPaintEngine::Raster) { - QS60WindowSurface *s60Surface; -#ifdef QT_GRAPHICSSYSTEM_RUNTIME - if (QApplicationPrivate::runtime_graphics_system) { - QRuntimeWindowSurface *rtSurface = - static_cast<QRuntimeWindowSurface*>(qwidget->windowSurface()); - s60Surface = static_cast<QS60WindowSurface *>(rtSurface->m_windowSurface.data()); - } else -#endif - s60Surface = static_cast<QS60WindowSurface *>(qwidget->windowSurface()); - - CFbsBitmap *bitmap = s60Surface->symbianBitmap(); - CWindowGc &gc = SystemGc(); - - QWExtra::NativePaintMode nativePaintMode = qwidget->d_func()->extraData()->nativePaintMode; - if(qwidget->d_func()->paintOnScreen()) - nativePaintMode = QWExtra::Disable; - - switch(nativePaintMode) { - case QWExtra::Disable: - // Do nothing - break; - case QWExtra::Blit: - case QWExtra::BlitWriteAlpha: - if (qwidget->d_func()->isOpaque || nativePaintMode == QWExtra::BlitWriteAlpha) - gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); - gc.BitBlt(controlRect.iTl, bitmap, backingStoreRect); - break; - case QWExtra::ZeroFill: - if (Window().DisplayMode() == EColor16MA - || Window().DisplayMode() == Q_SYMBIAN_ECOLOR16MAP) { - gc.SetBrushStyle(CGraphicsContext::ESolidBrush); - gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); - gc.SetBrushColor(TRgb::Color16MA(0)); - gc.Clear(controlRect); - } else { - gc.SetBrushColor(TRgb(0x000000)); - gc.Clear(controlRect); - }; - break; - default: - Q_ASSERT(false); - } - } - - if (sendNativePaintEvents) { - const QRect r = qt_TRect2QRect(wcontrolRect); - // The draw ops aren't actually sent to WSERV until the graphics - // context is deactivated, which happens in the function calling - // this one. We therefore delay the delivery of endNativePaintEvent, - // to ensure that drawing has completed by the time the widget - // receives the event. Note that, if the widget needs to ensure - // that the draw ops have actually been executed into the output - // framebuffer, a call to RWsSession::Flush is required in the - // endNativePaintEvent implementation. - QMetaObject::invokeMethod(qwidget, "endNativePaintEvent", Qt::QueuedConnection, Q_ARG(QRect, r)); - } -} - -void QSymbianControl::qwidgetResize_helper(const QSize &newSize) -{ - QRect cr = qwidget->geometry(); - QSize oldSize(cr.size()); - cr.setSize(newSize); - qwidget->data->crect = cr; - if (qwidget->isVisible()) { - QTLWExtra *tlwExtra = qwidget->d_func()->maybeTopData(); - bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); - if (!slowResize && tlwExtra) - tlwExtra->inTopLevelResize = true; - QResizeEvent e(newSize, oldSize); - qt_sendSpontaneousEvent(qwidget, &e); - if (!qwidget->testAttribute(Qt::WA_StaticContents)) - qwidget->d_func()->syncBackingStore(); - if (!slowResize && tlwExtra) - tlwExtra->inTopLevelResize = false; - } else { - if (!qwidget->testAttribute(Qt::WA_PendingResizeEvent)) { - QResizeEvent *e = new QResizeEvent(newSize, oldSize); - QApplication::postEvent(qwidget, e); - } - } -} - -void QSymbianControl::SizeChanged() -{ - CCoeControl::SizeChanged(); - - // When FixNativeOrientation had been called, the RWindow/CCoeControl size - // and the surface/QWidget size have nothing to do with each other. - if (qwidget->d_func()->fixNativeOrientationCalled) - return; - - QSize oldSize = qwidget->size(); - QSize newSize(Size().iWidth, Size().iHeight); - - if (oldSize != newSize) { - // Enforce the proper size for fullscreen widgets on the secondary screen. - const bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen; - const int screenNumber = S60->screenNumberForWidget(qwidget); - if (!m_inExternalScreenOverride && isFullscreen && screenNumber > 0) { - int screenWidth = S60->screenWidthInPixelsForScreen[screenNumber]; - int screenHeight = S60->screenHeightInPixelsForScreen[screenNumber]; - TSize screenSize(screenWidth, screenHeight); - if (screenWidth > 0 && screenHeight > 0 && screenSize != Size()) { - m_inExternalScreenOverride = true; - SetExtent(TPoint(0, 0), screenSize); - return; - } - } - - qwidgetResize_helper(newSize); - } - - m_inExternalScreenOverride = false; - - // CCoeControl::SetExtent calls SizeChanged, but does not call - // PositionChanged, so we call it here to ensure that the widget's - // position is updated. - PositionChanged(); -} - -void QSymbianControl::PositionChanged() -{ - CCoeControl::PositionChanged(); - - QPoint oldPos = qwidget->geometry().topLeft(); - QPoint newPos(Position().iX, Position().iY); - - if (oldPos != newPos) { - QRect cr = qwidget->geometry(); - cr.moveTopLeft(newPos); - qwidget->data->crect = cr; - QTLWExtra *top = qwidget->d_func()->maybeTopData(); - if (top && (qwidget->windowState() & (~Qt::WindowActive)) == Qt::WindowNoState) - top->normalGeometry.moveTopLeft(newPos); - if (qwidget->isVisible()) { - QMoveEvent e(newPos, oldPos); - qt_sendSpontaneousEvent(qwidget, &e); - } else { - QMoveEvent * e = new QMoveEvent(newPos, oldPos); - QApplication::postEvent(qwidget, e); - } - } -} - -void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) -{ - if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) - return; - -#ifdef Q_WS_S60 - if (S60->splitViewLastWidget) - return; -#endif - - // Popups never get focused, but still receive the FocusChanged when they are hidden. - if (QApplicationPrivate::popupWidgets != 0 - || (qwidget->windowType() & Qt::Popup) == Qt::Popup) - return; - - if (IsFocused() && IsVisible()) { - if (m_symbianPopupIsOpen) { - QWidget *fw = QApplication::focusWidget(); - if (fw) { - QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason); - QCoreApplication::sendEvent(fw, &event); - } - m_symbianPopupIsOpen = false; - } - - QApplication::setActiveWindow(qwidget->window()); - qwidget->d_func()->setWindowIcon_sys(true); - qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); -#ifdef Q_WS_S60 - if (qwidget->isWindow()) - S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState()); -#endif - } else if (QApplication::activeWindow() == qwidget->window()) { - bool focusedControlFound = false; - WId winId = 0; - for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) { - if (winId->IsFocused() && winId->IsVisible()) { - focusedControlFound = true; - break; - } else if (w->isWindow()) - break; - } - if (!focusedControlFound) { - if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { - QWidget *fw = QApplication::focusWidget(); - if (fw) { - QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); - QCoreApplication::sendEvent(fw, &event); - } - m_symbianPopupIsOpen = true; - return; - } - - QApplication::setActiveWindow(0); - } - } - // else { We don't touch the active window unless we were explicitly activated or deactivated } -} - -void QSymbianControl::handleClientAreaChange() -{ - const bool cbaVisibilityHint = qwidget->windowFlags() & Qt::WindowSoftkeysVisibleHint; - if (qwidget->isFullScreen() && !cbaVisibilityHint) { - SetExtentToWholeScreen(); - } else if (qwidget->isMaximized() || (qwidget->isFullScreen() && cbaVisibilityHint)) { - TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); - SetExtent(r.iTl, r.Size()); - } else if (!qwidget->isMinimized()) { // Normal geometry - if (!qwidget->testAttribute(Qt::WA_Resized)) { - qwidget->adjustSize(); - qwidget->setAttribute(Qt::WA_Resized, false); //not a user resize - } - if (!qwidget->testAttribute(Qt::WA_Moved) && qwidget->windowType() != Qt::Dialog) { - TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); - SetPosition(r.iTl); - qwidget->setAttribute(Qt::WA_Moved, false); // not really an explicit position - } - } -} - -bool QSymbianControl::isSplitViewWidget(QWidget *widget) { - bool returnValue = true; - //Ignore events sent to non-active windows, not visible widgets and not parents of input widget. - if (!qwidget->isActiveWindow() - || !qwidget->isVisible() - || !qwidget->isAncestorOf(widget)) { - - returnValue = false; - } - return returnValue; -} - -void QSymbianControl::HandleResourceChange(int resourceType) -{ - switch (resourceType) { - case KSplitViewCloseEvent: //intentional fall-through - case KSplitViewOpenEvent: { -#if !defined(QT_NO_IM) && defined(Q_WS_S60) - - //Fetch widget getting the text input - QWidget *widget = QWidget::keyboardGrabber(); - if (!widget) { - if (QApplicationPrivate::popupWidgets) { - widget = QApplication::activePopupWidget()->focusWidget(); - if (!widget) { - widget = QApplication::activePopupWidget(); - } - } else { - widget = QApplicationPrivate::focus_widget; - if (!widget) { - widget = qwidget; - } - } - } - if (widget) { - QCoeFepInputContext *ic = qobject_cast<QCoeFepInputContext *>(widget->inputContext()); - if (!ic) { - ic = qobject_cast<QCoeFepInputContext *>(qApp->inputContext()); - } - if (ic && isSplitViewWidget(widget)) { - if (resourceType == KSplitViewCloseEvent) { - ic->resetSplitViewWidget(); - } else { - ic->ensureFocusWidgetVisible(widget); - } - } - } -#endif // !defined(QT_NO_IM) && defined(Q_WS_S60) - } - break; - case KInternalStatusPaneChange: - // When status pane is not visible, only handle client area change if status pane was - // previously visible, as size changes to hidden status pane should not affect - // client area. - if (S60->statusPane() && (S60->statusPane()->IsVisible() || m_lastStatusPaneVisibility)) { - m_lastStatusPaneVisibility = S60->statusPane()->IsVisible(); - handleClientAreaChange(); - } - if (IsFocused() && IsVisible()) { - qwidget->d_func()->setWindowIcon_sys(true); - qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); - } - break; - case KUidValueCoeFontChangeEvent: - // font change event - break; -#ifdef Q_WS_S60 - case KEikDynamicLayoutVariantSwitch: - { - handleClientAreaChange(); - // Send resize event to trigger desktopwidget workAreaResized signal - if (qt_desktopWidget) { - QResizeEvent e(qt_desktopWidget->size(), qt_desktopWidget->size()); - QApplication::sendEvent(qt_desktopWidget, &e); - } - break; - } -#endif - default: - break; - } - - CCoeControl::HandleResourceChange(resourceType); - -} -void QSymbianControl::CancelLongTapTimer() -{ - m_longTapDetector->Cancel(); -} - -TTypeUid::Ptr QSymbianControl::MopSupplyObject(TTypeUid id) -{ - if (id.iUid == ETypeId) - return id.MakePtr(this); - - return CCoeControl::MopSupplyObject(id); -} - -void QSymbianControl::setFocusSafely(bool focus) -{ - // The stack hack in here is very unfortunate, but it is the only way to ensure proper - // focus in Symbian. If this is not executed, the control which happens to be on - // the top of the stack may randomly be assigned focus by Symbian, for example - // when creating new windows (specifically in CCoeAppUi::HandleStackChanged()). - - // Close any popups. - CEikonEnv::Static()->EikAppUi()->StopDisplayingMenuBar(); - - if (focus) { - S60->appUi()->RemoveFromStack(this); - // Symbian doesn't automatically remove focus from the last focused control, so we need to - // remember it and clear focus ourselves. - if (lastFocusedControl && lastFocusedControl != this) - lastFocusedControl->SetFocus(false); - QT_TRAP_THROWING(S60->appUi()->AddToStackL(this, - ECoeStackPriorityDefault + 1, ECoeStackFlagStandard)); // Note the + 1 - lastFocusedControl = this; - this->SetFocus(true); - } else { - S60->appUi()->RemoveFromStack(this); - QT_TRAP_THROWING(S60->appUi()->AddToStackL(this, - ECoeStackPriorityDefault, ECoeStackFlagStandard)); - if(this == lastFocusedControl) - lastFocusedControl = 0; - this->SetFocus(false); - } -} - -bool QSymbianControl::isControlActive() -{ - return IsActivated() ? true : false; -} - -void QSymbianControl::ensureFixNativeOrientation() -{ -#if defined(Q_SYMBIAN_SUPPORTS_FIXNATIVEORIENTATION) - if (!qwidget->isWindow() || qwidget->windowType() == Qt::Desktop) - return; - if (S60->screenNumberForWidget(qwidget) > 0) - return; - const bool isFixed = qwidget->d_func()->fixNativeOrientationCalled; - const bool isFixEnabled = qwidget->testAttribute(Qt::WA_SymbianNoSystemRotation); - const bool isFullScreen = qwidget->windowState().testFlag(Qt::WindowFullScreen); - if (isFullScreen && isFixEnabled) { - const bool surfaceBasedGs = - QApplicationPrivate::graphics_system_name == QLatin1String("openvg") - || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"); - if (!surfaceBasedGs) - qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); - if (!isFixed && surfaceBasedGs) { - if (Window().FixNativeOrientation() == KErrNone) { - qwidget->d_func()->fixNativeOrientationCalled = true; - // The EGL window surface is now fixed to the native orientation - // of the device, no matter what size we pass when creating it. - // Enforce the same size for the QWidget too. For the underlying - // CCoeControl and RWindow it is up to the system to resize them - // when the standard auto-rotation mechanism is in use, we must not - // change that behavior by forcing any size for those. In practice - // this means that the QWidget and the underlying native control - // dimensions will be out of sync when FixNativeOrientation was - // called and the device is turned to the non-native (typically - // landscape) orientation. The pointer event handling and certain - // functions like Draw() will need to compensate for this. - QSize newSize(S60->nativeScreenWidthInPixels, S60->nativeScreenHeightInPixels); - if (qwidget->size() != newSize) - qwidgetResize_helper(newSize); - } else { - qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); - } - } - } else if (isFixed) { - qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); - qwidget->d_func()->fixNativeOrientationCalled = false; - qwidget->hide(); - qwidget->d_func()->create_sys(0, false, true); - qwidget->show(); - } -#else - qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); -#endif -} - -/*! - \typedef QApplication::QS60MainApplicationFactory - \since 4.6 - - This is a typedef for a pointer to a function with the following - signature: - - \snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 47 - - \sa QApplication::QApplication() -*/ - -/*! - \since 4.6 - - Creates an application using the application factory given in - \a factory, and using \a argc command line arguments in \a argv. - \a factory can be leaving, but the error will be converted to a - standard exception. - - This function is only available on S60. -*/ -QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000)) -{ - Q_D(QApplication); - S60->s60ApplicationFactory = factory; - d->construct(); -} - -QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int _internal) - : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal)) -{ - Q_D(QApplication); - S60->s60ApplicationFactory = factory; - d->construct(); - QApplicationPrivate::app_compile_version = _internal; -} - -void qt_init(QApplicationPrivate * /* priv */, int) -{ - if (!CCoeEnv::Static()) { - // The S60 framework creates a new trap handler which will render any existing traps - // invalid as long as it is active. This means that all code in main() that occurs after - // the QApplication construction needs to be surrounded by a new trap, despite having - // an outer one already. To avoid this, we save the original trap handler here, and set - // it back after the S60 framework is constructed. Then we restore it right before the S60 - // framework destruction. - TTrapHandler *origTrapHandler = User::TrapHandler(); - - // The S60 framework has not been initialized. We need to do it. - TApaApplicationFactory factory(S60->s60ApplicationFactory ? - S60->s60ApplicationFactory : newS60Application); - CApaCommandLine* commandLine = q_check_ptr(QCoreApplicationPrivate::symbianCommandLine()); - if (commandLine) { - // After this construction, CEikonEnv will be available from CEikonEnv::Static(). - // (much like our qApp). - QtEikonEnv* coe = new QtEikonEnv; - //not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there. - TRAPD(err, coe->ConstructAppFromCommandLineL(factory, *commandLine)); - if(err != KErrNone) { - qWarning() << "qt_init: Eikon application construct failed (" - << err - << "), maybe missing resource file on S60 3.1?"; - delete coe; - qt_symbian_throwIfError(err); - } - } - - S60->s60InstalledTrapHandler = User::SetTrapHandler(origTrapHandler); - - S60->qtOwnsS60Environment = true; - } else { - S60->qtOwnsS60Environment = false; - } - -#ifdef QT_NO_DEBUG - if (!qgetenv("QT_S60_AUTO_FLUSH_WSERV").isEmpty()) -#endif - S60->wsSession().SetAutoFlush(ETrue); - -#ifdef Q_SYMBIAN_WINDOW_SIZE_CACHE - TRAP_IGNORE(S60->wsSession().EnableWindowSizeCacheL()); -#endif - - S60->updateScreenSize(); - - - TDisplayMode mode = S60->screenDevice()->DisplayMode(); - S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode); - - //NB: RWsSession::GetColorModeList tells you what window modes are supported, - //not what bitmap formats. - if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2) - S60->supportsPremultipliedAlpha = 0; - else - S60->supportsPremultipliedAlpha = 1; - - RProcess me; - TSecureId securId = me.SecureId(); - S60->uid = securId.operator TUid(); - - // enable focus events - used to re-enable mouse after focus changed between mouse and non mouse app, - // and for dimming behind modal windows - S60->windowGroup().EnableFocusChangeEvents(); - - //Check if mouse interaction is supported (either EMouse=1 in the HAL, or EMachineUID is one of the phones known to support this) - const TInt KMachineUidSamsungI8510 = 0x2000C51E; - // HAL::Get(HALData::EPen, TInt& result) may set 'result' to 1 on some 3.1 systems (e.g. N95). - // But we know that S60 systems below 5.0 did not support touch. - static const bool touchIsUnsupportedOnSystem = - QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 - || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2; - TInt machineUID; - TInt mouse; - TInt touch; - TInt err; - err = HAL::Get(HALData::EMouse, mouse); - if (err != KErrNone) - mouse = 0; - err = HAL::Get(HALData::EMachineUid, machineUID); - if (err != KErrNone) - machineUID = 0; - err = HAL::Get(HALData::EPen, touch); - if (err != KErrNone || touchIsUnsupportedOnSystem) - touch = 0; -#ifdef __WINS__ - if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) { - //for symbian SDK emulator, force values to match typical devices. - mouse = 0; - touch = touchIsUnsupportedOnSystem ? 0 : 1; - } -#endif - if (mouse || machineUID == KMachineUidSamsungI8510) { - S60->hasTouchscreen = false; - S60->virtualMouseRequired = false; - } - else if (!touch) { - S60->hasTouchscreen = false; - S60->virtualMouseRequired = true; - } - else { - S60->hasTouchscreen = true; - S60->virtualMouseRequired = false; - } - - S60->avkonComponentsSupportTransparency = false; - S60->menuBeingConstructed = false; - -#ifdef Q_WS_S60 - TUid KCRUidAvkon = { 0x101F876E }; - TUint32 KAknAvkonTransparencyEnabled = 0x0000000D; - - CRepository* repository = 0; - TRAP(err, repository = CRepository::NewL(KCRUidAvkon)); - - if(err == KErrNone) { - TInt value = 0; - err = repository->Get(KAknAvkonTransparencyEnabled, value); - if(err == KErrNone) { - S60->avkonComponentsSupportTransparency = (value==1) ? true : false; - } - } - delete repository; - repository = 0; -#endif - - qt_keymapper_private()->updateInputLanguage(); - -#ifdef QT_KEYPAD_NAVIGATION - if (touch) { - QApplicationPrivate::navigationMode = Qt::NavigationModeNone; - } else { - QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional; - } -#endif - -#ifndef QT_NO_CURSOR - //Check if window server pointer cursors are supported or not -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - //In generic binary, use the HAL and OS version - //Any other known good phones should be added here. - if (machineUID == KMachineUidSamsungI8510 || (QSysInfo::symbianVersion() != QSysInfo::SV_9_4 - && QSysInfo::symbianVersion() != QSysInfo::SV_9_3 && QSysInfo::symbianVersion() - != QSysInfo::SV_9_2)) { - S60->brokenPointerCursors = false; - qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup()); - } - else - S60->brokenPointerCursors = true; -#endif - - if (S60->mouseInteractionEnabled) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) { - qt_symbian_set_pointer_sprite(Qt::ArrowCursor); - qt_symbian_show_pointer_sprite(); - } - else -#endif - S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); - } -#endif - - QFont systemFont; - systemFont.setFamily(systemFont.defaultFamily()); - QApplicationPrivate::setSystemFont(systemFont); - - QObject::connect(qApp, SIGNAL(aboutToQuit()), qApp, SLOT(_q_aboutToQuit())); - -#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE - QApplicationPrivate::instance()->useTranslucentEGLSurfaces = true; - - const TUid KIvePropertyCat = {0x2726beef}; - enum TIvePropertyChipType { - EVCBCM2727B1 = 0x00000000, - EVCBCM2763A0 = 0x04000100, - EVCBCM2763B0 = 0x04000102, - EVCBCM2763C0 = 0x04000103, - EVCBCM2763C1 = 0x04000104, - EVCBCMUnknown = 0x7fffffff - }; - - TInt chipType = EVCBCMUnknown; - if (RProperty::Get(KIvePropertyCat, 0 /*chip type*/, chipType) == KErrNone) { - if (chipType == EVCBCM2727B1) { - // We have only 32MB GPU memory. Use raster surfaces - // for transparent TLWs. - QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; - } - } else { - QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; - } - if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) - QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; -#else - QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; -#endif -/* - ### Commented out for now as parameter handling not needed in SOS(yet). Code below will break testlib with -o flag - int argc = priv->argc; - char **argv = priv->argv; - - // 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; - } - -#if defined(QT_DEBUG) - if (qstrcmp(argv[i], "-nograb") == 0) - appNoGrab = !appNoGrab; - else -#endif // QT_DEBUG - ; - } -*/ - - // Register WId with the metatype system. This is to enable - // QWidgetPrivate::create_sys to used delayed slot invocation in order - // to destroy WId objects during reparenting. - qRegisterMetaType<WId>("WId"); -} - -#ifdef QT_NO_FREETYPE -extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp -#endif - -/***************************************************************************** - qt_cleanup() - cleans up when the application is finished - *****************************************************************************/ -void qt_cleanup() -{ -#ifdef Q_WS_S60 - S60->setButtonGroupContainer(0); -#endif - if(qt_S60Beep) { - delete qt_S60Beep; - qt_S60Beep = 0; - } - QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles - QPixmapCache::clear(); // Has to happen now, since QS60PixmapData has FBS handles - -#ifdef QT_NO_FREETYPE - qt_cleanup_symbianFontDatabase(); -#endif -// S60 structure and window server session are freed in eventdispatcher destructor as they are needed there - - // It's important that this happens here, before the event dispatcher gets - // deleted, because the input context needs the event loop one last time before - // it dies. - delete QApplicationPrivate::inputContext; - QApplicationPrivate::inputContext = 0; - - //Change mouse pointer back - S60->wsSession().SetPointerCursorMode(EPointerCursorNone); - -#ifdef Q_WS_S60 - // Clear CBA - CEikonEnv::Static()->AppUiFactory()->SwapButtonGroup(0); - delete S60->buttonGroupContainer(); - S60->setButtonGroupContainer(0); -#endif - - // Call EndFullScreen() to prevent confusing the system effect state machine. - qt_endFullScreenEffect(); - - if (S60->qtOwnsS60Environment) { - // Restore the S60 framework trap handler. See qt_init(). - User::SetTrapHandler(S60->s60InstalledTrapHandler); - - CEikonEnv* coe = CEikonEnv::Static(); - coe->PrepareToExit(); - // The CEikonEnv itself is destroyed in here. - coe->DestroyEnvironment(); - } -} - -void QApplicationPrivate::initializeWidgetPaletteHash() -{ - // TODO: Implement QApplicationPrivate::initializeWidgetPaletteHash() - // Possibly a task fot the S60Style guys -} - -void QApplicationPrivate::createEventDispatcher() -{ - Q_Q(QApplication); - eventDispatcher = new QEventDispatcherS60(q); -} - -QString QApplicationPrivate::appName() const -{ - return QCoreApplicationPrivate::appName(); -} - -bool QApplicationPrivate::modalState() -{ - return app_do_modal; -} - -void QApplicationPrivate::enterModal_sys(QWidget *widget) -{ -#ifdef Q_SYMBIAN_TRANSITION_EFFECTS - S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeEnter); -#endif - if (widget) { - static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(ETrue); - // Modal partial screen dialogs (like queries) capture pointer events. - // ### FixMe: Add specialized behaviour for fullscreen modal dialogs - widget->effectiveWinId()->SetGloballyCapturing(ETrue); - widget->effectiveWinId()->SetPointerCapture(ETrue); - } - 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) -{ -#ifdef Q_SYMBIAN_TRANSITION_EFFECTS - S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeExit); -#endif - if (widget) { - static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(EFalse); - // ### FixMe: Add specialized behaviour for fullscreen modal dialogs - widget->effectiveWinId()->SetGloballyCapturing(EFalse); - widget->effectiveWinId()->SetPointerCapture(EFalse); - } - 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; -} - -void QApplicationPrivate::openPopup(QWidget *popup) -{ - if (popup && qobject_cast<QComboBox *>(popup->parentWidget())) - static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(ETrue); - - if (!QApplicationPrivate::popupWidgets) - QApplicationPrivate::popupWidgets = new QWidgetList; - QApplicationPrivate::popupWidgets->append(popup); - - // Cancel focus widget pointer capture and long tap timer - if (QApplication::focusWidget()) { - static_cast<QSymbianControl*>(QApplication::focusWidget()->effectiveWinId())->CancelLongTapTimer(); - QApplication::focusWidget()->effectiveWinId()->SetPointerCapture(false); - } - - if (!qt_nograb()) { - // Cancel pointer capture and long tap timer for earlier popup - int popupCount = QApplicationPrivate::popupWidgets->count(); - if (popupCount > 1) { - QWidget* prevPopup = QApplicationPrivate::popupWidgets->at(popupCount-2); - static_cast<QSymbianControl*>(prevPopup->effectiveWinId())->CancelLongTapTimer(); - prevPopup->effectiveWinId()->SetPointerCapture(false); - } - - // Enable pointer capture for this (topmost) popup - Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); - WId id = popup->effectiveWinId(); - id->SetPointerCapture(true); - } - - // 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 - QWidget *fw = popup->focusWidget(); - if (fw) { - fw->setFocus(Qt::PopupFocusReason); - } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup - fw = QApplication::focusWidget(); - if (fw) { - QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); - q_func()->sendEvent(fw, &e); - } - } -} - -void QApplicationPrivate::closePopup(QWidget *popup) -{ - if (popup && qobject_cast<QComboBox *>(popup->parentWidget())) - static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(EFalse); - - if (!QApplicationPrivate::popupWidgets) - return; - QApplicationPrivate::popupWidgets->removeAll(popup); - - // Cancel pointer capture and long tap for this popup - WId id = popup->effectiveWinId(); - id->SetPointerCapture(false); - static_cast<QSymbianControl*>(id)->CancelLongTapTimer(); - - if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup - delete QApplicationPrivate::popupWidgets; - QApplicationPrivate::popupWidgets = 0; - if (!qt_nograb()) { // grabbing not disabled - Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); - if (QWidgetPrivate::mouseGrabber != 0) - QWidgetPrivate::mouseGrabber->grabMouse(); - - if (QWidgetPrivate::keyboardGrabber != 0) - QWidgetPrivate::keyboardGrabber->grabKeyboard(); - - QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget() - : q_func()->focusWidget(); - if (fw) { - if(fw->window()->isModal()) // restore pointer capture for modal window - fw->effectiveWinId()->SetPointerCapture(true); - - 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 (QWidget *fw = QApplication::focusWidget()) { - QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); - q_func()->sendEvent(fw, &e); - } - - // Enable pointer capture for previous popup - if (aw) { - aw->effectiveWinId()->SetPointerCapture(true); - } - } -} - -QWidget * QApplication::topLevelAt(QPoint const& point) -{ - QWidget *found = 0; - int lowestZ = INT_MAX; - QWidgetList list = QApplication::topLevelWidgets(); - for (int i = 0; i < list.count(); ++i) { - QWidget *widget = list.at(i); - if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) { - Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created)); - if (widget->geometry().adjusted(0,0,1,1).contains(point)) { - // At this point we know there is a Qt widget under the point. - // Now we need to make sure it is the top most in the z-order. - RDrawableWindow *const window = widget->effectiveWinId()->DrawableWindow(); - int z = window->OrdinalPosition(); - if (z < lowestZ) { - lowestZ = z; - found = widget; - } - } - } - } - return found; -} - -void QApplication::alert(QWidget * /* widget */, int /* duration */) -{ - // TODO: Implement QApplication::alert(QWidget *widget, int duration) -} - -int QApplication::doubleClickInterval() -{ - TTimeIntervalMicroSeconds32 us; - TInt distance; - S60->wsSession().GetDoubleClickSettings(us, distance); - return (us.Int() / 1000); -} - -void QApplication::setDoubleClickInterval(int ms) -{ - TTimeIntervalMicroSeconds32 newUs( ms * 1000); - TTimeIntervalMicroSeconds32 us; - TInt distance; - S60->wsSession().GetDoubleClickSettings(us, distance); - if (us != newUs) - S60->wsSession().SetDoubleClick(newUs, distance); -} - -int QApplication::keyboardInputInterval() -{ - return QApplicationPrivate::keyboard_input_time; -} - -void QApplication::setKeyboardInputInterval(int ms) -{ - QApplicationPrivate::keyboard_input_time = ms; -} - -int QApplication::cursorFlashTime() -{ - return QApplicationPrivate::cursor_flash_time; -} - -void QApplication::setCursorFlashTime(int msecs) -{ - QApplicationPrivate::cursor_flash_time = msecs; -} - -void QApplication::beep() -{ - if (!qt_S60Beep) { - TInt frequency = 880; - TTimeIntervalMicroSeconds duration(500000); - TRAP_IGNORE(qt_S60Beep=QS60Beep::NewL(frequency, duration)); - } - if (qt_S60Beep) - qt_S60Beep->Play(); -} - -static inline bool callSymbianEventFilters(const QSymbianEvent *event) -{ - long unused; - return qApp->filterEvent(const_cast<QSymbianEvent *>(event), &unused); -} - -/*! - \warning This function is only available on Symbian. - \since 4.6 - - This function processes an individual Symbian event - \a event. It returns 1 if the event was handled, 0 if - the \a event was not handled, and -1 if the event was - not handled because the event is not known to Qt. - */ - -int QApplication::symbianProcessEvent(const QSymbianEvent *event) -{ - Q_D(QApplication); - - QScopedLoopLevelCounter counter(d->threadData); - - if (d->eventDispatcher->filterEvent(const_cast<QSymbianEvent *>(event))) - return 1; - - QWidget *w = qApp ? qApp->focusWidget() : 0; - if (w) { - QInputContext *ic = w->inputContext(); - if (ic && ic->symbianFilterEvent(w, event)) - return 1; - } - - if (symbianEventFilter(event)) - return 1; - - switch (event->type()) { - case QSymbianEvent::WindowServerEvent: - return d->symbianProcessWsEvent(event); - case QSymbianEvent::CommandEvent: - return d->symbianHandleCommand(event); - case QSymbianEvent::ResourceChangeEvent: - return d->symbianResourceChange(event); - default: - return -1; - } -} - -int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent) -{ - // Qt event handling. Handle some events regardless of if the handle is in our - // widget map or not. - const TWsEvent *event = symbianEvent->windowServerEvent(); - CCoeControl* control = reinterpret_cast<CCoeControl*>(event->Handle()); - const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control); - switch (event->Type()) { - case EEventPointerEnter: - if (controlInMap) { - callSymbianEventFilters(symbianEvent); - return 1; // Qt::Enter will be generated in HandlePointerL - } - break; - case EEventPointerExit: - if (controlInMap) { - if (callSymbianEventFilters(symbianEvent)) - return 1; - if (S60) { - // mouseEvent outside our window, send leave event to last focused widget - QMouseEvent mEvent(QEvent::Leave, S60->lastPointerEventPos, S60->lastCursorPos, - Qt::NoButton, QApplicationPrivate::mouse_buttons, Qt::NoModifier); - if (S60->lastPointerEventTarget) - qt_sendSpontaneousEvent(S60->lastPointerEventTarget,&mEvent); - S60->lastPointerEventTarget = 0; - } - return 1; - } - break; - case EEventScreenDeviceChanged: // fallthrough -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - case EEventDisplayChanged: -#endif - if (callSymbianEventFilters(symbianEvent)) - return 1; - if (S60) - S60->updateScreenSize(); - if (qt_desktopWidget) { - QSize oldSize = qt_desktopWidget->size(); - qt_desktopWidget->data->crect.setWidth(S60->screenWidthInPixels); - qt_desktopWidget->data->crect.setHeight(S60->screenHeightInPixels); - QResizeEvent e(qt_desktopWidget->size(), oldSize); - QApplication::sendEvent(qt_desktopWidget, &e); - } - return 0; // Propagate to CONE - case EEventWindowVisibilityChanged: - if (controlInMap) { - if (callSymbianEventFilters(symbianEvent)) - return 1; - const TWsVisibilityChangedEvent *visChangedEvent = event->VisibilityChanged(); - if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::ENotVisible) - S60->controlVisibilityChanged(control, false); - else if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::EPartiallyVisible) - S60->controlVisibilityChanged(control, true); - return 1; - } - break; - case EEventFocusGained: - if (callSymbianEventFilters(symbianEvent)) - return 1; -#ifndef QT_NO_CURSOR - //re-enable mouse interaction - if (S60->mouseInteractionEnabled) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_show_pointer_sprite(); - else -#endif - S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); - } -#endif -#ifdef QT_SOFTKEYS_ENABLED - if (!CEikonEnv::Static()->EikAppUi()->IsDisplayingMenuOrDialog()) - QSoftKeyManager::updateSoftKeys(); -#endif - break; - case EEventFocusLost: - if (callSymbianEventFilters(symbianEvent)) - return 1; -#ifndef QT_NO_CURSOR - //disable mouse as may be moving to application that does not support it - if (S60->mouseInteractionEnabled) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_hide_pointer_sprite(); - else -#endif - S60->wsSession().SetPointerCursorMode(EPointerCursorNone); - } -#endif - break; - case KGoomMemoryLowEvent: -#ifdef QT_DEBUG - qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryLowEvent"; -#endif - if (callSymbianEventFilters(symbianEvent)) - return 1; -#ifdef QT_GRAPHICSSYSTEM_RUNTIME - if(QApplicationPrivate::runtime_graphics_system) { - bool switchToSwRendering(false); - - foreach (QWidget *w, QApplication::topLevelWidgets()) { - if(w->d_func()->topData()->backingStore) { - switchToSwRendering = true; - break; - } - } - - if (switchToSwRendering) { - QRuntimeGraphicsSystem *gs = - static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system); - gs->setGraphicsSystem(QLatin1String("raster")); - } - } -#endif - break; - case KGoomMemoryGoodEvent: -#ifdef QT_DEBUG - qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryGoodEvent"; -#endif - if (callSymbianEventFilters(symbianEvent)) - return 1; -#ifdef QT_GRAPHICSSYSTEM_RUNTIME - if(QApplicationPrivate::runtime_graphics_system) { - QRuntimeGraphicsSystem *gs = - static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system); - gs->setGraphicsSystem(QLatin1String("openvg")); - } -#endif - break; -#ifdef Q_SYMBIAN_SUPPORTS_SURFACES - case EEventUser: - { - // GOOM is looking for candidates to kill so indicate that we are - // capable of cleaning up by handling this event - TInt32 *data = reinterpret_cast<TInt32 *>(event->EventData()); - if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent) - return 1; - } - break; -#endif - -#ifdef Q_WS_S60 - case KEikInputLanguageChange: - qt_keymapper_private()->updateInputLanguage(); - break; -#endif - - default: - break; - } - - if (!controlInMap) - return -1; - - return 0; -} - -/*! - \warning This virtual function is only available on Symbian. - \since 4.6 - - If you create an application that inherits QApplication and reimplement - this function, you get direct access to events that the are received - from Symbian. 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, and does nothing with \a event. - */ -bool QApplication::symbianEventFilter(const QSymbianEvent *event) -{ - Q_UNUSED(event); - return false; -} - -/*! - \warning This function is only available on Symbian. - \since 4.6 - - Handles \a{command}s which are typically handled by - CAknAppUi::HandleCommandL(). Qts Ui integration into Symbian is - partially achieved by deriving from CAknAppUi. Currently, exit, - menu and softkey commands are handled. - - \sa s60EventFilter(), s60ProcessEvent() -*/ -int QApplicationPrivate::symbianHandleCommand(const QSymbianEvent *symbianEvent) -{ - Q_Q(QApplication); - int ret = 0; - - if (callSymbianEventFilters(symbianEvent)) - return 1; - - int command = symbianEvent->command(); - - switch (command) { -#ifdef Q_WS_S60 - case EAknSoftkeyExit: { - QCloseEvent ev; - QApplication::sendSpontaneousEvent(q, &ev); - if (ev.isAccepted()) { - q->quit(); - ret = 1; - } - break; - } -#endif - case EEikCmdExit: - q->quit(); - ret = 1; - break; - default: -#ifdef Q_WS_S60 - bool handled = QSoftKeyManager::handleCommand(command); - if (handled) - ret = 1; - else - ret = QMenuBarPrivate::symbianCommands(command); -#endif - break; - } - - return ret; -} - -/*! - \warning This function is only available on Symbian. - \since 4.6 - - Handles the resource change specified by \a type. - - Currently, KEikDynamicLayoutVariantSwitch and - KAknsMessageSkinChange are handled. - */ -int QApplicationPrivate::symbianResourceChange(const QSymbianEvent *symbianEvent) -{ - int ret = 0; - - int type = symbianEvent->resourceChangeType(); - - switch (type) { -#ifdef Q_WS_S60 - case KEikDynamicLayoutVariantSwitch: - { - if (callSymbianEventFilters(symbianEvent)) - return 1; - if (S60) - S60->updateScreenSize(); - -#ifndef QT_NO_STYLE_S60 - QS60Style *s60Style = 0; - -#ifndef QT_NO_STYLE_STYLESHEET - QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplication::style()); - if (proxy) - s60Style = qobject_cast<QS60Style*>(proxy->baseStyle()); - else -#endif - s60Style = qobject_cast<QS60Style*>(QApplication::style()); - - if (s60Style) { - s60Style->d_func()->handleDynamicLayoutVariantSwitch(); - ret = 1; - } -#endif - } - break; - -#ifndef QT_NO_STYLE_S60 - case KAknsMessageSkinChange: - if (callSymbianEventFilters(symbianEvent)) - return 1; - if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) { - s60Style->d_func()->handleSkinChange(); - ret = 1; - } - break; -#endif -#endif // Q_WS_S60 - default: - break; - } - - return ret; -} - -#ifndef QT_NO_WHEELEVENT -int QApplication::wheelScrollLines() -{ - return QApplicationPrivate::wheel_scroll_lines; -} - -void QApplication::setWheelScrollLines(int n) -{ - QApplicationPrivate::wheel_scroll_lines = n; -} -#endif //QT_NO_WHEELEVENT - -bool QApplication::isEffectEnabled(Qt::UIEffect /* effect */) -{ - // TODO: Implement QApplication::isEffectEnabled(Qt::UIEffect effect) - return false; -} - -void QApplication::setEffectEnabled(Qt::UIEffect /* effect */, bool /* enable */) -{ - // TODO: Implement QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) -} - -TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym) -{ - if (!scanCode) - return keysym; - - QApplicationPrivate *d = QApplicationPrivate::instance(); - - if (keysym) { - // If keysym is specified, cache it. - d->scanCodeCache.insert(scanCode, keysym); - return keysym; - } else { - // If not, retrieve the cached version. - return d->scanCodeCache[scanCode]; - } -} - -void QApplicationPrivate::initializeMultitouch_sys() -{ -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - if (HAL::Get(HALData::EPointer3DPressureSupported, pressureSupported) != KErrNone) - pressureSupported = 0; - if (HAL::Get(HALData::EPointer3DMaxPressure, maxTouchPressure) != KErrNone) - maxTouchPressure = KMaxTInt; -#else - pressureSupported = 0; - maxTouchPressure = KMaxTInt; -#endif -} - -void QApplicationPrivate::cleanupMultitouch_sys() -{ } - -#ifndef QT_NO_SESSIONMANAGER -QSessionManager::QSessionManager(QApplication * /* app */, QString & /* id */, QString& /* key */) -{ - -} - -QSessionManager::~QSessionManager() -{ - -} - -bool QSessionManager::allowsInteraction() -{ - return false; -} - -void QSessionManager::cancel() -{ - -} -#endif //QT_NO_SESSIONMANAGER - -#ifdef QT_KEYPAD_NAVIGATION -/* - * Show/Hide the mouse cursor depending on phone type and chosen mode - */ -void QApplicationPrivate::setNavigationMode(Qt::NavigationMode mode) -{ -#ifndef QT_NO_CURSOR - const bool wasCursorOn = (QApplicationPrivate::navigationMode == Qt::NavigationModeCursorAuto - && !S60->hasTouchscreen) - || QApplicationPrivate::navigationMode == Qt::NavigationModeCursorForceVisible; - const bool isCursorOn = (mode == Qt::NavigationModeCursorAuto - && !S60->hasTouchscreen) - || mode == Qt::NavigationModeCursorForceVisible; - - if (!wasCursorOn && isCursorOn) { - //Show the cursor, when changing from another mode to cursor mode - qt_symbian_set_cursor_visible(true); - } - else if (wasCursorOn && !isCursorOn) { - //Hide the cursor, when leaving cursor mode - qt_symbian_set_cursor_visible(false); - } -#endif - QApplicationPrivate::navigationMode = mode; -} -#endif - -#ifndef QT_NO_CURSOR -/***************************************************************************** - QApplication cursor stack - *****************************************************************************/ - -void QApplication::setOverrideCursor(const QCursor &cursor) -{ - qApp->d_func()->cursor_list.prepend(cursor); - qt_symbian_setGlobalCursor(cursor); -} - -void QApplication::restoreOverrideCursor() -{ - if (qApp->d_func()->cursor_list.isEmpty()) - return; - qApp->d_func()->cursor_list.removeFirst(); - - if (!qApp->d_func()->cursor_list.isEmpty()) { - qt_symbian_setGlobalCursor(qApp->d_func()->cursor_list.first()); - } - else { - //determine which widget has focus - QWidget *w = QApplication::widgetAt(QCursor::pos()); -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) { - qt_symbian_set_pointer_sprite(w ? w->cursor() : Qt::ArrowCursor); - } - else -#endif - { - //because of the internals of window server, we need to force the cursor - //to be set in all child windows too, otherwise when the cursor is over - //the child window it may show a widget cursor or arrow cursor instead, - //depending on construction order. - QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys()); - while (iter.hasNext()) { - CCoeControl *ctrl = iter.next(); - if(ctrl->OwnsWindow()) { - ctrl->DrawableWindow()->ClearPointerCursor(); - } - } - if (w) - qt_symbian_setWindowCursor(w->cursor(), w->effectiveWinId()); - else - qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup()); - } - } -} - -#endif // QT_NO_CURSOR - -void QApplicationPrivate::_q_aboutToQuit() -{ - qt_beginFullScreenEffect(); - -#ifdef Q_SYMBIAN_TRANSITION_EFFECTS - // Send the shutdown tfx command - S60->wsSession().SendEffectCommand(ETfxCmdAppShutDown); -#endif -} - -QS60ThreadLocalData::QS60ThreadLocalData() -{ - CCoeEnv *env = CCoeEnv::Static(); - if (env) { - //if this is the UI thread, share objects owned by CONE - usingCONEinstances = true; - wsSession = env->WsSession(); - screenDevice = env->ScreenDevice(); - } - else { - usingCONEinstances = false; - qt_symbian_throwIfError(wsSession.Connect(qt_s60GetRFs())); - screenDevice = new CWsScreenDevice(wsSession); - screenDevice->Construct(); - } -} - -QS60ThreadLocalData::~QS60ThreadLocalData() -{ - if (!usingCONEinstances) { - delete screenDevice; - wsSession.Close(); - } -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp deleted file mode 100644 index 72a05afbd0..0000000000 --- a/src/gui/kernel/qapplication_win.cpp +++ /dev/null @@ -1,4243 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef Q_WS_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_WS_WINCE_WM -#include <windowsm.h> -#include <tpcshell.h> -#ifdef QT_WINCE_GESTURES -#ifndef QT_NO_GESTURES -#include <gesture.h> -#endif -#endif -#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 "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" -#include "qscrollbar.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> -#include <private/qsystemlibrary_p.h> -#include "qevent_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 - -#if !defined(WINABLEAPI) -# if defined(Q_WS_WINCE) -# include <bldver.h> -# endif -# if !defined(Q_WS_WINCE) -# include <winable.h> -# endif -#endif - -#ifndef QT_NO_GESTURES -# ifndef GID_ZOOM -# define GID_ZOOM 3 -# define GID_TWOFINGERTAP 6 -# define GID_PRESSANDTAP 7 -# define GID_ROLLOVER GID_PRESSANDTAP -# endif -#endif - -#ifndef WM_TOUCH -# define WM_TOUCH 0x0240 -#endif - -#ifndef TOUCHEVENTF_MOVE -# define TOUCHEVENTF_MOVE 0x0001 -# define TOUCHEVENTF_DOWN 0x0002 -# define TOUCHEVENTF_UP 0x0004 -# define TOUCHEVENTF_INRANGE 0x0008 -# define TOUCHEVENTF_PRIMARY 0x0010 -# define TOUCHEVENTF_NOCOALESCE 0x0020 -# define TOUCHEVENTF_PEN 0x0040 -# define TOUCHEVENTF_PALM 0x0080 - -# define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001 -# define TOUCHINPUTMASKF_EXTRAINFO 0x0002 -# define TOUCHINPUTMASKF_CONTACTAREA 0x0004 - -typedef struct tagTOUCHINPUT -{ - LONG x; - LONG y; - HANDLE hSource; - DWORD dwID; - DWORD dwFlags; - DWORD dwMask; - DWORD dwTime; - ULONG_PTR dwExtraInfo; - DWORD cxContact; - DWORD cyContact; -} TOUCHINPUT, *PTOUCHINPUT; - -#endif - -#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 - -#ifndef IMR_RECONVERTSTRING -#define IMR_RECONVERTSTRING 4 -#endif - -#ifndef IMR_CONFIRMRECONVERTSTRING -#define IMR_CONFIRMRECONVERTSTRING 0x0005 -#endif -QT_BEGIN_NAMESPACE - -#ifdef Q_WS_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 - -#ifndef QT_NO_GESTURES -typedef DWORD (API *AygRecognizeGesture)(SHRGINFO*); -static AygRecognizeGesture ptrRecognizeGesture = 0; -static bool aygResolved = false; -static void resolveAygLibs() -{ - if (!aygResolved) { - aygResolved = true; - QSystemLibrary ayglib(QLatin1String("aygshell")); - ptrRecognizeGesture = (AygRecognizeGesture) ayglib.resolve("SHRecognizeGesture"); - } -} -#endif // QT_NO_GESTURES - -#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 - -Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; -Q_GUI_EXPORT 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; - -#ifndef QT_NO_TABLETEVENT -static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab); -static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor); -static void initWinTabFunctions(); // resolve the WINTAB api functions -#endif // QT_NO_TABLETEVENT - - -#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<quint64, 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); - -// 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 -#endif -#ifndef GET_KEYSTATE_WPARAM -#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam)) -#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) -#define XBUTTON1 0x0001 -#define XBUTTON2 0x0002 -#endif -#ifndef MK_XBUTTON1 -#define MK_XBUTTON1 0x0020 -#define MK_XBUTTON2 0x0040 -#endif - -// support for multi-media-keys -#ifndef WM_APPCOMMAND -#define WM_APPCOMMAND 0x0319 -#endif - -#ifndef FAPPCOMMAND_MOUSE -#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 -#endif // FAPPCOMMAND_MOUSE - -// 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 - -#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 - *****************************************************************************/ - -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 - -extern "C" LRESULT QT_WIN_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); -#ifndef QT_NO_GESTURES - bool translateGestureEvent(const MSG &msg, const GESTUREINFO &gi); -#endif - 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_WS_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 (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_WS_WINCE - NONCLIENTMETRICS ncm; - ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); - - QFont menuFont = qt_LOGFONTtoQFont(ncm.lfMenuFont, true); - QFont messageFont = qt_LOGFONTtoQFont(ncm.lfMessageFont, true); - QFont statusFont = qt_LOGFONTtoQFont(ncm.lfStatusFont, true); - QFont titleFont = qt_LOGFONTtoQFont(ncm.lfCaptionFont, true); - - LOGFONT lfIconTitleFont; - SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); - QFont iconTitleFont = qt_LOGFONTtoQFont(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"); - smallerFont.setBold(true); - QApplication::setFont(smallerFont, "QAbstractButton"); - } -#endif// Q_WS_WINCE -} - -static void qt_win_read_cleartype_settings() -{ - UINT result = 0; -#ifdef Q_OS_WINCE - if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &result, 0)) - qt_cleartype_enabled = result; -#else - if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) - qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE); -#endif - - int winSmooth; - if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) { - qt_fontsmoothing_gamma = winSmooth / qreal(1000.0); - } else { - qt_fontsmoothing_gamma = 1.0; - } - - // Safeguard ourselves against corrupt registry values... - if (qt_fontsmoothing_gamma > 5 || qt_fontsmoothing_gamma < 1) - qt_fontsmoothing_gamma = qreal(1.4); -} - -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 = false; - if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP - && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) - SystemParametersInfo(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)); - 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"); -} - -static void qt_set_windows_updateScrollBar(QWidget *widget) -{ - QList<QObject*> children = widget->children(); - for (int i = 0; i < children.size(); ++i) { - QObject *o = children.at(i); - if(!o->isWidgetType()) - continue; - if (QWidget *w = static_cast<QWidget *>(o)) - qt_set_windows_updateScrollBar(w); - } -#ifndef QT_NO_SCROLLBAR - if (qobject_cast<QScrollBar*>(widget)) - widget->updateGeometry(); -#endif -} - - -/***************************************************************************** - qt_init() - initializes Qt for Windows - *****************************************************************************/ - -typedef BOOL (WINAPI *PtrSetProcessDPIAware) (VOID); -static PtrSetProcessDPIAware ptrSetProcessDPIAware = 0; -PtrUpdateLayeredWindow ptrUpdateLayeredWindow = 0; -PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect = 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 - argv[j++] = argv[i]; - } - if(j < priv->argc) { - priv->argv[j] = 0; - priv->argc = j; - } - -#ifndef Q_WS_WINCE - // No message boxes but important ones - SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); -#endif - -#ifndef Q_WS_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_WS_WINCE) - GdiSetBatchLimit(1); -#endif - - // initialize key mapper - QKeyMapper::changeKeyboard(); - - QColormap::initialize(); - QFont::initialize(); -#ifndef QT_NO_CURSOR - QCursorData::initialize(); -#endif - qApp->setObjectName(priv->appName()); - - // default font -#ifndef Q_WS_WINCE - HGDIOBJ stockFont = GetStockObject(DEFAULT_GUI_FONT); -#else - HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT); -#endif - - LOGFONT lf; - GetObject(stockFont, sizeof(lf), &lf); - QFont systemFont = qt_LOGFONTtoQFont(lf, true); - -#ifndef Q_WS_WINCE - if (systemFont.family() == QLatin1String("MS Shell Dlg")) { - systemFont.setFamily(QLatin1String("MS Shell Dlg 2")); - } -#endif - - QApplicationPrivate::setSystemFont(systemFont); - - // QFont::locale_init(); ### Uncomment when it does something on Windows - - if (QApplication::desktopSettingsAware()) - qt_set_windows_resources(); - -#ifndef QT_NO_TABLETEVENT - initWinTabFunctions(); -#endif // QT_NO_TABLETEVENT - 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) QSystemLibrary::resolve(QLatin1String("user32"), - "UpdateLayeredWindowIndirect"); - ptrUpdateLayeredWindow = - (PtrUpdateLayeredWindow) QSystemLibrary::resolve(QLatin1String("user32"), - "UpdateLayeredWindow"); - - if (ptrUpdateLayeredWindow && !ptrUpdateLayeredWindowIndirect) - ptrUpdateLayeredWindowIndirect = qt_updateLayeredWindowIndirect; - - // Notify Vista and Windows 7 that we support highter DPI settings - ptrSetProcessDPIAware = (PtrSetProcessDPIAware) - QSystemLibrary::resolve(QLatin1String("user32"), "SetProcessDPIAware"); - if (ptrSetProcessDPIAware) - ptrSetProcessDPIAware(); -#endif - -#ifndef QT_NO_GESTURES - priv->GetGestureInfo = 0; - priv->GetGestureExtraArgs = 0; - priv->CloseGestureInfoHandle = 0; - priv->SetGestureConfig = 0; - priv->GetGestureConfig = 0; - priv->BeginPanningFeedback = 0; - priv->UpdatePanningFeedback = 0; - priv->EndPanningFeedback = 0; - -#if defined(Q_WS_WINCE_WM) && defined(QT_WINCE_GESTURES) - priv->GetGestureInfo = (PtrGetGestureInfo) &TKGetGestureInfo; - priv->GetGestureExtraArgs = (PtrGetGestureExtraArgs) &TKGetGestureExtraArguments; -#elif !defined(Q_WS_WINCE) - #if !defined(QT_NO_NATIVE_GESTURES) - priv->GetGestureInfo = - (PtrGetGestureInfo)QSystemLibrary::resolve(QLatin1String("user32"), - "GetGestureInfo"); - priv->GetGestureExtraArgs = - (PtrGetGestureExtraArgs)QSystemLibrary::resolve(QLatin1String("user32"), - "GetGestureExtraArgs"); - priv->CloseGestureInfoHandle = - (PtrCloseGestureInfoHandle)QSystemLibrary::resolve(QLatin1String("user32"), - "CloseGestureInfoHandle"); - priv->SetGestureConfig = - (PtrSetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"), - "SetGestureConfig"); - priv->GetGestureConfig = - (PtrGetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"), - "GetGestureConfig"); - #endif // QT_NO_NATIVE_GESTURES - QSystemLibrary libTheme(QLatin1String("uxtheme")); - priv->BeginPanningFeedback = - (PtrBeginPanningFeedback)libTheme.resolve("BeginPanningFeedback"); - priv->UpdatePanningFeedback = - (PtrUpdatePanningFeedback)libTheme.resolve("UpdatePanningFeedback"); - priv->EndPanningFeedback = - (PtrEndPanningFeedback)libTheme.resolve("EndPanningFeedback"); -#endif -#endif // QT_NO_GESTURES -} - -/***************************************************************************** - 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_WS_WINCE - // Deinitialize OLE/COM - OleUninitialize(); -#endif -} - - -/***************************************************************************** - Platform specific global and internal functions - *****************************************************************************/ - -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) - -// -// If 0 is passed as the widget pointer, register a window class -// for QWidget as default. This is used in QGLTemporaryContext -// during GL initialization, where we don't want to use temporary -// QWidgets or QGLWidgets, neither do we want to have separate code -// to register window classes. -// -const QString qt_reg_winclass(QWidget *w) // register window class -{ - Qt::WindowFlags flags = w ? w->windowFlags() : (Qt::WindowFlags)0; - Qt::WindowFlags type = flags & Qt::WindowType_Mask; - - uint style; - bool icon; - QString cname; - if (w && qt_widget_private(w)->isGLWidget) { - cname = QLatin1String("QGLWidget"); - style = CS_DBLCLKS; -#ifndef Q_WS_WINCE - style |= CS_OWNDC; -#endif - icon = true; - } else if (w && (flags & Qt::MSWindowsOwnDC)) { - cname = QLatin1String("QWidgetOwnDC"); - style = CS_DBLCLKS; -#ifndef Q_WS_WINCE - style |= CS_OWNDC; -#endif - icon = true; - } else if (w && (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 |= CS_DROPSHADOW; - } - cname = QLatin1String("QToolTip"); - } else { - cname = QLatin1String("QTool"); - } -#ifndef Q_WS_WINCE - style |= CS_SAVEBITS; -#endif - icon = false; - } else if (w && (type == Qt::Popup)) { - cname = QLatin1String("QPopup"); - style = CS_DBLCLKS; -#ifndef Q_WS_WINCE - style |= CS_SAVEBITS; -#endif - if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP - && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) - style |= CS_DROPSHADOW; - icon = false; - } else { - cname = QLatin1String("QWidget"); - style = CS_DBLCLKS; - icon = true; - } - -#ifndef Q_WS_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[MAX_PATH]; - GetModuleFileName(0, uniqueAppID, MAX_PATH); - cname = QString::number(RegisterWindowMessage( - (const wchar_t *) QString::fromWCharArray(uniqueAppID).toLower().replace(QLatin1Char('\\'), - QLatin1Char('_')).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) { - WNDCLASS wcinfo; - classExists = GetClassInfo((HINSTANCE)qWinAppInst(), (wchar_t*)cname.utf16(), &wcinfo); - classExists = classExists && wcinfo.lpfnWndProc != QtWndProc; - } - - if (classExists) - cname += QString::number((quintptr)QtWndProc); - - if (winclassNames()->contains(cname)) // already registered in our list - return cname; - -#ifndef Q_WS_WINCE - WNDCLASSEX wc; - wc.cbSize = sizeof(WNDCLASSEX); -#else - WNDCLASS wc; -#endif - wc.style = style; - wc.lpfnWndProc = (WNDPROC)QtWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = qWinAppInst(); - if (icon) { - wc.hIcon = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); -#ifndef Q_WS_WINCE - if (wc.hIcon) { - int sw = GetSystemMetrics(SM_CXSMICON); - int sh = GetSystemMetrics(SM_CYSMICON); - wc.hIconSm = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, sw, sh, 0); - } else { - wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); - wc.hIconSm = 0; - } -#endif - } else { - wc.hIcon = 0; -#ifndef Q_WS_WINCE - wc.hIconSm = 0; -#endif - } - wc.hCursor = 0; -#ifndef Q_WS_WINCE - HBRUSH brush = 0; - if (w && !qt_widget_private(w)->isGLWidget) - brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); - wc.hbrBackground = brush; -#else - wc.hbrBackground = 0; -#endif - wc.lpszMenuName = 0; - wc.lpszClassName = (wchar_t*)cname.utf16(); - -#ifndef Q_WS_WINCE - ATOM atom = RegisterClassEx(&wc); -#else - ATOM atom = RegisterClass(&wc); -#endif - -#ifndef QT_NO_DEBUG - if (!atom) - qErrnoWarning("QApplication::regClass: Registering window class failed."); -#else - Q_UNUSED(atom); -#endif - - winclassNames()->insert(cname, 1); - return cname; -} - -Q_GUI_EXPORT const QString qt_getRegisteredWndClass() -{ - return qt_reg_winclass(0); -} - -static void unregWinClasses() -{ - WinClassNameHash *hash = winclassNames(); - QHash<QString, int>::ConstIterator it = hash->constBegin(); - while (it != hash->constEnd()) { - UnregisterClass((wchar_t*)it.key().utf16(), 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 -} - -static 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) -{ -#ifdef Q_OS_WINCE - Q_UNUSED(widget); - Q_UNUSED(duration); -#else - bool stopFlash = duration < 0; - - if (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; - - FlashWindowEx(&info); - } -#endif -} - -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(); - -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_WS_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 - && message != WM_MOUSEHWHEEL) - -#ifndef Q_WS_WINCE - || (message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK) -#endif - ; -} - -extern "C" LRESULT QT_WIN_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_WS_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_WS_WINCE - || message == WM_NCMBUTTONDOWN || message == WM_NCLBUTTONDOWN - || message == WM_NCRBUTTONDOWN)) { -#else - )) { -#endif - ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0); - } - - switch (message) { -#ifndef Q_WS_WINCE -#ifndef QT_NO_SESSIONMANAGER - 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 = QApplicationPrivate::instance(); - 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 - QApplication::quit(); - } - - RETURN(0); - } -#endif - case WM_DISPLAYCHANGE: - if (QApplication::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_WS_WINCE - // CE SIP hide/show - if (qt_desktopWidget && 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 (QApplication::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, QApplication::topLevelWidgets()) { - if (!w->isVisible()) - continue; - ((QETWidget *) w)->forceUpdate(); - } - } - - break; - case WM_SYSCOLORCHANGE: - if (QApplication::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: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDBLCLK: - case WM_XBUTTONDBLCLK: - 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 (QApplication::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_WS_WINCE) && !defined(QT_NO_CONTEXTMENU) - if (message == WM_LBUTTONDOWN && widget != QApplication::activePopupWidget()) { - QWidget* alienWidget = widget; - if ((alienWidget != QApplication::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); - if (alienWidget) - pos = alienWidget->mapFromGlobal(globalPos); - } - if (alienWidget) { - 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(); -#ifndef QT_NO_GESTURES - if (ptrRecognizeGesture && (ptrRecognizeGesture(&shrg) == GN_CONTEXTMENU)) { - if (QApplication::activePopupWidget()) - QApplication::activePopupWidget()->close(); - QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos); - result = qt_sendSpontaneousEvent(alienWidget, &e); - } -#endif // QT_NO_GESTURES - } - } - } -#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 PeekMessage - // 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 (PeekMessage(&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 { - switch (message) { - case WM_TOUCH: - result = QApplicationPrivate::instance()->translateTouchEvent(msg); - break; - case WM_KEYDOWN: // keyboard event - case WM_SYSKEYDOWN: - qt_keymapper_private()->updateKeyMap(msg); - // fall-through intended - case WM_KEYUP: - case WM_SYSKEYUP: -#if Q_OS_WINCE_WM - case WM_HOTKEY: - if(HIWORD(msg.lParam) == VK_TBACK) { - const bool hotKeyDown = !(LOWORD(msg.lParam) & MOD_KEYUP); - msg.lParam = 0x69 << 16; - msg.wParam = VK_BACK; - if (hotKeyDown) { - msg.message = WM_KEYDOWN; - qt_keymapper_private()->updateKeyMap(msg); - } else { - msg.message = WM_KEYUP; - } - } - // fall-through intended -#endif - case WM_IME_CHAR: - case WM_IME_KEYDOWN: - case WM_CHAR: { - MSG msg1; - bool anyMsg = PeekMessage(&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 (QApplication::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: - case WM_MOUSEHWHEEL: - 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_TREBLE_DOWN: - key = Qt::Key_TrebleDown; - break; - case APPCOMMAND_TREBLE_UP: - key = Qt::Key_TrebleUp; - break; - case APPCOMMAND_HELP: - key = Qt::Key_Help; - break; - case APPCOMMAND_FIND: - key = Qt::Key_Search; - break; - default: - break; - } - if (key) { - bool res = false; - QWidget *g = QWidget::keyboardGrabber(); - if (g) - widget = (QETWidget*)g; - else if (QApplication::focusWidget()) - widget = (QETWidget*)QApplication::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_WS_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->minimumHeight() == widget->maximumHeight()) { - if (pos.y() < -(fs.top() - fs.left())) - return HTCAPTION; - if (pos.y() >= widget->height()) - return HTBORDER; - } - if (widget->minimumWidth() == widget->maximumWidth() && (pos.x() < 0 || pos.x() >= widget->width())) - return HTBORDER; - } - } - - result = false; - break; -#endif - - case WM_SYSCOMMAND: { -#ifndef Q_WS_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 - DefWindowProc(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); - const QString title = widget->windowIconText(); - if (!title.isEmpty()) - widget->setWindowTitle_helper(title); - } - 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); - const QString title = widget->windowTitle(); - if (!title.isEmpty()) - widget->setWindowTitle_helper(title); - } - result = false; - break; - default: - result = false; - break; - } - - if (window_state_change) { - QWindowStateChangeEvent e(oldstate); - qt_sendSpontaneousEvent(widget, &e); - } -#endif // #ifndef Q_OS_WINCE - - break; - } - - case WM_SETTINGCHANGE: - if ( QApplication::type() == QApplication::Tty ) - break; - - if (!msg.wParam) { -#ifdef Q_WS_WINCE - // On Windows CE, lParam parameter is a constant, not a char pointer. - if (msg.lParam == INI_INTL) { -#else - QString area = QString::fromWCharArray((wchar_t*)msg.lParam); - if (area == QLatin1String("intl")) { -#endif - QLocalePrivate::updateSystemPrivate(); - if (!widget->testAttribute(Qt::WA_SetLocale)) - widget->dptr()->setLocale_helper(QLocale(), true); - QEvent e(QEvent::LocaleChange); - QApplication::sendEvent(qApp, &e); - } - } - else if (msg.wParam == SPI_SETICONTITLELOGFONT) { - if (QApplication::desktopSettingsAware()) { - widget = (QETWidget*)QWidget::find(hwnd); - if (widget && !widget->parentWidget()) { - qt_set_windows_font_resources(); - } - } - } - else if (msg.wParam == SPI_SETNONCLIENTMETRICS) { - widget = (QETWidget*)QWidget::find(hwnd); - if (widget && !widget->parentWidget()) { - qt_set_windows_updateScrollBar(widget); - QEvent e(QEvent::LayoutRequest); - QApplication::sendEvent(widget, &e); - } - } - - break; - - case WM_PAINT: // paint event - case WM_ERASEBKGND: // erase window background - result = widget->translatePaintEvent(msg); - break; - -#ifndef Q_WS_WINCE - case WM_ENTERSIZEMOVE: - autoCaptureWnd = hwnd; - break; - case WM_EXITSIZEMOVE: - autoCaptureWnd = 0; - 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 ( QApplication::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. -#ifdef Q_WS_WINCE - { -#ifdef Q_WS_WINCE_WM - // On Windows mobile we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages. - // Thus we have to unset the minimized state explicitly. We must do this for all - // top-level widgets, because we get the HWND of a random widget here. - foreach (QWidget* tlw, QApplication::topLevelWidgets()) { - if (tlw->isMinimized()) - tlw->setWindowState(tlw->windowState() & ~Qt::WindowMinimized); - } -#else - // On Windows CE we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages. - // Thus we have to unset the minimized state explicitly. - if (widget->windowState() & Qt::WindowMinimized) - widget->setWindowState(widget->windowState() & ~Qt::WindowMinimized); -#endif // Q_WS_WINCE_WM - -#else - if (!(widget->windowState() & Qt::WindowMinimized)) { -#endif - // Ignore the activate message send by WindowsXP to a minimized window -#ifdef Q_WS_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_WS_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_WS_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; - -#ifndef QT_NO_CONTEXTMENU - 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 (QApplication::activePopupWidget()) - fw = (QApplication::activePopupWidget()->focusWidget() - ? QApplication::activePopupWidget()->focusWidget() - : QApplication::activePopupWidget()); - else if (QApplication::focusWidget()) - fw = QApplication::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 -#endif - - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: { - QWidget *fw = QApplication::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; - } - case WM_IME_REQUEST: { - QWidget *fw = QApplication::focusWidget(); - QWinInputContext *im = fw ? qobject_cast<QWinInputContext *>(fw->inputContext()) : 0; - if (fw && im) { - if(wParam == IMR_RECONVERTSTRING) { - int ret = im->reconvertString((RECONVERTSTRING *)lParam); - if (ret == -1) { - result = false; - } else { - return ret; - } - } else if (wParam == IMR_CONFIRMRECONVERTSTRING) { - RETURN(TRUE); - } else { - // in all other cases, call DefWindowProc() - result = false; - } - } - break; - } -#ifndef Q_WS_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 (QApplication::startingUp() || QApplication::closingDown() || lParam != (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)QSystemLibrary::resolve(QLatin1String("oleacc"), "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); - memcpy((void *)lParam, text.utf16(), (text.size() + 1) * sizeof(ushort)); - 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: - - #ifndef QT_NO_TABLETEVENT - if (ptrWTPacketsGet && ptrWTInfo) { - const bool enteredProximity = LOWORD(lParam) != 0; - PACKET proximityBuffer[1]; // we are only interested in the first packet in this case - const int totalPacks = ptrWTPacketsGet(qt_tablet_context, 1, proximityBuffer); - if (totalPacks > 0) { - const UINT currentCursor = proximityBuffer[0].pkCursor; - - UINT csr_physid; - ptrWTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &csr_physid); - UINT csr_type; - ptrWTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &csr_type); - const UINT deviceIdMask = 0xFF6; // device type mask && device color mask - quint64 uniqueId = (csr_type & deviceIdMask); - uniqueId = (uniqueId << 32) | csr_physid; - - // initialising and updating the cursor should be done in response to - // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send - // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES - const QTabletCursorInfo *const globalCursorInfo = tCursorInfo(); - if (!globalCursorInfo->contains(uniqueId)) - tabletInit(uniqueId, csr_type, qt_tablet_context); - - currentTabletPointer = globalCursorInfo->value(uniqueId); - tabletUpdateCursor(currentTabletPointer, currentCursor); - } - qt_tabletChokeMouse = false; - - 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_WS_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*)QApplication::focusWidget(); - HWND focus = ::GetFocus(); - //if there is a current widget and the new widget belongs to the same toplevel window - //or if the current widget was embedded into non-qt window (i.e. we won't get WM_ACTIVATEAPP) - //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 - const bool embedded = widget && ((QETWidget*)widget->window())->topData()->embedded; - if (widget && (embedded || ::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 || QApplication::closingDown() - || QApplication::type() == QApplication::Tty) - break; - - if (widget->testAttribute(Qt::WA_WState_Polished)) - QApplication::style()->unpolish(widget); - - if (widget->testAttribute(Qt::WA_WState_Polished)) - QApplication::style()->polish(widget); - widget->repolishStyle(*QApplication::style()); - if (widget->isVisible()) - widget->update(); - break; - -#ifndef Q_WS_WINCE - case WM_INPUTLANGCHANGE: { - wchar_t info[7]; - if (!GetLocaleInfo(MAKELCID(lParam, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, info, 6)) { - inputcharset = CP_ACP; - } else { - inputcharset = QString::fromWCharArray(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) - widget->showMinimized(); - else -#ifndef QT_NO_MENUBAR - QMenuBar::wceCommands(LOWORD(wParam)); -#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; -#ifndef QT_NO_GESTURES -#if !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES) - case WM_GESTURE: { - GESTUREINFO gi; - memset(&gi, 0, sizeof(GESTUREINFO)); - gi.cbSize = sizeof(GESTUREINFO); - - QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); - BOOL bResult = false; - if (qAppPriv->GetGestureInfo) - bResult = qAppPriv->GetGestureInfo((HANDLE)msg.lParam, &gi); - if (bResult) { - if (gi.dwID == GID_BEGIN) { - // find the alien widget for the gesture position. - // This might not be accurate as the position is the center - // point of two fingers for multi-finger gestures. - QPoint pt(gi.ptsLocation.x, gi.ptsLocation.y); - QWidget *w = widget->childAt(widget->mapFromGlobal(pt)); - qAppPriv->gestureWidget = w ? w : widget; - } - if (qAppPriv->gestureWidget) - static_cast<QETWidget*>(qAppPriv->gestureWidget)->translateGestureEvent(msg, gi); - if (qAppPriv->CloseGestureInfoHandle) - qAppPriv->CloseGestureInfoHandle((HANDLE)msg.lParam); - if (gi.dwID == GID_END) - qAppPriv->gestureWidget = 0; - } else { - DWORD dwErr = GetLastError(); - if (dwErr > 0) - qWarning() << "translateGestureEvent: error = " << dwErr; - } - result = true; - break; - } -#endif // !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES) -#endif // QT_NO_GESTURES -#ifndef QT_NO_CURSOR - case WM_SETCURSOR: { - QCursor *ovr = QApplication::overrideCursor(); - if (ovr) { - SetCursor(ovr->handle()); - RETURN(TRUE); - } - result = false; - break; - } -#endif - 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_WS_WINCE - if (type != WM_NCHITTEST) { -#endif - if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) || - type == WM_MOUSEWHEEL || type == WM_MOUSEHWHEEL || - type == WM_MOUSELEAVE || - (type >= WM_KEYFIRST && type <= WM_KEYLAST) -#ifndef Q_WS_WINCE - || type == WM_NCMOUSEMOVE -#endif - ) { - if (type == WM_MOUSEMOVE -#ifndef Q_WS_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_WS_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 = QApplication::focusWidget()) { - QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); - QApplication::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() - : QApplication::focusWidget(); - if (fw) { - 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 = 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_WS_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} }; - while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) - ; - if (msg.message == WM_MOUSEMOVE) - PostMessage(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 -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 (PeekMessage(&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 (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST, - PM_NOREMOVE)) { - if (keyMsg.time < mouseMsg.time) { - if ((keyMsg.lParam & 0xC0000000) == 0x40000000) { - PeekMessage(&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; - // Extract the x,y coordinates from the lParam as we do in the WndProc - msgPtr->pt.x = GET_X_LPARAM(mouseMsg.lParam); - msgPtr->pt.y = GET_Y_LPARAM(mouseMsg.lParam); - ClientToScreen(msg.hwnd, &(msgPtr->pt)); - // Remove the mouse move message - PeekMessage(&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; - } - } -#ifndef Q_OS_WINCE - static bool trackMouseEventLookup = false; - typedef BOOL (WINAPI *PtrTrackMouseEvent)(LPTRACKMOUSEEVENT); - static PtrTrackMouseEvent ptrTrackMouseEvent = 0; -#endif - 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 - || type == QEvent::TabletMove) { - - 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 = QApplication::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) { - if (!trackMouseEventLookup) { - trackMouseEventLookup = true; - ptrTrackMouseEvent = (PtrTrackMouseEvent)QSystemLibrary::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 = QApplication::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: - case QEvent::TabletRelease: - - releaseAfter = true; - break; - default: - break; // nothing for mouse move - } - - if (target->isEnabled()) { - if (popupButtonFocus) { - target = popupButtonFocus; - } else if (popupChild) { - 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; - } - -#ifndef Q_OS_WINCE - if (type == QEvent::MouseButtonPress - && QApplication::activePopupWidget() != activePopupWidget - && ptrTrackMouseEvent - && curWin) { - // Since curWin is already the window we clicked on, - // we have to setup the mouse tracking here. - 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 - if (type == QEvent::MouseButtonPress - && QApplication::activePopupWidget() != activePopupWidget - && replayPopupMouseEvent) { - // the popup disappeared. 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); - PostMessage(hwndTarget, msg.message, msg.wParam, lParam); - } - } else if (type == QEvent::MouseButtonRelease && button == Qt::RightButton - && QApplication::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 || msg.message == WM_MOUSEHWHEEL) - delta = (short) HIWORD (msg.wParam); - else - delta = (int) msg.wParam; - - Qt::Orientation orient = (msg.message == WM_MOUSEHWHEEL || 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; - - // according to the MSDN documentation on WM_MOUSEHWHEEL: - // a positive value indicates that the wheel was rotated to the right; - // a negative value indicates that the wheel was rotated to the left. - // Qt defines this value as the exact opposite, so we have to flip the value! - if (msg.message == WM_MOUSEHWHEEL) - delta = -delta; - - 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 = QApplication::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 != QApplication::focusWidget() && (w = QApplication::focusWidget())) { - QWidget* popup = QApplication::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) -/* -------------------------------------------------------------------------- */ -// Initialize the "static" information of a cursor device (pen, airbrush, etc). -// The QTabletDeviceData is initialized with the data that do not change in time -// (number of button, type of device, etc) but do not initialize the variable data -// (e.g.: pen or eraser) -#ifndef QT_NO_TABLETEVENT - -static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab) -{ - Q_ASSERT(ptrWTInfo); - Q_ASSERT(ptrWTGet); - - Q_ASSERT(!tCursorInfo()->contains(uniqueId)); - - /* browse WinTab's many info items to discover pressure handling. */ - AXIS np; - LOGCONTEXT lc; - - /* get the current context for its device variable. */ - ptrWTGet(hTab, &lc); - - /* get the size of the pressure axis. */ - QTabletDeviceData tdd; - tdd.llId = uniqueId; - - 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); - - LOGCONTEXT lcMine; - - /* get default region */ - ptrWTInfo(WTI_DEFCONTEXT, 0, &lcMine); - - tdd.minX = 0; - tdd.maxX = int(lcMine.lcInExtX) - int(lcMine.lcInOrgX); - - tdd.minY = 0; - tdd.maxY = int(lcMine.lcInExtY) - int(lcMine.lcInOrgY); - - tdd.minZ = 0; - tdd.maxZ = int(lcMine.lcInExtZ) - int(lcMine.lcInOrgZ); - - const uint cursorTypeBitMask = 0x0F06; // bitmask to find the specific cursor type (see Wacom FAQ) - if (((csr_type & 0x0006) == 0x0002) && ((csr_type & cursorTypeBitMask) != 0x0902)) { - tdd.currentDevice = QTabletEvent::Stylus; - } else { - switch (csr_type & cursorTypeBitMask) { - 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; - } - } - tCursorInfo()->insert(uniqueId, tdd); -} -#endif // QT_NO_TABLETEVENT - -// Update the "dynamic" information of a cursor device (pen, airbrush, etc). -// The dynamic information is the information of QTabletDeviceData that can change -// in time (eraser or pen if a device is turned around). -#ifndef QT_NO_TABLETEVENT - -static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor) -{ - switch (currentCursor % 3) { // %3 for dual track - case 0: - tdd.currentPointerType = QTabletEvent::Cursor; - break; - case 1: - tdd.currentPointerType = QTabletEvent::Pen; - break; - case 2: - tdd.currentPointerType = QTabletEvent::Eraser; - break; - default: - tdd.currentPointerType = QTabletEvent::UnknownPointer; - } -} -#endif // QT_NO_TABLETEVENT - -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())); - - if (t == QEvent::TabletPress) - { - qt_button_down = QApplication::widgetAt(globalPos); - } - - // make sure the tablet event get's sent to the proper widget... - QWidget *w = 0; - - if (qt_button_down) - w = qt_button_down; // Pass it to the thing that's grabbed it. - else - w = QApplication::widgetAt(globalPos); - - if (!w) - w = this; - - if (t == QEvent::TabletRelease) - { - if (qt_win_ignoreNextMouseReleaseEvent) { - qt_win_ignoreNextMouseReleaseEvent = false; - if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) { - releaseAutoCapture(); - qt_button_down = 0; - } - } - - } - - 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; - - -#ifndef QT_NO_TABLETEVENT - -static void initWinTabFunctions() -{ -#if defined(Q_OS_WINCE) - return; -#else - if (!qt_is_gui_used) - return; - - QSystemLibrary library(QLatin1String("wintab32")); - ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); - ptrWTGet = (PtrWTGet)library.resolve("WTGetW"); - ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable"); - ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap"); - ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet"); -#endif // Q_OS_WINCE -} -#endif // QT_NO_TABLETEVENT - - -// -// 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); - - if (d_func()->isGLWidget) { - if (d_func()->usesDoubleBufferedGLContext) - InvalidateRect(internalWinId(), 0, false); - } else { - 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_WS_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_WS_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) { - bool window_state_changed = false; - Qt::WindowStates oldstate = Qt::WindowStates(dataPtr()->window_state); - if (isMinimized()) { -#ifndef Q_WS_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); - // Capture SIZE_MAXIMIZED and SIZE_RESTORED without preceding WM_SYSCOMMAND - // (Aero Snap on Win7) - } else if (msg.wParam == SIZE_MAXIMIZED && !isMaximized()) { - data->window_state |= Qt::WindowMaximized; - window_state_changed = true; - } else if (msg.wParam == SIZE_RESTORED && isMaximized()) { - data->window_state &= ~(Qt::WindowMaximized); - window_state_changed = true; - } - if (window_state_changed) { - QWindowStateChangeEvent e(oldstate); - 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_WS_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_WS_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); -} - -#ifndef QT_NO_GESTURES -bool QETWidget::translateGestureEvent(const MSG &, const GESTUREINFO &gi) -{ - const QPoint widgetPos = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); - QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos); - if (alienWidget && alienWidget->internalWinId()) - alienWidget = 0; - QWidget *widget = alienWidget ? alienWidget : this; - - QNativeGestureEvent event; - event.sequenceId = gi.dwSequenceID; - event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); - event.argument = gi.ullArguments; - - switch (gi.dwID) { - case GID_BEGIN: - event.gestureType = QNativeGestureEvent::GestureBegin; - break; - case GID_END: - event.gestureType = QNativeGestureEvent::GestureEnd; - break; - case GID_ZOOM: - event.gestureType = QNativeGestureEvent::Zoom; - break; - case GID_PAN: - event.gestureType = QNativeGestureEvent::Pan; - break; - case GID_ROTATE: - event.gestureType = QNativeGestureEvent::Rotate; - break; - case GID_TWOFINGERTAP: - case GID_ROLLOVER: - default: - break; - } - if (event.gestureType != QNativeGestureEvent::None) - qt_sendSpontaneousEvent(widget, &event); - return true; -} -#endif // QT_NO_GESTURES - -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_WS_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; - SystemParametersInfo(SPI_SETWHEELSCROLLLINES, (uint)n, 0, 0); -#else - QApplicationPrivate::wheel_scroll_lines = n; -#endif -} - -int QApplication::wheelScrollLines() -{ -#ifdef SPI_GETWHEELSCROLLLINES - uint i = 3; - SystemParametersInfo(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()) { - // 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: - api = SPI_GETMENUFADE; - break; - case Qt::UI_AnimateCombo: - api = SPI_GETCOMBOBOXANIMATION; - break; - case Qt::UI_AnimateTooltip: - api = SPI_GETTOOLTIPANIMATION; - break; - case Qt::UI_FadeTooltip: - api = SPI_GETTOOLTIPFADE; - break; - default: - api = SPI_GETUIEFFECTS; - break; - } - SystemParametersInfo(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 - - -bool QApplicationPrivate::HasTouchSupport = false; -PtrRegisterTouchWindow QApplicationPrivate::RegisterTouchWindow = 0; -PtrGetTouchInputInfo QApplicationPrivate::GetTouchInputInfo = 0; -PtrCloseTouchInputHandle QApplicationPrivate::CloseTouchInputHandle = 0; - -void QApplicationPrivate::initializeMultitouch_sys() -{ - if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { - static const int QT_SM_DIGITIZER = 94; - int value = GetSystemMetrics(QT_SM_DIGITIZER); - static const int QT_NID_INTEGRATED_TOUCH = 0x01; - static const int QT_NID_EXTERNAL_TOUCH = 0x02; - static const int QT_NID_MULTI_INPUT = 0x40; - QApplicationPrivate::HasTouchSupport = - value & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT); - } - - QSystemLibrary library(QLatin1String("user32")); - // MinGW (g++ 3.4.5) accepts only C casts. - RegisterTouchWindow = (PtrRegisterTouchWindow)(library.resolve("RegisterTouchWindow")); - GetTouchInputInfo = (PtrGetTouchInputInfo)(library.resolve("GetTouchInputInfo")); - CloseTouchInputHandle = (PtrCloseTouchInputHandle)(library.resolve("CloseTouchInputHandle")); - - touchInputIDToTouchPointID.clear(); -} - -void QApplicationPrivate::cleanupMultitouch_sys() -{ - touchInputIDToTouchPointID.clear(); -} - -bool QApplicationPrivate::translateTouchEvent(const MSG &msg) -{ - QWidget *widgetForHwnd = QWidget::find(msg.hwnd); - if (!widgetForHwnd) - return false; - - QRect screenGeometry = QApplication::desktop()->screenGeometry(widgetForHwnd); - - QList<QTouchEvent::TouchPoint> touchPoints; - - QVector<TOUCHINPUT> winTouchInputs(msg.wParam); - memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); - Qt::TouchPointStates allStates = 0; - QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); - for (int i = 0; i < winTouchInputs.count(); ++i) { - const TOUCHINPUT &touchInput = winTouchInputs.at(i); - - int touchPointID = touchInputIDToTouchPointID.value(touchInput.dwID, -1); - if (touchPointID == -1) { - touchPointID = touchInputIDToTouchPointID.count(); - touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID); - } - - QTouchEvent::TouchPoint touchPoint(touchPointID); - - // update state - QPointF screenPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); - QRectF screenRect; - if (touchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) - screenRect.setSize(QSizeF(qreal(touchInput.cxContact) / qreal(100.), - qreal(touchInput.cyContact) / qreal(100.))); - screenRect.moveCenter(screenPos); - - Qt::TouchPointStates state; - if (touchInput.dwFlags & TOUCHEVENTF_DOWN) { - state = Qt::TouchPointPressed; - } else if (touchInput.dwFlags & TOUCHEVENTF_UP) { - state = Qt::TouchPointReleased; - } else { - state = (screenPos == touchPoint.screenPos() - ? Qt::TouchPointStationary - : Qt::TouchPointMoved); - } - if (touchInput.dwFlags & TOUCHEVENTF_PRIMARY) - state |= Qt::TouchPointPrimary; - touchPoint.setState(state); - touchPoint.setScreenRect(screenRect); - touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(), - screenPos.y() / screenGeometry.height())); - - allStates |= state; - - touchPoints.append(touchPoint); - } - QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); - - if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) { - // all touch points released, forget the ids we've seen, they may not be reused - touchInputIDToTouchPointID.clear(); - } - - translateRawTouchEvent(widgetForHwnd, QTouchEvent::TouchScreen, touchPoints); - return true; -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp deleted file mode 100644 index 20542ea328..0000000000 --- a/src/gui/kernel/qapplication_x11.cpp +++ /dev/null @@ -1,6239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// ### 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/qcrashhandler_p.h> -#include <private/qcolor_p.h> -#include <private/qcursor_p.h> -#include <private/qiconloader_p.h> -#include <qgtkstyle.h> -#include "qstyle.h" -#include "qmetaobject.h" -#include "qtimer.h" -#include "qlibrary.h" -#include <private/qgraphicssystemfactory_p.h> -#include "qguiplatformplugin_p.h" -#include "qkde_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 - -#ifndef QT_GUI_DOUBLE_CLICK_RADIUS -#define QT_GUI_DOUBLE_CLICK_RADIUS 5 -#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> -#if !defined(QT_NO_XINPUT) -#include <X11/extensions/XI.h> -#endif - -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <locale.h> - -#include "qwidget_p.h" - -#include <private/qbackingstore_p.h> - -#ifdef QT_RX71_MULTITOUCH -# include <qsocketnotifier.h> -# include <linux/input.h> -# include <errno.h> -#endif - -#if _POSIX_VERSION+0 < 200112L && !defined(Q_OS_BSD4) -# define QT_NO_UNSETENV -#endif - -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" - "_NET_WM_SYNC_REQUEST\0" - "_NET_WM_SYNC_REQUEST_COUNTER\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" - "SAVE_TARGETS\0" - "CLIP_TEMPORARY\0" - "_QT_SELECTION\0" - "_QT_CLIPBOARD_SENTINEL\0" - "_QT_SELECTION_SENTINEL\0" - "CLIPBOARD_MANAGER\0" - - "RESOURCE_MANAGER\0" - - "_XSETROOT_ID\0" - - "_QT_SCROLL_DONE\0" - "_QT_INPUT_ENCODING\0" - - "_MOTIF_WM_HINTS\0" - - "DTWM_IS_RUNNING\0" - "ENLIGHTENMENT_DESKTOP\0" - "_DT_SAVE_MODE\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" - - "_NET_ACTIVE_WINDOW\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" - - // Wacom old. (before version 0.10) - "Wacom Stylus\0" - "Wacom Cursor\0" - "Wacom Eraser\0" - - // Tablet - "STYLUS\0" - "ERASER\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 - -typedef bool(*QX11FilterFunction)(XEvent *event); - -Q_GLOBAL_STATIC(QList<QX11FilterFunction>, x11Filters) - -Q_GUI_EXPORT void qt_installX11EventFilter(QX11FilterFunction func) -{ - Q_ASSERT(func); - - if (QList<QX11FilterFunction> *list = x11Filters()) - list->append(func); -} - -Q_GUI_EXPORT void qt_removeX11EventFilter(QX11FilterFunction func) -{ - Q_ASSERT(func); - - if (QList<QX11FilterFunction> *list = x11Filters()) - list->removeOne(func); -} - - -static bool qt_x11EventFilter(XEvent* ev) -{ - long unused; - if (qApp->filterEvent(ev, &unused)) - return true; - if (const QList<QX11FilterFunction> *list = x11Filters()) { - for (QList<QX11FilterFunction>::const_iterator it = list->constBegin(); it != list->constEnd(); ++it) { - if ((*it)(ev)) - 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); - 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: - QWidgetPrivate* d_func() { return QWidget::d_func(); } - 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) -{ - if (X11->display != dpy) { - // only handle X errors for our display - return 0; - } - - 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(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; - - default: -#if !defined(QT_NO_XINPUT) - if (err->request_code == X11->xinput_major - && err->error_code == (X11->xinput_errorbase + XI_BadDevice) - && err->minor_code == 3 /* X_OpenDevice */) { - return 0; - } -#endif - 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"; -#ifndef QT_NO_XKB - else if(err->request_code == X11->xkb_major) - extensionName = "XKEYBOARD"; -#endif - - 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 - -#ifndef QT_NO_XSYNC -struct qt_sync_request_event_data -{ - WId window; -}; - -#if defined(Q_C_CALLBACKS) -extern "C" { -#endif - -static Bool qt_sync_request_scanner(Display*, XEvent *event, XPointer arg) -{ - qt_sync_request_event_data *data = - reinterpret_cast<qt_sync_request_event_data*>(arg); - if (event->type == ClientMessage && - event->xany.window == data->window && - event->xclient.message_type == ATOM(WM_PROTOCOLS) && - (Atom)event->xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST)) { - QWidget *w = QWidget::find(event->xany.window); - if (QTLWExtra *tlw = ((QETWidget*)w)->d_func()->maybeTopData()) { - const ulong timestamp = (const ulong) event->xclient.data.l[1]; - if (timestamp > X11->time) - X11->time = timestamp; - if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) { - tlw->syncRequestTimestamp = timestamp; - tlw->newCounterValueLo = event->xclient.data.l[2]; - tlw->newCounterValueHi = event->xclient.data.l[3]; - } - } - return true; - } - return false; -} - -#if defined(Q_C_CALLBACKS) -} -#endif -#endif // QT_NO_XSYNC - -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 - 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])); - } - - // ### Fix properly for 4.6 - bool usingGtkSettings = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle"); - if (!usingGtkSettings) { - if (groupCount == QPalette::NColorGroups) - QApplicationPrivate::setSystemPalette(pal); - } - - if (!appFont) { - // ### Fix properly for 4.6 - if (!usingGtkSettings) { - QFont font(QApplication::font()); - QString fontDescription; - // Override Qt font if KDE4 settings can be used - if (X11->desktopVersion == 4) { - QSettings kdeSettings(QKde::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.isNull() && X11->use_xrender) { - stylename = qt_guiPlatformPlugin()->styleName(); - } - - static QString currentStyleName = stylename; - if (QCoreApplication::startingUp()) { - if (!stylename.isEmpty() && QApplicationPrivate::styleOverride.isNull()) - QApplicationPrivate::styleOverride = stylename; - } else { - if (currentStyleName != stylename) { - currentStyleName = stylename; - 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"); - - 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); -} - -// 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); - } - // QGtkStyle sets it's own system palette - bool gtkStyle = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle"); - bool kdeColors = (QApplication::desktopSettingsAware() && X11->desktopEnvironment == DE_KDE); - if (!gtkStyle && (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; - } - - 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); - } - - 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); - } - - pal = qt_guiPlatformPlugin()->palette().resolve(pal); - 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"))); - } - - QIconLoader::instance()->updateSystemTheme(); -} - - -// 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 *) ×tamp, 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; -Q_GLOBAL_STATIC(QByteArray, wacomDeviceName) -#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; - // otherwise use fontconfig to convert the string to integer - else - FcNameConstant((FcChar8 *) str, val); - } -} - -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; - - X11->use_xkb = false; - X11->xkb_major = 0; - X11->xkb_eventbase = 0; - X11->xkb_errorbase = 0; - - // MIT-SHM - X11->use_mitshm = false; - X11->use_mitshm_pixmaps = 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 = 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) - - if (QApplication::testAttribute(Qt::AA_X11InitThreads)) - XInitThreads(); - - // 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; - - int widthMM = DisplayWidthMM(X11->display, s); - if (widthMM != 0) { - screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10); - } else { - screen->dpiX = 72; - } - - int heightMM = DisplayHeightMM(X11->display, s); - if (heightMM != 0) { - screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10); - } else { - screen->dpiY = 72; - } - - 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)); - - // MITSHM only works for local displays, so do a quick check here - // to determine whether the display is local or not (not 100 % accurate). - // BGR server layouts are not supported either, since it requires the raster - // engine to work on a QImage with BGR layout. - bool local = displayName.isEmpty() || displayName.lastIndexOf(QLatin1Char(':')) == 0; - if (local && (qgetenv("QT_X11_NO_MITSHM").toInt() == 0)) { - Visual *defaultVisual = DefaultVisual(X11->display, DefaultScreen(X11->display)); - X11->use_mitshm = ((defaultVisual->red_mask == 0xff0000 - || defaultVisual->red_mask == 0xf800) - && (defaultVisual->green_mask == 0xff00 - || defaultVisual->green_mask == 0x7e0) - && (defaultVisual->blue_mask == 0xff - || defaultVisual->blue_mask == 0x1f)); - X11->use_mitshm_pixmaps = 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"); - X11->ptrXRRSizes = - (PtrXRRSizes) xrandrLib.resolve("XRRSizes"); - } -# else - X11->ptrXRRSelectInput = XRRSelectInput; - X11->ptrXRRUpdateConfiguration = XRRUpdateConfiguration; - X11->ptrXRRRootToScreen = XRRRootToScreen; - X11->ptrXRRQueryExtension = XRRQueryExtension; - X11->ptrXRRSizes = XRRSizes; -# 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; - } - } -#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_XSYNC - int xsync_evbase, xsync_errbase; - int major, minor; - if (XSyncQueryExtension(X11->display, &xsync_evbase, &xsync_errbase)) - XSyncInitialize(X11->display, &major, &minor); -#endif // QT_NO_XSYNC - -#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 - -#ifndef QT_NO_XKB - int xkblibMajor = XkbMajorVersion; - int xkblibMinor = XkbMinorVersion; - X11->use_xkb = XkbQueryExtension(X11->display, - &X11->xkb_major, - &X11->xkb_eventbase, - &X11->xkb_errorbase, - &xkblibMajor, - &xkblibMinor); - if (X11->use_xkb) { - // 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); - - // select for group change events - XkbSelectEventDetails(X11->display, - XkbUseCoreKbd, - XkbStateNotify, - XkbAllStateComponentsMask, - XkbGroupStateMask); - - // current group state is queried when creating the keymapper, no need to do it here - } -#endif - - -#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 !defined(QT_NO_XRENDER) && (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 - X11->fc_hint_style = -1; - 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(); - - // 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 - -#ifndef QT_NO_XFIXES - if (X11->ptrXFixesSelectSelectionInput) - X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(), ATOM(_NET_WM_CM_S0), - XFixesSetSelectionOwnerNotifyMask - | XFixesSelectionWindowDestroyNotifyMask - | XFixesSelectionClientCloseNotifyMask); -#endif // QT_NO_XFIXES - X11->compositingManagerRunning = XGetSelectionOwner(X11->display, - ATOM(_NET_WM_CM_S0)); - X11->desktopEnvironment = DE_UNKNOWN; - X11->desktopVersion = 0; - - Atom type; - int format; - unsigned long length, after; - uchar *data = 0; - int rc; - - do { - if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { - X11->desktopEnvironment = DE_KDE; - X11->desktopVersion = qgetenv("KDE_SESSION_VERSION").toInt(); - break; - } - - if (qgetenv("DESKTOP_SESSION") == "gnome") { - X11->desktopEnvironment = DE_GNOME; - break; - } - - // GNOME_DESKTOP_SESSION_ID is deprecated for some reason, but still check it - if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) { - X11->desktopEnvironment = DE_GNOME; - break; - } - - rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(_DT_SAVE_MODE), - 0, 2, False, XA_STRING, &type, &format, &length, - &after, &data); - if (rc == Success && length) { - if (!strcmp(reinterpret_cast<char *>(data), "xfce4")) { - // Pretend that xfce4 is gnome, as it uses the same libraries. - // The detection above is stolen from xdg-open. - X11->desktopEnvironment = DE_GNOME; - break; - } - - // We got the property but it wasn't xfce4. Free data before it gets overwritten. - XFree(data); - data = 0; - } - - rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(DTWM_IS_RUNNING), - 0, 1, False, AnyPropertyType, &type, &format, &length, - &after, &data); - if (rc == Success && length) { - // DTWM is running, meaning most likely CDE is running... - X11->desktopEnvironment = DE_CDE; - break; - } - - rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), - ATOM(_SGI_DESKS_MANAGER), 0, 1, False, XA_WINDOW, - &type, &format, &length, &after, &data); - if (rc == Success && length) { - X11->desktopEnvironment = DE_4DWM; - break; - } - - if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), - ATOM(_NET_SUPPORTING_WM_CHECK), - 0, 1024, False, XA_WINDOW, &type, - &format, &length, &after, &data) == Success) { - if (type == XA_WINDOW && format == 32) { - Window windowManagerWindow = *((Window*) data); - XFree(data); - data = 0; - - if (windowManagerWindow != XNone) { - Atom utf8atom = ATOM(UTF8_STRING); - if (XGetWindowProperty(QX11Info::display(), windowManagerWindow, ATOM(_NET_WM_NAME), - 0, 1024, False, utf8atom, &type, - &format, &length, &after, &data) == Success) { - if (type == utf8atom && format == 8) { - if (qstrcmp((const char *)data, "MCompositor") == 0) - X11->desktopEnvironment = DE_MEEGO_COMPOSITOR; - } - } - } - } - } - - } while(0); - - if (data) - XFree((char *)data); - -#if !defined(QT_NO_STYLE_GTK) - if (X11->desktopEnvironment == DE_GNOME) { - static bool menusHaveIcons = QGtkStyle::getGConfBool(QLatin1String("/desktop/gnome/interface/menus_have_icons"), true); - QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, !menusHaveIcons); - } -#endif - 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 (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; - -#if defined(Q_OS_IRIX) - QString devName = QString::fromLocal8Bit(devs->name).toLower(); - if (devName == QLatin1String(WACOM_NAME)) { - deviceType = QTabletEvent::Stylus; - gotStylus = true; - } -#else - if (devs->type == ATOM(XWacomStylus) || devs->type == ATOM(XTabletStylus)) { - deviceType = QTabletEvent::Stylus; - if (wacomDeviceName()->isEmpty()) - wacomDeviceName()->append(devs->name); - gotStylus = true; - } else if (devs->type == ATOM(XWacomEraser) || devs->type == ATOM(XTabletEraser)) { - 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"); - if (X11->startupId) { -#ifndef QT_NO_UNSETENV - unsetenv("DESKTOP_STARTUP_ID"); -#else - // it's a small memory leak, however we won't crash if Qt is - // unloaded and someones tries to use the envoriment. - putenv(strdup("DESKTOP_STARTUP_ID=")); -#endif - } - } 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. - // 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 -} - -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 - } - -#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 - *****************************************************************************/ - -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(); - if (!XTranslateCoordinates(X11->display, - QX11Info::appRootWindow(screen), - ctarget, x, y, &unused, &unused, &ctarget) - || X11->badwindow()) - break; - if (ctarget == wid) { - // Found! - w = widget; - break; - } - } - } - if (w) - break; - } - } - } - return w ? w->window() : 0; -#endif -} - -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; -} - -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 && amw->testAttribute(Qt::WA_X11DoNotAcceptFocus)) - amw = 0; - 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); - } -#ifndef QT_NO_XSYNC - } else if (a == ATOM(_NET_WM_SYNC_REQUEST)) { - const ulong timestamp = (const ulong) event->xclient.data.l[1]; - if (timestamp > X11->time) - X11->time = timestamp; - if (QTLWExtra *tlw = w->d_func()->maybeTopData()) { - if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) { - tlw->syncRequestTimestamp = timestamp; - tlw->newCounterValueLo = event->xclient.data.l[2]; - tlw->newCounterValueHi = event->xclient.data.l[3]; - } - } -#endif - } - } 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; -} - -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; - if (req->selection == ATOM(_NET_WM_CM_S0)) - X11->compositingManagerRunning = req->owner; - } -#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; - } -#ifndef QT_NO_XKB - else if (X11->use_xkb && event->type == X11->xkb_eventbase) { - XkbAnyEvent *xkbevent = (XkbAnyEvent *) event; - switch (xkbevent->xkb_type) { - case XkbStateNotify: - { - XkbStateNotifyEvent *xkbstateevent = (XkbStateNotifyEvent *) xkbevent; - if ((xkbstateevent->changed & XkbGroupStateMask) != 0) { - qt_keymapper_private()->xkb_currentGroup = xkbstateevent->group; - QKeyMapper::changeKeyboard(); - } - break; - } - default: - break; - } - } -#endif - - 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); - QDesktopWidget *desktop = QApplication::desktop(); - 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)); - QResizeEvent e(w->size(), oldSize); - QApplication::sendEvent(w, &e); - if (w != desktop) - QApplication::sendEvent(desktop, &e); - } -#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()) { - // if we got a MapNotify when we were not waiting for it, it most - // likely means the user has already asked to hide the window before - // it ever being shown, so we try to withdraw a window after sending - // the QShowEvent. - bool pendingHide = widget->testAttribute(Qt::WA_WState_ExplicitShowHide) && widget->testAttribute(Qt::WA_WState_Hidden); - 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); - } - } - if (pendingHide) // hide the window - XWithdrawWindow(X11->display, widget->internalWinId(), widget->x11Info().screen()); - } - 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 don't 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(); - - // emit the workAreaResized() signal - QDesktopWidget *desktop = QApplication::desktop(); - int numScreens = desktop->numScreens(); - for (int i = 0; i < numScreens; ++i) - emit desktop->workAreaResized(i); - } - } else if (widget) { - widget->translatePropertyEvent(event); - } else { - return -1; // don't know this window - } - break; - - default: - break; - } - - return 0; -} - -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) || - (nextEvent.xclient.message_type == ATOM(WM_PROTOCOLS) && - (Atom)nextEvent.xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST))))) { - 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) < QT_GUI_DOUBLE_CLICK_RADIUS && - qAbs(event->xbutton.y - mouseYPos) < QT_GUI_DOUBLE_CLICK_RADIUS) { - 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(); -#ifndef QT_NO_WHEELEVENT - QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient); - if (QApplication::sendSpontaneousEvent(widget, &e)) -#endif - 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(); -#ifndef QT_NO_WHEELEVENT - QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient); - if (QApplication::sendSpontaneousEvent(widget, &e)) -#endif - 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; - WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, wacomDeviceName()->constData()); - 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 - -struct qt_tablet_motion_data -{ - bool filterByWidget; - const QWidget *widget; - const QWidget *etWidget; - int tabletMotionType; - bool error; // found a reason to stop searching -}; - -static Bool qt_mouseMotion_scanner(Display *, XEvent *event, XPointer arg) -{ - qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg; - if (data->error) - return false; - - if (event->type == MotionNotify) - return true; - - data->error = event->type != data->tabletMotionType; // we stop compression when another event gets in between. - return false; -} - -static Bool qt_tabletMotion_scanner(Display *, XEvent *event, XPointer arg) -{ - qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg; - if (data->error) - return false; - if (event->type == data->tabletMotionType) { - const XDeviceMotionEvent *const motion = reinterpret_cast<const XDeviceMotionEvent*>(event); - if (data->filterByWidget) { - const QPoint curr(motion->x, motion->y); - const QWidget *w = data->etWidget; - const QWidget *const child = w->childAt(curr); - if (child) { - w = child; - } - if (w == data->widget) - return true; - } else { - return true; - } - } - - data->error = event->type != MotionNotify; // we stop compression when another event gets in between. - return false; -} - -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; - const XDeviceMotionEvent *motion = 0; - XDeviceButtonEvent *button = 0; - const XProximityNotifyEvent *proximity = 0; - QEvent::Type t; - Qt::KeyboardModifiers modifiers = 0; -#if !defined (Q_OS_IRIX) - XID device_id; -#endif - - if (ev->type == tablet->xinput_motion) { - motion = reinterpret_cast<const XDeviceMotionEvent*>(ev); - 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 - - if (tablet->widgetToGetPress) { - w = tablet->widgetToGetPress; - } else { - 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); - const bool accepted = e.isAccepted(); - if (!accepted && ev->type == tablet->xinput_motion) { - // If the widget does not accept tablet events, we drop the next ones from the event queue - // for this widget so it is not overloaded with the numerous tablet events. - qt_tablet_motion_data tabletMotionData; - tabletMotionData.tabletMotionType = tablet->xinput_motion; - tabletMotionData.widget = w; - tabletMotionData.etWidget = this; - // if nothing is pressed, the events are filtered by position - tabletMotionData.filterByWidget = (tablet->widgetToGetPress == 0); - - bool reinsertMouseEvent = false; - XEvent mouseMotionEvent; - while (true) { - // Find first mouse event since we expect them in pairs inside Qt - tabletMotionData.error =false; - if (XCheckIfEvent(X11->display, &mouseMotionEvent, &qt_mouseMotion_scanner, (XPointer) &tabletMotionData)) { - reinsertMouseEvent = true; - } else { - break; - } - - // Now discard any duplicate tablet events. - tabletMotionData.error = false; - XEvent dummy; - while (XCheckIfEvent(X11->display, &dummy, &qt_tabletMotion_scanner, (XPointer) &tabletMotionData)) { - // just discard the event - } - } - - if (reinsertMouseEvent) { - XPutBackEvent(X11->display, &mouseMotionEvent); - } - } - } - 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()); - bool isCPos = false; - - 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; - isCPos = true; - } - 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; - isCPos = true; - } - } -#ifndef QT_NO_XSYNC - qt_sync_request_event_data sync_event; - sync_event.window = internalWinId(); - for (XEvent ev;;) { - if (!XCheckIfEvent(X11->display, &ev, &qt_sync_request_scanner, (XPointer)&sync_event)) - break; - } -#endif // QT_NO_XSYNC - } - - if (!isCPos) { - // we didn't get an updated position of the toplevel. - // either we haven't moved or there is a bug in the window manager. - // anyway, let's query the position to be certain. - int x, y; - Window child; - XTranslateCoordinates(X11->display, internalWinId(), - QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), - 0, 0, &x, &y, &child); - newCPos.rx() = x; - newCPos.ry() = y; - } - - 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) { - if (isVisible() && data->crect.size() != oldSize) { - Q_ASSERT(d->extra->topextra); - QWidgetBackingStore *bs = d->extra->topextra->backingStore.data(); - 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 (!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; - } -#ifndef QT_NO_XSYNC - if (QTLWExtra *tlwExtra = d->maybeTopData()) { - if (tlwExtra->newCounterValueLo != 0 || tlwExtra->newCounterValueHi != 0) { - XSyncValue value; - XSyncIntsToValue(&value, - tlwExtra->newCounterValueLo, - tlwExtra->newCounterValueHi); - - XSyncSetCounter(X11->display, tlwExtra->syncUpdateCounter, value); - tlwExtra->newCounterValueHi = 0; - tlwExtra->newCounterValueLo = 0; - } - } -#endif - 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; -} - -#ifndef QT_NO_WHEELEVENT -void QApplication::setWheelScrollLines(int n) -{ - QApplicationPrivate::wheel_scroll_lines = n; -} - -int QApplication::wheelScrollLines() -{ - return QApplicationPrivate::wheel_scroll_lines; -} -#endif - -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), - restartHint(QSessionManager::RestartIfRunning), 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] = ∝ - 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(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) - QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L)); - struct passwd entry; - while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) { - if (buf.size() >= 32768) { - // too big already, fail - static char badusername[] = ""; - entryPtr = &entry; - entry.pw_name = badusername; - break; - } - - // retry with a bigger buffer - buf.resize(buf.size() * 2); - } -#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 - -#if defined(QT_RX71_MULTITOUCH) - -static inline int testBit(const char *array, int bit) -{ - return (array[bit/8] & (1<<(bit%8))); -} - -static int openRX71Device(const QByteArray &deviceName) -{ - int fd = open(deviceName, O_RDONLY | O_NONBLOCK); - if (fd == -1) { - fd = -errno; - return fd; - } - - // fetch the event type mask and check that the device reports absolute coordinates - char eventTypeMask[(EV_MAX + sizeof(char) - 1) * sizeof(char) + 1]; - memset(eventTypeMask, 0, sizeof(eventTypeMask)); - if (ioctl(fd, EVIOCGBIT(0, sizeof(eventTypeMask)), eventTypeMask) < 0) { - close(fd); - return -1; - } - if (!testBit(eventTypeMask, EV_ABS)) { - close(fd); - return -1; - } - - // make sure that we can get the absolute X and Y positions from the device - char absMask[(ABS_MAX + sizeof(char) - 1) * sizeof(char) + 1]; - memset(absMask, 0, sizeof(absMask)); - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absMask)), absMask) < 0) { - close(fd); - return -1; - } - if (!testBit(absMask, ABS_X) || !testBit(absMask, ABS_Y)) { - close(fd); - return -1; - } - - return fd; -} - -void QApplicationPrivate::initializeMultitouch_sys() -{ - Q_Q(QApplication); - - QByteArray deviceName = QByteArray("/dev/input/event"); - int currentDeviceNumber = 0; - for (;;) { - int fd = openRX71Device(QByteArray(deviceName + QByteArray::number(currentDeviceNumber++))); - if (fd == -ENOENT) { - // no more devices - break; - } - if (fd < 0) { - // not a touch device - continue; - } - - struct input_absinfo abs_x, abs_y, abs_z; - ioctl(fd, EVIOCGABS(ABS_X), &abs_x); - ioctl(fd, EVIOCGABS(ABS_Y), &abs_y); - ioctl(fd, EVIOCGABS(ABS_Z), &abs_z); - - int deviceNumber = allRX71TouchPoints.count(); - - QSocketNotifier *socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); - QObject::connect(socketNotifier, SIGNAL(activated(int)), q, SLOT(_q_readRX71MultiTouchEvents())); - - RX71TouchPointState touchPointState = { - socketNotifier, - QTouchEvent::TouchPoint(deviceNumber), - - abs_x.minimum, abs_x.maximum, q->desktop()->screenGeometry().width(), - abs_y.minimum, abs_y.maximum, q->desktop()->screenGeometry().height(), - abs_z.minimum, abs_z.maximum - }; - allRX71TouchPoints.append(touchPointState); - } - - hasRX71MultiTouch = allRX71TouchPoints.count() > 1; - if (!hasRX71MultiTouch) { - for (int i = 0; i < allRX71TouchPoints.count(); ++i) { - QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; - close(socketNotifier->socket()); - delete socketNotifier; - } - allRX71TouchPoints.clear(); - } -} - -void QApplicationPrivate::cleanupMultitouch_sys() -{ - hasRX71MultiTouch = false; - for (int i = 0; i < allRX71TouchPoints.count(); ++i) { - QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; - close(socketNotifier->socket()); - delete socketNotifier; - } - allRX71TouchPoints.clear(); -} - -bool QApplicationPrivate::readRX71MultiTouchEvents(int deviceNumber) -{ - RX71TouchPointState &touchPointState = allRX71TouchPoints[deviceNumber]; - QSocketNotifier *socketNotifier = touchPointState.socketNotifier; - int fd = socketNotifier->socket(); - - QTouchEvent::TouchPoint &touchPoint = touchPointState.touchPoint; - - bool down = touchPoint.state() != Qt::TouchPointReleased; - if (down) - touchPoint.setState(Qt::TouchPointStationary); - - bool changed = false; - for (;;) { - struct input_event inputEvent; - int bytesRead = read(fd, &inputEvent, sizeof(inputEvent)); - if (bytesRead <= 0) - break; - if (bytesRead != sizeof(inputEvent)) { - qWarning("Qt: INTERNAL ERROR: short read in readRX71MultiTouchEvents()"); - return false; - } - - switch (inputEvent.type) { - case EV_SYN: - changed = true; - switch (touchPoint.state()) { - case Qt::TouchPointPressed: - case Qt::TouchPointReleased: - // make sure we don't compress pressed and releases with any other events - return changed; - default: - break; - } - continue; - case EV_KEY: - case EV_ABS: - break; - default: - qWarning("Qt: WARNING: unknown event type %d on multitouch device", inputEvent.type); - continue; - } - - QPointF screenPos = touchPoint.screenPos(); - switch (inputEvent.code) { - case BTN_TOUCH: - if (!down && inputEvent.value != 0) - touchPoint.setState(Qt::TouchPointPressed); - else if (down && inputEvent.value == 0) - touchPoint.setState(Qt::TouchPointReleased); - break; - case ABS_TOOL_WIDTH: - case ABS_VOLUME: - case ABS_PRESSURE: - // ignore for now - break; - case ABS_X: - { - qreal newValue = ((qreal(inputEvent.value - touchPointState.minX) - / qreal(touchPointState.maxX - touchPointState.minX)) - * touchPointState.scaleX); - screenPos.rx() = newValue; - touchPoint.setScreenPos(screenPos); - break; - } - case ABS_Y: - { - qreal newValue = ((qreal(inputEvent.value - touchPointState.minY) - / qreal(touchPointState.maxY - touchPointState.minY)) - * touchPointState.scaleY); - screenPos.ry() = newValue; - touchPoint.setScreenPos(screenPos); - break; - } - case ABS_Z: - { - // map Z (signal strength) to pressure for now - qreal newValue = (qreal(inputEvent.value - touchPointState.minZ) - / qreal(touchPointState.maxZ - touchPointState.minZ)); - touchPoint.setPressure(newValue); - break; - } - default: - qWarning("Qt: WARNING: unknown event code %d on multitouch device", inputEvent.code); - continue; - } - } - - if (down && touchPoint.state() != Qt::TouchPointReleased) - touchPoint.setState(changed ? Qt::TouchPointMoved : Qt::TouchPointStationary); - - return changed; -} - -void QApplicationPrivate::_q_readRX71MultiTouchEvents() -{ - // read touch events from all devices - bool changed = false; - for (int i = 0; i < allRX71TouchPoints.count(); ++i) - changed = readRX71MultiTouchEvents(i) || changed; - if (!changed) - return; - - QList<QTouchEvent::TouchPoint> touchPoints; - for (int i = 0; i < allRX71TouchPoints.count(); ++i) - touchPoints.append(allRX71TouchPoints.at(i).touchPoint); - - translateRawTouchEvent(0, QTouchEvent::TouchScreen, touchPoints); -} - -#else // !QT_RX71_MULTITOUCH - -void QApplicationPrivate::initializeMultitouch_sys() -{ } -void QApplicationPrivate::cleanupMultitouch_sys() -{ } - -#endif // QT_RX71_MULTITOUCH - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoaapplication_mac.mm b/src/gui/kernel/qcocoaapplication_mac.mm deleted file mode 100644 index 872f31dec7..0000000000 --- a/src/gui/kernel/qcocoaapplication_mac.mm +++ /dev/null @@ -1,222 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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> -#include <private/qcocoaintrospection_p.h> - -QT_USE_NAMESPACE - -@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; -} - -- (void)qt_sendPostedMessage:(NSEvent *)event -{ - // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! - // That is why we need to split the address in two parts: - quint64 lower = [event data1]; - quint64 upper = [event data2]; - QCocoaPostMessageArgs *args = reinterpret_cast<QCocoaPostMessageArgs *>(lower | (upper << 32)); - // Special case for convenience: if the argument is an NSNumber, we unbox it directly. - // Use NSValue instead if this behaviour is unwanted. - id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1; - id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2; - switch (args->argCount) { - case 0: - [args->target performSelector:args->selector]; - break; - case 1: - [args->target performSelector:args->selector withObject:a1]; - break; - case 3: - [args->target performSelector:args->selector withObject:a1 withObject:a2]; - break; - } - - delete args; -} - -- (BOOL)qt_filterEvent:(NSEvent *)event -{ - if (qApp->macEventFilter(0, reinterpret_cast<EventRef>(event))) - return true; - - if ([event type] == NSApplicationDefined) { - switch ([event subtype]) { - case QtCocoaEventSubTypePostMessage: - [NSApp qt_sendPostedMessage:event]; - return true; - default: - break; - } - } - return false; -} - -@end - -@implementation QNSApplication - -- (void)qt_sendEvent_original:(NSEvent *)event -{ - Q_UNUSED(event); - // This method will only be used as a signature - // template for the method we add into NSApplication - // containing the original [NSApplication sendEvent:] implementation -} - -- (void)qt_sendEvent_replacement:(NSEvent *)event -{ - // This method (or its implementation to be precise) will - // be called instead of sendEvent if redirection occurs. - // 'self' will then be an instance of NSApplication - // (and not QNSApplication) - if (![NSApp qt_filterEvent:event]) - [self qt_sendEvent_original:event]; -} - -- (void)sendEvent:(NSEvent *)event -{ - // This method will be called if - // no redirection occurs - if (![NSApp qt_filterEvent:event]) - [super sendEvent:event]; -} - -- (void)qtDispatcherToQAction:(id)sender -{ - // Forward actions sendt from the menu bar (e.g. quit) to the menu loader. - // Having this method here means that we are the last stop in the responder - // chain, and that we are able to handle menu actions even when no window is - // visible on screen. Note: If Qt is used as a plugin, Qt will not use a - // native menu bar. Hence, we will also not need to do any redirection etc. as - // we do with sendEvent. - [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; -} - -@end - -QT_BEGIN_NAMESPACE - -void qt_redirectNSApplicationSendEvent() -{ - if ([NSApp isMemberOfClass:[QNSApplication class]]) { - // No need to change implementation since Qt - // already controls a subclass of NSApplication - return; - } - - // Change the implementation of [NSApplication sendEvent] to the - // implementation of qt_sendEvent_replacement found in QNSApplication. - // And keep the old implementation that gets overwritten inside a new - // method 'qt_sendEvent_original' that we add to NSApplication - qt_cocoa_change_implementation( - [NSApplication class], - @selector(sendEvent:), - [QNSApplication class], - @selector(qt_sendEvent_replacement:), - @selector(qt_sendEvent_original:)); - } - -QT_END_NAMESPACE -#endif diff --git a/src/gui/kernel/qcocoaapplication_mac_p.h b/src/gui/kernel/qcocoaapplication_mac_p.h deleted file mode 100644 index 0c3f5e442d..0000000000 --- a/src/gui/kernel/qcocoaapplication_mac_p.h +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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; - -- (void)qt_sendPostedMessage:(NSEvent *)event; -- (BOOL)qt_filterEvent:(NSEvent *)event; -@end - -@interface QNSApplication : NSApplication { -} -@end - -QT_BEGIN_NAMESPACE - -void qt_redirectNSApplicationSendEvent(); - -QT_END_NAMESPACE - -#endif diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm b/src/gui/kernel/qcocoaapplicationdelegate_mac.mm deleted file mode 100644 index 77cd8902c3..0000000000 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac.mm +++ /dev/null @@ -1,354 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** - ** - ** 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> -#import <private/qcocoaapplication_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 <qurl.h> -#include <qapplication.h> - -QT_BEGIN_NAMESPACE -extern void onApplicationChangedActivation(bool); // qapplication_mac.mm -extern void qt_release_apple_event_handler(); //qapplication_mac.mm -extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp -extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm -extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp - -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]; - if (self) - inLaunch = true; - 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]; -} - -// This function will only be called when NSApp is actually running. Before -// that, the kAEQuitApplication Apple event will be sent to -// QApplicationPrivate::globalAppleEventProcessor in qapplication_mac.mm -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ - Q_UNUSED(sender); - // The reflection delegate gets precedence - if (reflectionDelegate - && [reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) { - return [reflectionDelegate applicationShouldTerminate:sender]; - } - - if (qtPrivate->canQuit()) { - if (!startedQuit) { - startedQuit = true; - qAppInstance()->quit(); - startedQuit = false; - } - } - - if (qtPrivate->threadData->eventLoops.size() == 0) { - // INVARIANT: No event loop is executing. This probably - // means that Qt is used as a plugin, or as a part of a native - // Cocoa application. In any case it should be fine to - // terminate now: - return NSTerminateNow; - } - - return NSTerminateCancel; -} - -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification -{ - Q_UNUSED(aNotification); - inLaunch = false; - qt_release_apple_event_handler(); -} - -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames -{ - for (NSString *fileName in filenames) { - QString qtFileName = qt_mac_NSStringToQString(fileName); - if (inLaunch) { - // We need to be careful because Cocoa will be nice enough to take - // command line arguments and send them to us as events. Given the history - // of Qt Applications, this will result in behavior people don't want, as - // they might be doing the opening themselves with the command line parsing. - if (qApp->arguments().contains(qtFileName)) - continue; - } - QFileOpenEvent foe(qtFileName); - qt_sendSpontaneousEvent(qAppInstance(), &foe); - } - - 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); - - if (!QWidget::mouseGrabber()){ - // Update enter/leave immidiatly, don't wait for a move event. But only - // if no grab exists (even if the grab points to this widget, it seems, ref X11) - QPoint qlocal, qglobal; - QWidget *widgetUnderMouse = 0; - qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); - QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0); - qt_last_mouse_receiver = widgetUnderMouse; - qt_last_native_mouse_receiver = widgetUnderMouse ? - (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; - } -} - -- (void)applicationDidResignActive:(NSNotification *)notification -{ - if (reflectionDelegate - && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)]) - [reflectionDelegate applicationDidResignActive:notification]; - - onApplicationChangedActivation(false); - - if (!QWidget::mouseGrabber()) - QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); - qt_last_mouse_receiver = 0; - qt_last_native_mouse_receiver = 0; - qt_button_down = 0; -} - -- (void)applicationDidChangeScreenParameters:(NSNotification *)notification -{ - Q_UNUSED(notification); - QDesktopWidgetImplementation::instance()->onResize(); -} - -- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)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]; -} - -- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent -{ - Q_UNUSED(replyEvent); - - NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; - QUrl url(qt_mac_NSStringToQString(urlString)); - QFileOpenEvent qtEvent(url); - qt_sendSpontaneousEvent(qAppInstance(), &qtEvent); -} - -- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent -{ - Q_UNUSED(event); - Q_UNUSED(replyEvent); - [NSApp terminate:self]; -} - -- (void)qtDispatcherToQAction:(id)sender -{ - [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; -} - -@end -#endif diff --git a/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h b/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h deleted file mode 100644 index 714c046f48..0000000000 --- a/src/gui/kernel/qcocoaapplicationdelegate_mac_p.h +++ /dev/null @@ -1,128 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -/**************************************************************************** - ** - ** 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> -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; -- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames; -- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender; -- (void)applicationDidBecomeActive:(NSNotification *)notification; -- (void)applicationDidResignActive:(NSNotification *)notification; -@end - -#endif - -@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> { - bool startedQuit; - QApplicationPrivate *qtPrivate; - NSMenu *dockMenu; - QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader; - NSObject <NSApplicationDelegate> *reflectionDelegate; - bool inLaunch; -} -+ (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 <NSApplicationDelegate> *)oldDelegate; -- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; -@end -#endif diff --git a/src/gui/kernel/qcocoaintrospection_mac.mm b/src/gui/kernel/qcocoaintrospection_mac.mm deleted file mode 100644 index 70c893aeec..0000000000 --- a/src/gui/kernel/qcocoaintrospection_mac.mm +++ /dev/null @@ -1,125 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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/qcocoaintrospection_p.h> - -QT_BEGIN_NAMESPACE - -void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel) -{ -#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 - // The following code replaces the _implementation_ for the selector we want to hack - // (originalSel) with the implementation found in proxyClass. Then it creates - // a new 'backup' method inside baseClass containing the old, original, - // implementation (fakeSel). You can let the proxy implementation of originalSel - // call fakeSel if needed (similar approach to calling a super class implementation). - // fakeSel must also be implemented in proxyClass, as the signature is used - // as template for the method one we add into baseClass. - // NB: You will typically never create any instances of proxyClass; we use it - // only for stealing its contents and put it into baseClass. - if (!replacementSel) - replacementSel = originalSel; - - Method originalMethod = class_getInstanceMethod(baseClass, originalSel); - Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel); - IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod)); - - if (backupSel) { - Method backupMethod = class_getInstanceMethod(proxyClass, backupSel); - class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod)); - } -#endif - } -} - -void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel) -{ -#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 backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel); - method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass)); -#endif - } -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoaintrospection_p.h b/src/gui/kernel/qcocoaintrospection_p.h deleted file mode 100644 index 1c7d6ac13c..0000000000 --- a/src/gui/kernel/qcocoaintrospection_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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> -#import <objc/objc-class.h> - -QT_BEGIN_NAMESPACE - -void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel = 0, SEL backupSel = 0); -void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel); - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qcocoamenuloader_mac.mm b/src/gui/kernel/qcocoamenuloader_mac.mm deleted file mode 100644 index 71ff011069..0000000000 --- a/src/gui/kernel/qcocoamenuloader_mac.mm +++ /dev/null @@ -1,264 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmacdefines_mac.h" -#ifdef QT_MAC_USE_COCOA -#include <qaction.h> -#include <qcoreapplication.h> -#include <private/qcocoamenuloader_mac_p.h> -#include <private/qapplication_p.h> -#include <private/qt_mac_p.h> -#include <private/qmenubar_p.h> -#include <qmenubar.h> -#include <private/qt_cocoa_helpers_mac_p.h> - -QT_FORWARD_DECLARE_CLASS(QCFString) -QT_FORWARD_DECLARE_CLASS(QString) - -#ifndef QT_NO_TRANSLATION - QT_BEGIN_NAMESPACE - extern QString qt_mac_applicationmenu_string(int type); - QT_END_NAMESPACE -#endif - -QT_USE_NAMESPACE - -@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader) - -- (void)awakeFromNib -{ - servicesItem = [[appMenu itemWithTitle:@"Services"] retain]; - hideAllOthersItem = [[appMenu itemWithTitle:@"Hide Others"] retain]; - showAllItem = [[appMenu itemWithTitle:@"Show All"] retain]; - - // Get the names in the nib to match the app name set by Qt. - const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName())); - [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:const_cast<NSString *>(appName)]]; - [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:const_cast<NSString *>(appName)]]; - [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication" - withString:const_cast<NSString *>(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 -{ - // The application menu is the menu in the menu bar that contains the - // 'Quit' item. When changing menu bar (e.g when switching between - // windows with different menu bars), we never recreate this menu, but - // instead pull it out the current menu bar and place into the new one: - NSMenu *mainMenu = [NSApp mainMenu]; - if ([NSApp mainMenu] == menu) - return; // nothing to do (menu is the current menu bar)! - -#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)removeActionsFromAppMenu -{ - for (NSMenuItem *item in [appMenu itemArray]) - [item setTag:nil]; -} - -- (void)dealloc -{ - [servicesItem release]; - [hideAllOthersItem release]; - [showAllItem release]; - - [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]; -} - -- (void)qtUpdateMenubar -{ - QMenuBarPrivate::macUpdateMenuBarImmediatly(); -} - -- (void)qtTranslateApplicationMenu -{ -#ifndef QT_NO_TRANSLATION - [servicesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(0))]; - [hideItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(1).arg(qAppName()))]; - [hideAllOthersItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(2))]; - [showAllItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(3))]; - [preferencesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(4))]; - [quitItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(5).arg(qAppName()))]; - [aboutItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(6).arg(qAppName()))]; -#endif -} - -- (IBAction)qtDispatcherToQAction:(id)sender -{ - QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData); - 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(); - } -} - - - (void)orderFrontCharacterPalette:(id)sender - { - [NSApp orderFrontCharacterPalette:sender]; - } -@end -#endif // QT_MAC_USE_COCOA diff --git a/src/gui/kernel/qcocoamenuloader_mac_p.h b/src/gui/kernel/qcocoamenuloader_mac_p.h deleted file mode 100644 index cfcc7e00c6..0000000000 --- a/src/gui/kernel/qcocoamenuloader_mac_p.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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; - NSMenuItem *servicesItem; - NSMenuItem *hideAllOthersItem; - NSMenuItem *showAllItem; -} -- (void)ensureAppMenuInMenu:(NSMenu *)menu; -- (void)removeActionsFromAppMenu; -- (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; -- (void)qtUpdateMenubar; -- (void)orderFrontCharacterPalette:(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 deleted file mode 100644 index 67a12e25f8..0000000000 --- a/src/gui/kernel/qcocoapanel_mac.mm +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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/qcocoawindowdelegate_mac_p.h> -#import <private/qcocoaview_mac_p.h> -#import <private/qcocoawindowcustomthemeframe_mac_p.h> -#import <private/qcocoaapplication_mac_p.h> -#import <private/qmultitouch_mac_p.h> -#import <private/qapplication_p.h> -#import <private/qbackingstore_p.h> -#import <private/qdnd_p.h> - -#include <QtGui/QWidget> - -QT_FORWARD_DECLARE_CLASS(QWidget); -QT_USE_NAMESPACE - -@implementation QT_MANGLE_NAMESPACE(QCocoaPanel) - -/*********************************************************************** - Copy and Paste between QCocoaWindow and QCocoaPanel - This is a bit unfortunate, but thanks to the dynamic dispatch we - have to duplicate this code or resort to really silly forwarding methods -**************************************************************************/ -#include "qcocoasharedwindowmethods_mac_p.h" - -@end -#endif diff --git a/src/gui/kernel/qcocoapanel_mac_p.h b/src/gui/kernel/qcocoapanel_mac_p.h deleted file mode 100644 index 542615903e..0000000000 --- a/src/gui/kernel/qcocoapanel_mac_p.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 QCOCOAPANEL_MAC_P -#define QCOCOAPANEL_MAC_P - -#include "qmacdefines_mac.h" -#ifdef QT_MAC_USE_COCOA -#import <Cocoa/Cocoa.h> - -QT_FORWARD_DECLARE_CLASS(QStringList); -QT_FORWARD_DECLARE_CLASS(QCocoaDropData); - -@interface NSPanel (QtIntegration) -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; -- (void)draggingExited:(id <NSDraggingInfo>)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -@end - -@interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { - QStringList *currentCustomDragTypes; - QCocoaDropData *dropData; - NSInteger dragEnterSequence; -} - -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; -- (void)registerDragTypes; -- (void)drawRectOriginal:(NSRect)rect; - -@end -#endif - -#endif diff --git a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h b/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h deleted file mode 100644 index ee1115bd4e..0000000000 --- a/src/gui/kernel/qcocoasharedwindowmethods_mac_p.h +++ /dev/null @@ -1,610 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** - NB: This is not a header file, dispite the file name suffix. This file is - included directly into the source code of qcocoawindow_mac.mm and - qcocoapanel_mac.mm to avoid manually doing copy and paste of the exact - same code needed at both places. This solution makes it more difficult - to e.g fix a bug in qcocoawindow_mac.mm, but forget to do the same in - qcocoapanel_mac.mm. - The reason we need to do copy and paste in the first place, rather than - resolve to method overriding, is that QCocoaPanel needs to inherit from - NSPanel, while QCocoaWindow needs to inherit NSWindow rather than NSPanel). -****************************************************************************/ - -// WARNING: Don't include any header files from within this file. Put them -// directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h - -QT_BEGIN_NAMESPACE -extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm -extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp -extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp -extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm -extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm - -Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); -QT_END_NAMESPACE - -- (id)initWithContentRect:(NSRect)contentRect - styleMask:(NSUInteger)windowStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)deferCreation -{ - self = [super initWithContentRect:contentRect styleMask:windowStyle - backing:bufferingType defer:deferCreation]; - if (self) { - currentCustomDragTypes = 0; - } - return self; -} - -- (void)dealloc -{ - delete currentCustomDragTypes; - [super dealloc]; -} - -- (BOOL)canBecomeKeyWindow -{ - QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; - if (!widget) - return NO; // This should happen only for qt_root_win - if (QApplicationPrivate::isBlockedByModal(widget)) - return NO; - - bool isToolTip = (widget->windowType() == Qt::ToolTip); - bool isPopup = (widget->windowType() == Qt::Popup); - return !(isPopup || isToolTip); -} - -- (BOOL)canBecomeMainWindow -{ - QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; - if (!widget) - return NO; // This should happen only for qt_root_win - if ([self isSheet]) - return NO; - - bool isToolTip = (widget->windowType() == Qt::ToolTip); - bool isPopup = (widget->windowType() == Qt::Popup); - bool isTool = (widget->windowType() == Qt::Tool); - return !(isPopup || isToolTip || isTool); -} - -- (void)becomeMainWindow -{ - [super becomeMainWindow]; - // Cocoa sometimes tell a hidden window to become the - // main window (and as such, show it). This can e.g - // happend when the application gets activated. If - // this is the case, we tell it to hide again: - if (![self isVisible]) - [self orderOut:self]; -} - -- (void)toggleToolbarShown:(id)sender -{ - macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]); - [super toggleToolbarShown:sender]; -} - -- (void)flagsChanged:(NSEvent *)theEvent -{ - qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]); - [super flagsChanged:theEvent]; -} - - -- (void)tabletProximity:(NSEvent *)tabletEvent -{ - qt_dispatchTabletProximityEvent(tabletEvent); -} - -- (void)terminate:(id)sender -{ - // This function is called from the quit item in the menubar when this window - // is in the first responder chain (see also qtDispatcherToQAction above) - [NSApp terminate:sender]; -} - -- (void)setLevel:(NSInteger)windowLevel -{ - // Cocoa will upon activating/deactivating applications level modal - // windows up and down, regardsless of any explicit set window level. - // To ensure that modal stays-on-top dialogs actually stays on top after - // the application is activated (and therefore stacks in front of - // other stays-on-top windows), we need to add this little special-case override: - QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - if (widget && widget->isModal() && (widget->windowFlags() & Qt::WindowStaysOnTopHint)) - [super setLevel:NSPopUpMenuWindowLevel]; - else - [super setLevel:windowLevel]; -} - -- (void)sendEvent:(NSEvent *)event -{ - [self retain]; - - bool handled = false; - switch([event type]) { - case NSMouseMoved: - // Cocoa sends move events to a parent and all its children under the mouse, much - // like Qt handles hover events. But we only want to handle the move event once, so - // to optimize a bit (since we subscribe for move event for all views), we handle it - // here before this logic happends. Note: it might be tempting to do this shortcut for - // all mouse events. The problem is that Cocoa does more than just find the correct view - // when sending the event, like raising windows etc. So avoid it as much as possible: - handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0); - break; - default: - break; - } - - if (!handled) { - [super sendEvent:event]; - qt_mac_handleNonClientAreaMouseEvent(self, event); - } - [self release]; -} - -- (void)setInitialFirstResponder:(NSView *)view -{ - // This method is called the first time the window is placed on screen and - // is the earliest point in time we can connect OpenGL contexts to NSViews. - QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; - if (qwidget) { - qt_event_request_window_change(qwidget); - qt_mac_send_posted_gl_updates(qwidget); - } - - [super setInitialFirstResponder:view]; -} - -- (BOOL)makeFirstResponder:(NSResponder *)responder -{ - // For some reason Cocoa wants to flip the first responder - // when Qt doesn't want to, sorry, but "No" :-) - if (responder == nil && qApp->focusWidget()) - return NO; - return [super makeFirstResponder:responder]; -} - -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask -{ - if (styleMask & QtMacCustomizeWindow) - return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class]; - return [super frameViewClassForStyleMask:styleMask]; -} - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -- (void)touchesBeganWithEvent:(NSEvent *)event; -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetTouch = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); - if (!widgetToGetTouch) - return; - - bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesMovedWithEvent:(NSEvent *)event; -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetTouch = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); - if (!widgetToGetTouch) - return; - - bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesEndedWithEvent:(NSEvent *)event; -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetTouch = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); - if (!widgetToGetTouch) - return; - - bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} - -- (void)touchesCancelledWithEvent:(NSEvent *)event; -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetTouch = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); - if (!widgetToGetTouch) - return; - - bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); -} -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - --(void)registerDragTypes -{ - // Calling registerForDraggedTypes below is slow, so only do - // it once for each window, or when the custom types change. - QMacCocoaAutoReleasePool pool; - const QStringList& customTypes = qEnabledDraggedTypes(); - if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { - if (currentCustomDragTypes == 0) - currentCustomDragTypes = new QStringList(); - *currentCustomDragTypes = customTypes; - 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. - for (int i = 0; i < customTypes.size(); i++) { - [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])]; - } - [self registerForDraggedTypes:supportedTypes]; - } -} - -- (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); -} - -- (void)changeDraggingCursor:(NSDragOperation)newOperation -{ - static SEL action = nil; - static bool operationSupported = false; - if (action == nil) { - action = NSSelectorFromString(@"operationNotAllowedCursor"); - if ([NSCursor respondsToSelector:action]) { - operationSupported = true; - } - } - if (operationSupported) { - NSCursor *notAllowedCursor = [NSCursor performSelector:action]; - bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); - if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { - [notAllowedCursor push]; - } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { - [notAllowedCursor pop]; - } - - } -} - -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender -{ - // The user dragged something into the window. Send a draggingEntered message - // to the QWidget under the mouse. As the drag moves over the window, and over - // different widgets, we will handle enter and leave events from within - // draggingUpdated below. The reason why we handle this ourselves rather than - // subscribing for drag events directly in QCocoaView is that calling - // registerForDraggedTypes on the views will severly degrade initialization time - // for an application that uses a lot of drag subscribing widgets. - - NSPoint nswindowPoint = [sender draggingLocation]; - NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; - QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); - - QWidget *qwidget = QApplication::widgetAt(globalPoint); - *currentDragTarget() = qwidget; - if (!qwidget) - return [super draggingEntered:sender]; - if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) - return NSDragOperationNone; - - [self addDropData:sender]; - - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - - if ([sender draggingSource] != nil) { - // modifier flags might have changed, update it here since we don't send any input events. - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - // when the source is from another application the above technique will not work. - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - - // send the drag enter event to the widget. - QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); - QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - QApplication::sendEvent(qwidget, &qDEEvent); - - if (!qDEEvent.isAccepted()) { - // The enter event was not accepted. We mark this by removing - // the drop data so we don't send subsequent drag move events: - [self removeDropData]; - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } else { - // Send a drag move event immediately after a drag enter event (as per documentation). - QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - 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. - nsActions = NSDragOperationNone; - // Save as ignored in the answer rect. - qDMEvent.setDropAction(Qt::IgnoreAction); - } else { - nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); - } - - QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); - [self changeDraggingCursor:nsActions]; - return nsActions; - } - } - -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender -{ - NSPoint nswindowPoint = [sender draggingLocation]; - NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; - QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); - - QWidget *qwidget = QApplication::widgetAt(globalPoint); - if (!qwidget) - return [super draggingEntered:sender]; - - // First, check if the widget under the mouse has changed since the - // last drag move events. If so, we need to change target, and dispatch - // syntetic drag enter/leave events: - if (qwidget != *currentDragTarget()) { - if (*currentDragTarget() && dropData) { - QDragLeaveEvent de; - QApplication::sendEvent(*currentDragTarget(), &de); - [self removeDropData]; - } - return [self draggingEntered:sender]; - } - - if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) - return NSDragOperationNone; - - // If we have no drop data (which will be assigned inside draggingEntered), it means - // that the current drag target did not accept the enter event. If so, we ignore - // subsequent move events as well: - if (dropData == 0) { - [self changeDraggingCursor:NSDragOperationNone]; - return NSDragOperationNone; - } - - // If the mouse is still within the accepted rect (provided by - // the application on a previous event), we follow the optimization - // and just return the answer given at that point: - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); - if (qt_mac_mouse_inside_answer_rect(localPoint) - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { - NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); - [self changeDraggingCursor:operation]; - return operation; - } - - QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; - Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); - Qt::KeyboardModifiers modifiers = Qt::NoModifier; - - // Update modifiers: - if ([sender draggingSource] != nil) { - QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); - modifiers = QApplication::keyboardModifiers(); - } else { - modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); - } - - QMimeData *mimeData = dropData; - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - - // Insert the same drop action on the event according to - // what the application told us it should be on the previous event: - QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); - if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() - && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) - qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); - - // Now, end the drag move event to the widget: - qDMEvent.accept(); - QApplication::sendEvent(qwidget, &qDMEvent); - - NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); - if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { - // Ignore this event (we will still receive further - // notifications), save as ignored in the answer rect: - operation = NSDragOperationNone; - qDMEvent.setDropAction(Qt::IgnoreAction); - } - - qt_mac_copy_answer_rect(qDMEvent); - [self changeDraggingCursor:operation]; - - return operation; -} - -- (void)draggingExited:(id <NSDraggingInfo>)sender -{ - NSPoint nswindowPoint = [sender draggingLocation]; - NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; - QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); - - QWidget *qwidget = *currentDragTarget(); - if (!qwidget) - return [super draggingExited:sender]; - - if (dropData) { - QDragLeaveEvent de; - QApplication::sendEvent(qwidget, &de); - [self removeDropData]; - } - - // Clean-up: - [self removeDropData]; - *currentDragTarget() = 0; - [self changeDraggingCursor:NSDragOperationEvery]; -} - -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender -{ - QWidget *qwidget = *currentDragTarget(); - if (!qwidget) - return NO; - - *currentDragTarget() = 0; - NSPoint nswindowPoint = [sender draggingLocation]; - NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; - QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); - - [self addDropData:sender]; - - NSDragOperation nsActions = [sender draggingSourceOperationMask]; - Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); - QMimeData *mimeData = dropData; - - if (QDragManager::self()->source()) - mimeData = QDragManager::self()->dragPrivate()->data; - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->target = qwidget; - - QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); - QDropEvent de(localPoint, qtAllowed, mimeData, - QApplication::mouseButtons(), QApplication::keyboardModifiers()); - QApplication::sendEvent(qwidget, &de); - - if (QDragManager::self()->object) - QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); - - return de.isAccepted(); -} - -// This is a hack and it should be removed once we find the real cause for -// the painting problems. -// We have a static variable that signals if we have been called before or not. -static bool firstDrawingInvocation = true; - -// The method below exists only as a workaround to draw/not draw the baseline -// in the title bar. This is to support unifiedToolbar look. - -// This method is very special. To begin with, it is a -// method that will get called only if we enable documentMode. -// Furthermore, it won't get called as a normal method, we swap -// this method with the normal implementation of drawRect in -// _NSThemeFrame. When this method is active, its mission is to -// first call the original drawRect implementation so the widget -// gets proper painting. After that, it needs to detect if there -// is a toolbar or not, in order to decide how to handle the unified -// look. The distinction is important since the presence and -// visibility of a toolbar change the way we enter into unified mode. -// When there is a toolbar and that toolbar is visible, the problem -// is as simple as to tell the toolbar not to draw its baseline. -// However when there is not toolbar or the toolbar is not visible, -// we need to draw a line on top of the baseline, because the baseline -// in that case will belong to the title. For this case we need to draw -// a line on top of the baseline. -// As usual, there is a special case. When we first are called, we might -// need to repaint ourselves one more time. We only need that if we -// didn't get the activation, i.e. when we are launched via the command -// line. And this only if the toolbar is visible from the beginning, -// so we have a special flag that signals if we need to repaint or not. -- (void)drawRectSpecial:(NSRect)rect -{ - // Call the original drawing method. - [id(self) drawRectOriginal:rect]; - NSWindow *window = [id(self) window]; - NSToolbar *toolbar = [window toolbar]; - if(!toolbar) { - // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa. - macDrawRectOnTop((void *)window); - } else { - if([toolbar isVisible]) { - // We tell Cocoa to avoid drawing the line at the end. - if(firstDrawingInvocation) { - firstDrawingInvocation = false; - macSyncDrawingOnFirstInvocation((void *)window); - } else - [toolbar setShowsBaselineSeparator:NO]; - } else { - // There is a toolbar but it is not visible so - // we have to draw a line on top of the line drawn by Cocoa. - macDrawRectOnTop((void *)window); - } - } -} - -- (void)drawRectOriginal:(NSRect)rect -{ - Q_UNUSED(rect) - // This method implementation is here to silenct the compiler. - // See drawRectSpecial for information. -} - diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm deleted file mode 100644 index e885d1552c..0000000000 --- a/src/gui/kernel/qcocoaview_mac.mm +++ /dev/null @@ -1,1388 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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 <private/qevent_p.h> -#include <private/qbackingstore_p.h> -#include <private/qwindowsurface_raster_p.h> -#include <private/qunifiedtoolbarsurface_mac_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> - -@interface NSEvent (Qt_Compile_Leopard_DeviceDelta) - - (CGFloat)deviceDeltaX; - - (CGFloat)deviceDeltaY; - - (CGFloat)deviceDeltaZ; -@end - -@interface NSEvent (Qt_Compile_Leopard_Gestures) - - (CGFloat)magnification; -@end - -QT_BEGIN_NAMESPACE - -extern void qt_mac_update_cursor(); // qcursor_mac.mm -extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp -extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp -extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm -extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm -extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm -extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp -extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); -extern QWidget *mac_mouse_grabber; -extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp - -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_FORWARD_DECLARE_CLASS(QCursor) -QT_USE_NAMESPACE -extern "C" { - extern NSString *NSTextInputReplacementRangeAttributeName; -} - -//#define ALIEN_DEBUG 1 -#ifdef ALIEN_DEBUG -static int qCocoaViewCount = 0; -#endif - -@implementation QT_MANGLE_NAMESPACE(QCocoaView) - -- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate -{ - self = [super init]; - if (self) { - [self finishInitWithQWidget:widget widgetPrivate:widgetprivate]; - } - [self setFocusRingType:NSFocusRingTypeNone]; - composingText = new QString(); - -#ifdef ALIEN_DEBUG - ++qCocoaViewCount; - qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount; -#endif - - composing = false; - sendKeyEvents = true; - fromKeyDownEvent = false; - alienTouchCount = 0; - - [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)dealloc -{ - QMacCocoaAutoReleasePool pool; - delete composingText; - [[NSNotificationCenter defaultCenter] removeObserver:self]; - -#ifdef ALIEN_DEBUG - --qCocoaViewCount; - qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount; -#endif - - [super dealloc]; -} - -- (BOOL)isOpaque -{ - if (!qwidgetprivate) - return [super isOpaque]; - return qwidgetprivate->isOpaque; -} - -- (BOOL)isFlipped -{ - return YES; -} - -// We preserve the content of the view if WA_StaticContents is defined. -// -// More info in the Cocoa documentation: -// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html -- (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]; - } - - // Make sure the opengl context is updated on resize. - if (qwidgetprivate && qwidgetprivate->isGLWidget && [self window]) { - qwidgetprivate->needWindowChange = true; - QEvent event(QEvent::MacGLWindowChange); - qApp->sendEvent(qwidget, &event); - } -} - -// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint. -// During the resize, the top of the widget is repainted, probably because of the -// change of coordinate space (Quartz vs Qt). This is then followed by this message: -// -[NSView _setNeedsDisplayIfTopLeftChanged] -// which force a full repaint by sending the message 'setNeedsDisplay:'. -// That is what we are preventing here. -- (void)setNeedsDisplay:(BOOL)flag { - if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) { - [super setNeedsDisplay:flag]; - } -} - -- (void)drawRect:(NSRect)aRect -{ - if (!qwidget) - return; - - // Getting context. - CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - qt_mac_retain_graphics_context(context); - - // We use a different graphics system. - // - // Widgets that are set to paint on screen, specifically QGLWidget, - // requires the native engine to execute in order to be drawn. - if (QApplicationPrivate::graphicsSystem() != 0 && !qwidget->testAttribute(Qt::WA_PaintOnScreen)) { - - // Raster engine. - if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) { - - if (!qwidgetprivate->isInUnifiedToolbar) { - - // Qt handles the painting occuring inside the window. - // Cocoa also keeps track of all widgets as NSView and therefore might - // ask for a repainting of a widget even if Qt is already taking care of it. - // - // The only valid reason for Cocoa to call drawRect: is for window manipulation - // (ie. resize, ...). - // - // Qt will then forward the update to the children. - if (!qwidget->isWindow()) { - qt_mac_release_graphics_context(context); - return; - } - - QRasterWindowSurface *winSurface = dynamic_cast<QRasterWindowSurface *>(qwidget->windowSurface()); - if (!winSurface || !winSurface->needsFlush) { - qt_mac_release_graphics_context(context); - return; - } - - // Clip to region. - const QVector<QRect> &rects = winSurface->regionToFlush.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 = winSurface->regionToFlush.boundingRect(); - const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height()); - - qt_mac_draw_image(context, winSurface->imageContext(), area, area); - - winSurface->needsFlush = false; - winSurface->regionToFlush = QRegion(); - - } else { - - QUnifiedToolbarSurface *unifiedSurface = qwidgetprivate->unifiedSurface; - if (!unifiedSurface) { - qt_mac_release_graphics_context(context); - return; - } - - int areaX = qwidgetprivate->toolbar_offset.x(); - int areaY = qwidgetprivate->toolbar_offset.y(); - int areaWidth = qwidget->geometry().width(); - int areaHeight = qwidget->geometry().height(); - const CGRect area = CGRectMake(areaX, areaY, areaWidth, areaHeight); - const CGRect drawingArea = CGRectMake(0, 0, areaWidth, areaHeight); - - qt_mac_draw_image(context, unifiedSurface->imageContext(), area, drawingArea); - - qwidgetprivate->flushRequested = false; - - } - - CGContextFlush(context); - qt_mac_release_graphics_context(context); - return; - } - - // Qt handles the painting occuring inside the window. - // Cocoa also keeps track of all widgets as NSView and therefore might - // ask for a repainting of a widget even if Qt is already taking care of it. - // - // The only valid reason for Cocoa to call drawRect: is for window manipulation - // (ie. resize, ...). - // - // Qt will then forward the update to the children. - if (qwidget->isWindow()) { - qwidgetprivate->syncBackingStore(qwidget->rect()); - } - } - - // Native engine. - qwidgetprivate->hd = context; - - 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; - - const NSRect *rects; - NSInteger count; - [self getRectsBeingDrawn:&rects count:&count]; - for (int i = 0; i < count; ++i) { - QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); - qrgn += tmpRect; - } - - 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 (context, 0, widgetRect.size.height); - CGContextScaleCTM(context, 1, -1); - if (qwidget->isWindow()) - CGContextClearRect(context, widgetRect); - CGContextClipToMask(context, widgetRect, qwidgetprivate->extra->imageMask); - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM (context, 0, -widgetRect.size.height); - } - - if (qwidget->isWindow() && !qwidgetprivate->isOpaque - && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { - CGContextClearRect(context, NSRectToCGRect(aRect)); - } - - qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); - QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); - - // We specify that we want to draw the widget itself, and - // all its children recursive. But we skip native children, because - // they will receive drawRect calls by themselves as needed: - int flags = QWidgetPrivate::DrawPaintOnScreen - | QWidgetPrivate::DrawRecursive - | QWidgetPrivate::DontDrawNativeChildren; - - if (qwidget->isWindow()) - flags |= QWidgetPrivate::DrawAsRoot; - - // Start to draw: - qt_mac_clearDirtyOnWidgetInsideDrawWidget = true; - qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0); - qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; - - if (!redirectionOffset.isNull()) - QPainter::restoreRedirected(qwidget); - 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; - qt_mac_release_graphics_context(context); -} - -- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent -{ - // Find the widget that should receive the event: - QPoint qlocal, qglobal; - QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0); - if (!widgetToGetMouse) - return NO; - - return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough); -} - -- (NSView *)hitTest:(NSPoint)aPoint -{ - if (!qwidget) - return [super hitTest:aPoint]; - - if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) - return nil; // You cannot hit a transparent for mouse event widget. - return [super hitTest:aPoint]; -} - -- (void)updateTrackingAreas -{ - if (!qwidget) - return; - - // [NSView addTrackingArea] is slow, so bail out early if we can: - if (NSIsEmptyRect([self visibleRect])) - return; - - 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]; - } - } - - // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should - // only be turned on if mouseTracking, hover is on or a tool tip is set. - // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to - // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of - // mouse moves delivered to it (Apple recommends keeping it OFF because there - // is a performance hit). So it goes. - NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp - | NSTrackingInVisibleRect | 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 -{ - // Cocoa will not send a move event on mouseEnter. But since - // Qt expect this, we fake one now. See also mouseExited below - // for info about enter/leave event handling - NSEvent *nsmoveEvent = [NSEvent - mouseEventWithType:NSMouseMoved - location:[[self window] mouseLocationOutsideOfEventStream] - modifierFlags: [event modifierFlags] - timestamp: [event timestamp] - windowNumber: [event windowNumber] - context: [event context] - eventNumber: [event eventNumber] - clickCount: 0 - pressure: 0]; - - // Important: Cocoa sends us mouseEnter on all views under the mouse - // and not just the one on top. Therefore, to we cannot use qwidget - // as native widget for this case. Instead, we let qt_mac_handleMouseEvent - // resolve it (last argument set to 0): - qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0); -} - -- (void)mouseExited:(NSEvent *)event -{ - // Note: normal enter/leave handling is done from within mouseMove. This handler - // catches the case when the mouse moves out of the window (which mouseMove do not). - // Updating the mouse cursor follows the same logic as enter/leave. And we update - // neither if a grab exists (even if the grab points to this widget, it seems, ref X11) - Q_UNUSED(event); - if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) { - qt_mac_update_cursor(); - // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still - // reports a target, it means that either there is a grab involved, or the mouse - // hovered over another window in the application. In both cases, move events will - // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave. - QPoint qlocal, qglobal; - QWidget *widgetUnderMouse = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse); - - if (widgetUnderMouse == 0) { - QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); - qt_last_mouse_receiver = 0; - qt_last_native_mouse_receiver = 0; - } - } -} - -- (void)flagsChanged:(NSEvent *)theEvent -{ - QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); - if (!widgetToGetKey) - return; - - qt_dispatchModifiersChanged(theEvent, widgetToGetKey); - [super flagsChanged:theEvent]; -} - -- (void)mouseMoved:(NSEvent *)theEvent -{ - // Important: this method will only be called when the view's window is _not_ inside - // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event - // before it ends up here. So, this method is added for supporting QMacNativeWidget. - // TODO: Cocoa send move events to all views under the mouse. So make sure we only - // handle the event for the widget on top when using QMacNativeWidget. - qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); -} - -- (void)mouseDown:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget); - // 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 -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget); -} - -- (void)rightMouseDown:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget); -} - -- (void)rightMouseUp:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget); -} - -- (void)otherMouseDown:(NSEvent *)theEvent -{ - Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget); -} - -- (void)otherMouseUp:(NSEvent *)theEvent -{ - Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); - qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget); -} - -- (void)mouseDragged:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); -} - -- (void)rightMouseDragged:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); -} - -- (void)otherMouseDragged:(NSEvent *)theEvent -{ - qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); -} - -- (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]; - } - - Qt::MouseButtons buttons = QApplication::mouseButtons(); - Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); - - // Find the widget that should receive the event: - QPoint qlocal, qglobal; - QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0); - if (!widgetToGetMouse) - return; - - int deltaX = 0; - int deltaY = 0; - int deltaZ = 0; - - const EventRef carbonEvent = (EventRef)[theEvent eventRef]; - const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0; - const bool scrollEvent = carbonEventKind == kEventMouseScroll; - - if (scrollEvent) { - // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad). - // Since deviceDelta is delivered as pixels rather than degrees, we need to - // convert from pixels to degrees in a sensible manner. - // It looks like 1/4 degrees per pixel behaves most native. - // (NB: Qt expects the unit for delta to be 8 per degree): - const int pixelsToDegrees = 2; // 8 * 1/4 - deltaX = [theEvent deviceDeltaX] * pixelsToDegrees; - deltaY = [theEvent deviceDeltaY] * pixelsToDegrees; - deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees; - } else { - // carbonEventKind == kEventMouseWheelMoved - // Remove acceleration, and use either -120 or 120 as delta: - deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120); - deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120); - deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120); - } - -#ifndef QT_NO_WHEELEVENT - // ### Qt 5: Send one QWheelEvent with dx, dy and dz - - if (deltaX != 0 && deltaY != 0) - QMacScrollOptimization::initDelayedScroll(); - - if (deltaX != 0) { - QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal); - qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - } - - if (deltaY != 0) { - QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical); - qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); - } - - if (deltaZ != 0) { - // 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(widgetToGetMouse, &qwe); - } - - if (deltaX != 0 && deltaY != 0) - QMacScrollOptimization::performDelayedScroll(); -#endif //QT_NO_WHEELEVENT -} - -- (void)tabletProximity:(NSEvent *)tabletEvent -{ - qt_dispatchTabletProximityEvent(tabletEvent); -} - -- (void)tabletPoint:(NSEvent *)tabletEvent -{ - if (!qt_mac_handleTabletEvent(self, tabletEvent)) - [super tabletPoint:tabletEvent]; -} - -- (void)magnifyWithEvent:(NSEvent *)event -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetGesture = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); - if (!widgetToGetGesture) - return; - if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) - return; - -#ifndef QT_NO_GESTURES - QNativeGestureEvent qNGEvent; - qNGEvent.gestureType = QNativeGestureEvent::Zoom; - NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; - qNGEvent.position = flipPoint(p).toPoint(); - qNGEvent.percentage = [event magnification]; - qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); -#endif // QT_NO_GESTURES -} - -- (void)rotateWithEvent:(NSEvent *)event -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetGesture = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); - if (!widgetToGetGesture) - return; - if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) - return; - -#ifndef QT_NO_GESTURES - QNativeGestureEvent qNGEvent; - qNGEvent.gestureType = QNativeGestureEvent::Rotate; - NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; - qNGEvent.position = flipPoint(p).toPoint(); - qNGEvent.percentage = -[event rotation]; - qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); -#endif // QT_NO_GESTURES -} - -- (void)swipeWithEvent:(NSEvent *)event -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetGesture = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); - if (!widgetToGetGesture) - return; - if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) - return; - -#ifndef QT_NO_GESTURES - QNativeGestureEvent qNGEvent; - qNGEvent.gestureType = QNativeGestureEvent::Swipe; - NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; - qNGEvent.position = flipPoint(p).toPoint(); - if ([event deltaX] == 1) - qNGEvent.angle = 180.0f; - else if ([event deltaX] == -1) - qNGEvent.angle = 0.0f; - else if ([event deltaY] == 1) - qNGEvent.angle = 90.0f; - else if ([event deltaY] == -1) - qNGEvent.angle = 270.0f; - qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); -#endif // QT_NO_GESTURES -} - -- (void)beginGestureWithEvent:(NSEvent *)event -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetGesture = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); - if (!widgetToGetGesture) - return; - if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) - return; - -#ifndef QT_NO_GESTURES - QNativeGestureEvent qNGEvent; - qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; - NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; - qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); -#endif // QT_NO_GESTURES -} - -- (void)endGestureWithEvent:(NSEvent *)event -{ - QPoint qlocal, qglobal; - QWidget *widgetToGetGesture = 0; - qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); - if (!widgetToGetGesture) - return; - if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) - return; - -#ifndef QT_NO_GESTURES - QNativeGestureEvent qNGEvent; - qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; - NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; - qNGEvent.position = flipPoint(p).toPoint(); - qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); -} -#endif // QT_NO_GESTURES - -- (void)frameDidChange:(NSNotification *)note -{ - Q_UNUSED(note); - if (!qwidget) - return; - 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 && qwidget->isEnabled() != flag) - qwidget->setEnabled(flag); -} - -+ (Class)cellClass -{ - return [NSActionCell class]; -} - -- (BOOL)acceptsFirstResponder -{ - if (!qwidget) - return NO; - - // Disabled widget shouldn't get focus even if it's a window. - // hence disabled windows will not get any key or mouse events. - if (!qwidget->isEnabled()) - return NO; - - if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) { - QWidget *focusWidget = qApp->focusWidget(); - if (!focusWidget) { - // There is no focus widget, but we still want to receive key events - // for shortcut handling etc. So we accept first responer for the - // content view as a last resort: - return YES; - } - if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) { - // The current focus widget is alien, and hence, cannot get acceptsFirstResponder - // calls. Since the focus widget is a child of qwidget, we let this view say YES: - return YES; - } - if (focusWidget->window() != qwidget) { - // The current focus widget is in another window. Since cocoa - // suggest that this window should be key now, we accept: - return YES; - } - } - - return qwidget->focusPolicy() != Qt::NoFocus; -} - -- (BOOL)resignFirstResponder -{ - if (!qwidget) - return YES; - - // Seems like the following test only triggers if this - // view is inside a QMacNativeWidget: -// if (QWidget *fw = QApplication::focusWidget()) { -// if (qwidget == fw || qwidget == fw->nativeParentWidget()) -// fw->clearFocus(); -// } - return YES; -} - -- (BOOL)becomeFirstResponder -{ - // see the comment in the acceptsFirstResponder - if the window "stole" focus - // let it become the responder, but don't tell Qt - if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded - && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus) - qwidget->setFocus(Qt::OtherFocusReason); - return YES; -} - -- (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); - macCurrentDnDParameters()->performedAction = operation; - if (QDragManager::self()->object - && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) { - macCurrentDnDParameters()->performedAction = - qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); - } -} - -- (QWidget *)qt_qwidget -{ - return qwidget; -} - -- (void) qt_clearQWidget -{ - qwidget = 0; - qwidgetprivate = 0; -} - -- (void)keyDown:(NSEvent *)theEvent -{ - if (!qwidget) - return; - QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); - if (!widgetToGetKey) - return; - - sendKeyEvents = true; - - if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled) - && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly - || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly - || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) { - fromKeyDownEvent = true; - [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; - fromKeyDownEvent = false; - } - - if (sendKeyEvents && !composing) { - bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); - if (!keyEventEaten && qwidget) { - // The event is not yet eaten, and if Qt is embedded inside a native - // cocoa application, send it to first responder not owned by Qt. - // The exception is if widgetToGetKey was redirected to a popup. - QWidget *toplevel = qwidget->window(); - if (toplevel == widgetToGetKey->window()) { - if (qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyDown:theEvent]; - } - } - } - } -} - - -- (void)keyUp:(NSEvent *)theEvent -{ - if (sendKeyEvents) { - QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); - if (!widgetToGetKey) - return; - - bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); - if (!keyEventEaten && qwidget) { - // The event is not yet eaten, and if Qt is embedded inside a native - // cocoa application, send it to first responder not owned by Qt. - // The exception is if widgetToGetKey was redirected to a popup. - QWidget *toplevel = qwidget->window(); - if (toplevel == widgetToGetKey->window()) { - if (qt_widget_private(toplevel)->topData()->embedded) { - if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) - [w keyUp:theEvent]; - } - } - } - } -} - -- (void)viewWillMoveToWindow:(NSWindow *)window -{ - if (qwidget == 0) - return; - - if (qwidget->windowFlags() & Qt::MSWindowsOwnDC - && (window != [self window])) { // OpenGL Widget - QEvent event(QEvent::MacGLClearDrawable); - qApp->sendEvent(qwidget, &event); - } -} - -- (void)viewDidMoveToWindow -{ - if (qwidget == 0) - return; - - if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) { - // call update paint event - qwidgetprivate->needWindowChange = true; - QEvent event(QEvent::MacGLWindowChange); - qApp->sendEvent(qwidget, &event); - } -} - - -// NSTextInput Protocol implementation - -- (void) insertText:(id)aString -{ - QString commitText; - if ([aString length]) { - if ([aString isKindOfClass:[NSAttributedString class]]) { - commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string])); - } else { - commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString)); - }; - } - - // When entering characters through Character Viewer or Keyboard Viewer, the text is passed - // through this insertText method. Since we dont receive a keyDown Event in such cases, the - // composing flag will be false. - if (([aString length] && composing) || !fromKeyDownEvent) { - // Send the commit string to the widget. - composing = false; - sendKeyEvents = false; - QInputMethodEvent e; - e.setCommitString(commitText); - if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) - qt_sendSpontaneousEvent(widgetToGetKey, &e); - } else { - // The key sequence "`q" on a French Keyboard will generate two calls to insertText before - // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept - // the "`" key. The last keyDown event needs to be processed by the widget to get the - // character "q". The string parameter is ignored for the second call. - sendKeyEvents = true; - } - - composingText->clear(); -} - -- (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 + selRange.length, 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); - } - *composingText = qtText; - - QInputMethodEvent e(qtText, attrs); - if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) - qt_sendSpontaneousEvent(widgetToGetKey, &e); - - if (!composingLength) - composing = false; -} - -- (void) unmarkText -{ - if (composing) { - QInputMethodEvent e; - e.setCommitString(*composingText); - if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) - qt_sendSpontaneousEvent(widgetToGetKey, &e); - } - composingText->clear(); - 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:const_cast<NSString *>(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. - QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); - if (!widgetToGetKey) - return NSZeroRect; - - QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect()); - QPoint mp(widgetToGetKey->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 -{ - QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); - if (!widgetToGetKey) - return nil; - - if (!widgetToGetKey->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_effectiveview_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_effectiveview_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("dummy")); - 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 = macCurrentDnDParameters(); - QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view); - - // Save supported actions: - [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; - QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint); - NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()}; - NSSize mouseOffset = {0.0, 0.0}; - NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - dragPrivate()->executed_action = Qt::ActionMask; - - // Execute the drag: - [theView retain]; - [theView dragImage:image - at:imageLoc - offset:mouseOffset - event:dndParams->theEvent - pasteboard:pboard - source:theView - slideBack:YES]; - - // Reset the implicit grab widget when drag ends because we will not - // receive the mouse release event when DND is active: - qt_button_down = 0; - [theView release]; - [image release]; - if (dragPrivate()) - dragPrivate()->executed_action = Qt::IgnoreAction; - 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()); - } - } - } - - // Clean-up: - o->setMimeData(0); - o->deleteLater(); - 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 deleted file mode 100644 index cc79b6705b..0000000000 --- a/src/gui/kernel/qcocoaview_mac_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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(QString); -QT_FORWARD_DECLARE_CLASS(QStringList); - -Q_GUI_EXPORT -@interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> { - QWidget *qwidget; - QWidgetPrivate *qwidgetprivate; - NSDragOperation supportedActions; - bool composing; - int composingLength; - bool sendKeyEvents; - bool fromKeyDownEvent; - QString *composingText; - @public int alienTouchCount; -} -- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; -- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; -- (void)frameDidChange:(NSNotification *)note; -- (void)setSupportedActions:(NSDragOperation)actions; -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; -- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; -- (BOOL)isComposing; -- (QWidget *)qt_qwidget; -- (void) qt_clearQWidget; - -@end -#endif diff --git a/src/gui/kernel/qcocoawindow_mac.mm b/src/gui/kernel/qcocoawindow_mac.mm deleted file mode 100644 index 6e5023aaca..0000000000 --- a/src/gui/kernel/qcocoawindow_mac.mm +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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> -#import <private/qcocoaapplication_mac_p.h> -#import <private/qdnd_p.h> -#import <private/qmultitouch_mac_p.h> - -#include <QtGui/QWidget> - -QT_FORWARD_DECLARE_CLASS(QWidget); -QT_USE_NAMESPACE - -@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) - -/*********************************************************************** - Copy and Paste between QCocoaWindow and QCocoaPanel - This is a bit unfortunate, but thanks to the dynamic dispatch we - have to duplicate this code or resort to really silly forwarding methods -**************************************************************************/ -#include "qcocoasharedwindowmethods_mac_p.h" - -@end -#endif diff --git a/src/gui/kernel/qcocoawindow_mac_p.h b/src/gui/kernel/qcocoawindow_mac_p.h deleted file mode 100644 index d567cab244..0000000000 --- a/src/gui/kernel/qcocoawindow_mac_p.h +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 QCOCOAWINDOW_MAC_P -#define QCOCOAWINDOW_MAC_P - -#ifdef QT_MAC_USE_COCOA -#include "qmacdefines_mac.h" -#import <Cocoa/Cocoa.h> -#include <private/qapplication_p.h> -#include <private/qbackingstore_p.h> - -enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by - -QT_FORWARD_DECLARE_CLASS(QWidget); -QT_FORWARD_DECLARE_CLASS(QStringList); -QT_FORWARD_DECLARE_CLASS(QCocoaDropData); - -@interface NSWindow (QtCoverForHackWithCategory) -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; -@end - -@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 NSWindow (QtIntegration) -- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; -- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; -- (void)draggingExited:(id <NSDraggingInfo>)sender; -- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; -@end - -@interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { - QStringList *currentCustomDragTypes; - QCocoaDropData *dropData; - NSInteger dragEnterSequence; -} - -+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; -- (void)registerDragTypes; -- (void)drawRectOriginal:(NSRect)rect; - -@end -#endif - -#endif diff --git a/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm b/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm deleted file mode 100644 index b761934c01..0000000000 --- a/src/gui/kernel/qcocoawindowcustomthemeframe_mac.mm +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 deleted file mode 100644 index 09b40875f6..0000000000 --- a/src/gui/kernel/qcocoawindowcustomthemeframe_mac_p.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 deleted file mode 100644 index 1faf068a12..0000000000 --- a/src/gui/kernel/qcocoawindowdelegate_mac.mm +++ /dev/null @@ -1,439 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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> -#include <QMainWindow> -#include <QToolBar> -#include <private/qmainwindowlayout_p.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); - if (qwidget->testAttribute(Qt::WA_PendingResizeEvent)) { - qwidget->setAttribute(Qt::WA_PendingResizeEvent, false); - QApplication::sendEvent(qwidget, &qre); - } else { - 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); - 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); - } else { - widgetData->window_state = widgetData->window_state & ~Qt::WindowMaximized; - QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state - | Qt::WindowMaximized)); - qt_sendSpontaneousEvent(qwidget, &e); - } - 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]; - } - - // We force the repaint to be synchronized with the resize of the window. - // Otherwise, the resize looks sluggish because we paint one event loop later. - if ([[window contentView] inLiveResize]) { - qwidget->repaint(); - - // We need to repaint the toolbar as well. - QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window()); - if (mWindow) { - QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout()); - QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList; - - for (int i = 0; i < toolbarList.size(); ++i) { - QToolBar* toolbar = toolbarList.at(i); - toolbar->repaint(); - } - } - } -} - -- (void)windowDidMove:(NSNotification *)notification -{ - // The code underneath needs to translate the window location - // from bottom left (which is the origin used by Cocoa) to - // upper left (which is the origin used by Qt): - NSWindow *window = [notification object]; - NSRect newRect = [window frame]; - QWidget *qwidget = m_windowHash->value(window); - 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); - } - } -} - --(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); - 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); - 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); -} - -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame -{ - Q_UNUSED(newFrame); - // saving the current window geometry before the window is maximized - QWidget *qwidget = m_windowHash->value(window); - QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget); - if (qwidget->isWindow()) { - if(qwidget->windowState() & Qt::WindowMaximized) { - // Restoring - widgetPrivate->topData()->wasMaximized = false; - } else { - // Maximizing - widgetPrivate->topData()->normalGeometry = qwidget->geometry(); - // If the window was maximized we need to update the coordinates since now it will start at 0,0. - // We do this in a special field that is only used when not restoring but manually resizing the window. - // Since the coordinates are fixed we just set a boolean flag. - widgetPrivate->topData()->wasMaximized = true; - } - } - return YES; -} - -- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame -{ - NSRect frameToReturn = defaultFrame; - QWidget *qwidget = m_windowHash->value(window); - QSizeF size = qwidget->maximumSize(); - NSRect windowFrameRect = [window frame]; - NSRect viewFrameRect = [[window contentView] frame]; - // consider additional size required for titlebar & frame - frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width, - size.width()+(windowFrameRect.size.width - viewFrameRect.size.width)); - frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height, - size.height()+(windowFrameRect.size.height - viewFrameRect.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); -} - -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu -{ - Q_UNUSED(menu); - QWidget *qwidget = m_windowHash->value(window); - if (qwidget && !qwidget->windowFilePath().isEmpty()) { - return YES; - } - return NO; -} - -- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event - from:(NSPoint)dragImageLocation - withPasteboard:(NSPasteboard *)pasteboard -{ - Q_UNUSED(event); - Q_UNUSED(dragImageLocation); - Q_UNUSED(pasteboard); - QWidget *qwidget = m_windowHash->value(window); - if (qwidget && !qwidget->windowFilePath().isEmpty()) { - return YES; - } - return NO; -} - -- (void)syncContentViewFrame: (NSNotification *)notification -{ - NSView *cView = [notification object]; - if (cView) { - NSWindow *window = [cView window]; - QWidget *qwidget = m_windowHash->value(window); - if (qwidget) { - QWidgetData *widgetData = qt_qwidget_data(qwidget); - NSRect rect = [cView frame]; - const QSize newSize(rect.size.width, rect.size.height); - const QSize &oldSize = widgetData->crect.size(); - if (newSize != oldSize) { - [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize]; - } - } - - } -} - -@end -#endif// QT_MAC_USE_COCOA diff --git a/src/gui/kernel/qcocoawindowdelegate_mac_p.h b/src/gui/kernel/qcocoawindowdelegate_mac_p.h deleted file mode 100644 index 638ce2df9a..0000000000 --- a/src/gui/kernel/qcocoawindowdelegate_mac_p.h +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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) - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 -@protocol NSWindowDelegate <NSObject> -- (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; -- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; -- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; -- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; -@end - -@protocol NSDrawerDelegate <NSObject> -- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize; -@end - -#endif - - - -@interface QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) : NSObject<NSWindowDelegate, NSDrawerDelegate> { - 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; -- (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)syncContentViewFrame: (NSNotification *)notification; -@end -#endif diff --git a/src/gui/kernel/qdesktopwidget_mac.mm b/src/gui/kernel/qdesktopwidget_mac.mm deleted file mode 100644 index 0b529c9843..0000000000 --- a/src/gui/kernel/qdesktopwidget_mac.mm +++ /dev/null @@ -1,257 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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) -{ - onResize(); -} - -QDesktopWidgetImplementation::~QDesktopWidgetImplementation() -{ -} - -QDesktopWidgetImplementation *QDesktopWidgetImplementation::instance() -{ - return qdesktopWidgetImplementation(); -} - -QRect QDesktopWidgetImplementation::availableRect(int screenIndex) const -{ - if (screenIndex < 0 || screenIndex >= screenCount) - screenIndex = appScreen; - - return availableRects[screenIndex].toRect(); -} - -QRect QDesktopWidgetImplementation::screenRect(int screenIndex) const -{ - if (screenIndex < 0 || screenIndex >= screenCount) - screenIndex = appScreen; - - return screenRects[screenIndex].toRect(); -} - -void QDesktopWidgetImplementation::onResize() -{ - QMacCocoaAutoReleasePool pool; - NSArray *displays = [NSScreen screens]; - screenCount = [displays count]; - - screenRects.clear(); - availableRects.clear(); - NSRect primaryRect = [[displays objectAtIndex:0] frame]; - for (int i = 0; i<screenCount; i++) { - NSRect r = [[displays objectAtIndex:i] frame]; - int flippedY = - r.origin.y + // account for position offset and - primaryRect.size.height - r.size.height; // height difference. - screenRects.append(QRectF(r.origin.x, flippedY, - r.size.width, r.size.height)); - - r = [[displays objectAtIndex:i] visibleFrame]; - flippedY = - r.origin.y + // account for position offset and - primaryRect.size.height - r.size.height; // height difference. - availableRects.append(QRectF(r.origin.x, flippedY, - r.size.width, r.size.height)); - } -} - - - -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(); - - const int oldScreenCount = d->screenCount; - const QVector<QRectF> oldRects(d->screenRects); - const QVector<QRectF> oldWorks(d->availableRects); - - d->onResize(); - - for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { - if (oldRects.at(i) != d->screenRects.at(i)) - emit resized(i); - } - for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { - if (oldWorks.at(i) != d->availableRects.at(i)) - emit workAreaResized(i); - } - - if (oldScreenCount != d->screenCount) - emit screenCountChanged(d->screenCount); -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qdesktopwidget_mac_p.h b/src/gui/kernel/qdesktopwidget_mac_p.h deleted file mode 100644 index ac638ea7d8..0000000000 --- a/src/gui/kernel/qdesktopwidget_mac_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 <qrect.h> - -QT_BEGIN_NAMESPACE - -class QDesktopWidgetImplementation -{ -public: - QDesktopWidgetImplementation(); - ~QDesktopWidgetImplementation(); - static QDesktopWidgetImplementation *instance(); - - int appScreen; - int screenCount; - - QVector<QRectF> availableRects; - QVector<QRectF> screenRects; - - QRect availableRect(int screenIndex) const; - QRect screenRect(int screenIndex) const; - void onResize(); -}; - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qdesktopwidget_s60.cpp b/src/gui/kernel/qdesktopwidget_s60.cpp deleted file mode 100644 index 62a4d40eba..0000000000 --- a/src/gui/kernel/qdesktopwidget_s60.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdesktopwidget.h" -#include "qapplication_p.h" -#include "qwidget_p.h" -#include "qt_s60_p.h" -#include <w32std.h> -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) -#include <graphics/displaycontrol.h> -#endif - -QT_BEGIN_NAMESPACE - -extern int qt_symbian_create_desktop_on_screen; - -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(); - static void init(QDesktopWidget *that); - static void cleanup(); - static void init_sys(); - - static int screenCount; - static int primaryScreen; - - static QVector<QRect> *rects; - static QVector<QRect> *workrects; - static QVector<QWidget *> *screens; - - static int refcount; - -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - static MDisplayControl *displayControl; -#endif -}; - -int QDesktopWidgetPrivate::screenCount = 1; -int QDesktopWidgetPrivate::primaryScreen = 0; -QVector<QRect> *QDesktopWidgetPrivate::rects = 0; -QVector<QRect> *QDesktopWidgetPrivate::workrects = 0; -QVector<QWidget *> *QDesktopWidgetPrivate::screens = 0; -int QDesktopWidgetPrivate::refcount = 0; -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) -MDisplayControl *QDesktopWidgetPrivate::displayControl = 0; -#endif - -QDesktopWidgetPrivate::QDesktopWidgetPrivate() -{ - ++refcount; -} - -QDesktopWidgetPrivate::~QDesktopWidgetPrivate() -{ - if (!--refcount) - cleanup(); -} - -void QDesktopWidgetPrivate::init(QDesktopWidget *that) -{ - // Note that on S^3 devices the screen count retrieved via RWsSession - // will always be 2 but the width and height for screen number 1 will - // be 0 as long as TV-out is not connected. - // - // On the other hand a valid size for screen 1 will be reported even - // after the cable is disconnected. In order to overcome this, we use - // MDisplayControl::NumberOfResolutions() to check if the display is - // valid or not. - - screenCount = S60->screenCount(); -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - if (displayControl) { - if (displayControl->NumberOfResolutions() < 1) - screenCount = 1; - } -#endif - if (screenCount < 1) { - qWarning("No screen available"); - screenCount = 1; - } - - rects = new QVector<QRect>(); - workrects = new QVector<QRect>(); - screens = new QVector<QWidget *>(); - - rects->resize(screenCount); - workrects->resize(screenCount); - screens->resize(screenCount); - - for (int i = 0; i < screenCount; ++i) { - // All screens will have a position of (0, 0) as there is no true virtual desktop - // or pointer event support for multiple screens on Symbian. - QRect r(0, 0, - S60->screenWidthInPixelsForScreen[i], S60->screenHeightInPixelsForScreen[i]); - // Stop here if empty and ignore this screen. - if (r.isEmpty()) { - screenCount = i; - break; - } - (*rects)[i] = r; - QRect wr; - if (i == 0) - wr = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); - else - wr = rects->at(i); - (*workrects)[i].setRect(wr.x(), wr.y(), wr.width(), wr.height()); - (*screens)[i] = 0; - } - (*screens)[0] = that; -} - -void QDesktopWidgetPrivate::cleanup() -{ - delete rects; - rects = 0; - delete workrects; - workrects = 0; - if (screens) { - // First item is the QDesktopWidget so skip it. - for (int i = 1; i < screens->count(); ++i) - delete screens->at(i); - } - delete screens; - screens = 0; -} - -void QDesktopWidgetPrivate::init_sys() -{ -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - if (S60->screenCount() > 1) { - CWsScreenDevice *dev = S60->screenDevice(1); - if (dev) { - displayControl = static_cast<MDisplayControl *>( - dev->GetInterface(MDisplayControl::ETypeId)); - if (displayControl) { - displayControl->EnableDisplayChangeEvents(ETrue); - } - } - } -#endif -} - - -QDesktopWidget::QDesktopWidget() - : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) -{ - setObjectName(QLatin1String("desktop")); - QDesktopWidgetPrivate::init_sys(); - QDesktopWidgetPrivate::init(this); -} - -QDesktopWidget::~QDesktopWidget() -{ -} - -bool QDesktopWidget::isVirtualDesktop() const -{ - return false; -} - -int QDesktopWidget::primaryScreen() const -{ - return QDesktopWidgetPrivate::primaryScreen; -} - -int QDesktopWidget::numScreens() const -{ - Q_D(const QDesktopWidget); - return QDesktopWidgetPrivate::screenCount; -} - -static inline QWidget *newSingleDesktopWidget(int screen) -{ - qt_symbian_create_desktop_on_screen = screen; - QWidget *w = new QSingleDesktopWidget; - qt_symbian_create_desktop_on_screen = -1; - return w; -} - -QWidget *QDesktopWidget::screen(int screen) -{ - Q_D(QDesktopWidget); - if (screen < 0 || screen >= d->screenCount) - screen = d->primaryScreen; - if (!d->screens->at(screen) - || d->screens->at(screen)->windowType() != Qt::Desktop) - (*d->screens)[screen] = newSingleDesktopWidget(screen); - return (*d->screens)[screen]; -} - -const QRect QDesktopWidget::availableGeometry(int screen) const -{ - Q_D(const QDesktopWidget); - if (screen < 0 || screen >= d->screenCount) - screen = d->primaryScreen; - - return d->workrects->at(screen); -} - -const QRect QDesktopWidget::screenGeometry(int screen) const -{ - Q_D(const QDesktopWidget); - if (screen < 0 || screen >= d->screenCount) - screen = d->primaryScreen; - - return d->rects->at(screen); -} - -int QDesktopWidget::screenNumber(const QWidget *widget) const -{ - Q_D(const QDesktopWidget); - return widget - ? S60->screenNumberForWidget(widget) - : d->primaryScreen; -} - -int QDesktopWidget::screenNumber(const QPoint &point) const -{ - Q_UNUSED(point); - Q_D(const QDesktopWidget); - return d->primaryScreen; -} - -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); - - 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); - } - - if (oldscreencount != d->screenCount) { - emit screenCountChanged(d->screenCount); - } -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qdesktopwidget_win.cpp b/src/gui/kernel/qdesktopwidget_win.cpp deleted file mode 100644 index d57b355ef4..0000000000 --- a/src/gui/kernel/qdesktopwidget_win.cpp +++ /dev/null @@ -1,387 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qdesktopwidget.h" -#include "qt_windows.h" -#include "qapplication_p.h" -#include <private/qsystemlibrary_p.h> -#include <qvector.h> -#include <limits.h> -#ifdef Q_WS_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 (QT_WIN_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_WS_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 QT_WIN_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>(); - screenCount = 0; - -#ifndef Q_OS_WINCE - QSystemLibrary user32Lib(QLatin1String("user32")); - enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors"); - getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW"); - - 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 - QSystemLibrary coreLib(QLatin1String("coredll")); - // 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_WS_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_WS_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_WS_WINCE_WM - for(int i=0; i < d->workrects->size(); ++i) - qt_get_sip_info((*d->workrects)[i]); -#endif - if (screen < 0 || screen >= d->screenCount) - screen = d->primaryScreen; - - return d->workrects->at(screen); -} - -const QRect QDesktopWidget::screenGeometry(int screen) const -{ - const QDesktopWidgetPrivate *d = d_func(); - if (screen < 0 || screen >= d->screenCount) - screen = d->primaryScreen; - - return d->rects->at(screen); -} - -int QDesktopWidget::screenNumber(const QWidget *widget) const -{ - Q_D(const QDesktopWidget); - 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; -} - -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->at(i)); - if (thisDistance < shortestDistance) { - shortestDistance = thisDistance; - closestScreen = i; - } - } - - return closestScreen; -} - -void QDesktopWidget::resizeEvent(QResizeEvent *) -{ - Q_D(QDesktopWidget); - const QVector<QRect> oldrects(*d->rects); - const QVector<QRect> oldworkrects(*d->workrects); - int oldscreencount = d->screenCount; - - QDesktopWidgetPrivate::cleanup(); - QDesktopWidgetPrivate::init(this); -#ifdef Q_WS_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) { - const QRect oldrect = oldrects[i]; - const QRect newrect = d->rects->at(i); - if (oldrect != newrect) - emit resized(i); - } - - for (int j = 0; j < qMin(oldscreencount, d->screenCount); ++j) { - const QRect oldrect = oldworkrects[j]; - const QRect newrect = d->workrects->at(j); - if (oldrect != newrect) - emit workAreaResized(j); - } - - if (oldscreencount != d->screenCount) { - emit screenCountChanged(d->screenCount); - } -} - -#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 deleted file mode 100644 index b0f12903a1..0000000000 --- a/src/gui/kernel/qdesktopwidget_x11.cpp +++ /dev/null @@ -1,406 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 = q_check_ptr((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 (X11->isSupportedByWM(ATOM(_NET_WORKAREA))) { - int x11Screen = isVirtualDesktop() ? DefaultScreen(X11->display) : screen; - - Atom ret; - int format, e; - unsigned char *data = 0; - unsigned long nitems, after; - - e = XGetWindowProperty(X11->display, - QX11Info::appRootWindow(x11Screen), - ATOM(_NET_WORKAREA), 0, 4, False, XA_CARDINAL, - &ret, &format, &nitems, &after, &data); - - QRect workArea; - if (e == Success && ret == XA_CARDINAL && - format == 32 && nitems == 4) { - long *workarea = (long *) data; - workArea = QRect(workarea[0], workarea[1], workarea[2], workarea[3]); - } else { - workArea = screenGeometry(screen); - } - - if (isVirtualDesktop()) { - // intersect the workarea (which spawns all Xinerama screens) with the rect for the - // requested screen - workArea &= screenGeometry(screen); - } - - d->workareas[screen] = workArea; - - 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); - int oldScreenCount = d->screenCount; - QVector<QRect> oldRects(oldScreenCount); - for (int i = 0; i < oldScreenCount; ++i) { - oldRects[i] = d->rects[i]; - } - - d->init(); - - for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { - if (oldRects.at(i) != d->rects[i]) - emit resized(i); - } - - if (oldScreenCount != d->screenCount) { - emit screenCountChanged(d->screenCount); - } - - qt_desktopwidget_workarea_dirty = true; - QWidget::resizeEvent(event); -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qguifunctions_wince.cpp b/src/gui/kernel/qguifunctions_wince.cpp deleted file mode 100644 index bb4ed11589..0000000000 --- a/src/gui/kernel/qguifunctions_wince.cpp +++ /dev/null @@ -1,408 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "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 SHDB_HIDE -#define SHDB_HIDE 0x0002 -#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 -#ifndef SPI_GETPLATFORMTYPE -#define SPI_GETPLATFORMTYPE 257 -#endif - -typedef BOOL (*AygInitDialog)(AygSHINITDLGINFO*); -typedef BOOL (*AygFullScreen)(HWND, DWORD); -typedef BOOL (*AygSHSipInfo)(UINT, UINT, PVOID, UINT); -typedef BOOL (*AygSHDoneButton)(HWND, DWORD); - -static AygInitDialog ptrAygInitDialog = 0; -static AygFullScreen ptrAygFullScreen = 0; -static AygSHSipInfo ptrAygSHSipInfo = 0; -static AygSHDoneButton ptrAygSHDoneButton = 0; -static bool aygResolved = false; - -static void resolveAygLibs() -{ - if (!aygResolved) { - aygResolved = true; - QLibrary ayglib(QLatin1String("aygshell")); - ptrAygInitDialog = (AygInitDialog) ayglib.resolve("SHInitDialog"); - ptrAygFullScreen = (AygFullScreen) ayglib.resolve("SHFullScreen"); - ptrAygSHSipInfo = (AygSHSipInfo) ayglib.resolve("SHSipInfo"); - ptrAygSHDoneButton = (AygSHDoneButton) ayglib.resolve("SHDoneButton"); - } -} - -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; -} - -HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCWSTR, LPCWSTR file, LPCWSTR params, LPCWSTR 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; -} - -// Internal Qt ----------------------------------------------------- -bool qt_wince_is_platform(const QString &platformString) { - wchar_t tszPlatform[64]; - if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform) / sizeof(wchar_t), tszPlatform, 0)) - if (0 == _tcsicmp(reinterpret_cast<const wchar_t *> (platformString.utf16()), tszPlatform)) - return true; - return false; -} - -int qt_wince_get_build() -{ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof(osvi); - if (GetVersionEx(&osvi)) { - return osvi.dwBuildNumber; - } - return 0; -} - -int qt_wince_get_version() -{ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof(osvi); - if (GetVersionEx(&osvi)) { - return (osvi.dwMajorVersion * 10 + osvi.dwMinorVersion); - } - return 0; -} - -bool qt_wince_is_windows_mobile_65() -{ - const DWORD dwFirstWM65BuildNumber = 21139; - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof(osvi); - if (!GetVersionEx(&osvi)) - return false; - return osvi.dwMajorVersion > 5 - || (osvi.dwMajorVersion == 5 && (osvi.dwMinorVersion > 2 || - (osvi.dwMinorVersion == 2 && osvi.dwBuildNumber >= dwFirstWM65BuildNumber))); -} - -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; - if (!(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint))) - shidi.dwFlags |= SHIDIF_CANCELBUTTON; - 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_unmaximize(QWidget *widget) -{ - if (ptrAygSHDoneButton && qt_wince_is_mobile() - && !(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint))) - { - // Hide the [X] button, we've added in qt_wince_maximize. - ptrAygSHDoneButton(widget->winId(), SHDB_HIDE); - } -} - -void qt_wince_minimize(HWND hwnd) -{ -#ifdef Q_OS_WINCE_WM - ShowWindow(hwnd, SW_HIDE); -#else - if (!IsWindowVisible(hwnd)) { - // Hack for an initial showMinimized. - // Without it, our widget doesn't appear in the task bar. - ShowWindow(hwnd, SW_SHOW); - } - ShowWindow(hwnd, SW_MINIMIZE); -#endif -} - -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 deleted file mode 100644 index 2e14de0693..0000000000 --- a/src/gui/kernel/qguifunctions_wince.h +++ /dev/null @@ -1,151 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef 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 - -// 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, LPCWSTR operation, LPCWSTR file, LPCWSTR params, LPCWSTR 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) - -#endif // Q_OS_WINCE -#endif // QGUIFUNCTIONS_WCE_H diff --git a/src/gui/kernel/qkde.cpp b/src/gui/kernel/qkde.cpp deleted file mode 100644 index 7d333feb9a..0000000000 --- a/src/gui/kernel/qkde.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qkde_p.h" -#include <QtCore/QLibrary> -#include <QtCore/QDir> -#include <QtCore/qdebug.h> -#include <QtCore/QSettings> -#include "QtGui/qstylefactory.h" -#include "qt_x11_p.h" - -#if defined(Q_WS_X11) - -QT_BEGIN_NAMESPACE - -/*! \internal -Gets the current KDE home path -like "/home/troll/.kde" -*/ -QString QKde::kdeHome() -{ - static QString kdeHomePath; - if (kdeHomePath.isEmpty()) { - kdeHomePath = QString::fromLocal8Bit(qgetenv("KDEHOME")); - if (kdeHomePath.isEmpty()) { - QDir homeDir(QDir::homePath()); - QString kdeConfDir(QLatin1String("/.kde")); - if (4 == X11->desktopVersion && homeDir.exists(QLatin1String(".kde4"))) - kdeConfDir = QLatin1String("/.kde4"); - kdeHomePath = QDir::homePath() + kdeConfDir; - } - } - return kdeHomePath; -} - -/*!\internal - Reads the color from the config, and store it in the palette with the given color role if found - */ -static bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QSettings &kdeSettings, const QString &kde4Key, const QString &kde3Key = QString()) -{ - QVariant variant = kdeSettings.value(kde4Key); - if (!variant.isValid()) - QVariant variant = kdeSettings.value(kde3Key); - 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(); - pal->setBrush(role, QColor(r, g, b)); - return true; - } - } - return false; -} - - -/*!\internal - Returns the KDE palette -*/ -QPalette QKde::kdePalette() -{ - const QSettings theKdeSettings(QKde::kdeHome() + - QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat); - QPalette pal; - - // Setup KDE palette - kdeColor(&pal, QPalette::Button, theKdeSettings, QLatin1String("Colors:Button/BackgroundNormal"), QLatin1String("buttonBackground")); - kdeColor(&pal, QPalette::Window, theKdeSettings, QLatin1String("Colors:Window/BackgroundNormal"), QLatin1String("background")); - kdeColor(&pal, QPalette::Text, theKdeSettings, QLatin1String("Colors:View/ForegroundNormal"), QLatin1String("foreground")); - kdeColor(&pal, QPalette::WindowText, theKdeSettings, QLatin1String("Colors:Window/ForegroundNormal"), QLatin1String("windowForeground")); - kdeColor(&pal, QPalette::Base, theKdeSettings, QLatin1String("Colors:View/BackgroundNormal"), QLatin1String("windowBackground")); - kdeColor(&pal, QPalette::Highlight, theKdeSettings, QLatin1String("Colors:Selection/BackgroundNormal"), QLatin1String("selectBackground")); - kdeColor(&pal, QPalette::HighlightedText, theKdeSettings, QLatin1String("Colors:Selection/ForegroundNormal"), QLatin1String("selectForeground")); - kdeColor(&pal, QPalette::AlternateBase, theKdeSettings, QLatin1String("Colors:View/BackgroundAlternate"), QLatin1String("alternateBackground")); - kdeColor(&pal, QPalette::ButtonText, theKdeSettings, QLatin1String("Colors:Button/ForegroundNormal"), QLatin1String("buttonForeground")); - kdeColor(&pal, QPalette::Link, theKdeSettings, QLatin1String("Colors:View/ForegroundLink"), QLatin1String("linkColor")); - kdeColor(&pal, QPalette::LinkVisited, theKdeSettings, QLatin1String("Colors:View/ForegroundVisited"), QLatin1String("visitedLinkColor")); - //## TODO tooltip color - - return pal; -} - -/*!\internal - Returns the name of the QStyle to use. - (read from the kde config if needed) -*/ -QString QKde::kdeStyle() -{ - if (X11->desktopVersion >= 4) { - QSettings kdeSettings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat); - QString style = kdeSettings.value(QLatin1String("widgetStyle"), QLatin1String("Oxygen")).toString(); - - QStringList availableStyles = QStyleFactory::keys(); - if(availableStyles.contains(style, Qt::CaseInsensitive)) - return style; - } - - if (X11->use_xrender) - return QLatin1String("plastique"); - else - return QLatin1String("windows"); -} - - -int QKde::kdeToolButtonStyle() -{ - QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), - QSettings::IniFormat); - settings.beginGroup(QLatin1String("Toolbar style")); - QString toolbarStyle = settings.value(QLatin1String("ToolButtonStyle"), QLatin1String("TextBesideIcon")).toString(); - if (toolbarStyle == QLatin1String("TextBesideIcon")) - return Qt::ToolButtonTextBesideIcon; - else if (toolbarStyle == QLatin1String("TextOnly")) - return Qt::ToolButtonTextOnly; - else if (toolbarStyle == QLatin1String("TextUnderIcon")) - return Qt::ToolButtonTextUnderIcon; - - return Qt::ToolButtonTextBesideIcon; -} - -int QKde::kdeToolBarIconSize() -{ - static int iconSize = -1; - if (iconSize == -1) { - QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), - QSettings::IniFormat); - settings.beginGroup(QLatin1String("ToolbarIcons")); - iconSize = settings.value(QLatin1String("Size")).toInt(); - } - return iconSize; -} - -QT_END_NAMESPACE - -#endif //Q_WS_X11 - diff --git a/src/gui/kernel/qkde_p.h b/src/gui/kernel/qkde_p.h deleted file mode 100644 index 4e108f6e9e..0000000000 --- a/src/gui/kernel/qkde_p.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKDE_H -#define QKDE_H - -#include <QtCore/qglobal.h> -#include <QtGui/QPalette> -#include <QtGui/QIcon> - -// -// 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_WS_X11) - - -QT_BEGIN_NAMESPACE - -/*!\internal - This namespace contains helper function to help KDE integration - They are only used if we detect the use of KDE and the KDE platform plugin is not found (old KDE version) - Or if the detected KDE version is KDE3 -*/ -namespace QKde { - QString kdeHome(); - QString kdeStyle(); - QPalette kdePalette(); - int kdeToolButtonStyle(); - int kdeToolBarIconSize(); -} - - -QT_END_NAMESPACE - -#endif // Q_WS_X11 -#endif // QKDE_H diff --git a/src/gui/kernel/qmacdefines_mac.h b/src/gui/kernel/qmacdefines_mac.h deleted file mode 100644 index d6ccb93593..0000000000 --- a/src/gui/kernel/qmacdefines_mac.h +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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> - -#ifdef qDebug -# define old_qDebug qDebug -# undef qDebug -#endif - -#ifdef __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 - -#ifdef old_qDebug -# undef qDebug -# define qDebug QT_NO_QDEBUG_MACRO -# undef old_qDebug -#endif diff --git a/src/gui/kernel/qmacgesturerecognizer_mac.mm b/src/gui/kernel/qmacgesturerecognizer_mac.mm deleted file mode 100644 index 6a4f0bb445..0000000000 --- a/src/gui/kernel/qmacgesturerecognizer_mac.mm +++ /dev/null @@ -1,272 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmacgesturerecognizer_mac_p.h" -#include "qgesture.h" -#include "qgesture_p.h" -#include "qevent.h" -#include "qevent_p.h" -#include "qwidget.h" -#include "qdebug.h" - -#ifndef QT_NO_GESTURES - -QT_BEGIN_NAMESPACE - -QMacSwipeGestureRecognizer::QMacSwipeGestureRecognizer() -{ -} - -QGesture *QMacSwipeGestureRecognizer::create(QObject * /*target*/) -{ - return new QSwipeGesture; -} - -QGestureRecognizer::Result -QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) { - QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); - switch (ev->gestureType) { - case QNativeGestureEvent::Swipe: { - QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); - g->setSwipeAngle(ev->angle); - g->setHotSpot(ev->position); - return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; - break; } - default: - break; - } - } - - return QGestureRecognizer::Ignore; -} - -void QMacSwipeGestureRecognizer::reset(QGesture *gesture) -{ - QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); - g->setSwipeAngle(0); - QGestureRecognizer::reset(gesture); -} - -//////////////////////////////////////////////////////////////////////// - -QMacPinchGestureRecognizer::QMacPinchGestureRecognizer() -{ -} - -QGesture *QMacPinchGestureRecognizer::create(QObject * /*target*/) -{ - return new QPinchGesture; -} - -QGestureRecognizer::Result -QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) { - QPinchGesture *g = static_cast<QPinchGesture *>(gesture); - QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); - switch(ev->gestureType) { - case QNativeGestureEvent::GestureBegin: - reset(gesture); - g->setStartCenterPoint(static_cast<QWidget*>(obj)->mapFromGlobal(ev->position)); - g->setCenterPoint(g->startCenterPoint()); - g->setChangeFlags(QPinchGesture::CenterPointChanged); - g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); - g->setHotSpot(ev->position); - return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint; - case QNativeGestureEvent::Rotate: { - g->setLastScaleFactor(g->scaleFactor()); - g->setLastRotationAngle(g->rotationAngle()); - g->setRotationAngle(g->rotationAngle() + ev->percentage); - g->setChangeFlags(QPinchGesture::RotationAngleChanged); - g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); - g->setHotSpot(ev->position); - return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; - } - case QNativeGestureEvent::Zoom: - g->setLastScaleFactor(g->scaleFactor()); - g->setLastRotationAngle(g->rotationAngle()); - g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage)); - g->setChangeFlags(QPinchGesture::ScaleFactorChanged); - g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); - g->setHotSpot(ev->position); - return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; - case QNativeGestureEvent::GestureEnd: - return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; - default: - break; - } - } - - return QGestureRecognizer::Ignore; -} - -void QMacPinchGestureRecognizer::reset(QGesture *gesture) -{ - QPinchGesture *g = static_cast<QPinchGesture *>(gesture); - g->setChangeFlags(0); - g->setTotalChangeFlags(0); - g->setScaleFactor(1.0f); - g->setTotalScaleFactor(1.0f); - g->setLastScaleFactor(1.0f); - g->setRotationAngle(0.0f); - g->setTotalRotationAngle(0.0f); - g->setLastRotationAngle(0.0f); - g->setCenterPoint(QPointF()); - g->setStartCenterPoint(QPointF()); - g->setLastCenterPoint(QPointF()); - QGestureRecognizer::reset(gesture); -} - -//////////////////////////////////////////////////////////////////////// - -#if defined(QT_MAC_USE_COCOA) - -QMacPanGestureRecognizer::QMacPanGestureRecognizer() : _panCanceled(true) -{ -} - -QGesture *QMacPanGestureRecognizer::create(QObject *target) -{ - if (!target) - return new QPanGesture; - - if (QWidget *w = qobject_cast<QWidget *>(target)) { - w->setAttribute(Qt::WA_AcceptTouchEvents); - w->setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); - return new QPanGesture; - } - return 0; -} - -QGestureRecognizer::Result -QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent *event) -{ - const int panBeginDelay = 300; - const int panBeginRadius = 3; - - QPanGesture *g = static_cast<QPanGesture *>(gesture); - - switch (event->type()) { - case QEvent::TouchBegin: { - const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); - if (ev->touchPoints().size() == 1) { - reset(gesture); - _startPos = QCursor::pos(); - _panTimer.start(panBeginDelay, target); - _panCanceled = false; - return QGestureRecognizer::MayBeGesture; - } - break;} - case QEvent::TouchEnd: { - if (_panCanceled) - break; - - const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); - if (ev->touchPoints().size() == 1) - return QGestureRecognizer::FinishGesture; - break;} - case QEvent::TouchUpdate: { - if (_panCanceled) - break; - - const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); - if (ev->touchPoints().size() == 1) { - if (_panTimer.isActive()) { - // INVARIANT: Still in maybeGesture. Check if the user - // moved his finger so much that it makes sense to cancel the pan: - const QPointF p = QCursor::pos(); - if ((p - _startPos).manhattanLength() > panBeginRadius) { - _panCanceled = true; - _panTimer.stop(); - return QGestureRecognizer::CancelGesture; - } - } else { - const QPointF p = QCursor::pos(); - const QPointF posOffset = p - _startPos; - g->setLastOffset(g->offset()); - g->setOffset(QPointF(posOffset.x(), posOffset.y())); - g->setHotSpot(_startPos); - return QGestureRecognizer::TriggerGesture; - } - } else if (_panTimer.isActive()) { - // I only want to cancel the pan if the user is pressing - // more than one finger, and the pan hasn't started yet: - _panCanceled = true; - _panTimer.stop(); - return QGestureRecognizer::CancelGesture; - } - break;} - case QEvent::Timer: { - QTimerEvent *ev = static_cast<QTimerEvent *>(event); - if (ev->timerId() == _panTimer.timerId()) { - _panTimer.stop(); - if (_panCanceled) - break; - // Begin new pan session! - _startPos = QCursor::pos(); - g->setHotSpot(_startPos); - return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; - } - break; } - default: - break; - } - - return QGestureRecognizer::Ignore; -} - -void QMacPanGestureRecognizer::reset(QGesture *gesture) -{ - QPanGesture *g = static_cast<QPanGesture *>(gesture); - _startPos = QPointF(); - _panCanceled = true; - g->setOffset(QPointF(0, 0)); - g->setLastOffset(QPointF(0, 0)); - g->setAcceleration(qreal(1)); - QGestureRecognizer::reset(gesture); -} -#endif // QT_MAC_USE_COCOA - -QT_END_NAMESPACE - -#endif // QT_NO_GESTURES diff --git a/src/gui/kernel/qmacgesturerecognizer_mac_p.h b/src/gui/kernel/qmacgesturerecognizer_mac_p.h deleted file mode 100644 index 465f6a2ac8..0000000000 --- a/src/gui/kernel/qmacgesturerecognizer_mac_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMACSWIPEGESTURERECOGNIZER_MAC_P_H -#define QMACSWIPEGESTURERECOGNIZER_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 "qtimer.h" -#include "qpoint.h" -#include "qgesturerecognizer.h" - -#ifndef QT_NO_GESTURES - -QT_BEGIN_NAMESPACE - -class QMacSwipeGestureRecognizer : public QGestureRecognizer -{ -public: - QMacSwipeGestureRecognizer(); - - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); -}; - -class QMacPinchGestureRecognizer : public QGestureRecognizer -{ -public: - QMacPinchGestureRecognizer(); - - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); -}; - -#if defined(QT_MAC_USE_COCOA) - -class QMacPanGestureRecognizer : public QObject, public QGestureRecognizer -{ -public: - QMacPanGestureRecognizer(); - - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); - void reset(QGesture *gesture); -private: - QPointF _startPos; - QBasicTimer _panTimer; - bool _panCanceled; -}; - -#endif - -QT_END_NAMESPACE - -#endif // QT_NO_GESTURES - -#endif // QMACSWIPEGESTURERECOGNIZER_MAC_P_H diff --git a/src/gui/kernel/qmultitouch_mac.mm b/src/gui/kernel/qmultitouch_mac.mm deleted file mode 100644 index d9e845a01c..0000000000 --- a/src/gui/kernel/qmultitouch_mac.mm +++ /dev/null @@ -1,218 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qmultitouch_mac_p.h> -#include <qcursor.h> - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - -QT_BEGIN_NAMESPACE - -#ifdef QT_MAC_USE_COCOA - -QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches; -QPointF QCocoaTouch::_screenReferencePos; -QPointF QCocoaTouch::_trackpadReferencePos; -int QCocoaTouch::_idAssignmentCount = 0; -int QCocoaTouch::_touchCount = 0; -bool QCocoaTouch::_updateInternalStateOnly = true; - -QCocoaTouch::QCocoaTouch(NSTouch *nstouch) -{ - if (_currentTouches.size() == 0) - _idAssignmentCount = 0; - - _touchPoint.setId(_idAssignmentCount++); - _touchPoint.setPressure(1.0); - _identity = qint64([nstouch identity]); - _currentTouches.insert(_identity, this); - updateTouchData(nstouch, NSTouchPhaseBegan); -} - -QCocoaTouch::~QCocoaTouch() -{ - _currentTouches.remove(_identity); -} - -void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) -{ - if (_touchCount == 1) - _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary); - else - _touchPoint.setState(toTouchPointState(phase)); - - // From the normalized position on the trackpad, calculate - // where on screen the touchpoint should be according to the - // reference position: - NSPoint npos = [nstouch normalizedPosition]; - QPointF qnpos = QPointF(npos.x, 1 - npos.y); - _touchPoint.setNormalizedPos(qnpos); - - if (_touchPoint.id() == 0 && phase == NSTouchPhaseBegan) { - _trackpadReferencePos = qnpos; - _screenReferencePos = QCursor::pos(); - } - - NSSize dsize = [nstouch deviceSize]; - float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width; - float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height; - QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY); - _touchPoint.setScreenPos(_screenReferencePos - relativePos); -} - -QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch) -{ - qint64 identity = qint64([nstouch identity]); - if (_currentTouches.contains(identity)) - return _currentTouches.value(identity); - return 0; -} - -Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState) -{ - Qt::TouchPointState qtState = Qt::TouchPointReleased; - switch (nsState) { - case NSTouchPhaseBegan: - qtState = Qt::TouchPointPressed; - break; - case NSTouchPhaseMoved: - qtState = Qt::TouchPointMoved; - break; - case NSTouchPhaseStationary: - qtState = Qt::TouchPointStationary; - break; - case NSTouchPhaseEnded: - case NSTouchPhaseCancelled: - qtState = Qt::TouchPointReleased; - break; - default: - break; - } - return qtState; -} - -QList<QTouchEvent::TouchPoint> -QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) -{ - QMap<int, QTouchEvent::TouchPoint> touchPoints; - NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil]; - NSSet *active = [event - touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary - inView:nil]; - _touchCount = [active count]; - - // First: remove touches that were ended by the user. If we are - // currently not accepting single touches, a corresponding 'begin' - // has never been send to the app for these events. - // So should therefore not send the following removes either. - - for (int i=0; i<int([ended count]); ++i) { - NSTouch *touch = [[ended allObjects] objectAtIndex:i]; - QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch); - if (qcocoaTouch) { - qcocoaTouch->updateTouchData(touch, [touch phase]); - if (!_updateInternalStateOnly) - touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); - delete qcocoaTouch; - } - } - - bool wasUpdateInternalStateOnly = _updateInternalStateOnly; - _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2; - - // Next: update, or create, existing touches. - // We always keep track of all touch points, even - // when not accepting single touches. - - for (int i=0; i<int([active count]); ++i) { - NSTouch *touch = [[active allObjects] objectAtIndex:i]; - QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch); - if (!qcocoaTouch) - qcocoaTouch = new QCocoaTouch(touch); - else - qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]); - if (!_updateInternalStateOnly) - touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); - } - - // Next: sadly, we need to check that our touch hash is in - // sync with cocoa. This is typically not the case after a system - // gesture happend (like a four-finger-swipe to show expose). - - if (_touchCount != _currentTouches.size()) { - // Remove all instances, and basically start from scratch: - touchPoints.clear(); - foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) { - if (!_updateInternalStateOnly) { - qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); - touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); - } - delete qcocoaTouch; - } - _currentTouches.clear(); - _updateInternalStateOnly = !acceptSingleTouch; - return touchPoints.values(); - } - - // Finally: If this call _started_ to reject single - // touches, we need to fake a relase for the remaining - // touch now (and refake a begin for it later, if needed). - - if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) { - QCocoaTouch *qcocoaTouch = _currentTouches.values().first(); - qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); - touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); - // Since this last touch also will end up beeing the first - // touch (if the user adds a second finger without lifting - // the first), we promote it to be the primary touch: - qcocoaTouch->_touchPoint.setId(0); - _idAssignmentCount = 1; - } - - return touchPoints.values(); -} - -#endif - -QT_END_NAMESPACE - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - diff --git a/src/gui/kernel/qmultitouch_mac_p.h b/src/gui/kernel/qmultitouch_mac_p.h deleted file mode 100644 index 16be930d0a..0000000000 --- a/src/gui/kernel/qmultitouch_mac_p.h +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 QMULTITOUCH_MAC_P_H -#define QMULTITOUCH_MAC_P_H - -#ifdef QT_MAC_USE_COCOA -#import <Cocoa/Cocoa.h> -#endif - -#include <qevent.h> -#include <qhash.h> -#include <QtCore> - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - -QT_BEGIN_NAMESPACE - -#ifdef QT_MAC_USE_COCOA - -class QCocoaTouch -{ - public: - static QList<QTouchEvent::TouchPoint> getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch); - static void setMouseInDraggingState(bool inDraggingState); - - private: - static QHash<qint64, QCocoaTouch*> _currentTouches; - static QPointF _screenReferencePos; - static QPointF _trackpadReferencePos; - static int _idAssignmentCount; - static int _touchCount; - static bool _updateInternalStateOnly; - - QTouchEvent::TouchPoint _touchPoint; - qint64 _identity; - - QCocoaTouch(NSTouch *nstouch); - ~QCocoaTouch(); - - void updateTouchData(NSTouch *nstouch, NSTouchPhase phase); - static QCocoaTouch *findQCocoaTouch(NSTouch *nstouch); - static Qt::TouchPointState toTouchPointState(NSTouchPhase nsState); -}; - -#endif // QT_MAC_USE_COCOA - -QT_END_NAMESPACE - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - -#endif // QMULTITOUCH_MAC_P_H - diff --git a/src/gui/kernel/qnsframeview_mac_p.h b/src/gui/kernel/qnsframeview_mac_p.h deleted file mode 100644 index 6ec3f64efa..0000000000 --- a/src/gui/kernel/qnsframeview_mac_p.h +++ /dev/null @@ -1,154 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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 deleted file mode 100644 index 2cb4916c06..0000000000 --- a/src/gui/kernel/qnsthemeframe_mac_p.h +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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; // collides with hittest in qcocoasharedwindowmethods_mac_p.h -- (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 deleted file mode 100644 index 4eb5332194..0000000000 --- a/src/gui/kernel/qnstitledframe_mac_p.h +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// 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/qsoftkeymanager_s60.cpp b/src/gui/kernel/qsoftkeymanager_s60.cpp deleted file mode 100644 index 79ed91af5b..0000000000 --- a/src/gui/kernel/qsoftkeymanager_s60.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qapplication.h" -#include "qevent.h" -#include "qbitmap.h" -#include "qstyle.h" -#include "qmenubar.h" -#include "private/qt_s60_p.h" -#include "private/qmenu_p.h" -#include "private/qaction_p.h" -#include "private/qsoftkeymanager_p.h" -#include "private/qsoftkeymanager_s60_p.h" -#include "private/qobject_p.h" -#include <eiksoftkeyimage.h> -#include <eikcmbut.h> - -#ifndef QT_NO_SOFTKEYMANAGER -QT_BEGIN_NAMESPACE - -const int S60_COMMAND_START = 6000; -const int LSK_POSITION = 0; -const int MSK_POSITION = 3; -const int RSK_POSITION = 2; - -QSoftKeyManagerPrivateS60::QSoftKeyManagerPrivateS60() : cbaHasImage(4) // 4 since MSK position index is 3 -{ - cachedCbaIconSize[0] = QSize(0,0); - cachedCbaIconSize[1] = QSize(0,0); - cachedCbaIconSize[2] = QSize(0,0); - cachedCbaIconSize[3] = QSize(0,0); -} - -bool QSoftKeyManagerPrivateS60::skipCbaUpdate() -{ - // Lets not update softkeys if - // 1. We don't have application panes, i.e. cba - // 2. Our CBA is not active, i.e. S60 native dialog or menu with custom CBA is shown - // 2.1. Except if thre is no current CBA at all and WindowSoftkeysRespondHint is set - - // Note: Cannot use IsDisplayingMenuOrDialog since CBA update can be triggered before - // menu/dialog CBA is actually displayed i.e. it is being costructed. - CEikButtonGroupContainer *appUiCba = S60->buttonGroupContainer(); - if (!appUiCba) - return true; - // CEikButtonGroupContainer::Current returns 0 if CBA is not visible at all - CEikButtonGroupContainer *currentCba = CEikButtonGroupContainer::Current(); - // Check if softkey need to be update even they are not visible - bool cbaRespondsWhenInvisible = false; - QWidget *window = QApplication::activeWindow(); - if (window && (window->windowFlags() & Qt::WindowSoftkeysRespondHint)) - cbaRespondsWhenInvisible = true; - - if (QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes) - || (appUiCba != currentCba && !cbaRespondsWhenInvisible)) { - return true; - } - return false; -} - -void QSoftKeyManagerPrivateS60::ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba) -{ - RDrawableWindow *cbaWindow = cba.DrawableWindow(); - Q_ASSERT_X(cbaWindow, Q_FUNC_INFO, "Native CBA does not have window!"); - // Make sure CBA is visible, i.e. CBA window is on top - cbaWindow->SetOrdinalPosition(0); - // Qt shares same CBA instance between top-level widgets, - // make sure we are not faded by underlying window. - cbaWindow->SetFaded(EFalse, RWindowTreeNode::EFadeIncludeChildren); - // Modal dialogs capture pointer events, but shared cba instance - // shall stay responsive. Raise pointer capture priority to keep - // softkeys responsive in modal dialogs - cbaWindow->SetPointerCapturePriority(1); -} - -void QSoftKeyManagerPrivateS60::clearSoftkeys(CEikButtonGroupContainer &cba) -{ -#ifdef SYMBIAN_VERSION_SYMBIAN3 - QT_TRAP_THROWING( - //EAknSoftkeyEmpty is used, because using -1 adds softkeys without actions on Symbian3 - cba.SetCommandL(0, EAknSoftkeyEmpty, KNullDesC); - cba.SetCommandL(2, EAknSoftkeyEmpty, KNullDesC); - ); -#else - QT_TRAP_THROWING( - //Using -1 instead of EAknSoftkeyEmpty to avoid flickering. - cba.SetCommandL(0, -1, KNullDesC); - // TODO: Should we clear also middle SK? - cba.SetCommandL(2, -1, KNullDesC); - ); -#endif - realSoftKeyActions.clear(); -} - -QString QSoftKeyManagerPrivateS60::softkeyText(QAction &softkeyAction) -{ - // In S60 softkeys and menu items do not support key accelerators (i.e. - // CTRL+X). Therefore, removing the accelerator characters from both softkey - // and menu item texts. - const int underlineShortCut = QApplication::style()->styleHint(QStyle::SH_UnderlineShortcut); - QString iconText = softkeyAction.iconText(); - return underlineShortCut ? softkeyAction.text() : iconText; -} - -QAction *QSoftKeyManagerPrivateS60::highestPrioritySoftkey(QAction::SoftKeyRole role) -{ - QAction *ret = NULL; - // Priority look up is two level - // 1. First widget with softkeys always has highest priority - for (int level = 0; !ret; level++) { - // 2. Highest priority action within widget - QList<QAction*> actions = requestedSoftKeyActions.values(level); - if (actions.isEmpty()) - break; - qSort(actions.begin(), actions.end(), QSoftKeyManagerPrivateS60::actionPriorityMoreThan); - foreach (QAction *action, actions) { - if (action->softKeyRole() == role) { - ret = action; - break; - } - } - } - return ret; -} - -bool QSoftKeyManagerPrivateS60::actionPriorityMoreThan(const QAction *firstItem, const QAction *secondItem) -{ - return firstItem->priority() > secondItem->priority(); -} - -void QSoftKeyManagerPrivateS60::setNativeSoftkey(CEikButtonGroupContainer &cba, - TInt position, TInt command, const TDesC &text) -{ - // Calling SetCommandL causes CBA redraw - QT_TRAP_THROWING(cba.SetCommandL(position, command, text)); -} - -QPoint QSoftKeyManagerPrivateS60::softkeyIconPosition(int position, QSize sourceSize, QSize targetSize) -{ - QPoint iconPosition(0,0); - switch( AknLayoutUtils::CbaLocation() ) - { - case AknLayoutUtils::EAknCbaLocationBottom: - // RSK must be moved to right, LSK in on correct position by default - if (position == RSK_POSITION) - iconPosition.setX(targetSize.width() - sourceSize.width()); - break; - case AknLayoutUtils::EAknCbaLocationRight: - case AknLayoutUtils::EAknCbaLocationLeft: - // Already in correct position - default: - break; - } - - // Align horizontally to center - iconPosition.setY((targetSize.height() - sourceSize.height()) >> 1); - return iconPosition; -} - -QPixmap QSoftKeyManagerPrivateS60::prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize) -{ - QPixmap target(targetSize); - target.fill(Qt::transparent); - QPainter p; - p.begin(&target); - p.drawPixmap(softkeyIconPosition(position, src.size(), targetSize), src); - p.end(); - return target; -} - -bool QSoftKeyManagerPrivateS60::isOrientationLandscape() -{ - // Hard to believe that there is no public API in S60 to - // get current orientation. This workaround works with currently supported resolutions - return S60->screenHeightInPixels < S60->screenWidthInPixels; -} - -QSize QSoftKeyManagerPrivateS60::cbaIconSize(CEikButtonGroupContainer *cba, int position) -{ - - int index = position; - index += isOrientationLandscape() ? 0 : 1; - if(cachedCbaIconSize[index].isNull()) { - // Only way I figured out to get CBA icon size without RnD SDK, was - // to set some dummy icon to CBA first and then ask CBA button CCoeControl::Size() - // The returned value is cached to avoid unnecessary icon setting every time. - const bool left = (position == LSK_POSITION); - if(position == LSK_POSITION || position == RSK_POSITION) { - CEikImage* tmpImage = NULL; - QT_TRAP_THROWING(tmpImage = new (ELeave) CEikImage); - EikSoftkeyImage::SetImage(cba, *tmpImage, left); // Takes myimage ownership - int command = S60_COMMAND_START + position; - setNativeSoftkey(*cba, position, command, KNullDesC()); - cachedCbaIconSize[index] = qt_TSize2QSize(cba->ControlOrNull(command)->Size()); - EikSoftkeyImage::SetLabel(cba, left); - - if(cachedCbaIconSize[index] == QSize(138,72)) { - // Hack for S60 5.0 (5800) landscape orientation, which return wrong icon size - cachedCbaIconSize[index] = QSize(60,60); - } - } - } - - return cachedCbaIconSize[index]; -} - -bool QSoftKeyManagerPrivateS60::setSoftkeyImage(CEikButtonGroupContainer *cba, - QAction &action, int position) -{ - bool ret = false; - - const bool left = (position == LSK_POSITION); - if(position == LSK_POSITION || position == RSK_POSITION) { - QIcon icon = action.icon(); - if (!icon.isNull()) { - // Get size of CBA icon area based on button position and orientation - QSize requiredIconSize = cbaIconSize(cba, position); - // Get pixmap out of icon based on preferred size, the aspect ratio is kept - QPixmap pmWihtAspectRatio = icon.pixmap(requiredIconSize); - // Native softkeys require that pixmap size is exactly the same as requiredIconSize - // prepareSoftkeyPixmap creates a new pixmap with requiredIconSize and blits the 'pmWihtAspectRatio' - // to correct location of it - QPixmap softkeyPixmap = prepareSoftkeyPixmap(pmWihtAspectRatio, position, requiredIconSize); - - QPixmap softkeyAlpha = softkeyPixmap.alphaChannel(); - // Alpha channel in 5.1 and older devices need to be inverted - // TODO: Switch to use toSymbianCFbsBitmap with invert when available - if(QSysInfo::s60Version() <= QSysInfo::SV_S60_5_1) { - QImage alphaImage = softkeyAlpha.toImage(); - alphaImage.invertPixels(); - softkeyAlpha = QPixmap::fromImage(alphaImage); - } - - CFbsBitmap* nBitmap = softkeyPixmap.toSymbianCFbsBitmap(); - CFbsBitmap* nMask = softkeyAlpha.toSymbianCFbsBitmap(); - - CEikImage* myimage = new (ELeave) CEikImage; - myimage->SetPicture( nBitmap, nMask ); // nBitmap and nMask ownership transferred - - EikSoftkeyImage::SetImage(cba, *myimage, left); // Takes myimage ownership - cbaHasImage[position] = true; - ret = true; - } else { - // Restore softkey to text based - if (cbaHasImage[position]) { - EikSoftkeyImage::SetLabel(cba, left); - cbaHasImage[position] = false; - } - } - } - return ret; -} - -bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba, - QAction::SoftKeyRole role, int position) -{ - QAction *action = highestPrioritySoftkey(role); - if (action) { - setSoftkeyImage(&cba, *action, position); - QString text = softkeyText(*action); - TPtrC nativeText = qt_QString2TPtrC(text); - int command = S60_COMMAND_START + position; -#ifdef SYMBIAN_VERSION_SYMBIAN3 - if (softKeyCommandActions.contains(action)) - command = softKeyCommandActions.value(action); -#endif - setNativeSoftkey(cba, position, command, nativeText); - const bool dimmed = !action->isEnabled() && !QSoftKeyManager::isForceEnabledInSofkeys(action); - cba.DimCommand(command, dimmed); - realSoftKeyActions.insert(command, action); - return true; - } - return false; -} - -bool QSoftKeyManagerPrivateS60::setLeftSoftkey(CEikButtonGroupContainer &cba) -{ - return setSoftkey(cba, QAction::PositiveSoftKey, LSK_POSITION); -} - -bool QSoftKeyManagerPrivateS60::setMiddleSoftkey(CEikButtonGroupContainer &cba) -{ - // Note: In order to get MSK working, application has to have EAknEnableMSK flag set - // Currently it is not possible very easily) - // For more information see: http://wiki.forum.nokia.com/index.php/Middle_softkey_usage - return setSoftkey(cba, QAction::SelectSoftKey, MSK_POSITION); -} - -bool QSoftKeyManagerPrivateS60::setRightSoftkey(CEikButtonGroupContainer &cba) -{ - if (!setSoftkey(cba, QAction::NegativeSoftKey, RSK_POSITION)) { - const Qt::WindowType windowType = initialSoftKeySource - ? initialSoftKeySource->window()->windowType() : Qt::Window; - if (windowType != Qt::Dialog && windowType != Qt::Popup) { - QString text(QSoftKeyManager::tr("Exit")); - TPtrC nativeText = qt_QString2TPtrC(text); - if (cbaHasImage[RSK_POSITION]) { - EikSoftkeyImage::SetLabel(&cba, false); - cbaHasImage[RSK_POSITION] = false; - } - setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, nativeText); - cba.DimCommand(EAknSoftkeyExit, false); - return true; - } - } - return false; -} - -void QSoftKeyManagerPrivateS60::setSoftkeys(CEikButtonGroupContainer &cba) -{ - int requestedSoftkeyCount = requestedSoftKeyActions.count(); - const int maxSoftkeyCount = 2; // TODO: differs based on orientation ans S60 versions (some have MSK) - if (requestedSoftkeyCount > maxSoftkeyCount) { - // We have more softkeys than available slots - // Put highest priority negative action to RSK and Options menu with rest of softkey actions to LSK - // TODO: Build menu - setLeftSoftkey(cba); - if(AknLayoutUtils::MSKEnabled()) - setMiddleSoftkey(cba); - setRightSoftkey(cba); - } else { - // We have less softkeys than available slots - // Put softkeys to request slots based on role - setLeftSoftkey(cba); - if(AknLayoutUtils::MSKEnabled()) - setMiddleSoftkey(cba); - setRightSoftkey(cba); - } -} - -void QSoftKeyManagerPrivateS60::updateSoftKeys_sys() -{ - if (skipCbaUpdate()) - return; - - CEikButtonGroupContainer *nativeContainer = S60->buttonGroupContainer(); - Q_ASSERT_X(nativeContainer, Q_FUNC_INFO, "Native CBA does not exist!"); - ensureCbaVisibilityAndResponsiviness(*nativeContainer); - clearSoftkeys(*nativeContainer); - setSoftkeys(*nativeContainer); - - nativeContainer->DrawDeferred(); // 3.1 needs an extra invitation -} - -static void resetMenuBeingConstructed(TAny* /*aAny*/) -{ - S60->menuBeingConstructed = false; -} - -void QSoftKeyManagerPrivateS60::tryDisplayMenuBarL() -{ - CleanupStack::PushL(TCleanupItem(resetMenuBeingConstructed, NULL)); - S60->menuBeingConstructed = true; - S60->menuBar()->TryDisplayMenuBarL(); - CleanupStack::PopAndDestroy(); // Reset menuBeingConstructed to false in all cases -} - -bool QSoftKeyManagerPrivateS60::handleCommand(int command) -{ - QAction *action = realSoftKeyActions.value(command); - if (action) { - bool property = QActionPrivate::get(action)->menuActionSoftkeys; - if (property) { - QT_TRAP_THROWING(tryDisplayMenuBarL()); - } else if (action->menu()) { - // TODO: This is hack, in order to use exising QMenuBar implementation for Symbian - // menubar needs to have widget to which it is associated. Since we want to associate - // menubar to action (which is inherited from QObject), we create and associate QWidget - // to action and pass that for QMenuBar. This associates the menubar to action, and we - // can have own menubar for each action. - QWidget *actionContainer = action->property("_q_action_widget").value<QWidget*>(); - if(!actionContainer) { - actionContainer = new QWidget(action->parentWidget()); - QMenuBar *menuBar = new QMenuBar(actionContainer); - foreach(QAction *menuAction, action->menu()->actions()) { - QMenu *menu = menuAction->menu(); - if(menu) - menuBar->addMenu(menu); - else - menuBar->addAction(menuAction); - } - QVariant v; - v.setValue(actionContainer); - action->setProperty("_q_action_widget", v); - } - qt_symbian_next_menu_from_action(actionContainer); - QT_TRAP_THROWING(tryDisplayMenuBarL()); - } - - Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey); - QWidget *actionParent = action->parentWidget(); - Q_ASSERT_X(actionParent, Q_FUNC_INFO, "No parent set for softkey action!"); - if (actionParent->isEnabled()) { - action->activate(QAction::Trigger); - return true; - } - } - return false; -} - -QT_END_NAMESPACE -#endif //QT_NO_SOFTKEYMANAGER diff --git a/src/gui/kernel/qsoftkeymanager_s60_p.h b/src/gui/kernel/qsoftkeymanager_s60_p.h deleted file mode 100644 index 9cb3787cb8..0000000000 --- a/src/gui/kernel/qsoftkeymanager_s60_p.h +++ /dev/null @@ -1,113 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QSOFTKEYMANAGER_S60_P_H -#define QSOFTKEYMANAGER_S60_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 "qbitarray.h" -#include "private/qobject_p.h" -#include "private/qsoftkeymanager_common_p.h" - -QT_BEGIN_HEADER - -#ifndef QT_NO_SOFTKEYMANAGER - -QT_BEGIN_NAMESPACE - -class CEikButtonGroupContainer; -class QAction; - -class QSoftKeyManagerPrivateS60 : public QSoftKeyManagerPrivate -{ - Q_DECLARE_PUBLIC(QSoftKeyManager) - -public: - QSoftKeyManagerPrivateS60(); - -public: - void updateSoftKeys_sys(); - bool handleCommand(int command); - -private: - void tryDisplayMenuBarL(); - bool skipCbaUpdate(); - void ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba); - void clearSoftkeys(CEikButtonGroupContainer &cba); - QString softkeyText(QAction &softkeyAction); - QAction *highestPrioritySoftkey(QAction::SoftKeyRole role); - static bool actionPriorityMoreThan(const QAction* item1, const QAction* item2); - void setNativeSoftkey(CEikButtonGroupContainer &cba, TInt position, TInt command, const TDesC& text); - QPoint softkeyIconPosition(int position, QSize sourceSize, QSize targetSize); - QPixmap prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize); - bool isOrientationLandscape(); - QSize cbaIconSize(CEikButtonGroupContainer *cba, int position); - bool setSoftkeyImage(CEikButtonGroupContainer *cba, QAction &action, int position); - bool setSoftkey(CEikButtonGroupContainer &cba, QAction::SoftKeyRole role, int position); - bool setLeftSoftkey(CEikButtonGroupContainer &cba); - bool setMiddleSoftkey(CEikButtonGroupContainer &cba); - bool setRightSoftkey(CEikButtonGroupContainer &cba); - void setSoftkeys(CEikButtonGroupContainer &cba); - -private: - QHash<int, QAction*> realSoftKeyActions; - QSize cachedCbaIconSize[4]; - QBitArray cbaHasImage; -}; - - -QT_END_NAMESPACE - -#endif //QT_NO_SOFTKEYMANAGER - -QT_END_HEADER - -#endif // QSOFTKEYMANAGER_S60_P_H diff --git a/src/gui/kernel/qsound_mac.mm b/src/gui/kernel/qsound_mac.mm deleted file mode 100644 index 5a9af135b0..0000000000 --- a/src/gui/kernel/qsound_mac.mm +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <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 - -#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 -@protocol NSSoundDelegate <NSObject> --(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)aBool; -@end -#endif - -QT_USE_NAMESPACE - -@interface QT_MANGLE_NAMESPACE(QMacSoundDelegate) : NSObject<NSSoundDelegate> { - QSound *qSound; // may be null. - QAuServerMac* server; -} --(id)initWithQSound:(QSound*)sound:(QAuServerMac*)server; -@end - -@implementation QT_MANGLE_NAMESPACE(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]; - QT_MANGLE_NAMESPACE(QMacSoundDelegate) * const delegate = [[QT_MANGLE_NAMESPACE(QMacSoundDelegate) alloc] initWithQSound:qSound:this]; - [nsSound setDelegate:delegate]; - [nsFileName release]; - 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_s60.cpp b/src/gui/kernel/qsound_s60.cpp deleted file mode 100644 index acc5c2a56f..0000000000 --- a/src/gui/kernel/qsound_s60.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - - -#ifndef QT_NO_SOUND - -#include "qdir.h" -#include "qapplication.h" -#include "qsound.h" -#include "qsound_p.h" -#include "qfileinfo.h" -#include <private/qcore_symbian_p.h> - -#include <e32std.h> -#include <mdaaudiosampleplayer.h> - -QT_BEGIN_NAMESPACE - -class QAuServerS60; - -class QAuBucketS60 : public QAuBucket, public MMdaAudioPlayerCallback -{ -public: - QAuBucketS60(QAuServerS60 *server, QSound *sound); - ~QAuBucketS60(); - - void play(); - void stop(); - - inline QSound *sound() const { return m_sound; } - -public: // from MMdaAudioPlayerCallback - void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration); - void MapcPlayComplete(TInt aError); - -private: - QSound *m_sound; - QAuServerS60 *m_server; - bool m_prepared; - bool m_playCalled; - CMdaAudioPlayerUtility *m_playUtility; -}; - - -class QAuServerS60 : public QAuServer -{ -public: - QAuServerS60(QObject *parent); - - void init(QSound *s) - { - QAuBucketS60 *bucket = new QAuBucketS60(this, s); - setBucket(s, bucket); - } - - void play(QSound *s) - { - bucket(s)->play(); - } - - void stop(QSound *s) - { - bucket(s)->stop(); - } - - bool okay() { return true; } - - void play(const QString& filename); - -protected: - void playCompleted(QAuBucketS60 *bucket, int error); - -protected: - QAuBucketS60 *bucket(QSound *s) - { - return (QAuBucketS60 *)QAuServer::bucket( s ); - } - - friend class QAuBucketS60; - - // static QSound::play(filename) cannot be stopped, meaning that playCompleted - // will get always called and QSound gets removed form this list. - QList<QSound *> staticPlayingSounds; -}; - -QAuServerS60::QAuServerS60(QObject *parent) : - QAuServer(parent) -{ - setObjectName(QLatin1String("QAuServerS60")); -} - -void QAuServerS60::play(const QString& filename) -{ - QSound *s = new QSound(filename); - staticPlayingSounds.append(s); - play(s); -} - -void QAuServerS60::playCompleted(QAuBucketS60 *bucket, int error) -{ - QSound *sound = bucket->sound(); - if (!error) { - // We need to handle repeats by ourselves, since with Symbian API we don't - // know how many loops have been played when user asks it - if (decLoop(sound)) { - play(sound); - } else { - if (staticPlayingSounds.removeAll(sound)) - delete sound; - } - } else { - // We don't have a way to inform about errors -> just decrement loops - // in order that QSound::isFinished will return true; - while (decLoop(sound) > 0) {} - if (staticPlayingSounds.removeAll(sound)) - delete sound; - } -} - -QAuServer *qt_new_audio_server() -{ - return new QAuServerS60(qApp); -} - -QAuBucketS60::QAuBucketS60(QAuServerS60 *server, QSound *sound) - : m_sound(sound), m_server(server), m_prepared(false), m_playCalled(false) -{ - QString filepath = QFileInfo(m_sound->fileName()).absoluteFilePath(); - filepath = QDir::toNativeSeparators(filepath); - TPtrC filepathPtr(qt_QString2TPtrC(filepath)); - TRAPD(err, m_playUtility = CMdaAudioPlayerUtility::NewL(*this); - m_playUtility->OpenFileL(filepathPtr)); - if (err) { - m_server->playCompleted(this, err); - } -} - -void QAuBucketS60::play() -{ - if (m_prepared) { - // OpenFileL call is completed we can start playing immediately - m_playUtility->Play(); - } else { - m_playCalled = true; - } - -} - -void QAuBucketS60::stop() -{ - m_playCalled = false; - m_playUtility->Stop(); -} - -void QAuBucketS60::MapcPlayComplete(TInt aError) -{ - m_server->playCompleted(this, aError); -} - -void QAuBucketS60::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& /*aDuration*/) -{ - if (aError) { - m_server->playCompleted(this, aError); - } else { - m_prepared = true; - if (m_playCalled){ - play(); - } - } -} - -QAuBucketS60::~QAuBucketS60() -{ - if (m_playUtility){ - m_playUtility->Stop(); - m_playUtility->Close(); - } - - delete m_playUtility; -} - - -#endif // QT_NO_SOUND - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qsound_win.cpp b/src/gui/kernel/qsound_win.cpp deleted file mode 100644 index c11482d608..0000000000 --- a/src/gui/kernel/qsound_win.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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) -{ - mutex = CreateMutex(0, 0, 0); - event = CreateEvent(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; - - PlaySound((wchar_t*)filename.utf16(), 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) { - PlaySound((wchar_t*)filename.utf16(), 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 deleted file mode 100644 index 12c06f0aa1..0000000000 --- a/src/gui/kernel/qsound_x11.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm deleted file mode 100644 index 32123ee682..0000000000 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ /dev/null @@ -1,1824 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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 <qaction.h> -#include <qwidget.h> -#include <qdesktopwidget.h> -#include <qevent.h> -#include <qpixmapcache.h> -#include <qvarlengtharray.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/qcocoaapplication_mac_p.h> -#include <private/qcocoawindow_mac_p.h> -#include <private/qcocoaview_mac_p.h> -#include <private/qkeymapper_p.h> -#include <private/qwidget_p.h> -#include <private/qcocoawindow_mac_p.h> - -QT_BEGIN_NAMESPACE - -#ifdef QT_MAC_USE_COCOA -// Cmd + left mousebutton should produce a right button -// press (mainly for mac users with one-button mice): -static bool qt_leftButtonIsRightButton = false; -#endif - -Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader); - -QMacWindowFader::QMacWindowFader() - : m_duration(0.250) -{ -} - -QMacWindowFader *QMacWindowFader::currentFader() -{ - return macwindowFader(); -} - -void QMacWindowFader::registerWindowToFade(QWidget *window) -{ - m_windowsToFade.append(window); -} - -void QMacWindowFader::performFade() -{ - const QWidgetList myWidgetsToFade = m_windowsToFade; - const int widgetCount = myWidgetsToFade.count(); -#if QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - [NSAnimationContext beginGrouping]; - [[NSAnimationContext currentContext] setDuration:NSTimeInterval(m_duration)]; -#endif - - for (int i = 0; i < widgetCount; ++i) { - QWidget *widget = m_windowsToFade.at(i); - OSWindowRef window = qt_mac_window_for(widget); -#if QT_MAC_USE_COCOA - [[window animator] setAlphaValue:0.0]; - QTimer::singleShot(qRound(m_duration * 1000), widget, SLOT(hide())); -#else - TransitionWindowOptions options = {0, m_duration, 0, 0}; - TransitionWindowWithOptions(window, kWindowFadeTransitionEffect, kWindowHideTransitionAction, - 0, 1, &options); -#endif - } -#if QT_MAC_USE_COCOA - [NSAnimationContext endGrouping]; -#endif - m_duration = 0.250; - m_windowsToFade.clear(); -} - -extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp; -extern QWidget * mac_mouse_grabber; -extern QWidget *qt_button_down; //qapplication_mac.cpp -extern QPointer<QWidget> qt_last_mouse_receiver; -extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); -extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm - -void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) -{ -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; -#endif - OSWindowRef wnd = static_cast<OSWindowRef>(window); - if (wnd) { - QWidget *widget; -#if QT_MAC_USE_COCOA - widget = [wnd QT_MANGLE_NAMESPACE(qt_qwidget)]; -#else - const UInt32 kWidgetCreatorQt = kEventClassQt; - enum { - kWidgetPropertyQWidget = 'QWId' //QWidget * - }; - if (GetWindowProperty(static_cast<WindowRef>(window), kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(widget), 0, &widget) != noErr) - widget = 0; -#endif - if (widget) { - QMacWindowFader::currentFader()->setFadeDuration(durationSeconds); - QMacWindowFader::currentFader()->registerWindowToFade(widget); - QMacWindowFader::currentFader()->performFade(); - } - } -} -struct dndenum_mapper -{ - NSDragOperation mac_code; - Qt::DropAction qt_code; - bool Qt2Mac; -}; - -#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__) - -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 } -}; - -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; -} - -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; -} - -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; -} - -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; -} - -Q_GLOBAL_STATIC(DnDParams, currentDnDParameters); -DnDParams *macCurrentDnDParameters() -{ - return currentDnDParameters(); -} -#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, ¤tAttributes); - 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 - if (NSToolbar *toolbar = [wnd toolbar]) { - QMacCocoaAutoReleasePool pool; - if (show != [toolbar isVisible]) { - [toolbar setVisible:show]; - } else { - // The toolbar may be in sync, but we are not, update our framestrut. - qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); - } - } -#else - qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); - 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 macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ) -{ - OSWindowRef wnd = static_cast<OSWindowRef>(window); -#if QT_MAC_USE_COCOA - if (NSToolbar *toolbar = [wnd 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->effectiveWinId()) 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; -} - -void macSendToolbarChangeEvent(QWidget *widget) -{ - QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey)); - qt_sendSpontaneousEvent(widget, &ev); -} - -Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash) -QMacTabletHash *qt_mac_tablet_hash() -{ - return tablet_hash(); -} - -#ifdef QT_MAC_USE_COCOA - -// Clears the QWidget pointer that each QCocoaView holds. -void qt_mac_clearCocoaViewQWidgetPointers(QWidget *widget) -{ - QT_MANGLE_NAMESPACE(QCocoaView) *cocoaView = reinterpret_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget)); - if (cocoaView && [cocoaView respondsToSelector:@selector(qt_qwidget)]) { - [cocoaView qt_clearQWidget]; - } -} - -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); -} - -// 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; -} - -bool operator<(const Qt::Key &key, const KeyPair &entry) -{ - return key < entry.qtKey; -} - -bool operator<(const KeyPair &entry, const Qt::Key &key) -{ - return entry.qtKey < key; -} - -static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2) -{ - return entry1.qtKey < entry2.qtKey; -} - -static const int NumEntries = 59; -static const KeyPair entries[NumEntries] = { - { NSEnterCharacter, Qt::Key_Enter }, - { NSBackspaceCharacter, Qt::Key_Backspace }, - { NSTabCharacter, Qt::Key_Tab }, - { NSNewlineCharacter, Qt::Key_Return }, - { NSCarriageReturnCharacter, Qt::Key_Return }, - { NSBackTabCharacter, Qt::Key_Backtab }, - { kEscapeCharCode, Qt::Key_Escape }, - // Cocoa sends us delete when pressing backspace! - // (NB when we reverse this list in qtKey2CocoaKey, there - // will be two indices of Qt::Key_Backspace. But is seems to work - // ok for menu shortcuts (which uses that function): - { 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; - -QChar qtKey2CocoaKey(Qt::Key key) -{ - // The first time this function is called, create a reverse - // looup table sorted on Qt Key rather than Cocoa key: - static QVector<KeyPair> rev_entries(NumEntries); - static bool mustInit = true; - if (mustInit){ - mustInit = false; - for (int i=0; i<NumEntries; ++i) - rev_entries[i] = entries[i]; - qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan); - } - const QVector<KeyPair>::iterator i - = qBinaryFind(rev_entries.begin(), rev_entries.end(), key); - if (i == rev_entries.end()) - return QChar(); - return i->cocoaKey; -} - -#ifdef QT_MAC_USE_COCOA -static Qt::Key cocoaKey2QtKey(QChar keyCode) -{ - 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; -} - -NSString *qt_mac_removePrivateUnicode(NSString* string) -{ - int len = [string length]; - if (len) { - QVarLengthArray <unichar, 10> characters(len); - bool changed = false; - for (int i = 0; i<len; i++) { - characters[i] = [string characterAtIndex:i]; - // check if they belong to key codes in private unicode range - // currently we need to handle only the NSDeleteFunctionKey - if (characters[i] == NSDeleteFunctionKey) { - characters[i] = NSDeleteCharacter; - changed = true; - } - } - if (changed) - return [NSString stringWithCharacters:characters.data() length:len]; - } - return string; -} - -Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations) -{ - Qt::KeyboardModifiers qtMods =Qt::NoModifier; - if (dragOperations & NSDragOperationLink) - qtMods |= Qt::MetaModifier; - if (dragOperations & NSDragOperationGeneric) - qtMods |= Qt::ControlModifier; - if (dragOperations & NSDragOperationCopy) - qtMods |= Qt::AltModifier; - 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! - bool ignoreText = false; - Qt::Key qtKey = Qt::Key_unknown; - if (keyLength == 1) { - QChar ch([keyChars characterAtIndex:0]); - if (ch.isLower()) - ch = ch.toUpper(); - qtKey = cocoaKey2QtKey(ch); - // Do not set the text for Function-Key Unicodes characters (0xF700–0xF8FF). - ignoreText = (ch.unicode() >= 0xF700 && ch.unicode() <= 0xF8FF); - } - 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 (!ignoreText && !(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]); - return qt_sendSpontaneousEvent(widgetToGetEvent, &ke) && ke.isAccepted(); -} -#endif - -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; -} - -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); - unsigned int info = 0; - - if ([event type] == NSKeyDown) { - NSString *characters = [event characters]; - if ([characters length]) { - unichar value = [characters characterAtIndex:0]; - qt_keymapper_private()->updateKeyMap(0, key_event, (void *)&value); - info = value; - } - } - - if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event)) - return true; - - if (mustUseCocoaKeyEvent()) - return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent); - - bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true); - return consumed && (info != 0); -#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())); -} - -#if QT_MAC_USE_COCOA && __OBJC__ - -void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event) -{ - QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; - if (widgetToGetEvent == 0) - return; - - 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]; - qglobalPoint = QPoint(flipPoint(globalPoint).toPoint()); - QWidget *w = widgetToGetEvent->childAt(widgetToGetEvent->mapFromGlobal(qglobalPoint)); - // check that the mouse pointer is on the non-client area and - // there are not widgets in it. - if (fakeNCEvents || (!NSMouseInRect(globalPoint, contentRect, NO) && !w)) { - 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) - // or if someone already got an explicit or implicit grab - if (mac_mouse_grabber || qt_button_down || - (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; - qt_leftButtonIsRightButton = true; - } - } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) { - if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { - button = Qt::RightButton; - qt_leftButtonIsRightButton = false; - } - } - - Qt::MouseButtons buttons = 0; - { - UInt32 mac_buttons; - if (GetEventParameter((EventRef)[event eventRef], kEventParamMouseChord, typeUInt32, 0, - sizeof(mac_buttons), 0, &mac_buttons) == noErr) - buttons = qt_mac_get_buttons(mac_buttons); - } - - QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods); - qt_sendSpontaneousEvent(widgetToGetEvent, &qme); - - // We don't need to set the implicit grab widget here because we won't - // reach this point if then event type is Press over a Qt widget. - // However we might need to unset it if the event is Release. - if (eventType == QEvent::MouseButtonRelease) - qt_button_down = 0; -} - -QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent) -{ - if (QWidget *popup = QApplication::activePopupWidget()) { - QWidget *focusInPopup = popup->focusWidget(); - return focusInPopup ? focusInPopup : popup; - } - - QWidget *widgetToGetKey = qApp->focusWidget(); - if (!widgetToGetKey) - widgetToGetKey = widgetThatReceivedEvent; - - return widgetToGetKey; -} - -// This function will find the widget that should receive the -// mouse event. Because of explicit/implicit mouse grabs, popups, -// etc, this might not end up being the same as the widget under -// the mouse (which is more interresting when handling enter/leave -// events -QWidget *qt_mac_getTargetForMouseEvent( - // You can call this function without providing an event. - NSEvent *event, - QEvent::Type eventType, - QPoint &returnLocalPoint, - QPoint &returnGlobalPoint, - QWidget *nativeWidget, - QWidget **returnWidgetUnderMouse) -{ - Q_UNUSED(event); - NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation]; - returnGlobalPoint = flipPoint(nsglobalpoint).toPoint(); - QWidget *mouseGrabber = QWidget::mouseGrabber(); - bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); - QWidget *popup = QApplication::activePopupWidget(); - - // Resolve the widget under the mouse: - QWidget *widgetUnderMouse = 0; - if (popup || qt_button_down || !nativeWidget || !nativeWidget->isVisible()) { - // Using QApplication::widgetAt for finding the widget under the mouse - // is most safe, since it ignores cocoas own mouse down redirections (which - // we need to be prepared for when using nativeWidget as starting point). - // (the only exception is for QMacNativeWidget, where QApplication::widgetAt fails). - // But it is also slower (I guess), so we try to avoid it and use nativeWidget if we can: - widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); - } - - if (!widgetUnderMouse && nativeWidget) { - // Entering here should be the common case. We - // also handle the QMacNativeWidget fallback case. - QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint); - widgetUnderMouse = nativeWidget->childAt(p); - if (!widgetUnderMouse && nativeWidget->rect().contains(p)) - widgetUnderMouse = nativeWidget; - } - - if (widgetUnderMouse) { - // Check if widgetUnderMouse is blocked by a modal - // window, or the mouse if over the frame strut: - if (widgetUnderMouse == qt_button_down) { - // Small optimization to avoid an extra call to isBlockedByModal: - if (buttonDownNotBlockedByModal == false) - widgetUnderMouse = 0; - } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) { - widgetUnderMouse = 0; - } - - if (widgetUnderMouse && widgetUnderMouse->isWindow()) { - // Exclude the titlebar (and frame strut) when finding widget under mouse: - QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); - if (!widgetUnderMouse->rect().contains(p)) - widgetUnderMouse = 0; - } - } - if (returnWidgetUnderMouse) - *returnWidgetUnderMouse = widgetUnderMouse; - - // Resolve the target for the mouse event. Default will be - // widgetUnderMouse, except if there is a grab (popup/mouse/button-down): - if (popup && !mouseGrabber) { - // We special case handling of popups, since they have an implicitt mouse grab. - QWidget *candidate = buttonDownNotBlockedByModal ? qt_button_down : widgetUnderMouse; - if (!popup->isAncestorOf(candidate)) { - // INVARIANT: we have a popup, but the candidate is not - // in it. But the popup will grab the mouse anyway, - // except if the user scrolls: - if (eventType == QEvent::Wheel) - return 0; - returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); - return popup; - } else if (popup == candidate) { - // INVARIANT: The candidate is the popup itself, and not a child: - returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); - return popup; - } else { - // INVARIANT: The candidate is a child inside the popup: - returnLocalPoint = candidate->mapFromGlobal(returnGlobalPoint); - return candidate; - } - } - - QWidget *target = mouseGrabber; - if (!target && buttonDownNotBlockedByModal) - target = qt_button_down; - if (!target) - target = widgetUnderMouse; - if (!target) - return 0; - - returnLocalPoint = target->mapFromGlobal(returnGlobalPoint); - return target; -} - -QPointer<QWidget> qt_last_native_mouse_receiver = 0; - -static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget) -{ - // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do - // not. This will in general be the cases when alien widgets are not involved: - // 1. from a native widget to another native widget or - // 2. from a native widget to no widget - // 3. from no widget to a native or alien widget - - if (qt_button_down || QWidget::mouseGrabber()) - return; - - if ((maybeEnterWidget == qt_last_native_mouse_receiver) && qt_last_native_mouse_receiver) - return; - if (maybeEnterWidget) { - if (!qt_last_native_mouse_receiver) { - // case 3 - QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0); - qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); - } else if (maybeEnterWidget->internalWinId()) { - // case 1 - QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_native_mouse_receiver); - qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); - } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate - } else { - if (qt_last_native_mouse_receiver) { - // case 2 - QApplicationPrivate::dispatchEnterLeave(0, qt_last_native_mouse_receiver); - qt_last_mouse_receiver = 0; - qt_last_native_mouse_receiver = 0; - } - } -} - -bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget) -{ - // Give the Input Manager a chance to process the mouse events. - NSInputManager *currentIManager = [NSInputManager currentInputManager]; - if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { - [currentIManager handleMouseEvent:event]; - } - - // Find the widget that should receive the event, and the widget under the mouse. Those - // can differ if an implicit or explicit mouse grab is active: - QWidget *widgetUnderMouse = 0; - QPoint localPoint, globalPoint; - QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); - if (!widgetToGetMouse) - return false; - - // From here on, we let nativeWidget actually be the native widget under widgetUnderMouse. The reason - // for this, is that qt_mac_getTargetForMouseEvent will set cocoa's mouse event redirection aside when - // determining which widget is under the mouse (in other words, it will usually ignore nativeWidget). - // nativeWidget will be used in QApplicationPrivate::sendMouseEvent to correctly dispatch enter/leave events. - if (widgetUnderMouse) - nativeWidget = widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget(); - if (!nativeWidget) - return false; - NSView *view = qt_mac_effectiveview_for(nativeWidget); - - // Handle tablet events (if any) first. - if (qt_mac_handleTabletEvent(view, event)) { - // Tablet event was handled. In Qt we aren't supposed to send the mouse event. - return true; - } - - EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef])); - if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent)) - return true; - - // Keep previousButton to make sure we don't send double click - // events when the user double clicks using two different buttons: - static Qt::MouseButton previousButton = Qt::NoButton; - - Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); - NSInteger clickCount = [event 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); - } - - // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not: - qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); - - switch (eventType) { - default: - qWarning("not handled! %d", eventType); - break; - case QEvent::MouseMove: - if (button == Qt::LeftButton && qt_leftButtonIsRightButton) - button = Qt::RightButton; - break; - case QEvent::MouseButtonPress: - qt_button_down = widgetUnderMouse; - if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button)) - eventType = QEvent::MouseButtonDblClick; - if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { - button = Qt::RightButton; - qt_leftButtonIsRightButton = true; - } - break; - case QEvent::MouseButtonRelease: - if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { - button = Qt::RightButton; - qt_leftButtonIsRightButton = false; - } - qt_button_down = 0; - break; - } - - qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); - - DnDParams *dndParams = currentDnDParameters(); - dndParams->view = view; - dndParams->theEvent = event; - dndParams->globalPoint = globalPoint; - - // Send the mouse event: - QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods); - QApplicationPrivate::sendMouseEvent( - widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget, - &qt_button_down, qt_last_mouse_receiver, true); - - if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) { - QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods); - qt_sendSpontaneousEvent(widgetToGetMouse, &qcme); - } - - if (eventType == QEvent::MouseButtonRelease) { - // A mouse button was released, which means that the implicit grab was - // released. We therefore need to re-check if should send (delayed) enter leave events: - // qt_button_down has now become NULL since the call at the top of the function. Also, since - // the relase might have closed a window, we dont give the nativeWidget hint - qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); - qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); - } - - previousButton = button; - return true; -} -#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 -} - -#if QT_MAC_USE_COCOA -void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget) -{ - QMacCocoaAutoReleasePool pool; - OSWindowRef theWindow = static_cast<OSWindowRef>(window); - if(!theWindow) - return; - id theClass = [[[theWindow contentView] superview] class]; - // What we do here is basically to add a new selector to NSThemeFrame called - // "drawRectOriginal:" which will contain the original implementation of - // "drawRect:". After that we get the new implementation from QCocoaWindow - // and exchange them. The new implementation is called drawRectSpecial. - // We cannot just add the method because it might have been added before and since - // we cannot remove a method once it has been added we need to ask QCocoaWindow if - // we did the swap or not. - if(!widget->drawRectOriginalAdded) { - Method m2 = class_getInstanceMethod(theClass, @selector(drawRect:)); - if(!m2) { - // This case is pretty extreme, no drawRect means no drawing! - return; - } - class_addMethod(theClass, @selector(drawRectOriginal:), method_getImplementation(m2), method_getTypeEncoding(m2)); - widget->drawRectOriginalAdded = true; - } - if(widget->originalDrawMethod) { - Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:)); - if(!m0) { - // Ok, this means the methods were never swapped. Just ignore - return; - } - Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:)); - if(!m1) { - // Ok, this means the methods were never swapped. Just ignore - return; - } - // We have the original method here. Proceed and swap the methods. - method_exchangeImplementations(m1, m0); - widget->originalDrawMethod = false; - [theWindow display]; - } -} - -void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget) -{ - QMacCocoaAutoReleasePool pool; - OSWindowRef theWindow = static_cast<OSWindowRef>(window); - id theClass = [[[theWindow contentView] superview] class]; - // Now we need to revert the methods to their original state. - // We cannot remove the method, so we just keep track of it in QCocoaWindow. - Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:)); - if(!m0) { - // Ok, this means the methods were never swapped. Just ignore - return; - } - Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:)); - if(!m1) { - // Ok, this means the methods were never swapped. Just ignore - return; - } - method_exchangeImplementations(m1, m0); - widget->originalDrawMethod = true; - [theWindow display]; -} -#endif // QT_MAC_USE_COCOA - -#if QT_MAC_USE_COCOA -void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show) -{ - if(!window) - return; - QMacCocoaAutoReleasePool pool; - OSWindowRef theWindow = static_cast<OSWindowRef>(window); - NSToolbar *macToolbar = [theWindow toolbar]; - [macToolbar setShowsBaselineSeparator:show]; -} -#endif // QT_MAC_USE_COCOA - -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; -} - -#if QT_MAC_USE_COCOA -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]; -} -#endif // QT_MAC_USE_COCOA - -// 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; -} - -void qt_mac_dispatchPendingUpdateRequests(QWidget *widget) -{ - if (!widget) - return; -#ifndef QT_MAC_USE_COCOA - HIViewRender(qt_mac_nativeview_for(widget)); -#else - [qt_mac_nativeview_for(widget) displayIfNeeded]; -#endif -} - -CGFloat qt_mac_get_scalefactor() -{ -#ifndef QT_MAC_USE_COCOA - return HIGetScaleFactor(); -#else - return [[NSScreen mainScreen] userSpaceScaleFactor]; -#endif -} - -QString qt_mac_get_pasteboardString(OSPasteboardRef paste) -{ - QMacCocoaAutoReleasePool pool; - NSPasteboard *pb = nil; - CFStringRef pbname; - if (PasteboardCopyName(paste, &pbname) == noErr) { - pb = [NSPasteboard pasteboardWithName:const_cast<NSString *>(reinterpret_cast<const NSString *>(pbname))]; - CFRelease(pbname); - } else { - pb = [NSPasteboard generalPasteboard]; - } - if (pb) { - NSString *text = [pb stringForType:NSStringPboardType]; - if (text) - return qt_mac_NSStringToQString(text); - } - return QString(); -} - -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; -} - -void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon) -{ - 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 - } -} - -#ifdef QT_MAC_USE_COCOA -void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) -{ - QMacCocoaAutoReleasePool pool; - OSMenuRef menu = static_cast<OSMenuRef>(theMenu); - if (collapse) { - bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. - NSMenuItem *previousItem = nil; - - NSArray *itemArray = [menu itemArray]; - for (unsigned int i = 0; i < [itemArray count]; ++i) { - NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); - if ([item isSeparatorItem]) { - [item setHidden:previousIsSeparator]; - } - - if (![item isHidden]) { - previousItem = item; - previousIsSeparator = ([previousItem isSeparatorItem]); - } - } - - // We now need to check the final item since we don't want any separators at the end of the list. - if (previousItem && previousIsSeparator) - [previousItem setHidden:YES]; - } else { - NSArray *itemArray = [menu itemArray]; - for (unsigned int i = 0; i < [itemArray count]; ++i) { - NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); - if (QAction *action = reinterpret_cast<QAction *>([item tag])) - [item setHidden:!action->isVisible()]; - } - } -} - -class CocoaPostMessageAfterEventLoopExitHelp : public QObject -{ - id target; - SEL selector; - int argCount; - id arg1; - id arg2; -public: - CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2) - : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){ - deleteLater(); - } - - ~CocoaPostMessageAfterEventLoopExitHelp() - { - qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); - } -}; - -void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2) -{ - // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! - // That is why we need to split the address in two parts: - QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector, argCount, arg1, arg2); - quint32 lower = quintptr(args); - quint32 upper = quintptr(args) >> 32; - NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined - location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 - context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; - [NSApp postEvent:e atStart:NO]; -} - -void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2) -{ - if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1) - qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); - else - new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2); -} - -#endif - -QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() -{ -#ifndef QT_MAC_USE_COCOA - NSApplicationLoad(); -#endif - pool = (void*)[[NSAutoreleasePool alloc] init]; -} - -QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool() -{ - [(NSAutoreleasePool*)pool release]; -} - -void qt_mac_post_retranslateAppMenu() -{ -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - qt_cocoaPostMessage([NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)], @selector(qtTranslateApplicationMenu)); -#endif -} - -QWidgetPrivate *QMacScrollOptimization::_target = 0; -bool QMacScrollOptimization::_inWheelEvent = false; -int QMacScrollOptimization::_dx = 0; -int QMacScrollOptimization::_dy = 0; -QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1); - -#ifdef QT_MAC_USE_COCOA -// This method implements the magic for the drawRectSpecial method. -// We draw a line at the upper edge of the content view in order to -// override the title baseline. -void macDrawRectOnTop(void * /*OSWindowRef */window) -{ - OSWindowRef theWindow = static_cast<OSWindowRef>(window); - NSView *contentView = [theWindow contentView]; - if(!contentView) - return; - // Get coordinates of the content view - NSRect contentRect = [contentView frame]; - // Draw a line on top of the already drawn line. - // We need to check if we are active or not to use the proper color. - if([theWindow isKeyWindow] || [theWindow isMainWindow]) { - [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; - } else { - [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; - } - NSPoint origin = NSMakePoint(0, contentRect.size.height); - NSPoint end = NSMakePoint(contentRect.size.width, contentRect.size.height); - [NSBezierPath strokeLineFromPoint:origin toPoint:end]; -} - -// This method will (or at least should) get called only once. -// Its mission is to find out if we are active or not. If we are active -// we assume that we were launched via finder, otherwise we assume -// we were called from the command line. The distinction is important, -// since in the first case we don't need to trigger a paintEvent, while -// in the second case we do. -void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window) -{ - OSWindowRef theWindow = static_cast<OSWindowRef>(window); - NSApplication *application = [NSApplication sharedApplication]; - NSToolbar *toolbar = [theWindow toolbar]; - if([application isActive]) { - // Launched from finder - [toolbar setShowsBaselineSeparator:NO]; - } else { - // Launched from commandline - [toolbar setVisible:false]; - [toolbar setShowsBaselineSeparator:NO]; - [toolbar setVisible:true]; - [theWindow display]; - } -} - -void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *childWidget) -{ - if (!childWidget) - return; - - QWidget *parent = childWidget->parentWidget(); - if (childWidget->isWindow() && parent) { - if ([[qt_mac_window_for(parent) childWindows] containsObject:qt_mac_window_for(childWidget)]) { - QWidgetPrivate *d = qt_widget_private(childWidget); - d->setSubWindowStacking(false); - d->setSubWindowStacking(true); - } - } -} - -void qt_mac_display(QWidget *widget) -{ - NSView *theNSView = qt_mac_nativeview_for(widget); - [theNSView display]; -} - -void qt_mac_setNeedsDisplay(QWidget *widget) -{ - NSView *theNSView = qt_mac_nativeview_for(widget); - [theNSView setNeedsDisplay:YES]; -} - -void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region) -{ - NSView *theNSView = qt_mac_nativeview_for(widget); - if (region.isEmpty()) { - [theNSView setNeedsDisplay:YES]; - return; - } - - QVector<QRect> rects = region.rects(); - for (int i = 0; i < rects.count(); ++i) { - const QRect &rect = rects.at(i); - NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); - [theNSView setNeedsDisplayInRect:nsrect]; - } - -} - -#endif // QT_MAC_USE_COCOA - -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 deleted file mode 100644 index a49753ae2f..0000000000 --- a/src/gui/kernel/qt_cocoa_helpers_mac_p.h +++ /dev/null @@ -1,340 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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 QT_COCOA_HELPERS_MAC_P_H -#define QT_COCOA_HELPERS_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 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 <private/qwidget_p.h> -#include <qtextdocument.h> -#include <qdebug.h> -#include <qpoint.h> -#include "private/qt_mac_p.h" - -struct HIContentBorderMetrics; - -#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 - -enum { - QtCocoaEventSubTypeWakeup = SHRT_MAX, - QtCocoaEventSubTypePostMessage = SHRT_MAX-1 -}; - -Qt::MouseButtons qt_mac_get_buttons(int buttons); -Qt::MouseButton qt_mac_get_button(EventMouseButton button); -void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds = 0.15); -bool macWindowIsTextured(void * /*OSWindowRef*/ window); -void macWindowToolbarShow(const QWidget *widget, bool show ); -void macWindowToolbarSet( void * /*OSWindowRef*/ window, void* toolbarRef ); -bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ); -void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow ); -void macWindowFlush(void * /*OSWindowRef*/ window); -void macSendToolbarChangeEvent(QWidget *widget); -void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics); -void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget); -void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget); -void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show); -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); -// These methods exists only for supporting unified mode. -void macDrawRectOnTop(void * /*OSWindowRef */ window); -void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window); -void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget); -void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); -#endif -bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); -void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent); -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); -Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations); -QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height); -void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, - QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase); - -#if QT_MAC_USE_COCOA && __OBJC__ -struct DnDParams -{ - NSView *view; - NSEvent *theEvent; - QPoint globalPoint; - NSDragOperation performedAction; -}; - -DnDParams *macCurrentDnDParameters(); -NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); -NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); -Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); -Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); - -QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent); -QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType, - QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse); -bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget); -void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event); -#endif - -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); - -CGFloat qt_mac_get_scalefactor(); -QString qt_mac_get_pasteboardString(OSPasteboardRef paste); - -#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]; } - -#ifdef QT_MAC_USE_COCOA -class QCocoaPostMessageArgs { -public: - id target; - SEL selector; - int argCount; - id arg1; - id arg2; - QCocoaPostMessageArgs(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0) - : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2) - { - [target retain]; - [arg1 retain]; - [arg2 retain]; - } - - ~QCocoaPostMessageArgs() - { - [arg2 release]; - [arg1 release]; - [target release]; - } -}; -void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); -void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); -#endif - -#endif - -class QMacScrollOptimization { - // This class is made to optimize for the case when the user - // scrolls both horizontally and vertically at the same - // time. This will result in two QWheelEvents (one for each - // direction), which will typically result in two calls to - // QWidget::_scroll_sys. Rather than copying pixels twize on - // screen because of this, we add this helper class to try to - // get away with only one blit. - static QWidgetPrivate *_target; - static bool _inWheelEvent; - static int _dx; - static int _dy; - static QRect _scrollRect; - -public: - static void initDelayedScroll() - { - _inWheelEvent = true; - } - - static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect) - { - if (!_inWheelEvent) - return false; - if (_target && _target != target) - return false; - if (_scrollRect.width() != -1 && _scrollRect != scrollRect) - return false; - - _target = target; - _dx += dx; - _dy += dy; - _scrollRect = scrollRect; - return true; - } - - static void performDelayedScroll() - { - if (!_inWheelEvent) - return; - _inWheelEvent = false; - if (!_target) - return; - - _target->scroll_sys(_dx, _dy, _scrollRect); - - _target = 0; - _dx = 0; - _dy = 0; - _scrollRect = QRect(0, 0, -1, -1); - } -}; - -void qt_mac_post_retranslateAppMenu(); - -#ifdef QT_MAC_USE_COCOA -void qt_mac_display(QWidget *widget); -void qt_mac_setNeedsDisplay(QWidget *widget); -void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region); -#endif // QT_MAC_USE_COCOA - - -// Utility functions to ease the use of Core Graphics contexts. - -inline void qt_mac_retain_graphics_context(CGContextRef context) -{ - CGContextRetain(context); - CGContextSaveGState(context); -} - -inline void qt_mac_release_graphics_context(CGContextRef context) -{ - CGContextRestoreGState(context); - CGContextRelease(context); -} - -inline void qt_mac_draw_image(CGContextRef context, CGContextRef imageContext, CGRect area, CGRect drawingArea) -{ - CGImageRef image = CGBitmapContextCreateImage(imageContext); - CGImageRef subImage = CGImageCreateWithImageInRect(image, area); - - CGContextTranslateCTM (context, 0, drawingArea.origin.y + CGRectGetMaxY(drawingArea)); - CGContextScaleCTM(context, 1, -1); - CGContextDrawImage(context, drawingArea, subImage); - - CGImageRelease(subImage); - CGImageRelease(image); -} - -QT_END_NAMESPACE - -#endif // QT_COCOA_HELPERS_MAC_P_H diff --git a/src/gui/kernel/qt_mac.cpp b/src/gui/kernel/qt_mac.cpp deleted file mode 100644 index 046bcf6a54..0000000000 --- a/src/gui/kernel/qt_mac.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/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 if (model == kCGColorSpaceModelMonochrome) { - pc.setRgbF(components[0], components[0], components[0], components[1]); - } 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) -{ -#ifdef Q_OS_MAC32 - RGBColor c; - GetThemeTextColor(themeColor, 32, true, &c); - QColor color = QColor(c.red / 256, c.green / 256, c.blue / 256); - return color; -#else - // There is no equivalent to GetThemeTextColor in 64-bit and it was rather bad that - // I didn't file a request to implement this for Snow Leopard. So, in the meantime - // I've encoded the values from the GetThemeTextColor. This is not exactly ideal - // as if someone really wants to mess with themeing, these colors will be wrong. - // It also means that we need to make sure the values for differences between - // OS releases (and it will be likely that we are a step behind.) - switch (themeColor) { - case kThemeTextColorAlertActive: - case kThemeTextColorTabFrontActive: - case kThemeTextColorBevelButtonActive: - case kThemeTextColorListView: - case kThemeTextColorPlacardActive: - case kThemeTextColorPopupButtonActive: - case kThemeTextColorPopupLabelActive: - case kThemeTextColorPushButtonActive: - return Qt::black; - case kThemeTextColorAlertInactive: - case kThemeTextColorDialogInactive: - case kThemeTextColorPlacardInactive: - return QColor(69, 69, 69, 255); - case kThemeTextColorPopupButtonInactive: - case kThemeTextColorPopupLabelInactive: - case kThemeTextColorPushButtonInactive: - case kThemeTextColorTabFrontInactive: - case kThemeTextColorBevelButtonInactive: - return QColor(127, 127, 127, 255); - default: { - QNativeImage nativeImage(16,16, QNativeImage::systemFormat()); - CGRect cgrect = CGRectMake(0, 0, 16, 16); - HIThemeSetTextFill(themeColor, 0, nativeImage.cg, kHIThemeOrientationNormal); - CGContextFillRect(nativeImage.cg, cgrect); - QColor color = nativeImage.image.pixel(0,0); - 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 deleted file mode 100644 index b2bb804ff0..0000000000 --- a/src/gui/kernel/qt_mac_p.h +++ /dev/null @@ -1,286 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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> -#ifdef QT_MAC_USE_COCOA -#include <objc/runtime.h> -#endif // QT_MAC_USE_COCOA -#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 for chaining to gether a bunch of fades. It pretty much is only used for qmenu fading. -class QMacWindowFader -{ - QWidgetList m_windowsToFade; - float m_duration; - Q_DISABLE_COPY(QMacWindowFader) -public: - QMacWindowFader(); // PLEASE DON'T CALL THIS. - static QMacWindowFader *currentFader(); - void registerWindowToFade(QWidget *window); - void setFadeDuration(float durationInSecs) { m_duration = durationInSecs; } - float fadeDuration() const { return m_duration; } - void performFade(); -}; - -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 ©) : 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 ©) { - 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 -extern QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt); //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; - unsigned int lastOperation; - void clear() { - rect = QRect(); - modifiers = Qt::NoModifier; - buttons = Qt::NoButton; - lastAction = Qt::IgnoreAction; - lastOperation = 0; - } -}; -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_s60_p.h b/src/gui/kernel/qt_s60_p.h deleted file mode 100644 index 8aba53a168..0000000000 --- a/src/gui/kernel/qt_s60_p.h +++ /dev/null @@ -1,625 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT_S60_P_H -#define QT_S60_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 "private/qcore_symbian_p.h" -#include "qhash.h" -#include "qpoint.h" -#include "QtGui/qfont.h" -#include "QtGui/qimage.h" -#include "QtGui/qevent.h" -#include "qpointer.h" -#include "qapplication.h" -#include "qelapsedtimer.h" -#include "QtCore/qthreadstorage.h" -#include "qwidget_p.h" -#include <w32std.h> -#include <coecntrl.h> -#include <eikenv.h> -#include <eikappui.h> - -#ifdef Q_WS_S60 -#include <AknUtils.h> // AknLayoutUtils -#include <avkon.hrh> // EEikStatusPaneUidTitle -#include <akntitle.h> // CAknTitlePane -#include <akncontext.h> // CAknContextPane -#include <eikspane.h> // CEikStatusPane -#include <AknPopupFader.h> // MAknFadedComponent and TAknPopupFader -#include <gfxtranseffect/gfxtranseffect.h> // BeginFullScreen -#ifdef QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H -#include <akntranseffect.h> // BeginFullScreen -#endif -#endif - -QT_BEGIN_NAMESPACE - -// Application internal HandleResourceChangeL events, -// system events seems to start with 0x10 -const TInt KInternalStatusPaneChange = 0x50000000; - -// For BeginFullScreen(). -const TUint KQtAppExitFlag = 0x400; - -static const int qt_symbian_max_screens = 4; - -//this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2 -#define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13) - -class Q_AUTOTEST_EXPORT QS60ThreadLocalData -{ -public: - QS60ThreadLocalData(); - ~QS60ThreadLocalData(); - bool usingCONEinstances; - RWsSession wsSession; - CWsScreenDevice *screenDevice; -}; - -class QS60Data -{ -public: - QS60Data(); - QThreadStorage<QS60ThreadLocalData *> tls; - TUid uid; - int screenDepth; - QPoint lastCursorPos; - QPoint lastPointerEventPos; - QPointer<QWidget> lastPointerEventTarget; - QPointer<QWidget> mousePressTarget; - int screenWidthInPixels; - int screenHeightInPixels; - int screenWidthInTwips; - int screenHeightInTwips; - int defaultDpiX; - int defaultDpiY; - WId curWin; - enum PressedKeys { - Select = 0x1, - Right = 0x2, - Down = 0x4, - Left = 0x8, - Up = 0x10, - LeftUp = 0x20, - RightUp = 0x40, - RightDown = 0x80, - LeftDown = 0x100 - }; - int virtualMousePressedKeys; // of the above type, but avoids casting problems - int virtualMouseAccelDX; - int virtualMouseAccelDY; - QElapsedTimer virtualMouseAccelTimeout; - int virtualMouseMaxAccel; -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - int brokenPointerCursors : 1; -#endif - int hasTouchscreen : 1; - int mouseInteractionEnabled : 1; - int virtualMouseRequired : 1; - int qtOwnsS60Environment : 1; - int supportsPremultipliedAlpha : 1; - int avkonComponentsSupportTransparency : 1; - int menuBeingConstructed : 1; - int orientationSet : 1; - int partial_keyboard : 1; - QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type - QPointer<QWidget> splitViewLastWidget; - - static CEikButtonGroupContainer *cba; - - enum ScanCodeState { - Unpressed, - KeyDown, - KeyDownAndKey - }; - QHash<TInt, ScanCodeState> scanCodeStates; - - static inline void updateScreenSize(); - inline RWsSession& wsSession(); - static inline int screenCount(); - static inline RWindowGroup& windowGroup(); - static inline RWindowGroup& windowGroup(const QWidget *widget); - static inline RWindowGroup& windowGroup(int screenNumber); - inline CWsScreenDevice* screenDevice(); - inline CWsScreenDevice* screenDevice(const QWidget *widget); - inline CWsScreenDevice* screenDevice(int screenNumber); - static inline int screenNumberForWidget(const QWidget *widget); - static inline CCoeAppUi* appUi(); - static inline CEikMenuBar* menuBar(); -#ifdef Q_WS_S60 - static inline CEikStatusPane* statusPane(); - static inline CCoeControl* statusPaneSubPane(TInt aPaneId); - static inline CAknTitlePane* titlePane(); - static inline CAknContextPane* contextPane(); - static inline CEikButtonGroupContainer* buttonGroupContainer(); - static inline void setButtonGroupContainer(CEikButtonGroupContainer* newCba); - static void setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible); - static bool setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState); -#endif - static void controlVisibilityChanged(CCoeControl *control, bool visible); - -#ifdef Q_OS_SYMBIAN - TTrapHandler *s60InstalledTrapHandler; -#endif - - int screenWidthInPixelsForScreen[qt_symbian_max_screens]; - int screenHeightInPixelsForScreen[qt_symbian_max_screens]; - int screenWidthInTwipsForScreen[qt_symbian_max_screens]; - int screenHeightInTwipsForScreen[qt_symbian_max_screens]; - - int nativeScreenWidthInPixels; - int nativeScreenHeightInPixels; - - int beginFullScreenCalled : 1; - int endFullScreenCalled : 1; -}; - -Q_AUTOTEST_EXPORT QS60Data* qGlobalS60Data(); -#define S60 qGlobalS60Data() - -class QAbstractLongTapObserver -{ -public: - virtual void HandleLongTapEventL( const TPoint& aPenEventLocation, - const TPoint& aPenEventScreenLocation ) = 0; -}; -class QLongTapTimer; - - -class QSymbianControl : public CCoeControl, public QAbstractLongTapObserver -#ifdef Q_WS_S60 -, public MAknFadedComponent, public MEikStatusPaneObserver -#endif -{ -public: - DECLARE_TYPE_ID(0x51740000) // Fun fact: the two first values are "Qt" in ASCII. - -public: - QSymbianControl(QWidget *w); - void ConstructL(bool isWindowOwning = false, bool desktop = false); - ~QSymbianControl(); - void HandleResourceChange(int resourceType); - void HandlePointerEventL(const TPointerEvent& aPointerEvent); - TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType); -#if !defined(QT_NO_IM) && defined(Q_WS_S60) - TCoeInputCapabilities InputCapabilities() const; -#endif - TTypeUid::Ptr MopSupplyObject(TTypeUid id); - - inline QWidget* widget() const { return qwidget; } - void setWidget(QWidget *w); - void sendInputEvent(QWidget *widget, QInputEvent *inputEvent); - void setIgnoreFocusChanged(bool enabled) { m_ignoreFocusChanged = enabled; } - void CancelLongTapTimer(); - - void setFocusSafely(bool focus); - - bool isControlActive(); - - void ensureFixNativeOrientation(); - QPoint translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const; - TRect translateRectForFixedNativeOrientation(const TRect &controlRect) const; - -#ifdef Q_WS_S60 - void FadeBehindPopup(bool fade){ popupFader.FadeBehindPopup( this, this, fade); } - void HandleStatusPaneSizeChange(); - -protected: // from MAknFadedComponent - TInt CountFadedComponents() {return 1;} - CCoeControl* FadedComponent(TInt /*aIndex*/) {return this;} -#else - // #warning No fallback implementation for QSymbianControl::FadeBehindPopup - void FadeBehindPopup(bool /*fade*/){ } -#endif - -protected: - void Draw(const TRect& aRect) const; - void SizeChanged(); - void PositionChanged(); - void FocusChanged(TDrawNow aDrawNow); - -protected: - void qwidgetResize_helper(const QSize &newSize); - -private: - void HandlePointerEvent(const TPointerEvent& aPointerEvent); - TKeyResponse OfferKeyEvent(const TKeyEvent& aKeyEvent,TEventCode aType); - TKeyResponse sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type); - TKeyResponse sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent); - TKeyResponse handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type); - bool sendMouseEvent(QWidget *widget, QMouseEvent *mEvent); - void sendMouseEvent( - QWidget *receiver, - QEvent::Type type, - const QPoint &globalPos, - Qt::MouseButton button, - Qt::KeyboardModifiers modifiers); - void processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure); - void HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ); -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event); -#endif - bool isSplitViewWidget(QWidget *widget); - -public: - void handleClientAreaChange(); - -private: - static QSymbianControl *lastFocusedControl; - -private: - QWidget *qwidget; - QLongTapTimer* m_longTapDetector; - QElapsedTimer m_doubleClickTimer; - bool m_ignoreFocusChanged : 1; - bool m_symbianPopupIsOpen : 1; - -#ifdef Q_WS_S60 - // Fader object used to fade everything except this menu and the CBA. - TAknPopupFader popupFader; -#endif - - bool m_inExternalScreenOverride : 1; - bool m_lastStatusPaneVisibility : 1; -}; - -inline QS60Data::QS60Data() -: uid(TUid::Null()), - screenDepth(0), - screenWidthInPixels(0), - screenHeightInPixels(0), - screenWidthInTwips(0), - screenHeightInTwips(0), - defaultDpiX(0), - defaultDpiY(0), - curWin(0), - virtualMousePressedKeys(0), - virtualMouseAccelDX(0), - virtualMouseAccelDY(0), - virtualMouseMaxAccel(0), -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - brokenPointerCursors(0), -#endif - hasTouchscreen(0), - mouseInteractionEnabled(0), - virtualMouseRequired(0), - qtOwnsS60Environment(0), - supportsPremultipliedAlpha(0), - avkonComponentsSupportTransparency(0), - menuBeingConstructed(0), - orientationSet(0), - partial_keyboard(0), - s60ApplicationFactory(0) -#ifdef Q_OS_SYMBIAN - ,s60InstalledTrapHandler(0) -#endif - ,beginFullScreenCalled(0), - endFullScreenCalled(0) -{ -} - -inline void QS60Data::updateScreenSize() -{ - CWsScreenDevice *dev = S60->screenDevice(); - int screenModeCount = dev->NumScreenModes(); - int mode = dev->CurrentScreenMode(); - TPixelsTwipsAndRotation params; - dev->GetScreenModeSizeAndRotation(mode, params); - S60->screenWidthInPixels = params.iPixelSize.iWidth; - S60->screenHeightInPixels = params.iPixelSize.iHeight; - S60->screenWidthInTwips = params.iTwipsSize.iWidth; - S60->screenHeightInTwips = params.iTwipsSize.iHeight; - - S60->virtualMouseMaxAccel = qMax(S60->screenHeightInPixels, S60->screenWidthInPixels) / 10; - - TReal inches = S60->screenHeightInTwips / (TReal)KTwipsPerInch; - S60->defaultDpiY = S60->screenHeightInPixels / inches; - inches = S60->screenWidthInTwips / (TReal)KTwipsPerInch; - S60->defaultDpiX = S60->screenWidthInPixels / inches; - - int screens = S60->screenCount(); - for (int i = 0; i < screens; ++i) { - CWsScreenDevice *dev = S60->screenDevice(i); - mode = dev->CurrentScreenMode(); - dev->GetScreenModeSizeAndRotation(mode, params); - S60->screenWidthInPixelsForScreen[i] = params.iPixelSize.iWidth; - S60->screenHeightInPixelsForScreen[i] = params.iPixelSize.iHeight; - S60->screenWidthInTwipsForScreen[i] = params.iTwipsSize.iWidth; - S60->screenHeightInTwipsForScreen[i] = params.iTwipsSize.iHeight; - } - - // Look for a screen mode with rotation 0 - // in order to decide what the native orientation is. - for (mode = 0; mode < screenModeCount; ++mode) { - TPixelsAndRotation sizeAndRotation; - dev->GetScreenModeSizeAndRotation(mode, sizeAndRotation); - if (sizeAndRotation.iRotation == CFbsBitGc::EGraphicsOrientationNormal) { - S60->nativeScreenWidthInPixels = sizeAndRotation.iPixelSize.iWidth; - S60->nativeScreenHeightInPixels = sizeAndRotation.iPixelSize.iHeight; - break; - } - } -} - -inline RWsSession& QS60Data::wsSession() -{ - if(!tls.hasLocalData()) { - tls.setLocalData(new QS60ThreadLocalData); - } - return tls.localData()->wsSession; -} - -inline int QS60Data::screenCount() -{ -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - CCoeEnv *env = CCoeEnv::Static(); - if (env) { - return qMin(env->WsSession().NumberOfScreens(), qt_symbian_max_screens); - } -#endif - return 1; -} - -inline RWindowGroup& QS60Data::windowGroup() -{ - return CCoeEnv::Static()->RootWin(); -} - -inline RWindowGroup& QS60Data::windowGroup(const QWidget *widget) -{ - return windowGroup(screenNumberForWidget(widget)); -} - -inline RWindowGroup& QS60Data::windowGroup(int screenNumber) -{ -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - RWindowGroup *wg = CCoeEnv::Static()->RootWin(screenNumber); - return wg ? *wg : windowGroup(); -#else - Q_UNUSED(screenNumber); - return windowGroup(); -#endif -} - -inline CWsScreenDevice* QS60Data::screenDevice() -{ - if(!tls.hasLocalData()) { - tls.setLocalData(new QS60ThreadLocalData); - } - return tls.localData()->screenDevice; -} - -inline CWsScreenDevice* QS60Data::screenDevice(const QWidget *widget) -{ - return screenDevice(screenNumberForWidget(widget)); -} - -inline CWsScreenDevice* QS60Data::screenDevice(int screenNumber) -{ -#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) - CCoeEnv *env = CCoeEnv::Static(); - if (env) { - CWsScreenDevice *dev = env->ScreenDevice(screenNumber); - return dev ? dev : screenDevice(); - } else { - return screenDevice(); - } -#else - return screenDevice(); -#endif -} - -inline int QS60Data::screenNumberForWidget(const QWidget *widget) -{ - if (!widget) - return 0; - const QWidget *w = widget; - while (w->parentWidget()) - w = w->parentWidget(); - return qt_widget_private(const_cast<QWidget *>(w))->symbianScreenNumber; -} - -inline CCoeAppUi* QS60Data::appUi() -{ - return CCoeEnv::Static()-> AppUi(); -} - -inline CEikMenuBar* QS60Data::menuBar() -{ - return CEikonEnv::Static()->AppUiFactory()->MenuBar(); -} - -#ifdef Q_WS_S60 -inline CEikStatusPane* QS60Data::statusPane() -{ - return CEikonEnv::Static()->AppUiFactory()->StatusPane(); -} - -// Returns the application's status pane control, if not present returns NULL. -inline CCoeControl* QS60Data::statusPaneSubPane( TInt aPaneId ) -{ - const TUid paneUid = { aPaneId }; - CEikStatusPane* statusPane = S60->statusPane(); - if (statusPane && statusPane->PaneCapabilities(paneUid).IsPresent()) { - CCoeControl* control = NULL; - // ControlL shouldn't leave because the pane is present - TRAPD(err, control = statusPane->ControlL(paneUid)); - return err != KErrNone ? NULL : control; - } - return NULL; -} - -// Returns the application's title pane, if not present returns NULL. -inline CAknTitlePane* QS60Data::titlePane() -{ - return static_cast<CAknTitlePane*>(S60->statusPaneSubPane(EEikStatusPaneUidTitle)); -} - -// Returns the application's title pane, if not present returns NULL. -inline CAknContextPane* QS60Data::contextPane() -{ - return static_cast<CAknContextPane*>(S60->statusPaneSubPane(EEikStatusPaneUidContext)); -} - -inline CEikButtonGroupContainer* QS60Data::buttonGroupContainer() -{ - return QS60Data::cba; -} - -inline void QS60Data::setButtonGroupContainer(CEikButtonGroupContainer *newCba) -{ - QS60Data::cba = newCba; -} -#endif // Q_WS_S60 - -static inline QFont qt_TFontSpec2QFontL(const TFontSpec &fontSpec) -{ - return QFont( - qt_TDesC2QString(fontSpec.iTypeface.iName), - fontSpec.iHeight / KTwipsPerPoint, - fontSpec.iFontStyle.StrokeWeight() == EStrokeWeightNormal ? QFont::Normal : QFont::Bold, - fontSpec.iFontStyle.Posture() == EPostureItalic - ); -} - -static inline QImage::Format qt_TDisplayMode2Format(TDisplayMode mode) -{ - QImage::Format format; - switch(mode) { - case EGray2: - format = QImage::Format_MonoLSB; - break; - case EColor256: - case EGray256: - format = QImage::Format_Indexed8; - break; - case EColor4K: - format = QImage::Format_RGB444; - break; - case EColor64K: - format = QImage::Format_RGB16; - break; - case EColor16M: - format = QImage::Format_RGB888; - break; - case EColor16MU: - format = QImage::Format_RGB32; - break; - case EColor16MA: - format = QImage::Format_ARGB32; - break; - case Q_SYMBIAN_ECOLOR16MAP: - format = QImage::Format_ARGB32_Premultiplied; - break; - default: - format = QImage::Format_Invalid; - break; - } - return format; -} - -#ifndef QT_NO_CURSOR -void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid); -void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node); -void qt_symbian_setGlobalCursor(const QCursor &cursor); -void qt_symbian_set_cursor_visible(bool visible); -bool qt_symbian_is_cursor_visible(); -#endif - -static inline bool qt_beginFullScreenEffect() -{ -#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) - // Only for post-S^3. On earlier versions the system transition effects - // may not be able to capture the non-Avkon content, leading to confusing - // looking effects, so just skip the whole thing. - if (S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) - return false; - S60->beginFullScreenCalled = true; - // For Avkon apps the app-exit effect is triggered from CAknAppUi::PrepareToExit(). - // That is good for Avkon apps, but in case of Qt the RWindows are destroyed earlier. - // Therefore we call BeginFullScreen() ourselves. - GfxTransEffect::BeginFullScreen(AknTransEffect::EApplicationExit, - TRect(0, 0, 0, 0), - AknTransEffect::EParameterType, - AknTransEffect::GfxTransParam(S60->uid, - AknTransEffect::TParameter::EAvkonCheck | KQtAppExitFlag)); - return true; -#else - return false; -#endif -} - -static inline void qt_abortFullScreenEffect() -{ -#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) - if (!S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) - return; - GfxTransEffect::AbortFullScreen(); - S60->beginFullScreenCalled = S60->endFullScreenCalled = false; -#endif -} - -static inline void qt_endFullScreenEffect() -{ -#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) - if (S60->endFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) - return; - S60->endFullScreenCalled = true; - GfxTransEffect::EndFullScreen(); -#endif -} - -QT_END_NAMESPACE - -#endif // QT_S60_P_H diff --git a/src/gui/kernel/qt_x11_p.h b/src/gui/kernel/qt_x11_p.h deleted file mode 100644 index 69079cfaad..0000000000 --- a/src/gui/kernel/qt_x11_p.h +++ /dev/null @@ -1,757 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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 -#ifdef Q_OS_VXWORS -# ifdef open -# undef open -# endif -# ifdef getpid -# undef getpid -# endif -#endif // Q_OS_VXWORKS -#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 - -#ifndef QT_NO_XSYNC -extern "C" { -# include "X11/extensions/sync.h" -} -#endif - -// #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 *); -typedef XRRScreenSize *(*PtrXRRSizes)(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 Q_GUI_EXPORT QX11Data *qt_x11Data; - -enum DesktopEnvironment { - DE_UNKNOWN, - DE_KDE, - DE_GNOME, - DE_CDE, - DE_MEEGO_COMPOSITOR, - 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); - 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; - bool use_mitshm_pixmaps; - 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; - - // for XKEYBOARD support - bool use_xkb; - int xkb_major; - int xkb_eventbase; - int xkb_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; - - DesktopEnvironment desktopEnvironment : 8; - uint desktopVersion : 8; /* Used only for KDE */ - - /* 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, - _NET_WM_SYNC_REQUEST, - _NET_WM_SYNC_REQUEST_COUNTER, - - // 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, - SAVE_TARGETS, - CLIP_TEMPORARY, - _QT_SELECTION, - _QT_CLIPBOARD_SENTINEL, - _QT_SELECTION_SENTINEL, - CLIPBOARD_MANAGER, - - RESOURCE_MANAGER, - - _XSETROOT_ID, - - _QT_SCROLL_DONE, - _QT_INPUT_ENCODING, - - _MOTIF_WM_HINTS, - - DTWM_IS_RUNNING, - ENLIGHTENMENT_DESKTOP, - _DT_SAVE_MODE, - _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, - - _NET_ACTIVE_WINDOW, - - // 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, - - XWacomStylus, - XWacomCursor, - XWacomEraser, - - XTabletStylus, - XTabletEraser, - - 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; - PtrXRRSizes ptrXRRSizes; -#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/qwidget_mac.mm b/src/gui/kernel/qwidget_mac.mm deleted file mode 100644 index 354f05ba10..0000000000 --- a/src/gui/kernel/qwidget_mac.mm +++ /dev/null @@ -1,5420 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/**************************************************************************** -** -** 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/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 "qfileinfo.h" -#include "qimage.h" -#include "qlayout.h" -#include "qmenubar.h" -#include <private/qbackingstore_p.h> -#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 "qevent_p.h" -#include "qdnd_p.h" -#include <QtGui/qgraphicsproxywidget.h> -#include "qmainwindow.h" - -QT_BEGIN_NAMESPACE - -// qmainwindow.cpp -extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); - -#define XCOORD_MAX 16383 -#define WRECT_MAX 8191 - -#ifndef QT_MAC_USE_COCOA - -extern "C" { - extern OSStatus _HIViewScrollRectWithOptions(HIViewRef, const HIRect *, CGFloat, CGFloat, - OptionBits) __attribute__ ((weak)); -} -#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 QPointer<QWidget> qt_button_down; //qapplication_mac.cpp -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_last_mouse_receiver; //qapplication_mac.mm -extern QPointer<QWidget> qt_last_native_mouse_receiver; //qt_cocoa_helpers_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 -extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm -extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm -/***************************************************************************** - 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) -{ - NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId()); - NSArray *windows = [NSApp windows]; - for (NSWindow *window in windows) { - NSArray *drawers = [window drawers]; - for (NSDrawer *drawer in drawers) { - if ([drawer contentView] == widgetView) - return drawer; - } - } - return 0; -} -#endif - -static void qt_mac_destructView(OSViewRef view) -{ -#ifdef QT_MAC_USE_COCOA - NSWindow *window = [view window]; - if ([window contentView] == view) - [window setContentView:[[NSView alloc] initWithFrame:[view bounds]]]; - [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; - return w->parentWidget() && (modality == Qt::WindowModal || w->windowType() == Qt::Sheet); -} - -bool qt_mac_is_macdrawer(const QWidget *w) -{ - return (w && w->parentWidget() && w->windowType() == Qt::Drawer); -} - -bool qt_mac_insideKeyWindow(const QWidget *w) -{ -#ifdef QT_MAC_USE_COCOA - return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow]; -#else - Q_UNUSED(w); -#endif - return false; -} - -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.mm - if(qt_mac_app_fullscreen == b) - return; - qt_mac_app_fullscreen = b; - if (b) { - SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); - } else { - SetSystemUIMode(kUIModeNormal, 0); - } -} - -Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) -{ - return reinterpret_cast<OSViewRef>(w->internalWinId()); -} - -Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w) -{ - // Get the first non-alien (parent) widget for - // w, and return its NSView (if it has one): - return reinterpret_cast<OSViewRef>(w->effectiveWinId()); -} - -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) -{ - if (!window) - return false; - - if (!window->internalWinId()) - return true; //alien - - return qt_isGenuineQWidget(OSViewRef(window->internalWinId())); -} - -Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) -{ - if (OSViewRef hiview = qt_mac_effectiveview_for(w)) { - OSWindowRef window = qt_mac_window_for(hiview); - if (window) - return window; - - if (qt_isGenuineQWidget(hiview)) { - // This is a workaround for NSToolbar. When a widget is hidden - // by clicking the toolbar button, Cocoa reparents the widgets - // to another window (but Qt doesn't know about it). - // When we start showing them, it reparents back, - // but at this point it's window is nil, but the window it's being brought - // into (the Qt one) is for sure created. - // This stops the hierarchy moving under our feet. - QWidget *toplevel = w->window(); - if (toplevel != w) { - hiview = qt_mac_nativeview_for(toplevel); - if (OSWindowRef w = qt_mac_window_for(hiview)) - return w; - } - - toplevel->d_func()->createWindow_sys(); - // Reget the hiview since "create window" could potentially move the view (I guess). - hiview = qt_mac_nativeview_for(toplevel); - return qt_mac_window_for(hiview); - } - } - 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; - CGWindowLevel tmpLevel; - GetWindowGroupLevelOfType(GetWindowGroupOfClass(wclass), kWindowGroupLevelActive, &tmpLevel); - group_level = tmpLevel; - 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::macSetNeedsDisplay(QRegion region) -{ - Q_Q(QWidget); -#ifndef QT_MAC_USE_COCOA - if (region.isEmpty()) - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); - else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys()) - HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); - else - HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. -#else - if (NSView *nativeView = qt_mac_nativeview_for(q)) { - // INVARIANT: q is _not_ alien. So we can optimize a little: - if (region.isEmpty()) { - [nativeView setNeedsDisplay:YES]; - } else { - QVector<QRect> rects = region.rects(); - for (int i = 0; i<rects.count(); ++i) { - const QRect &rect = rects.at(i); - NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); - [nativeView setNeedsDisplayInRect:nsrect]; - } - } - } else if (QWidget *effectiveWidget = q->nativeParentWidget()) { - // INVARIANT: q is alien, and effectiveWidget is native. - if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) { - if (region.isEmpty()) { - const QRect &rect = q->rect(); - QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); - NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); - [effectiveView setNeedsDisplayInRect:nsrect]; - } else { - QVector<QRect> rects = region.rects(); - for (int i = 0; i<rects.count(); ++i) { - const QRect &rect = rects.at(i); - QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); - NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); - [effectiveView setNeedsDisplayInRect:nsrect]; - } - } - } - } -#endif -} - -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; -} - -#ifndef QT_NO_GESTURES -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 -/* We build the release package against the 10.4 SDK. - So, to enable gestures for applications running on - 10.6+, we define the missing constants here: */ -enum { - kEventClassGesture = 'gest', - kEventGestureStarted = 1, - kEventGestureEnded = 2, - kEventGestureMagnify = 4, - kEventGestureSwipe = 5, - kEventGestureRotate = 6, - kEventParamRotationAmount = 'rota', - kEventParamSwipeDirection = 'swip', - kEventParamMagnificationAmount = 'magn' -}; -#endif -#endif // QT_NO_GESTURES - -// window events -static EventTypeSpec window_events[] = { - { kEventClassWindow, kEventWindowClose }, - { kEventClassWindow, kEventWindowExpanded }, - { kEventClassWindow, kEventWindowHidden }, - { kEventClassWindow, kEventWindowZoom }, - { kEventClassWindow, kEventWindowZoomed }, - { kEventClassWindow, kEventWindowCollapsed }, - { kEventClassWindow, kEventWindowToolbarSwitchMode }, - { kEventClassWindow, kEventWindowProxyBeginDrag }, - { kEventClassWindow, kEventWindowProxyEndDrag }, - { kEventClassWindow, kEventWindowResizeCompleted }, - { kEventClassWindow, kEventWindowBoundsChanging }, - { kEventClassWindow, kEventWindowGetRegion }, - { kEventClassWindow, kEventWindowGetClickModality }, - { kEventClassWindow, kEventWindowTransitionCompleted }, - { kEventClassGesture, kEventGestureStarted }, - { kEventClassGesture, kEventGestureEnded }, - { kEventClassGesture, kEventGestureMagnify }, - { kEventClassGesture, kEventGestureSwipe }, - { kEventClassGesture, kEventGestureRotate }, - { 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; - } 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; - } 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 == kEventWindowZoom) { - widget->d_func()->topData()->normalGeometry = widget->geometry(); - handled_event = false; - } 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); - } - 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); - qt_button_down = 0; - } else if(ekind == kEventWindowToolbarSwitchMode) { - macSendToolbarChangeEvent(widget); - 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) { - 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 if (ekind == kEventWindowHidden) { - // Make sure that we also hide any visible sheets on our window. - // Cocoa does the right thing for us. - const QObjectList children = widget->children(); - const int childCount = children.count(); - for (int i = 0; i < childCount; ++i) { - QObject *obj = children.at(i); - if (obj->isWidgetType()) { - QWidget *widget = static_cast<QWidget *>(obj); - if (qt_mac_is_macsheet(widget) && widget->isVisible()) - widget->hide(); - } - } - } 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; } - -#ifndef QT_NO_GESTURES - case kEventClassGesture: { - // First, find the widget that was under - // the mouse when the gesture happened: - HIPoint screenLocation; - if (GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0, - sizeof(screenLocation), 0, &screenLocation) != noErr) { - handled_event = false; - break; - } - QWidget *widget = QApplication::widgetAt(screenLocation.x, screenLocation.y); - if (!widget) { - handled_event = false; - break; - } - - QNativeGestureEvent qNGEvent; - qNGEvent.position = QPoint(screenLocation.x, screenLocation.y); - - switch (ekind) { - case kEventGestureStarted: - qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; - break; - case kEventGestureEnded: - qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; - break; - case kEventGestureRotate: { - CGFloat amount; - if (GetEventParameter(event, kEventParamRotationAmount, 'cgfl', 0, - sizeof(amount), 0, &amount) != noErr) { - handled_event = false; - break; - } - qNGEvent.gestureType = QNativeGestureEvent::Rotate; - qNGEvent.percentage = float(-amount); - break; } - case kEventGestureSwipe: { - HIPoint swipeDirection; - if (GetEventParameter(event, kEventParamSwipeDirection, typeHIPoint, 0, - sizeof(swipeDirection), 0, &swipeDirection) != noErr) { - handled_event = false; - break; - } - qNGEvent.gestureType = QNativeGestureEvent::Swipe; - if (swipeDirection.x == 1) - qNGEvent.angle = 180.0f; - else if (swipeDirection.x == -1) - qNGEvent.angle = 0.0f; - else if (swipeDirection.y == 1) - qNGEvent.angle = 90.0f; - else if (swipeDirection.y == -1) - qNGEvent.angle = 270.0f; - break; } - case kEventGestureMagnify: { - CGFloat amount; - if (GetEventParameter(event, kEventParamMagnificationAmount, 'cgfl', 0, - sizeof(amount), 0, &amount) != noErr) { - handled_event = false; - break; - } - qNGEvent.gestureType = QNativeGestureEvent::Zoom; - qNGEvent.percentage = float(amount); - break; } - } - - QApplication::sendSpontaneousEvent(widget, &qNGEvent); - break; } -#endif // QT_NO_GESTURES - - 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; - if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr) { - Q_ASSERT(false); - } - 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"); - 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())); - } - - 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); - widget->d_func()->paintBackground(&p, qrgn, 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), tint); - } - p.end(); - if (!redirectionOffset.isNull()) - widget->d_func()->restoreRedirected(); - } - - 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(); - - //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); - } 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; - } else if (ekind == kEventControlDragLeave) { - dropWidget = QDragManager::self()->currentTarget(); - if (dropWidget) { - dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragLeave, 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()))) { - if (widget == qt_button_down) - qt_button_down = 0; - } - } - } - break; } - case kEventClassMouse: { - bool send_to_app = false; - 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]; - -#ifdef ALIEN_DEBUG - qDebug() << "Creating NSView for" << widget; -#endif - - 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)) { - bool oldState = widget->testAttribute(Qt::WA_WState_ExplicitShowHide); - if(visible) { - if (!widget->testAttribute(Qt::WA_WState_ExplicitShowHide)) - widget->show(); - } else { - widget->hide(); - if(!oldState) - widget->setAttribute(Qt::WA_WState_ExplicitShowHide, false); - } - } - } -} - -/***************************************************************************** - QWidgetPrivate member functions - *****************************************************************************/ -bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) -{ - // I'm not sure what "up" is - if(!w || !w->isWindow()) - return false; - - QTLWExtra *topData = w->d_func()->topData(); - QWExtra *extraData = w->d_func()->extraData(); - // topData->resizer is only 4 bits, so subtracting -1 from zero causes bad stuff - // to happen, prevent that here (you really want the thing hidden). - if (up >= 0 || topData->resizer != 0) - topData->resizer += up; - OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId())); - { -#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 - QMacCocoaAutoReleasePool pool; - [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_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); -#if !defined(QT_NO_MAINWINDOW) && !defined(QT_NO_TOOLBAR) - // Make sure that QMainWindow has the MacWindowToolBarButtonHint when the - // unifiedTitleAndToolBarOnMac property is ON. This is to avoid reentry of - // setParent() triggered by the QToolBar::event(QEvent::ParentChange). - QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q); - if (mainWindow && mainWindow->unifiedTitleAndToolBarOnMac()) { - data.window_flags |= Qt::MacWindowToolBarButtonHint; - } -#endif -#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) { - wattr |= kWindowNoTitleBarAttribute; - } else if(wclass == kFloatingWindowClass) { - wattr |= kWindowNoTitleBarAttribute; - } 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(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 if(q->testAttribute(Qt::WA_ShowModal)) - wclass = kMovableModalWindowClass; - 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; - } - } - 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 (wattr & kWindowHideOnSuspendAttribute) - HIWindowChangeAvailability(windowRef, kHIWindowExposeHidden, 0); - else - HIWindowChangeAvailability(windowRef, 0, kHIWindowExposeHidden); - 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); - applyMaxAndMinSizeOnWindow(); -} -#else // QT_MAC_USE_COCOA - -void QWidgetPrivate::setWindowLevel() -{ - Q_Q(QWidget); - const QWidget * const windowParent = q->window()->parentWidget(); - const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0; - 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) { - // Correct modality level (NSModalPanelWindowLevel) will be - // set by cocoa when creating a modal session later. - winLevel = NSNormalWindowLevel; - } - - // 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; - [qt_mac_window_for(q) setLevel:winLevel]; -} - -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]; - } else { - [windowRef setHidesOnDeactivate:NO]; - } - if (q->testAttribute(Qt::WA_MacNoShadow)) - [windowRef setHasShadow:NO]; - else - [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; - if (!nsview) { - nsview = qt_mac_create_widget(q, this, 0); - setWinId(WId(nsview)); - } - [windowRef setContentView:nsview]; - [nsview setHidden:NO]; - transferChildren(); - - // Tell Cocoa explicit that we wan't the view to receive key events - // (regardless of focus policy) because this is how it works on other - // platforms (and in the carbon port): - [windowRef makeFirstResponder:nsview]; - - 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); - } - - // Its more performant to handle the mouse cursor - // ourselves, expecially when using alien widgets: - [windowRef disableCursorRects]; - - setWindowLevel(); - 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); - applyMaxAndMinSizeOnWindow(); -} - -#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 = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { - 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 Qt Support 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); - QMacCocoaAutoReleasePool pool; - - 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 { - 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 - // Alien note: a 'window' was supplied as argument, meaning this widget - // is not alien. So therefore the parent cannot be alien either. - 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 - -#ifdef QT_MAC_USE_COCOA - if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) { - // INVARIANT: q is Alien, and we should not create an NSView to back it up. - } else -#endif - 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); - if (q->isVisible()) { - // If q were Alien before, but now became native (e.g. if a call to - // winId was done from somewhere), we need to show the view immidiatly: - QMacCocoaAutoReleasePool pool; - [osview setHidden:NO]; - } -#endif - } - } - - updateIsOpaque(); - - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - registerDropSite(true); - if (q->hasFocus()) - setFocus_sys(); - 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 qt_mac_repaintParentUnderAlienWidget(QWidget *alienWidget) -{ - QWidget *nativeParent = alienWidget->nativeParentWidget(); - if (!nativeParent) - return; - - QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0)); - QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size()); - nativeParent->repaint(dirtyRect); -} - -void QWidget::destroy(bool destroyWindow, bool destroySubWindows) -{ - Q_D(QWidget); - QMacCocoaAutoReleasePool pool; - d->aboutToDestroy(); - if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); - if (!internalWinId()) - qt_mac_repaintParentUnderAlienWidget(this); - d->deactivateWidgetCleanup(); - qt_mac_event_release(this); - if(testAttribute(Qt::WA_WState_Created)) { - 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(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); - } - } - QT_TRY { - d->setWinId(0); - } QT_CATCH (const std::bad_alloc &) { - // swallow - destructors must not throw - } - } -} - -void QWidgetPrivate::transferChildren() -{ - Q_Q(QWidget); - if (!q->internalWinId()) - 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->internalWinId()) { -#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(w) retain]; - [qt_mac_nativeview_for(w) removeFromSuperview]; - [qt_mac_nativeview_for(q) addSubview:qt_mac_nativeview_for(w)]; - [qt_mac_nativeview_for(w) release]; - w->setAttribute(Qt::WA_DropSiteRegistered, oldRegistered); -#endif - } - } - } - } -} - -#ifdef QT_MAC_USE_COCOA -void QWidgetPrivate::setSubWindowStacking(bool set) -{ - // After hitting too many unforeseen bugs trying to put Qt on top of the cocoa child - // window API, we have decided to revert this behaviour as much as we can. We - // therefore now only allow child windows to exist for children of modal dialogs. - static bool use_behaviour_qt473 = !qgetenv("QT_MAC_USE_CHILDWINDOWS").isEmpty(); - - // This will set/remove a visual relationship between parent and child on screen. - // The reason for doing this is to ensure that a child always stacks infront of - // its parent. Unfortunatly is turns out that [NSWindow addChildWindow] has - // several unwanted side-effects, one of them being the moving of a child when - // moving the parent, which we choose to accept. A way tougher side-effect is - // that Cocoa will hide the parent if you hide the child. And in the case of - // a tool window, since it will normally hide when you deactivate the - // application, Cocoa will hide the parent upon deactivate as well. The result often - // being no more visible windows on screen. So, to make a long story short, we only - // allow parent-child relationships between windows that both are either a plain window - // or a dialog. - - Q_Q(QWidget); - if (!q->isWindow()) - return; - NSWindow *qwin = [qt_mac_nativeview_for(q) window]; - if (!qwin) - return; - Qt::WindowType qtype = q->windowType(); - if (set && !(qtype == Qt::Window || qtype == Qt::Dialog)) - return; - if (set && ![qwin isVisible]) - return; - - if (QWidget *parent = q->parentWidget()) { - if (NSWindow *pwin = [qt_mac_nativeview_for(parent) window]) { - if (set) { - Qt::WindowType ptype = parent->window()->windowType(); - if ([pwin isVisible] - && (ptype == Qt::Window || ptype == Qt::Dialog) - && ![qwin parentWindow] - && (use_behaviour_qt473 || parent->windowModality() == Qt::ApplicationModal)) { - NSInteger level = [qwin level]; - [pwin addChildWindow:qwin ordered:NSWindowAbove]; - if ([qwin level] < level) - [qwin setLevel:level]; - } - } else { - [pwin removeChildWindow:qwin]; - } - } - } - - // Only set-up child windows for q if q is modal: - if (set && !use_behaviour_qt473 && q->windowModality() != Qt::ApplicationModal) - return; - - QObjectList widgets = q->children(); - for (int i=0; i<widgets.size(); ++i) { - QWidget *child = qobject_cast<QWidget *>(widgets.at(i)); - if (child && child->isWindow()) { - if (NSWindow *cwin = [qt_mac_nativeview_for(child) window]) { - if (set) { - Qt::WindowType ctype = child->window()->windowType(); - if ([cwin isVisible] && (ctype == Qt::Window || ctype == Qt::Dialog) && ![cwin parentWindow]) { - NSInteger level = [cwin level]; - [qwin addChildWindow:cwin ordered:NSWindowAbove]; - if ([cwin level] < level) - [cwin setLevel:level]; - } - } else { - [qwin removeChildWindow:qt_mac_window_for(child)]; - } - } - } - } -} -#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(effectiveRectFor(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 - if (qt_mac_is_macdrawer(q)) { - oldDrawer = qt_mac_drawer_for(q); - } - if (wasWindow) { - OSWindowRef oldWindow = qt_mac_window_for(old_id); - oldToolbar = [oldWindow toolbar]; - if (oldToolbar) { - [oldToolbar retain]; - oldToolbarVisible = [oldToolbar isVisible]; - [oldWindow setToolbar:nil]; - } - } -#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); - bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); - if (wasCreated && !qt_isGenuineQWidget(q)) - return; - - if (!q->testAttribute(Qt::WA_WState_WindowOpacitySet)) { - q->setWindowOpacity(1.0f); - q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); - } - - 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, - // unless this is an alien widget. ) - const bool nonWindowWithCreatedParent = !q->isWindow() && parent->testAttribute(Qt::WA_WState_Created); - const bool nativeWidget = q->internalWinId() != 0; - if (wasCreated || (nativeWidget && nonWindowWithCreatedParent)) { - createWinId(); - if (q->isWindow()) { -#ifndef QT_MAC_USE_COCOA - // We do this down below for wasCreated, so avoid doing this twice - // (only for performance, it gets called a lot anyway). - if (!wasCreated) { - if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { - mwl->updateHIToolBarStatus(); - } - } -#else - // Simply transfer our toolbar over. Everything should stay put, unlike in Carbon. - if (oldToolbar && !(f & Qt::FramelessWindowHint)) { - OSWindowRef newWindow = qt_mac_window_for(q); - [newWindow setToolbar:oldToolbar]; - [oldToolbar release]; - [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(); -#ifndef QT_MAC_USE_COCOA - // If we were a unified window, We just transfered our toolbars out of the unified toolbar. - // So redo the status one more time. It apparently is not an issue with Cocoa. - if (q->isWindow()) { - if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { - mwl->updateHIToolBarStatus(); - } - } -#endif - - 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 (!internalWinId()) { - 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 (!internalWinId()) { - 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:qt_mac_QStringToNSString(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; - QFileInfo fi(filePath); - [qt_mac_window_for(q) setRepresentedFilename:fi.exists() ? qt_mac_QStringToNSString(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 (validRef) { - status = HIWindowSetProxyFSRef(qt_mac_window_for(q), &ref); - } 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; - if (icon.isNull()) - return; - NSButton *iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; - if (iconButton == nil) { - QCFString string(q->windowTitle()); - const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); - [qt_mac_window_for(q) setRepresentedURL:[NSURL fileURLWithPath:const_cast<NSString *>(tmpString)]]; - iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; - } - if (icon.isNull()) { - [iconButton setImage:nil]; - } else { - QPixmap scaled = pm->scaled(QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation); - NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(scaled)); - [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:qt_mac_QStringToNSString(iconText)]; -#endif - } -} - -void QWidget::grabMouse() -{ - if(isVisible() && !qt_nograb()) { - if(mac_mouse_grabber) - mac_mouse_grabber->releaseMouse(); - mac_mouse_grabber=this; - qt_mac_setMouseGrabCursor(true); - } -} - -#ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &cursor) -{ - if(isVisible() && !qt_nograb()) { - if(mac_mouse_grabber) - mac_mouse_grabber->releaseMouse(); - mac_mouse_grabber=this; - qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor)); - } -} -#endif - -void QWidget::releaseMouse() -{ - if(!qt_nograb() && mac_mouse_grabber == this) { - mac_mouse_grabber = 0; - qt_mac_setMouseGrabCursor(false); - } -} - -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); - qApp->setActiveWindow(tlw); -#else - [win makeKeyWindow]; -#endif - } 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 (updateRedirectedToGraphicsProxyWidget(q, r)) - return; - dirtyOnWidget += r; - macSetNeedsDisplay(r != q->rect() ? r : QRegion()); -} - -void QWidgetPrivate::update_sys(const QRegion &rgn) -{ - Q_Q(QWidget); - if (updateRedirectedToGraphicsProxyWidget(q, rgn)) - return; - dirtyOnWidget += rgn; - macSetNeedsDisplay(rgn); -} - -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(); -#ifndef QT_MAC_USE_COCOA - if (realWindow && !q->testAttribute(Qt::WA_Moved)) { - if (qt_mac_is_macsheet(q)) - recreateMacWindow(); - q->createWinId(); - if (QWidget *p = q->parentWidget()) { - p->createWinId(); - RepositionWindow(qt_mac_window_for(q), qt_mac_window_for(p), kWindowCenterOnParentWindow); - } else { - RepositionWindow(qt_mac_window_for(q), 0, kWindowCenterOnMainScreen); - } - } -#endif - - data.fstrut_dirty = true; - if (realWindow) { - 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 - -#ifdef QT_MAC_USE_COCOA - // Make sure that we end up sending a repaint event to - // the widget if the window has been visible one before: - [qt_mac_get_contentview_for(window) setNeedsDisplay:YES]; -#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()]; - - QWidget *top = 0; - if (QApplicationPrivate::tryModalHelper(q, &top)) { - [window makeKeyAndOrderFront:window]; - // If this window is app modal, we need to start spinning - // a modal session for it. Interrupting - // the event dispatcher will make this happend: - if (data.window_modality == Qt::ApplicationModal) - QEventDispatcherMac::instance()->interrupt(); - } else { - // The window is modally shaddowed, so we need to make - // sure that we don't pop in front of the modal window: - [window orderFront:window]; - if (!top->testAttribute(Qt::WA_DontShowOnScreen)) { - if (NSWindow *modalWin = qt_mac_window_for(top)) - [modalWin orderFront:window]; - } - } - setSubWindowStacking(true); - qt_mac_update_cursor(); -#endif - if (q->windowType() == Qt::Popup) { - qt_button_down = 0; - 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)) { -#ifndef QT_MAC_USE_COCOA - qt_event_request_activate(q); -#endif - } - } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) { -#ifndef QT_MAC_USE_COCOA - HIViewSetVisible(qt_mac_nativeview_for(q), true); -#else - if (NSView *view = qt_mac_nativeview_for(q)) { - // INVARIANT: q is native. Just show the view: - [view setHidden:NO]; - } else { - // INVARIANT: q is alien. Repaint q instead: - q->repaint(); - } -#endif - } - -#ifdef QT_MAC_USE_COCOA - if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ - // Update enter/leave immidiatly, don't wait for a move event. But only - // if no grab exists (even if the grab points to this widget, it seems, ref X11) - QPoint qlocal, qglobal; - QWidget *widgetUnderMouse = 0; - qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); - QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); - qt_last_mouse_receiver = widgetUnderMouse; - qt_last_native_mouse_receiver = widgetUnderMouse ? - (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; - } -#endif - - topLevelAt_cache = 0; - qt_event_request_window_change(q); -} - -QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) -{ -#ifndef QT_MAC_USE_COCOA - CGPoint nativePoint = CGPointMake(pt.x(), pt.y()); - HIViewConvertPoint(&nativePoint, qt_mac_nativeview_for(child->parentWidget()), - qt_mac_nativeview_for(child)); -#else - NSPoint nativePoint = [qt_mac_nativeview_for(child) convertPoint:NSMakePoint(pt.x(), pt.y()) fromView:qt_mac_nativeview_for(child->parentWidget())]; -#endif - return QPoint(nativePoint.x, nativePoint.y); -} - - -void QWidgetPrivate::hide_sys() -{ - Q_Q(QWidget); - if((q->windowType() == Qt::Desktop)) //you can't hide the desktop! - return; - QMacCocoaAutoReleasePool pool; - if(q->isWindow()) { -#ifdef QT_MAC_USE_COCOA - setSubWindowStacking(false); -#endif - 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]; - // Unfortunately it is not as easy as just hiding the window, we need - // to find out if we were in full screen mode. If we were and this is - // the last window in full screen mode then we need to unset the full screen - // mode. If this is not the last visible window in full screen mode then we - // don't change the full screen mode. - if(q->isFullScreen()) - { - bool keepFullScreen = false; - QWidgetList windowList = qApp->topLevelWidgets(); - int windowCount = windowList.count(); - for(int i = 0; i < windowCount; i++) - { - QWidget *w = windowList[i]; - // If it is the same window, we don't need to check :-) - if(q == w) - continue; - // If they are not visible or if they are minimized then - // we just ignore them. - if(!w->isVisible() || w->isMinimized()) - continue; - // Is it full screen? - // Notice that if there is one window in full screen mode then we - // cannot switch the full screen mode off, therefore we just abort. - if(w->isFullScreen()) { - keepFullScreen = true; - break; - } - } - // No windows in full screen mode, so let just unset that flag. - if(!keepFullScreen) - qt_mac_set_fullscreen_mode(false); - } -#endif - toggleDrawers(false); - qt_mac_update_cursor(); -#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 - } -#ifndef QT_MAC_USE_COCOA - // If the window we now hide was the active window, we need - // to find, and activate another window on screen. NB: Cocoa takes care of this - // logic for us (and distinquishes between main windows and key windows) - if (q->isActiveWindow() && !(q->windowType() == Qt::Popup)) { - QWidget *w = 0; - if(q->parentWidget()) - w = q->parentWidget()->window(); - if(!w || (!w->isVisible() && !w->isMinimized())) { - for (WindowPtr wp = GetFrontWindowOfClass(kMovableModalWindowClass, true); - wp; wp = GetNextWindowOfClass(wp, kMovableModalWindowClass, true)) { - if((w = qt_mac_find_window(wp))) - break; - } - if (!w){ - 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; - } - } - } - if(w && w->isVisible() && !w->isMinimized()) { - qt_event_request_activate(w); - } - } -#endif - } else { - invalidateBuffer(q->rect()); -#ifndef QT_MAC_USE_COCOA - HIViewSetVisible(qt_mac_nativeview_for(q), false); -#else - if (NSView *view = qt_mac_nativeview_for(q)) { - // INVARIANT: q is native. Just hide the view: - [view setHidden:YES]; - } else { - // INVARIANT: q is alien. Repaint where q is placed instead: - qt_mac_repaintParentUnderAlienWidget(q); - } -#endif - } - -#ifdef QT_MAC_USE_COCOA - if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ - // Update enter/leave immidiatly, don't wait for a move event. But only - // if no grab exists (even if the grab points to this widget, it seems, ref X11) - QPoint qlocal, qglobal; - QWidget *widgetUnderMouse = 0; - qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse); - QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_native_mouse_receiver); - qt_last_mouse_receiver = widgetUnderMouse; - qt_last_native_mouse_receiver = widgetUnderMouse ? - (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; - } -#endif - - topLevelAt_cache = 0; - 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(); - if(!qApp->desktop()->screenNumber(this)) - qt_mac_set_fullscreen_mode(false); - setParent(parentWidget(), d->topData()->savedFlags); - setGeometry(d->topData()->normalGeometry); - 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 && !(oldstate & Qt::WindowFullScreen)) { -#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 - } -} - -NSComparisonResult compareViews2Raise(id view1, id view2, void *context) -{ - id topView = reinterpret_cast<id>(context); - if (view1 == topView) - return NSOrderedDescending; - if (view2 == topView) - return NSOrderedAscending; - return NSOrderedSame; -} - -void QWidgetPrivate::raise_sys() -{ - Q_Q(QWidget); - if((q->windowType() == Qt::Desktop)) - return; - -#if QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; - if (isRealWindow()) { - // With the introduction of spaces it is not as simple as just raising the window. - // First we need to check if we are in the right space. If we are, then we just continue - // as usual. The problem comes when we are not in the active space. There are two main cases: - // 1. Our parent was moved to a new space. In this case we want the window to be raised - // in the same space as its parent. - // 2. We don't have a parent. For this case we will just raise the window and let Cocoa - // switch to the corresponding space. - // NOTICE: There are a lot of corner cases here. We are keeping this simple for now, if - // required we will introduce special handling for some of them. - if (!q->testAttribute(Qt::WA_DontShowOnScreen) && q->isVisible()) { - OSWindowRef window = qt_mac_window_for(q); - // isOnActiveSpace is available only from 10.6 onwards, so we need to check if it is - // available before calling it. - if([window respondsToSelector:@selector(isOnActiveSpace)]) { - if(![window performSelector:@selector(isOnActiveSpace)]) { - QWidget *parentWidget = q->parentWidget(); - if(parentWidget) { - OSWindowRef parentWindow = qt_mac_window_for(parentWidget); - if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) { - if ([parentWindow performSelector:@selector(isOnActiveSpace)]) { - // The window was created in a different space. Therefore if we want - // to show it in the current space we need to recreate it in the new - // space. - recreateMacWindow(); - window = qt_mac_window_for(q); - } - } - } - } - } - [window orderFront:window]; - } - if (qt_mac_raise_process) { //we get to be the active process now - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly); - } - } else { - NSView *view = qt_mac_nativeview_for(q); - NSView *parentView = [view superview]; - [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)]; - } - topLevelAt_cache = 0; -#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 -} - -NSComparisonResult compareViews2Lower(id view1, id view2, void *context) -{ - id topView = reinterpret_cast<id>(context); - if (view1 == topView) - return NSOrderedAscending; - if (view2 == topView) - return NSOrderedDescending; - return NSOrderedSame; -} - -void QWidgetPrivate::lower_sys() -{ - Q_Q(QWidget); - if((q->windowType() == Qt::Desktop)) - return; -#ifdef QT_MAC_USE_COCOA - if (isRealWindow()) { - OSWindowRef window = qt_mac_window_for(q); - [window orderBack:window]; - } else { - NSView *view = qt_mac_nativeview_for(q); - NSView *parentView = [view superview]; - [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)]; - } - topLevelAt_cache = 0; -#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 -} - -NSComparisonResult compareViews2StackUnder(id view1, id view2, void *context) -{ - const QHash<NSView *, int> &viewOrder = *reinterpret_cast<QHash<NSView *, int> *>(context); - if (viewOrder[view1] < viewOrder[view2]) - return NSOrderedAscending; - if (viewOrder[view1] > viewOrder[view2]) - return NSOrderedDescending; - return NSOrderedSame; -} - -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. - NSView *myView = qt_mac_nativeview_for(q); - NSView *wView = qt_mac_nativeview_for(w); - - QHash<NSView *, int> viewOrder; - NSView *parentView = [myView superview]; - NSArray *subviews = [parentView subviews]; - NSUInteger index = 1; - // make a hash of view->zorderindex and make sure z-value is always odd, - // so that when we modify the order we create a new (even) z-value which - // will not interfere with others. - for (NSView *subview in subviews) { - viewOrder.insert(subview, index * 2); - ++index; - } - viewOrder[myView] = viewOrder[wView] - 1; - - [parentView sortSubviewsUsingFunction:compareViews2StackUnder context:reinterpret_cast<void *>(&viewOrder)]; -#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 -} - -#ifndef QT_MAC_USE_COCOA -/* - 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 - flag -*/ -static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect) -{ - 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 ( - // always repaint on move. - (isMove) || - - // 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 || - - // we are on Panther (no HIViewSetNeedsDisplayInRect) - QSysInfo::MacintoshVersion < QSysInfo::MV_10_4 - ){ - 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); -} -#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)); - - if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) { - // We have no view to move, and no paint engine that - // we can update dirty regions on. So just return: - return; - } - - QMacCocoaAutoReleasePool pool; - - /* - 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). - */ - - // wrect is the same as crect, except that it is - // clipped to fit inside parent (and screen): - QRect wrect; - - // wrectInParentCoordSys will be the same as wrect, except that it is - // originated in q's parent rather than q itself. It starts out in - // parent's Qt coord system, and ends up in parent's coordinate system: - QRect wrectInParentCoordSys = data.crect; - - // If q's parent has been clipped, parentWRect will - // be filled with the parents clipped crect: - QRect parentWRect; - - // Embedded have different meaning on each platform, and on - // Mac, it means that q is a QMacNativeWidget. - bool isEmbeddedWindow = (q->isWindow() && topData()->embedded); -#ifdef QT_MAC_USE_COCOA - NSView *nsview = qt_mac_nativeview_for(q); -#endif - if (!isEmbeddedWindow) { - parentWRect = q->parentWidget()->data->wrect; - } else { - // INVARIANT: q's parent view is not owned by Qt. So we need to - // do some extra calls to get the clipped rect of the parent view: -#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 { - const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX); - parentWRect = wrectRange; - } - } - - if (parentWRect.isValid()) { - // INVARIANT: q's parent has been clipped. - // So we fit our own wrects inside it: - if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) { - wrectInParentCoordSys &= parentWRect; - wrect = wrectInParentCoordSys; - // Make sure wrect is originated in q's coordinate system: - wrect.translate(-data.crect.topLeft()); - } - // // Make sure wrectInParentCoordSys originated in q's parent coordinate system: - wrectInParentCoordSys.translate(-parentWRect.topLeft()); - } else { - // INVARIANT: we dont know yet the clipping rect of q's parent. - // So we may or may not have to adjust our wrects: - - if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) { - // This is where the main optimization is: we have an old wrect from an earlier - // setGeometry call, and the new crect is smaller than it. If the final wrect is - // also inside the old wrect, we can just move q and its children to the new - // location without any clipping: - - // vrect will be the part of q that's will be visible inside - // q's parent. If it inside the old wrect, then we can just move: - QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect(); - vrect.translate(-data.crect.topLeft()); - - if (data.wrect.contains(vrect)) { - wrectInParentCoordSys = data.wrect; - wrectInParentCoordSys.translate(data.crect.topLeft()); -#ifndef QT_MAC_USE_COCOA - HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), - wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); - HIViewSetFrame(qt_mac_nativeview_for(q), &bounds); -#else - if (nsview) { - // INVARIANT: q is native. Set view frame: - NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), - wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); - [nsview setFrame:bounds]; - } else { - // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect): - QWidget *parent = q->parentWidget(); - QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft()); - - QWidget *nativeParent = q->nativeParentWidget(); - QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size()); - - nativeParent->update(dirtyWRect); - } -#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 - // If q is Alien, the following call does nothing: - [nsview setHidden:NO]; -#endif - } - } - return; - } - } - -#ifndef QT_MAC_USE_COCOA - const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX); - if (!validRange.contains(wrectInParentCoordSys)) { - // We're too big, and must clip: - QPoint screenOffset(0, 0); // offset of the part being on screen - const QWidget *parentWidget = q->parentWidget(); - while (parentWidget && !parentWidget->isWindow()) { - screenOffset -= parentWidget->data->crect.topLeft(); - parentWidget = parentWidget->parentWidget(); - } - QRect cropRect(screenOffset.x() - WRECT_MAX, - screenOffset.y() - WRECT_MAX, - 2*WRECT_MAX, - 2*WRECT_MAX); - - wrectInParentCoordSys &=cropRect; - wrect = wrectInParentCoordSys; - wrect.translate(-data.crect.topLeft()); - } -#endif //QT_MAC_USE_COCOA - } - - // unmap if we are outside the valid window system coord system - bool outsideRange = !wrectInParentCoordSys.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 - // If q is Alien, the following call does nothing: - [nsview setHidden:YES]; -#endif - q->setAttribute(Qt::WA_Mapped, false); - } else if (!q->isHidden()) { - mapWindow = true; - } - } - - if (outsideRange) - return; - - // Store the new clipped rect: - 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(); - } - } - -#ifndef QT_MAC_USE_COCOA - // Move the actual HIView: - qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys); - if (jump) - q->update(); -#else - if (nsview) { - // INVARIANT: q is native. Move the actual NSView: - NSRect bounds = NSMakeRect( - wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), - wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); - [nsview setFrame:bounds]; - if (jump) - q->update(); - } else if (QApplicationPrivate::graphicsSystem() == 0){ - // INVARIANT: q is alien and we use native paint engine. - // Schedule updates where q is moved from and to: - const QWidget *parent = q->parentWidget(); - const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft()); - const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft()); - - QWidget *nativeParent = q->nativeParentWidget(); - const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size()); - const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size()); - - const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size(); - const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft(); - - // Resolve/minimize the region that needs to update: - if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) { - // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only - // need to blit pixels, and skip a repaint. But we can only make this work if we - // had access to the backbuffer, so we need to update all: - nativeParent->update(dirtyOldWRect | dirtyNewWRect); - } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) { - // We only need to redraw exposed areas: - nativeParent->update(dirtyNewWRect - dirtyOldWRect); - } else { - nativeParent->update(dirtyOldWRect | dirtyNewWRect); - } - } -#endif - - if (mapWindow && !dontShow) { - q->setAttribute(Qt::WA_Mapped); -#ifndef QT_MAC_USE_COCOA - HIViewSetVisible(qt_mac_nativeview_for(q), true); -#else - // If q is Alien, the following call does nothing: - [nsview setHidden:NO]; -#endif - } -} - -void QWidgetPrivate::adjustWithinMaxAndMinSize(int &w, int &h) -{ - if (QWExtra *extra = extraData()) { - 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 (isRealWindow()) { - w = qMax(0, w); - h = qMax(0, h); - } -} - -void QWidgetPrivate::applyMaxAndMinSizeOnWindow() -{ - Q_Q(QWidget); - QMacCocoaAutoReleasePool pool; - - 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) setContentMinSize:min]; - [qt_mac_window_for(q) setContentMaxSize:max]; -#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 && !q->testAttribute(Qt::WA_DontShowOnScreen)){ - adjustWithinMaxAndMinSize(w, h); -#ifndef QT_MAC_USE_COCOA - if (w != 0 && h != 0) { - topData()->isSetGeometry = 1; - topData()->isMove = isMove; - Rect r; SetRect(&r, x, y, x + w, y + h); - SetWindowBounds(qt_mac_window_for(q), kWindowContentRgn, &r); - topData()->isSetGeometry = 0; - } else { - setGeometry_sys_helper(x, y, w, h, isMove); - } -#else - if (!isMove && !q->testAttribute(Qt::WA_Moved) && !q->isVisible()) { - // INVARIANT: The location of the window has not yet been set. The default will - // instead be to center it on the desktop, or over the parent, if any. Since we now - // resize the window, we need to adjust the top left position to keep the window - // centeralized. And we need to to this now (and before show) in case the positioning - // of other windows (e.g. sub-windows) depend on this position: - if (QWidget *p = q->parentWidget()) { - x = p->geometry().center().x() - (w / 2); - y = p->geometry().center().y() - (h / 2); - } else { - QRect availGeo = QApplication::desktop()->availableGeometry(q); - x = availGeo.center().x() - (w / 2); - y = availGeo.center().y() - (h / 2); - } - } - - QSize olds = q->size(); - const bool isResize = (olds != QSize(w, h)); - 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()); - // The setFrame call will trigger a 'windowDidResize' notification for the corresponding - // NSWindow. The pending flag is set, so that the resize event can be send as non-spontaneous. - if (isResize) - q->setAttribute(Qt::WA_PendingResizeEvent); - QPoint currTopLeft = data.crect.topLeft(); - if (currTopLeft.x() == x && currTopLeft.y() == y - && cocoaFrameRect.size.width != 0 - && cocoaFrameRect.size.height != 0) { - [window setFrame:cocoaFrameRect display:realWindow]; - } else { - // The window is moved and resized (or resized to zero). - // Since Cocoa usually only sends us a resize callback after - // setting a window frame, we issue an explicit move as - // well. To stop Cocoa from optimize away the move (since the move - // would have the same origin as the setFrame call) we shift the - // window back and forth inbetween. - cocoaFrameRect.origin.y += 1; - [window setFrame:cocoaFrameRect display:realWindow]; - cocoaFrameRect.origin.y -= 1; - [window setFrameOrigin:cocoaFrameRect.origin]; - } -#endif - } else { - setGeometry_sys_helper(x, y, w, h, isMove); - } - - topLevelAt_cache = 0; -} - -void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove) -{ - Q_Q(QWidget); - bool realWindow = isRealWindow(); - - 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) - data.window_state = data.window_state & ~Qt::WindowMaximized; - - const bool visible = q->isVisible(); - // Apply size restrictions, applicable for Windows & Widgets. - if (QWExtra *extra = extraData()) { - w = qMin(w, extra->maxw); - h = qMin(h, extra->maxh); - w = qMax(w, extra->minw); - h = qMax(h, extra->minh); - } - data.crect = QRect(x, y, w, h); - - if (realWindow) { - adjustWithinMaxAndMinSize(w, h); - qt_mac_update_sizer(q); - -#ifndef QT_MAC_USE_COCOA - if (q->windowFlags() & Qt::WindowMaximizeButtonHint) { - 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); - } - } - 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_resizeHelper(oldp, olds); - } - - 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(); - applyMaxAndMinSizeOnWindow(); -} - -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()) { - // INVARIANT: Alien paint engine - 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 &qscrollRect) -{ - if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect)) - return; - - Q_Q(QWidget); - if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { - // INVARIANT: Alien paint engine - scrollRect(qscrollRect, dx, dy); - return; - } - - static int accelEnv = -1; - if (accelEnv == -1) { - accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; - } - - // Scroll the whole widget if qscrollRect is not valid: - QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect(); - validScrollRect &= clipRect(); - - // If q is overlapped by other widgets, we cannot just blit pixels since - // this will move overlapping widgets as well. In case we just update: - const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft())); - const bool accelerateScroll = accelEnv && isOpaque && !overlapped; - const bool isAlien = (q->internalWinId() == 0); - const QPoint scrollDelta(dx, dy); - - // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented). - // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is - // documented as undefined, but we exploit it to help factor our code into one function. - const bool scrollChildren = !qscrollRect.isValid(); - - if (!q->updatesEnabled()) { - // We are told not to update anything on q at this point. So unless - // we are supposed to scroll children, we bail out early: - if (!scrollChildren || q->children().isEmpty()) - return; - } - - if (!accelerateScroll) { - if (overlapped) { - QRegion region(validScrollRect); - subtractOpaqueSiblings(region); - update_sys(region); - }else { - update_sys(qscrollRect); - } - return; - } - -#ifdef QT_MAC_USE_COCOA - QMacCocoaAutoReleasePool pool; -#else - Q_UNUSED(isAlien); - // We're not sure what the following call is supposed to achive - // but until we see what it breaks, we don't bring it into the - // Cocoa port: - qt_event_request_window_change(q); -#endif - - // First move all native children. Alien children will indirectly be - // moved when the parent is scrolled. All directly or indirectly moved - // children will receive a move event before the function call returns. - QWidgetList movedChildren; - if (scrollChildren) { - QObjectList children = q->children(); - - for (int i=0; i<children.size(); i++) { - QObject *obj = children.at(i); - if (QWidget *w = qobject_cast<QWidget*>(obj)) { - if (!w->isWindow()) { - w->data->crect = QRect(w->pos() + scrollDelta, w->size()); -#ifndef QT_MAC_USE_COCOA - if (w->testAttribute(Qt::WA_WState_Created)) { - 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 = q->testAttribute(Qt::WA_OpaquePaintEvent); - - if (opaque) - HIViewSetDrawingEnabled(hiview, false); - HIViewSetFrame(hiview, &bounds); - if (opaque) - HIViewSetDrawingEnabled(hiview, true); - } -#else - if (NSView *view = qt_mac_nativeview_for(w)) { - // INVARIANT: w is not alien - [view setFrame:NSMakeRect( - w->data->crect.x(), w->data->crect.y(), - w->data->crect.width(), w->data->crect.height())]; - } -#endif - movedChildren.append(w); - } - } - } - } - - if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) { - // Scroll q itself according to the qscrollRect, and - // call update on any exposed areas so that they get redrawn: - -#ifndef QT_MAC_USE_COCOA - OSViewRef view = qt_mac_nativeview_for(q); - HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height()); - OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &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(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0), - qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height())); - _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); - } -#else - - QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q; - if (!nativeWidget) - return; - OSViewRef view = qt_mac_nativeview_for(nativeWidget); - if (!view) - return; - - // Calculate the rectangles that needs to be redrawn - // after the scroll. This will be source rect minus destination rect: - QRect deltaXRect; - if (dx != 0) { - deltaXRect.setY(validScrollRect.y()); - deltaXRect.setHeight(validScrollRect.height()); - if (dx > 0) { - deltaXRect.setX(validScrollRect.x()); - deltaXRect.setWidth(dx); - } else { - deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx); - deltaXRect.setWidth(-dx); - } - } - - QRect deltaYRect; - if (dy != 0) { - deltaYRect.setX(validScrollRect.x()); - deltaYRect.setWidth(validScrollRect.width()); - if (dy > 0) { - deltaYRect.setY(validScrollRect.y()); - deltaYRect.setHeight(dy); - } else { - deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy); - deltaYRect.setHeight(-dy); - } - } - - if (isAlien) { - // Adjust the scroll rect to the location as seen from the native parent: - QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft())); - validScrollRect.moveTo(scrollTopLeftInsideNative); - } - - // Make the pixel copy rect within the validScrollRect bounds: - NSRect nsscrollRect = NSMakeRect( - validScrollRect.x() + (dx < 0 ? -dx : 0), - validScrollRect.y() + (dy < 0 ? -dy : 0), - validScrollRect.width() + (dx > 0 ? -dx : 0), - validScrollRect.height() + (dy > 0 ? -dy : 0)); - - NSSize deltaSize = NSMakeSize(dx, dy); - [view scrollRect:nsscrollRect by:deltaSize]; - - // Some areas inside the scroll rect might have been marked as dirty from before, which - // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects - // should also move along to ensure that q receives repaints on the correct places. - // Since some of the dirty rects might lay outside, or only intersect with, the scroll - // rect, the old calls to setNeedsDisplay still makes sense. - // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have - // so far not been proven fruitful to solve this problem. - const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects(); - for (int i=0; i<dirtyRectsToScroll.size(); ++i) { - QRect qdirtyRect = dirtyRectsToScroll[i]; - qdirtyRect.translate(dx, dy); - update_sys(qdirtyRect); - } - - // Update newly exposed areas. This will generate new dirty areas on - // q, and therefore, we do it after updating the old dirty rects above: - if (dx != 0) - update_sys(deltaXRect); - if (dy != 0) - update_sys(deltaYRect); - -#endif // QT_MAC_USE_COCOA - } - - for (int i=0; i<movedChildren.size(); i++) { - QWidget *w = movedChildren.at(i); - QMoveEvent e(w->pos(), w->pos() - scrollDelta); - QApplication::sendEvent(w, &e); - } -} - -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); - if(m == PdmWidth) - return (int)rect.size.width; - return (int)rect.size.height; } -#else - if (m == PdmWidth) - return data->crect.width(); - else - return data->crect.height(); -#endif - 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->resizer = 0; - extra->topextra->isSetGeometry = 0; - extra->topextra->isMove = 0; - extra->topextra->wattr = 0; - extra->topextra->wclass = 0; - extra->topextra->group = 0; - extra->topextra->windowIcon = 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; - } - if (extra->topextra->windowIcon) { - ReleaseIconRef(extra->topextra->windowIcon); - extra->topextra->windowIcon = 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 - NSWindow *win = qt_mac_window_for(q); - if (on) { - if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaWindow) class]]) - [static_cast<QT_MANGLE_NAMESPACE(QCocoaWindow) *>(win) registerDragTypes]; - else if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaPanel) class]]) - [static_cast<QT_MANGLE_NAMESPACE(QCocoaPanel) *>(win) registerDragTypes]; - } -#endif -} - -void QWidgetPrivate::registerTouchWindow(bool enable) -{ - Q_UNUSED(enable); -#ifdef QT_MAC_USE_COCOA -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) - return; - - Q_Q(QWidget); - if (enable == touchEventsEnabled) - return; - - QCocoaView *view = static_cast<QCocoaView *>(qt_mac_effectiveview_for(q)); - if (!view) - return; - - if (enable) { - ++view->alienTouchCount; - if (view->alienTouchCount == 1) { - touchEventsEnabled = true; - [view setAcceptsTouchEvents:YES]; - } - } else { - --view->alienTouchCount; - if (view->alienTouchCount == 0) { - touchEventsEnabled = false; - [view setAcceptsTouchEvents:NO]; - } - } -#endif -#endif -} - -void QWidgetPrivate::setMask_sys(const QRegion ®ion) -{ - Q_UNUSED(region); - Q_Q(QWidget); - -#ifndef QT_MAC_USE_COCOA - if (q->isWindow()) - ReshapeCustomWindow(qt_mac_window_for(q)); - else - HIViewReshapeStructure(qt_mac_nativeview_for(q)); -#else - if (!q->internalWinId()) - return; - - if (extra->mask.isEmpty()) { - extra->maskBits = QImage(); - finishCocoaMaskSetup(); - } else { - syncCocoaMask(); - } - - topLevelAt_cache = 0; -#endif -} - -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; - - OSWindowRef oswindow = qt_mac_window_for(q); -#if QT_MAC_USE_COCOA - [oswindow setAlphaValue:level]; -#else - SetWindowAlpha(oswindow, 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) { - if(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]; - } - macSetNeedsDisplay(QRegion()); -} -#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; - if (!pe) - pe = new QCoreGraphicsPaintEngine(); - if (pe->isActive()) { - QPaintEngine *engine = new QCoreGraphicsPaintEngine(); - engine->setAutoDestruct(true); - return engine; - } - 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 - QMacCocoaAutoReleasePool pool; - bool alreadySheet = [windowRef styleMask] & NSDocModalWindowMask; - - if (windowParent && q->windowModality() == Qt::WindowModal){ - // INVARIANT: Window should be window-modal (which implies a sheet). - if (!alreadySheet) { - // NB: the following call will call setModal_sys recursivly: - recreateMacWindow(); - windowRef = qt_mac_window_for(q); - } - 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 { - // INVARIANT: Window shold _not_ be window-modal (and as such, not a sheet). - if (alreadySheet){ - // NB: the following call will call setModal_sys recursivly: - recreateMacWindow(); - windowRef = qt_mac_window_for(q); - } - if (q->windowModality() == Qt::NonModal - && primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) { - // INVARIANT: Our window has a parent that is application modal. - // This means that q is supposed to be on top of this window and - // not be modally shaddowed: - if ([windowRef isKindOfClass:[NSPanel class]]) - [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES]; - } - } - -#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 { - // Window should be application-modal (which implies NOT using a sheet). - if (old_wclass == kSheetWindowClass){ - // We cannot convert a sheet to a window. - // 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 = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q)); - if (q->testAttribute(Qt::WA_MacBrushedMetal)) { - if (layout) - layout->updateHIToolBarStatus(); - ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalAttribute, 0); - ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalNoContentSeparatorAttribute, 0); - } else { - 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_s60.cpp b/src/gui/kernel/qwidget_s60.cpp deleted file mode 100644 index e28a75a6ab..0000000000 --- a/src/gui/kernel/qwidget_s60.cpp +++ /dev/null @@ -1,1450 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwidget_p.h" -#include "qdesktopwidget.h" -#include "qapplication.h" -#include "qapplication_p.h" -#include "private/qbackingstore_p.h" -#include "qevent.h" -#include "qt_s60_p.h" - -#include "qbitmap.h" -#include "private/qwindowsurface_s60_p.h" - -#include <qinputcontext.h> - -#ifdef Q_WS_S60 -#include <aknappui.h> -#include <eikbtgpc.h> -#endif - -// This is necessary in order to be able to perform delayed invocation on slots -// which take arguments of type WId. One example is -// QWidgetPrivate::_q_delayedDestroy, which is used to delay destruction of -// CCoeControl objects until after the CONE event handler has finished running. -Q_DECLARE_METATYPE(WId) - -// Workaround for the fact that S60 SDKs 3.x do not contain the akntoolbar.h -// header, even though the documentation says that it should be there, and indeed -// it is present in the library. -class CAknToolbar : public CAknControl, - public MCoeControlObserver, - public MCoeControlBackground, - public MEikCommandObserver, - public MAknFadedComponent -{ -public: - IMPORT_C void SetToolbarVisibility(const TBool visible); -}; - -QT_BEGIN_NAMESPACE - -extern bool qt_nograb(); - -QWidget *QWidgetPrivate::mouseGrabber = 0; -QWidget *QWidgetPrivate::keyboardGrabber = 0; -CEikButtonGroupContainer *QS60Data::cba = 0; - -int qt_symbian_create_desktop_on_screen = -1; - -static bool isEqual(const QList<QAction*>& a, const QList<QAction*>& b) -{ - if ( a.count() != b.count()) - return false; - int index=0; - while (index<a.count()) { - if (a.at(index)->softKeyRole() != b.at(index)->softKeyRole()) - return false; - if (a.at(index)->text().compare(b.at(index)->text())!=0) - return false; - index++; - } - return true; -} - -void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &) -{ - // Note: based on x11 implementation - - static const int XCOORD_MAX = 16383; - static const int WRECT_MAX = 16383; - - 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. - Symbian coordinate system for this widget (relative to wrect). - Qt coordinate system for parent - Symbian 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 Symbian geometry of my widget. (starts out in parent's Qt coord sys, and ends up in parent's Symbian 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) - data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height())); - 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) - data.winid->DrawableWindow()->SetVisible(EFalse); - 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(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 (!parent->internalWinId()) - xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0))); - - data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height())); - } - - if (mapWindow and !dontShow) { - q->setAttribute(Qt::WA_Mapped); - if (q->internalWinId()) - q->internalWinId()->DrawableWindow()->SetVisible(ETrue); - } - - if (jump && data.winid) { - RWindow *const window = static_cast<RWindow *>(data.winid->DrawableWindow()); - window->Invalidate(TRect(0, 0, wrect.width(), wrect.height())); - } -} - -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; - - QPoint oldPos(q->pos()); - QSize oldSize(q->size()); - QRect oldGeom(data.crect); - - // Lose maximized status if deliberate resize - if (w != oldSize.width() || h != oldSize.height()) - data.window_state &= ~Qt::WindowMaximized; - - 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); - else { - uint s = data.window_state; - s &= ~(Qt::WindowMaximized | Qt::WindowFullScreen); - data.window_state = s; - } - - bool isResize = w != oldSize.width() || h != oldSize.height(); - if (!isMove && !isResize) - return; - - if (q->isWindow()) { - 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); - data.window_state &= ~Qt::WindowFullScreen; - } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) { - q->setAttribute(Qt::WA_OutsideWSRange, false); - - // put the window in its place and show it - q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h))); - data.crect.setRect(x, y, w, h); - show_sys(); - } else { - QRect r = QRect(x, y, w, h); - data.crect = r; - q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h))); - topData()->normalGeometry = data.crect; - } - QSymbianControl *window = static_cast<QSymbianControl *>(q->internalWinId()); - window->ensureFixNativeOrientation(); - } else { - data.crect.setRect(x, y, w, h); - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false; - - 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 && maybeBackingStore()) - 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(); - } - - if (q->isVisible()) { - if (isMove && q->pos() != oldPos) { - QMoveEvent e(q->pos(), oldPos); - QApplication::sendEvent(q, &e); - } - if (isResize) { - bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); - const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra - && !extra->topextra->inTopLevelResize; - if (setTopLevelResize) - extra->topextra->inTopLevelResize = true; - QResizeEvent e(q->size(), oldSize); - QApplication::sendEvent(q, &e); - if (!q->testAttribute(Qt::WA_StaticContents) && q->internalWinId()) - q->internalWinId()->DrawDeferred(); - 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::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(); - - 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); - - if (popup) - flags |= Qt::WindowStaysOnTopHint; // a popup stays on top - - TRect clientRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); - int sw = clientRect.Width(); - int sh = clientRect.Height(); - - if (desktop) { - symbianScreenNumber = qMax(qt_symbian_create_desktop_on_screen, 0); - TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); - data.crect.setRect(0, 0, screenSize.iWidth, screenSize.iHeight); - q->setAttribute(Qt::WA_DontShowOnScreen); - } else if (topLevel && !q->testAttribute(Qt::WA_Resized)){ - int width = sw; - int height = sh; - if (symbianScreenNumber > 0) { - TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); - width = screenSize.iWidth; - height = screenSize.iHeight; - } - if (extra) { - width = qMax(qMin(width, extra->maxw), extra->minw); - height = qMax(qMin(height, extra->maxh), extra->minh); - } - data.crect.setSize(QSize(width, height)); - } - - CCoeControl *const destroyw = destroyOldWindow ? data.winid : 0; - - createExtra(); - if (window) { - setWinId(window); - TRect tr = window->Rect(); - data.crect.setRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height()); - - } else if (topLevel) { - if (!q->testAttribute(Qt::WA_Moved) && !q->testAttribute(Qt::WA_DontShowOnScreen)) - data.crect.moveTopLeft(QPoint(clientRect.iTl.iX, clientRect.iTl.iY)); - - QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); - Q_CHECK_PTR(control); - - QT_TRAP_THROWING(control->ConstructL(true, desktop)); - control->SetMopParent(static_cast<CEikAppUi*>(S60->appUi())); - - // Symbian windows are always created in an inactive state - // We perform this assignment for the case where the window is being re-created - // as a result of a call to setParent_sys, on either this widget or one of its - // ancestors. - extra->activated = 0; - - if (!desktop) { - TInt stackingFlags; - if ((q->windowType() & Qt::Popup) == Qt::Popup) { - stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus; - } else { - stackingFlags = ECoeStackFlagStandard; - } - control->MakeVisible(false); - QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags)); - // Avoid keyboard focus to a hidden window. - control->setFocusSafely(false); - - RDrawableWindow *const drawableWindow = control->DrawableWindow(); - // Request mouse move events. - drawableWindow->PointerFilter(EPointerFilterEnterExit - | EPointerFilterMove | EPointerFilterDrag, 0); - drawableWindow->EnableVisibilityChangeEvents(); - - } - - q->setAttribute(Qt::WA_WState_Created); - - int x, y, w, h; - data.crect.getRect(&x, &y, &w, &h); - control->SetRect(TRect(TPoint(x, y), TSize(w, h))); - - // We wait until the control is fully constructed before calling setWinId, because - // this generates a WinIdChanged event. - setWinId(control.take()); - - if (!desktop) - s60UpdateIsOpaque(); // must be called after setWinId() - - } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create native child widget - - QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); - Q_CHECK_PTR(control); - - QT_TRAP_THROWING(control->ConstructL(!parentWidget)); - - // Symbian windows are always created in an inactive state - // We perform this assignment for the case where the window is being re-created - // as a result of a call to setParent_sys, on either this widget or one of its - // ancestors. - extra->activated = 0; - - TInt stackingFlags; - if ((q->windowType() & Qt::Popup) == Qt::Popup) { - stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus; - } else { - stackingFlags = ECoeStackFlagStandard; - } - control->MakeVisible(false); - QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags)); - // Avoid keyboard focus to a hidden window. - control->setFocusSafely(false); - - q->setAttribute(Qt::WA_WState_Created); - int x, y, w, h; - data.crect.getRect(&x, &y, &w, &h); - control->SetRect(TRect(TPoint(x, y), TSize(w, h))); - - RDrawableWindow *const drawableWindow = control->DrawableWindow(); - // Request mouse move events. - drawableWindow->PointerFilter(EPointerFilterEnterExit - | EPointerFilterMove | EPointerFilterDrag, 0); - drawableWindow->EnableVisibilityChangeEvents(); - - if (q->isVisible() && q->testAttribute(Qt::WA_Mapped)) { - activateSymbianWindow(control.data()); - control->MakeVisible(true); - } - - // We wait until the control is fully constructed before calling setWinId, because - // this generates a WinIdChanged event. - setWinId(control.take()); - } - - if (destroyw) { - destroyw->ControlEnv()->AppUi()->RemoveFromStack(destroyw); - - // Delay deletion of the control in case this function is called in the - // context of a CONE event handler such as - // CCoeControl::ProcessPointerEventL - QMetaObject::invokeMethod(q, "_q_delayedDestroy", - Qt::QueuedConnection, Q_ARG(WId, destroyw)); - } - - if (q->testAttribute(Qt::WA_AcceptTouchEvents)) - registerTouchWindow(); -} - - -void QWidgetPrivate::show_sys() -{ - Q_Q(QWidget); - - if (q->testAttribute(Qt::WA_OutsideWSRange)) - return; - - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - - q->setAttribute(Qt::WA_Mapped); - - if (q->testAttribute(Qt::WA_DontShowOnScreen)) { - invalidateBuffer(q->rect()); - return; - } - - if (q->internalWinId()) { - if (!extra->activated) - activateSymbianWindow(); - - QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId()); - const bool isFullscreen = q->windowState() & Qt::WindowFullScreen; - const TBool cbaRequested = q->windowFlags() & Qt::WindowSoftkeysVisibleHint; - -#ifdef Q_WS_S60 - // Lazily initialize the S60 screen furniture when the first window is shown. - if (q->isWindow() && !QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes) - && !S60->buttonGroupContainer() && !S60->statusPane()) { - - if (!q->testAttribute(Qt::WA_DontShowOnScreen)) { - - // Create the status pane and CBA here - CEikAppUi *ui = static_cast<CEikAppUi *>(S60->appUi()); - MEikAppUiFactory *factory = CEikonEnv::Static()->AppUiFactory(); - - QT_TRAP_THROWING( - factory->CreateResourceIndependentFurnitureL(ui); - - TRect boundingRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); - - CEikButtonGroupContainer *cba = CEikButtonGroupContainer::NewL(CEikButtonGroupContainer::ECba, - CEikButtonGroupContainer::EHorizontal,ui,R_AVKON_SOFTKEYS_EMPTY_WITH_IDS); - if (isFullscreen && !cbaRequested) - cba->MakeVisible(false); - - CEikButtonGroupContainer *oldCba = factory->SwapButtonGroup(cba); - Q_ASSERT(!oldCba); - S60->setButtonGroupContainer(cba); - - // If the creation of the first widget is delayed, for example by doing it - // inside the event loop, S60 somehow "forgets" to set the visibility of the - // toolbar (the three middle softkeys) when you flip the phone over, so we - // need to do it ourselves to avoid a "hole" in the application, even though - // Qt itself does not use the toolbar directly.. - CAknAppUi *appui = dynamic_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi()); - if (appui) { - CAknToolbar *toolbar = appui->PopupToolbar(); - if (toolbar && !toolbar->IsVisible()) - toolbar->SetToolbarVisibility(ETrue); - } - - CEikMenuBar *menuBar = new(ELeave) CEikMenuBar; - menuBar->ConstructL(ui, 0, R_AVKON_MENUPANE_EMPTY); - menuBar->SetMenuType(CEikMenuBar::EMenuOptions); - S60->appUi()->AddToStackL(menuBar,ECoeStackPriorityMenu,ECoeStackFlagRefusesFocus); - - CEikMenuBar *oldMenu = factory->SwapMenuBar(menuBar); - Q_ASSERT(!oldMenu); - ) - - if (S60->statusPane()) { - // Use QDesktopWidget as the status pane observer to proxy for the AppUi. - // Can't use AppUi directly because it privately inherits from MEikStatusPaneObserver. - QSymbianControl *desktopControl = static_cast<QSymbianControl *>(QApplication::desktop()->winId()); - S60->statusPane()->SetObserver(desktopControl); - if (isFullscreen) { - const bool cbaVisible = S60->buttonGroupContainer() && S60->buttonGroupContainer()->IsVisible(); - S60->setStatusPaneAndButtonGroupVisibility(false, cbaVisible); - } - } - } - } -#endif - - // Fill client area if maximized OR - // Put window below status pane unless the window has an explicit position. - if (!isFullscreen) { - if (q->windowState() & Qt::WindowMaximized) { - TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); - id->SetExtent(r.iTl, r.Size()); - } else if (!q->testAttribute(Qt::WA_Moved) && q->windowType() != Qt::Dialog) { - id->SetPosition(static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl); - } - } - - id->MakeVisible(true); - - if(q->isWindow()&&!q->testAttribute(Qt::WA_ShowWithoutActivating)) - id->setFocusSafely(true); - } - - invalidateBuffer(q->rect()); -} - -void QWidgetPrivate::activateSymbianWindow(WId wid) -{ - Q_Q(QWidget); - - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - Q_ASSERT(q->testAttribute(Qt::WA_Mapped)); - Q_ASSERT(!extra->activated); - - if(!wid) - wid = q->internalWinId(); - - Q_ASSERT(wid); - - QT_TRAP_THROWING(wid->ActivateL()); - extra->activated = 1; -} - -void QWidgetPrivate::hide_sys() -{ - Q_Q(QWidget); - - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - deactivateWidgetCleanup(); - QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId()); - - if (id) { - //Incorrect optimization - for popup windows, Qt's focus is moved before - //hide_sys is called, resulting in the popup window keeping its elevated - //position in the CONE control stack. - //This can result in keyboard focus being in an invisible widget in some - //conditions - e.g. QTBUG-4733 - //if(id->IsFocused()) // Avoid unnecessary calls to FocusChanged() - id->setFocusSafely(false); - id->MakeVisible(false); - if (QWidgetBackingStore *bs = maybeBackingStore()) - bs->releaseBuffer(); - } else { - invalidateBuffer(q->rect()); - } - - q->setAttribute(Qt::WA_Mapped, false); -} - -void QWidgetPrivate::setFocus_sys() -{ - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created) && q->window()->windowType() != Qt::Popup) - if (!q->effectiveWinId()->IsFocused()) // Avoid unnecessry calls to FocusChanged() - static_cast<QSymbianControl *>(q->effectiveWinId())->setFocusSafely(true); -} - -void QWidgetPrivate::raise_sys() -{ - Q_Q(QWidget); - - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - if (q->internalWinId()) { - q->internalWinId()->DrawableWindow()->SetOrdinalPosition(0); - - // If toplevel widget, raise app to foreground - if (q->isWindow()) - S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), 0); - } -} - -void QWidgetPrivate::lower_sys() -{ - Q_Q(QWidget); - - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - if (q->internalWinId()) { - // If toplevel widget, lower app to background - if (q->isWindow()) - S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), -1); - else - q->internalWinId()->DrawableWindow()->SetOrdinalPosition(-1); - } - - if (!q->isWindow()) - invalidateBuffer(q->rect()); -} - -void QWidgetPrivate::setModal_sys() -{ - -} - -void QWidgetPrivate::stackUnder_sys(QWidget* w) -{ - Q_Q(QWidget); - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - - if (q->internalWinId() && w->internalWinId()) { - RDrawableWindow *const thisWindow = q->internalWinId()->DrawableWindow(); - RDrawableWindow *const otherWindow = w->internalWinId()->DrawableWindow(); - thisWindow->SetOrdinalPosition(otherWindow->OrdinalPosition() + 1); - } - - if (!q->isWindow() || !w->internalWinId()) - invalidateBuffer(q->rect()); -} - -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->testAttribute(Qt::WA_WState_Created)) - continue; - if (!w->isWindow()) { - w->d_func()->invalidateBuffer(w->rect()); - WId parent = q->effectiveWinId(); - WId child = w->effectiveWinId(); - if (parent != child) { - // Child widget is native. Because Symbian windows cannot be - // re-parented, we must re-create the window. - const WId window = 0; - const bool initializeWindow = false; - const bool destroyOldWindow = true; - w->d_func()->create_sys(window, initializeWindow, destroyOldWindow); - } - // ### TODO: We probably also need to update the component array here - w->d_func()->reparentChildren(); - } else { - bool showIt = w->isVisible(); - QPoint old_pos = w->pos(); - w->setParent(q, w->windowFlags()); - w->move(old_pos); - if (showIt) - w->show(); - } - } - } -} - -void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) -{ - Q_Q(QWidget); - - if (parent && parent->windowType() == Qt::Desktop) { - symbianScreenNumber = qt_widget_private(parent)->symbianScreenNumber; - parent = 0; - } - - bool wasCreated = q->testAttribute(Qt::WA_WState_Created); - - if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); - - if (q->testAttribute(Qt::WA_DropSiteRegistered)) - q->setAttribute(Qt::WA_DropSiteRegistered, false); - - QSymbianControl *old_winid = static_cast<QSymbianControl *>(wasCreated ? data.winid : 0); - if ((q->windowType() == Qt::Desktop)) - old_winid = 0; - - // old_winid may not have received a 'not visible' visibility - // changed event before being destroyed; make sure that it is - // removed from the backing store's list of visible windows. - if (old_winid) - S60->controlVisibilityChanged(old_winid, false); - - setWinId(0); - - // 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) { - old_winid->MakeVisible(false); - if (old_winid->IsFocused()) // Avoid unnecessary calls to FocusChanged() - old_winid->setFocusSafely(false); - old_winid->SetParent(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 (old_winid) { - CBase::Delete(old_winid); - } - - if (q->testAttribute(Qt::WA_AcceptDrops) - || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) - q->setAttribute(Qt::WA_DropSiteRegistered, true); - - invalidateBuffer(q->rect()); -} - -void QWidgetPrivate::setConstraints_sys() -{ - -} - - -void QWidgetPrivate::s60UpdateIsOpaque() -{ - Q_Q(QWidget); - - if (!q->testAttribute(Qt::WA_WState_Created)) - return; - - const bool writeAlpha = extraData()->nativePaintMode == QWExtra::BlitWriteAlpha; - if (!q->testAttribute(Qt::WA_TranslucentBackground) && !writeAlpha) - return; - const bool requireAlphaChannel = !isOpaque || writeAlpha; - - createTLExtra(); - - RWindow *const window = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow()); - -#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE - if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { - window->SetSurfaceTransparency(!isOpaque); - extra->topextra->nativeWindowTransparencyEnabled = !isOpaque; - return; - } -#endif - if (requireAlphaChannel) { - const TDisplayMode displayMode = static_cast<TDisplayMode>(window->SetRequiredDisplayMode(EColor16MA)); - if (window->SetTransparencyAlphaChannel() == KErrNone) { - window->SetBackgroundColor(TRgb(255, 255, 255, 0)); - extra->topextra->nativeWindowTransparencyEnabled = 1; - if (extra->topextra->backingStore.data() && ( - QApplicationPrivate::graphics_system_name == QLatin1String("openvg") - || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"))) { - // Semi-transparent EGL surfaces aren't supported. We need to - // recreate backing store to get translucent surface (raster surface). - extra->topextra->backingStore.create(q); - extra->topextra->backingStore.registerWidget(q); - // FixNativeOrientation() will not work without an EGL surface. - q->setAttribute(Qt::WA_SymbianNoSystemRotation, false); - } - } - } else if (extra->topextra->nativeWindowTransparencyEnabled) { - window->SetTransparentRegion(TRegionFix<1>()); - extra->topextra->nativeWindowTransparencyEnabled = 0; - } -} - -void QWidgetPrivate::setWindowIcon_sys(bool forceReset) -{ -#ifdef Q_WS_S60 - Q_Q(QWidget); - - if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow() ) - return; - - QTLWExtra* topData = this->topData(); - if (topData->iconPixmap && !forceReset) - // already been set - return; - - TRect cPaneRect; - TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EContextPane, cPaneRect ); - CAknContextPane* contextPane = S60->contextPane(); - if (found && contextPane) { // We have context pane with valid metrics - QIcon icon = q->windowIcon(); - if (!icon.isNull()) { - // Valid icon -> set it as an context pane picture - QSize size = icon.actualSize(QSize(cPaneRect.Size().iWidth, cPaneRect.Size().iHeight)); - QPixmap pm = icon.pixmap(size); - QBitmap mask = pm.mask(); - if (mask.isNull()) { - mask = QBitmap(pm.size()); - mask.fill(Qt::color1); - } - - CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap(); - CFbsBitmap* nMask = mask.toSymbianCFbsBitmap(); - contextPane->SetPicture(nBitmap,nMask); - } else { - // Icon set to null -> set context pane picture to default - QT_TRAP_THROWING(contextPane->SetPictureToDefaultL()); - } - } else { - // Context pane does not exist, try setting small icon to title pane - TRect titlePaneRect; - TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ETitlePane, titlePaneRect ); - CAknTitlePane* titlePane = S60->titlePane(); - if (found && titlePane) { // We have title pane with valid metrics - // The API to get title_pane graphics size is not public -> assume square space based - // on titlebar font height. CAknBitmap would be optimum, wihtout setting the size, since - // then title pane would automatically scale the bitmap. Unfortunately it is not public API - // Also this function is leaving, although it is not named as such. - const CFont * font; - QT_TRAP_THROWING(font = AknLayoutUtils::FontFromId(EAknLogicalFontTitleFont)); - TSize iconSize(font->HeightInPixels(), font->HeightInPixels()); - - QIcon icon = q->windowIcon(); - if (!icon.isNull()) { - // Valid icon -> set it as an title pane small picture - QSize size = icon.actualSize(QSize(iconSize.iWidth, iconSize.iHeight)); - QPixmap pm = icon.pixmap(size); - QBitmap mask = pm.mask(); - if (mask.isNull()) { - mask = QBitmap(pm.size()); - mask.fill(Qt::color1); - } - - CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap(); - CFbsBitmap* nMask = mask.toSymbianCFbsBitmap(); - titlePane->SetSmallPicture( nBitmap, nMask, ETrue ); - } else { - // Icon set to null -> set context pane picture to default - titlePane->SetSmallPicture( NULL, NULL, EFalse ); - } - } - } - -#else - Q_UNUSED(forceReset) -#endif -} - -void QWidgetPrivate::setWindowTitle_sys(const QString &caption) -{ -#ifdef Q_WS_S60 - Q_Q(QWidget); - if (q->isWindow()) { - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - CAknTitlePane* titlePane = S60->titlePane(); - if (titlePane) { - if (caption.isEmpty()) { - QT_TRAP_THROWING(titlePane->SetTextToDefaultL()); - } else { - QT_TRAP_THROWING(titlePane->SetTextL(qt_QString2TPtrC(caption))); - } - } - } -#else - Q_UNUSED(caption) -#endif -} - -void QWidgetPrivate::setWindowIconText_sys(const QString & /*iconText */) -{ - -} - -void QWidgetPrivate::scroll_sys(int dx, int dy) -{ - Q_Q(QWidget); - - scrollChildren(dx, dy); - if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) { - scrollRect(q->rect(), dx, dy); - } else { - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - RDrawableWindow *const window = q->internalWinId()->DrawableWindow(); - window->Scroll(TPoint(dx, dy)); - } -} - -void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) -{ - Q_Q(QWidget); - - if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) { - scrollRect(r, dx, dy); - } else { - Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); - RDrawableWindow *const window = q->internalWinId()->DrawableWindow(); - window->Scroll(TPoint(dx, dy), qt_QRect2TRect(r)); - } -} - -/*! - For this function to work in the emulator, you must add: - TRANSPARENCY - To a line in the wsini.ini file. -*/ -void QWidgetPrivate::setWindowOpacity_sys(qreal) -{ - // ### TODO: Implement uniform window transparency -} - -void QWidgetPrivate::updateFrameStrut() -{ - -} - -void QWidgetPrivate::updateSystemBackground() -{ - -} - -void QWidgetPrivate::registerDropSite(bool /* on */) -{ - -} - -void QWidgetPrivate::createTLSysExtra() -{ - extra->topextra->inExpose = 0; - extra->topextra->nativeWindowTransparencyEnabled = 0; -} - -void QWidgetPrivate::deleteTLSysExtra() -{ - extra->topextra->backingStore.destroy(); -} - -void QWidgetPrivate::createSysExtra() -{ - extra->activated = 0; - extra->nativePaintMode = QWExtra::Default; - extra->receiveNativePaintEvents = 0; -} - -void QWidgetPrivate::deleteSysExtra() -{ - // this should only be non-zero if destroy() has not run due to constructor fail - if (data.winid) { - data.winid->ControlEnv()->AppUi()->RemoveFromStack(data.winid); - delete data.winid; - data.winid = 0; - } -} - -QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() -{ - return new QS60WindowSurface(q_func()); -} - -void QWidgetPrivate::setMask_sys(const QRegion& /* region */) -{ - -} - -void QWidgetPrivate::registerTouchWindow() -{ -#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_WState_Created) && q->windowType() != Qt::Desktop) { - RWindow *rwindow = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow()); - QSymbianControl *window = static_cast<QSymbianControl *>(q->effectiveWinId()); - //Enabling advanced pointer events for controls that already have active windows causes a panic. - if (!window->isControlActive()) - rwindow->EnableAdvancedPointers(); - } -#endif -} - -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 { - CWsScreenDevice *scr = S60->screenDevice(this); - switch(m) { - case PdmDpiX: - case PdmPhysicalDpiX: - if (d->extra && d->extra->customDpiX) { - val = d->extra->customDpiX; - } else { - const QWidgetPrivate *p = d; - while (p->parent) { - p = static_cast<const QWidget *>(p->parent)->d_func(); - if (p->extra && p->extra->customDpiX) { - val = p->extra->customDpiX; - break; - } - } - if (p == d || !(p->extra && p->extra->customDpiX)) - val = S60->defaultDpiX; - } - break; - case PdmDpiY: - case PdmPhysicalDpiY: - if (d->extra && d->extra->customDpiY) { - val = d->extra->customDpiY; - } else { - const QWidgetPrivate *p = d; - while (p->parent) { - p = static_cast<const QWidget *>(p->parent)->d_func(); - if (p->extra && p->extra->customDpiY) { - val = p->extra->customDpiY; - break; - } - } - if (p == d || !(p->extra && p->extra->customDpiY)) - val = S60->defaultDpiY; - } - break; - case PdmWidthMM: - { - TInt twips = scr->HorizontalPixelsToTwips(data->crect.width()); - val = (int)(twips * (25.4/KTwipsPerInch)); - break; - } - case PdmHeightMM: - { - TInt twips = scr->VerticalPixelsToTwips(data->crect.height()); - val = (int)(twips * (25.4/KTwipsPerInch)); - break; - } - case PdmNumColors: - val = TDisplayModeUtils::NumDisplayModeColors(scr->DisplayMode()); - break; - case PdmDepth: - val = TDisplayModeUtils::NumDisplayModeBitsPerPixel(scr->DisplayMode()); - break; - default: - val = 0; - qWarning("QWidget::metric: Invalid metric command"); - } - } - return val; -} - -QPaintEngine *QWidget::paintEngine() const -{ - return 0; -} - -QPoint QWidget::mapToGlobal(const QPoint &pos) const -{ - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { - - QPoint p = pos + data->crect.topLeft(); - return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p); - - } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel - QPoint tp = geometry().topLeft(); - return pos + tp; - } - - // Native window case - const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen(); - const QPoint globalPos = QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY) + pos; - return globalPos; -} - -QPoint QWidget::mapFromGlobal(const QPoint &pos) const -{ - Q_D(const QWidget); - if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { - QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos); - return p - data->crect.topLeft(); - } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel - QPoint tp = geometry().topLeft(); - return pos - tp; - } - - // Native window case - const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen(); - const QPoint widgetPos = pos - QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY); - return widgetPos; -} - -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 QWidget::setWindowState(Qt::WindowStates newstate) -{ - Q_D(QWidget); - - Qt::WindowStates oldstate = windowState(); - - const TBool isFullscreen = newstate & Qt::WindowFullScreen; -#ifdef Q_WS_S60 - const TBool cbaRequested = windowFlags() & Qt::WindowSoftkeysVisibleHint; - const TBool cbaVisible = CEikButtonGroupContainer::Current() ? true : false; - const TBool softkeyVisibilityChange = isFullscreen && (cbaRequested != cbaVisible); - - if (oldstate == newstate && !softkeyVisibilityChange) - return; -#endif // Q_WS_S60 - - if (isWindow()) { - createWinId(); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - - const bool wasResized = testAttribute(Qt::WA_Resized); - const bool wasMoved = testAttribute(Qt::WA_Moved); - - QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId()); - if (window && newstate & Qt::WindowMinimized) { - window->setFocusSafely(false); - window->MakeVisible(false); - } else if (window && oldstate & Qt::WindowMinimized) { - window->setFocusSafely(true); - window->MakeVisible(true); - } - -#ifdef Q_WS_S60 - // The window decoration visibility has to be changed before doing actual window state - // change since in that order the availableGeometry will return directly the right size and - // we will avoid unnecessary redraws - bool decorationsVisible = S60->setRecursiveDecorationsVisibility(this, newstate); -#endif // Q_WS_S60 - - // Ensure the initial size is valid, since we store it as normalGeometry below. - if (!wasResized && !isVisible()) - adjustSize(); - - QTLWExtra *top = d->topData(); - QRect normalGeometry = (top->normalGeometry.width() < 0) ? geometry() : top->normalGeometry; - - const bool cbaVisibilityHint = windowFlags() & Qt::WindowSoftkeysVisibleHint; - if (newstate & Qt::WindowFullScreen && !cbaVisibilityHint) { - setAttribute(Qt::WA_OutsideWSRange, false); - if (d->symbianScreenNumber > 0) { - int w = S60->screenWidthInPixelsForScreen[d->symbianScreenNumber]; - int h = S60->screenHeightInPixelsForScreen[d->symbianScreenNumber]; - if (w <= 0 || h <= 0) - window->SetExtentToWholeScreen(); - else - window->SetExtent(TPoint(0, 0), TSize(w, h)); - } else { - window->SetExtentToWholeScreen(); - } - } else if (newstate & Qt::WindowMaximized || ((newstate & Qt::WindowFullScreen) && cbaVisibilityHint)) { - setAttribute(Qt::WA_OutsideWSRange, false); - TRect maxExtent = qt_QRect2TRect(qApp->desktop()->availableGeometry(this)); - window->SetExtent(maxExtent.iTl, maxExtent.Size()); - } else { -#ifdef Q_WS_S60 - // With delayed creation of S60 app panes, the normalGeometry calculated above is not - // accurate because it did not consider the status pane. This means that when returning - // normal mode after showing the status pane, the geometry would overlap so we should - // move it if it never had an explicit position. - if (!wasMoved && S60->statusPane() && decorationsVisible) { - TPoint tl = static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl; - normalGeometry.setTopLeft(QPoint(tl.iX, tl.iY)); - } -#endif - setGeometry(normalGeometry); - } - - //restore normal geometry - top->normalGeometry = normalGeometry; - - // FixMe QTBUG-8977 - // In some platforms, WA_Resized and WA_Moved are also not set when application window state is - // anything else than normal. In Symbian we can restore them only for normal window state since - // restoring for other modes, will make fluidlauncher to be launched in wrong size (200x100) - if (effectiveState(newstate) == Qt::WindowNoState) { - setAttribute(Qt::WA_Resized, wasResized); - setAttribute(Qt::WA_Moved, wasMoved); - } - } - - data->window_state = newstate; - - if (newstate & Qt::WindowActive) - activateWindow(); - - if (isWindow()) { - // Now that the new state is set, fix the display memory layout, if needed. - QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId()); - window->ensureFixNativeOrientation(); - } - - QWindowStateChangeEvent e(oldstate); - QApplication::sendEvent(this, &e); -} - - -void QWidget::destroy(bool destroyWindow, bool destroySubWindows) -{ - Q_D(QWidget); - d->aboutToDestroy(); - if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); - d->deactivateWidgetCleanup(); - QSymbianControl *id = static_cast<QSymbianControl *>(internalWinId()); - if (testAttribute(Qt::WA_WState_Created)) { - -#ifndef QT_NO_IM - if (d->ic) { - delete d->ic; - } else { - QInputContext *ic = QApplicationPrivate::inputContext; - if (ic) { - ic->widgetDestroyed(this); - } - } -#endif - - if (QWidgetPrivate::mouseGrabber == this) - releaseMouse(); - if (QWidgetPrivate::keyboardGrabber == this) - releaseKeyboard(); - 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 (destroyWindow && !(windowType() == Qt::Desktop) && id) { - if (id->IsFocused()) // Avoid unnecessry calls to FocusChanged() - id->setFocusSafely(false); - id->ControlEnv()->AppUi()->RemoveFromStack(id); - } - } - - QT_TRY { - d->setWinId(0); - } QT_CATCH (const std::bad_alloc &) { - // swallow - destructors must not throw - } - - if (destroyWindow) { - delete id; - // At this point the backing store should already be destroyed - // so we flush the command buffer to ensure that the freeing of - // those resources and deleting the window can happen "atomically" - if (qApp) - S60->wsSession().Flush(); - } -} - -QWidget *QWidget::mouseGrabber() -{ - return QWidgetPrivate::mouseGrabber; -} - -QWidget *QWidget::keyboardGrabber() -{ - return QWidgetPrivate::keyboardGrabber; -} - -void QWidget::grabKeyboard() -{ - if (!qt_nograb()) { - if (QWidgetPrivate::keyboardGrabber && QWidgetPrivate::keyboardGrabber != this) - QWidgetPrivate::keyboardGrabber->releaseKeyboard(); - - // ### TODO: Native keyboard grab - - QWidgetPrivate::keyboardGrabber = this; - } -} - -void QWidget::releaseKeyboard() -{ - if (!qt_nograb() && QWidgetPrivate::keyboardGrabber == this) { - // ### TODO: Native keyboard release - QWidgetPrivate::keyboardGrabber = 0; - } -} - -void QWidget::grabMouse() -{ - if (isVisible() && !qt_nograb()) { - if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) - QWidgetPrivate::mouseGrabber->releaseMouse(); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - WId id = effectiveWinId(); - id->SetPointerCapture(true); - QWidgetPrivate::mouseGrabber = this; - -#ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(cursor()); -#endif - } -} - -#ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &cursor) -{ - if (isVisible() && !qt_nograb()) { - if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) - QWidgetPrivate::mouseGrabber->releaseMouse(); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - WId id = effectiveWinId(); - id->SetPointerCapture(true); - QWidgetPrivate::mouseGrabber = this; - - QApplication::setOverrideCursor(cursor); - } -} -#endif - -void QWidget::releaseMouse() -{ - if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) { - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - if(!window()->isModal()) { - WId id = effectiveWinId(); - id->SetPointerCapture(false); - } - QWidgetPrivate::mouseGrabber = 0; -#ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); -#endif - } -} - -void QWidget::activateWindow() -{ - Q_D(QWidget); - - QWidget *tlw = window(); - if (tlw->isVisible()) { - window()->createWinId(); - QSymbianControl *id = static_cast<QSymbianControl *>(tlw->internalWinId()); - if (!id->IsFocused()) - id->setFocusSafely(true); - } -} - -#ifndef QT_NO_CURSOR - -void QWidgetPrivate::setCursor_sys(const QCursor &cursor) -{ - Q_UNUSED(cursor); - Q_Q(QWidget); - qt_symbian_set_cursor(q, false); -} - -void QWidgetPrivate::unsetCursor_sys() -{ - Q_Q(QWidget); - qt_symbian_set_cursor(q, false); -} -#endif - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp deleted file mode 100644 index a02c5ba008..0000000000 --- a/src/gui/kernel/qwidget_win.cpp +++ /dev/null @@ -1,2139 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 "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" - -#include "qscrollbar.h" -#include "qabstractscrollarea.h" -#include <private/qabstractscrollarea_p.h> - -#include <qdebug.h> - -#include <private/qapplication_p.h> -#include <private/qwininputcontext_p.h> -#include <private/qpaintengine_raster_p.h> -#include <private/qsystemlibrary_p.h> - -#if defined(Q_WS_WINCE) -#include "qguifunctions_wince.h" -QT_USE_NAMESPACE -extern void qt_wince_maximize(QWidget *widget); //defined in qguifunctions_wince.cpp -extern void qt_wince_unmaximize(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; -#ifndef QT_NO_TABLETEVENT -static void init_wintab_functions(); -static void qt_tablet_init(); -static void qt_tablet_cleanup(); -#endif // QT_NO_TABLETEVENT -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; - -#ifndef QT_NO_TABLETEVENT -static void init_wintab_functions() -{ -#if defined(Q_OS_WINCE) - return; -#else - if (!qt_is_gui_used) - return; - QSystemLibrary library(QLatin1String("wintab32")); - ptrWTOpen = (PtrWTOpen)library.resolve("WTOpenW"); - ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); - 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")); - // We don't need this internal widget to appear in QApplication::topLevelWidgets() - if (QWidgetPrivate::allWidgets) - QWidgetPrivate::allWidgets->remove(qt_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; -} -#endif // QT_NO_TABLETEVENT - -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 QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); - -#define XCOORD_MAX 16383 -#define WRECT_MAX 16383 - -/***************************************************************************** - QWidget member functions - *****************************************************************************/ - -#ifndef Q_WS_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 = 0; - - 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 - data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN */), - GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */)); - } - - parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0; - - QString title; - int style = WS_CHILD; - int exsty = 0; - - if (window) { - style = GetWindowLong(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)) { - style |= WS_POPUP; - if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) - style |= WS_THICKFRAME; - else - style |= 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) { - title = q->isWindow() ? qAppName() : q->objectName(); - } - - // 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 = SetWindowLong(window, GWL_STYLE, style); - if (!res) - qErrnoWarning("QWidget::create: Failed to set window style"); -#ifdef _WIN64 - res = SetWindowLongPtr( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc ); -#else - res = SetWindowLong( 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 (extra) { - int dx = rect.right - rect.left; - int dy = rect.bottom - rect.top; - w = qMin(w, extra->maxw + dx); - h = qMin(h, extra->maxh + dy); - w = qMax(w, extra->minw + dx); - h = qMax(h, extra->minh + dy); - } - } - if (!wasMoved) { - x = sw/2 - w/2; - y = sh/2 - h/2; - } - } - - id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), - reinterpret_cast<const wchar_t *>(title.utf16()), style, - x, y, w, h, - parentw, NULL, appinst, NULL); - 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 - id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), - reinterpret_cast<const wchar_t *>(title.utf16()), 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(); - } - } - - if (topLevel) { - 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); - } - } - - q->setAttribute(Qt::WA_WState_Created); // accept move/resize events - hd = 0; // no display context - - if (q->testAttribute(Qt::WA_AcceptTouchEvents)) - registerTouchWindow(); - - 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); - } - -#ifndef QT_NO_TABLETEVENT - if (q != qt_tablet_widget && QWidgetPrivate::mapper) - qt_tablet_init(); -#endif // QT_NO_TABLETEVENT - - 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_WS_WINCE - - -void QWidget::destroy(bool destroyWindow, bool destroySubWindows) -{ - Q_D(QWidget); - d->aboutToDestroy(); - if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(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_WS_WINCE - if (destroyWindow && (windowType() == Qt::Desktop) && !GetDesktopWindow()) { - DestroyWindow(internalWinId()); - } - -#endif - QT_TRY { - d->setWinId(0); - } QT_CATCH (const std::bad_alloc &) { - // swallow - destructors must not throw - } - } -} - -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(effectiveRectFor(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); - -#ifdef Q_WS_WINCE - // Show borderless toplevel windows in tasklist & NavBar - if (!parent) { - QString txt = q->windowTitle().isEmpty()?qAppName():q->windowTitle(); - SetWindowText(q->internalWinId(), (wchar_t*)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 -#ifndef QT_NO_GRAPHICSVIEW - && extra->proxyWidget -#endif //QT_NO_GRAPHICSVIEW - )) { - 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 -#ifndef QT_NO_GRAPHICSVIEW - && extra->proxyWidget -#endif //QT_NO_GRAPHICSVIEW - )) { - 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() {} - -#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)); - SetWindowText(q->internalWinId(), (wchar_t*)caption.utf16()); -} - -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; - - result = pm.toWinHICON(); - - if (cache) { - delete *cache; - *cache = new QPixmap(pm);; - } - } - 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) { - SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall); - SendMessage(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconBig); - } else { - SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall); - SendMessage(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_WS_WINCE -LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam) -{ - return CallNextHookEx(journalRec, nCode, wParam, lParam); -} -#endif //Q_WS_WINCE - -/* Works only as long as pointer is inside the application's window, - which is good enough for QDockWidget. - - Doesn't call SetWindowsHookEx() - 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_WS_WINCE -void QWidget::grabMouse() -{ - if (!qt_nograb()) { - if (mouseGrb) - mouseGrb->releaseMouse(); - journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - SetCapture(effectiveWinId()); - mouseGrb = this; -#ifndef QT_NO_CURSOR - mouseGrbCur = new QCursor(mouseGrb->cursor()); -#endif - } -} - -#ifndef QT_NO_CURSOR -void QWidget::grabMouse(const QCursor &cursor) -{ - if (!qt_nograb()) { - if (mouseGrb) - mouseGrb->releaseMouse(); - journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0); - Q_ASSERT(testAttribute(Qt::WA_WState_Created)); - SetCapture(effectiveWinId()); - mouseGrbCur = new QCursor(cursor); - SetCursor(mouseGrbCur->handle()); - mouseGrb = this; - } -} -#endif - -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_WS_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 = Qt::WindowFlags(GetWindowLong(internalWinId(), GWL_STYLE)); -#ifndef Q_FLATTEN_EXPOSE - UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; -#else - UINT style = WS_POPUP; -#endif - if (ulong(d->topData()->savedFlags) & WS_SYSMENU) - style |= WS_SYSMENU; - if (isVisible()) - style |= WS_VISIBLE; - SetWindowLong(internalWinId(), GWL_STYLE, style); - QRect r = QApplication::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; - SetWindowLong(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_WS_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_WS_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_WS_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; - } - - if (data.window_flags & Qt::Window) { - QTLWExtra *extra = topData(); - if (!extra->hotkeyRegistered) { - // Try to set the hotkey using information from STARTUPINFO - STARTUPINFO startupInfo; - GetStartupInfo(&startupInfo); - // If STARTF_USEHOTKEY is set, hStdInput is the virtual keycode - if (startupInfo.dwFlags & 0x00000200) { - WPARAM hotKey = (WPARAM)startupInfo.hStdInput; - SendMessage(data.winid, WM_SETHOTKEY, hotKey, 0); - } - extra->hotkeyRegistered = 1; - } - } - - 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; - // This is to resolve the problem where popups are opened from the - // system tray and not being implicitly activated - if (q->windowType() == Qt::Popup && - !q->parentWidget() && !qApp->activeWindow()) - q->activateWindow(); - } - - winSetupGestures(); - - invalidateBuffer(q->rect()); -} -#endif //Q_WS_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, const QRect &) -{ - 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); - -#ifdef Q_WS_WINCE - // On Windows CE we can't just fiddle around with the window state. - // Too much magic in setWindowState. - if (isResize && q->isMaximized()) - q->setWindowState(q->windowState() & ~Qt::WindowMaximized); -#else - if (isResize) - data.window_state &= ~Qt::WindowMaximized; -#endif - - 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; - SetWindowLong(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) - && GetWindowLong(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_WS_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 & Qt::WindowMaximized) { - qt_wince_maximize(q); - } else { -#endif - MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true); - } - 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 the user explicitly asked for the maximize button, we try to add - // it even if the window has fixed size. - if (data.window_flags & Qt::CustomizeWindowHint && - data.window_flags & Qt::WindowMaximizeButtonHint) - return true; - 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_WS_WINCE - Q_Q(QWidget); - - if (!q->isWindow() || !q->testAttribute(Qt::WA_TranslucentBackground)) - return; - - if ((data.window_flags & Qt::FramelessWindowHint) == 0) - return; - - if (!isOpaque && ptrUpdateLayeredWindowIndirect) { - SetWindowLong(q->internalWinId(), GWL_EXSTYLE, - GetWindowLong(q->internalWinId(), GWL_EXSTYLE) | Q_WS_EX_LAYERED); - } else { - SetWindowLong(q->internalWinId(), GWL_EXSTYLE, - GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & ~Q_WS_EX_LAYERED); - } -#endif -} - -void QWidgetPrivate::setConstraints_sys() -{ -#ifndef Q_WS_WINCE_WM - Q_Q(QWidget); - if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) { - int style = GetWindowLong(q->internalWinId(), GWL_STYLE); - if (shouldShowMaximizeButton()) - style |= WS_MAXIMIZEBOX; - else - style &= ~WS_MAXIMIZEBOX; - SetWindowLong(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()); - } -} - -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 = GetDC(0); - 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"); - } - ReleaseDC(0, gdc); - } - return val; -} - -void QWidgetPrivate::createSysExtra() -{ -#ifndef QT_NO_DRAGANDDROP - extra->dropTarget = 0; -#endif -} - -#ifndef Q_WS_WINCE -void QWidgetPrivate::deleteSysExtra() -{ -} -#endif //Q_WS_WINCE - -void QWidgetPrivate::createTLSysExtra() -{ - extra->topextra->hotkeyRegistered = 0; - extra->topextra->savedFlags = 0; - extra->topextra->winIconBig = 0; - extra->topextra->winIconSmall = 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 ®ion) -{ - 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)); - if (!SetWindowRgn(data.winid, wr, true)) - DeleteObject(wr); -} - -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 = GetWindowLong(q->internalWinId(), GWL_EXSTYLE); - uint style = GetWindowLong(q->internalWinId(), GWL_STYLE); -#ifndef Q_WS_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_WS_WINCE -void QWidgetPrivate::setWindowOpacity_sys(qreal level) -{ - Q_Q(QWidget); - - if (!isOpaque && ptrUpdateLayeredWindow && (data.window_flags & Qt::FramelessWindowHint)) { - if (GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED) { - BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * level), AC_SRC_ALPHA}; - ptrUpdateLayeredWindow(q->internalWinId(), NULL, NULL, NULL, NULL, NULL, 0, &blend, Q_ULW_ALPHA); - } - return; - } - - static bool function_resolved = false; - if (!function_resolved) { - ptrSetLayeredWindowAttributes = - (PtrSetLayeredWindowAttributes) QSystemLibrary::resolve(QLatin1String("user32"), - "SetLayeredWindowAttributes"); - function_resolved = true; - } - - if (!ptrSetLayeredWindowAttributes) - return; - - int wl = GetWindowLong(q->internalWinId(), GWL_EXSTYLE); - - if (level != 1.0) { - if ((wl&Q_WS_EX_LAYERED) == 0) - SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl | Q_WS_EX_LAYERED); - } else if (wl&Q_WS_EX_LAYERED) { - SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl & ~Q_WS_EX_LAYERED); - } - ptrSetLayeredWindowAttributes(q->internalWinId(), 0, (int)(level * 255), Q_LWA_ALPHA); -} -#endif //Q_WS_WINCE - -// class QGlobalRasterPaintEngine: public QRasterPaintEngine -// { -// public: -// inline QGlobalRasterPaintEngine() : QRasterPaintEngine() { setFlushOnEnd(false); } -// }; -// Q_GLOBAL_STATIC(QGlobalRasterPaintEngine, globalRasterPaintEngine) - - -#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, - QApplication::desktop()->width(), - QApplication::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_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); - return new QRasterWindowSurface(q); -} - -void QWidgetPrivate::setModal_sys() -{ -} - -void QWidgetPrivate::registerTouchWindow() -{ - Q_Q(QWidget); - - // enable WM_TOUCH* messages on our window - if (q->testAttribute(Qt::WA_WState_Created) - && QApplicationPrivate::RegisterTouchWindow - && q->windowType() != Qt::Desktop) - QApplicationPrivate::RegisterTouchWindow(q->effectiveWinId(), 0); -} - -void QWidgetPrivate::winSetupGestures() -{ -#if !defined(QT_NO_GESTURES) && !defined(QT_NO_NATIVE_GESTURES) - Q_Q(QWidget); - if (!q || !q->isVisible() || !nativeGesturePanEnabled) - return; - - if (!QApplicationPrivate::HasTouchSupport) - return; - QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); - if (!qAppPriv->SetGestureConfig) - return; - WId winid = q->internalWinId(); - - bool needh = false; - bool needv = false; - bool singleFingerPanEnabled = false; - -#ifndef QT_NO_SCROLLAREA - if (QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea*>(q->parent())) { - QScrollBar *hbar = asa->horizontalScrollBar(); - QScrollBar *vbar = asa->verticalScrollBar(); - Qt::ScrollBarPolicy hbarpolicy = asa->horizontalScrollBarPolicy(); - Qt::ScrollBarPolicy vbarpolicy = asa->verticalScrollBarPolicy(); - needh = (hbarpolicy == Qt::ScrollBarAlwaysOn || - (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum())); - needv = (vbarpolicy == Qt::ScrollBarAlwaysOn || - (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum())); - singleFingerPanEnabled = asa->d_func()->singleFingerPanEnabled; - if (!winid) { - winid = q->winId(); // enforces the native winid on the viewport - } - } -#endif //QT_NO_SCROLLAREA - if (winid) { - GESTURECONFIG gc[1]; - memset(gc, 0, sizeof(gc)); - gc[0].dwID = GID_PAN; - if (nativeGesturePanEnabled) { - gc[0].dwWant = GC_PAN; - if (needv && singleFingerPanEnabled) - gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; - else - gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; - if (needh && singleFingerPanEnabled) - gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; - else - gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; - } else { - gc[0].dwBlock = GC_PAN; - } - - qAppPriv->SetGestureConfig(winid, 0, sizeof(gc)/sizeof(gc[0]), gc, sizeof(gc[0])); - } -#endif -} - -QT_END_NAMESPACE - -#ifdef Q_WS_WINCE -# include "qwidget_wince.cpp" -#endif diff --git a/src/gui/kernel/qwidget_wince.cpp b/src/gui/kernel/qwidget_wince.cpp deleted file mode 100644 index 7676182ef0..0000000000 --- a/src/gui/kernel/qwidget_wince.cpp +++ /dev/null @@ -1,675 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef Q_WS_WINCE - -#include "qguifunctions_wince.h" - -QT_BEGIN_NAMESPACE - -const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp -extern "C" LRESULT QT_WIN_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); - -#ifndef QT_NO_TABLETEVENT -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; -} -#endif // QT_NO_TABLETEVENT - - -// 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; - - QString title; - int style = WS_CHILD; - int exsty = WS_EX_NOPARENTNOTIFY; - - if (topLevel) { - if (!(flags & Qt::FramelessWindowHint) && !tool && !q->testAttribute(Qt::WA_DontShowOnScreen)) - 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_WS_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) { - title = q->isWindow() ? qAppName() : q->objectName(); - } - - // 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 = SetWindowLong(window, GWL_STYLE, style); - if (!res) - qErrnoWarning("QWidget::create: Failed to set window style"); - - res = SetWindowLong( 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); - id = CreateWindow(reinterpret_cast<const wchar_t *>(windowClassName.utf16()), - reinterpret_cast<const wchar_t *>(title.utf16()), 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); - } - } - - id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), - reinterpret_cast<const wchar_t *>(title.utf16()), 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 - id = CreateWindowEx(exsty, (wchar_t*)windowClassName.utf16(), (wchar_t*)title.utf16(), 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; - 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); - } - -#ifndef QT_NO_TABLETEVENT - if (q != qt_tablet_widget && QWidgetPrivate::mapper) - qt_tablet_init_wce(); -#endif // QT_NO_TABLETEVENT - - 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_WS_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 -#else - // Imitate minimizing on Windows mobile by hiding the widget. - if (q->isMinimized()) - sm = SW_HIDE; -#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; - } - - ShowWindow(q->internalWinId(), sm); - - if (q->isMaximized() && q->isWindow()) - qt_wince_maximize(q); - -#ifndef Q_WS_WINCE_WM - if (!qt_wince_is_mobile() && q->isFullScreen()) { - HWND handle = FindWindow(L"HHTaskBar", L""); - if (handle) { - ShowWindow(handle, SW_HIDE); - 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 normal = SW_SHOWNOACTIVATE; - - if ((oldstate & Qt::WindowMinimized) && !(newstate & Qt::WindowMinimized)) - newstate |= Qt::WindowActive; - if (newstate & Qt::WindowActive) - 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 (!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 = GetWindowLong(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); - qt_wince_unmaximize(this); - } - 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 = (Qt::WindowFlags)GetWindowLong(internalWinId(), GWL_STYLE); - UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; - if (isVisible()) - style |= WS_VISIBLE; - SetWindowLong(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; - SetWindowLong(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 (newstate & Qt::WindowMinimized) - qt_wince_minimize(internalWinId()); - else if (newstate & Qt::WindowMaximized) { - ShowWindow(internalWinId(), max); - qt_wince_maximize(this); - } else { - ShowWindow(internalWinId(), normal); - } - } - } - data->window_state = newstate; - QWindowStateChangeEvent e(oldstate); - QApplication::sendEvent(this, &e); -} - -void QWidgetPrivate::deleteSysExtra() -{ - Q_Q(QWidget); - if (!qt_wince_is_mobile() && q->isFullScreen()) { - HWND handle = FindWindow(L"HHTaskBar", L""); - if (handle) { - ShowWindow(handle, SW_SHOWNORMAL); - 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 QT_WIN_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_WS_WINCE diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp deleted file mode 100644 index 5ece7d65c6..0000000000 --- a/src/gui/kernel/qwidget_x11.cpp +++ /dev/null @@ -1,3146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 "qelapsedtimer.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); -} - -void qt_x11_wait_for_window_manager(QWidget *w, bool sendPostedEvents) -{ - if (!w || (!w->isWindow() && !w->internalWinId())) - return; - QApplication::flush(); - XEvent ev; - QElapsedTimer t; - t.start(); - static const int maximumWaitTime = 2000; - if (!w->testAttribute(Qt::WA_WState_Created)) - return; - - WId winid = w->internalWinId(); - - // first deliver events that are already in the local queue - if (sendPostedEvents) - QApplication::sendPostedEvents(); - - // the normal sequence is: - // ... ConfigureNotify ... ReparentNotify ... MapNotify ... Expose - // with X11BypassWindowManagerHint: - // ConfigureNotify ... MapNotify ... Expose - - enum State { - Initial, Mapped - } state = Initial; - - do { - if (XEventsQueued(X11->display, QueuedAlready)) { - XNextEvent(X11->display, &ev); - qApp->x11ProcessEvent(&ev); - - switch (state) { - case Initial: - if (ev.type == MapNotify && ev.xany.window == winid) - state = Mapped; - break; - case Mapped: - if (ev.type == Expose && ev.xany.window == winid) - return; - break; - } - } else { - if (!XEventsQueued(X11->display, QueuedAfterFlush)) - qApp->syncX(); // non-busy wait - } - if (t.elapsed() > maximumWaitTime) - return; - } while(1); -} - -Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget *w) -{ - qt_x11_wait_for_window_manager(w, true); -} - -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); - propertyData = 0; - - // 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)); - } - if (propertyData) - 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; - - QX11Info *parentXinfo = parentWidget ? &parentWidget->d_func()->xinfo : 0; - - 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 (parentXinfo && (parentXinfo->screen() != xinfo.screen() - || (parentXinfo->visual() != xinfo.visual() - && !q->inherits("QGLWidget")))) - { - // QGLWidgets have to be excluded here as they have a - // specially crafted QX11Info structure which can't be swapped - // out with the parent widgets QX11Info. The parent visual, - // for instance, might not even be GL capable. - xinfo = *parentXinfo; - } - - //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)) { - QDesktopWidget *desktopWidget = qApp->desktop(); - if (desktopWidget->isVirtualDesktop()) { - QRect r = desktopWidget->screenGeometry(); - sw = r.width(); - sh = r.height(); - } - - 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 = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : 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[5]; - 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 -#ifndef QT_NO_XSYNC - protocols[n++] = ATOM(_NET_WM_SYNC_REQUEST); // support _NET_WM_SYNC_REQUEST protocol -#endif // QT_NO_XSYNC - 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->internalWinId()) { - qt_x11_enforce_cursor(q); - if (QWidget *p = q->parentWidget()) // reset the cursor on the native parent - qt_x11_enforce_cursor(p); - } - - 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); - if (QTLWExtra *topData = maybeTopData()) { -#ifndef QT_NO_XSYNC - if (topData->syncUpdateCounter) - XSyncDestroyCounter(dpy, topData->syncUpdateCounter); -#endif - // we destroyed our old window - reset the top-level state - createTLSysExtra(); - } - } - - // 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 -} - -static void qt_x11_recreateWidget(QWidget *widget) -{ - if (widget->inherits("QGLWidget")) { - // We send QGLWidgets a ParentChange event which causes them to - // recreate their GL context, which in turn causes them to choose - // their visual again. Now that WA_TranslucentBackground is set, - // QGLContext::chooseVisual will select an ARGB visual. - QEvent e(QEvent::ParentChange); - QApplication::sendEvent(widget, &e); - } else { - // For regular widgets, reparent them with their parent which - // also triggers a recreation of the native window - QPoint pos = widget->pos(); - bool visible = widget->isVisible(); - if (visible) - widget->hide(); - - widget->setParent(widget->parentWidget(), widget->windowFlags()); - widget->move(pos); - if (visible) - widget->show(); - } -} - -static void qt_x11_recreateNativeWidgetsRecursive(QWidget *widget) -{ - if (widget->internalWinId()) - qt_x11_recreateWidget(widget); - - const QObjectList &children = widget->children(); - for (int i = 0; i < children.size(); ++i) { - QWidget *child = qobject_cast<QWidget*>(children.at(i)); - if (child) - qt_x11_recreateNativeWidgetsRecursive(child); - } -} - -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) - { - qt_x11_recreateNativeWidgetsRecursive(q); - } -#endif -} - -/* - 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; -} - -void QWidget::destroy(bool destroyWindow, bool destroySubWindows) -{ - Q_D(QWidget); - d->aboutToDestroy(); - if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(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); - } - QT_TRY { - d->setWinId(0); - } QT_CATCH (const std::bad_alloc &) { - // swallow - destructors must not throw - } - - 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 = QApplicationPrivate::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(effectiveRectFor(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) - 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 QWidgetPrivate::mapToGlobal(const QPoint &pos) const -{ - Q_Q(const QWidget); - if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { - QPoint p = pos + q->data->crect.topLeft(); - //cannot trust that !isWindow() implies parentWidget() before create - return (q->isWindow() || !q->parentWidget()) ? p : q->parentWidget()->d_func()->mapToGlobal(p); - } - int x, y; - Window child; - QPoint p = mapToWS(pos); - XTranslateCoordinates(X11->display, q->internalWinId(), - QApplication::desktop()->screen(xinfo.screen())->internalWinId(), - p.x(), p.y(), &x, &y, &child); - return QPoint(x, y); -} - -QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const -{ - Q_Q(const QWidget); - if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { - //cannot trust that !isWindow() implies parentWidget() before create - QPoint p = (q->isWindow() || !q->parentWidget()) ? pos : q->parentWidget()->d_func()->mapFromGlobal(pos); - return p - q->data->crect.topLeft(); - } - int x, y; - Window child; - XTranslateCoordinates(X11->display, - QApplication::desktop()->screen(xinfo.screen())->internalWinId(), - q->internalWinId(), pos.x(), pos.y(), &x, &y, &child); - return mapFromWS(QPoint(x, y)); -} - -QPoint QWidget::mapToGlobal(const QPoint &pos) const -{ - Q_D(const QWidget); - QPoint offset = data->crect.topLeft(); - const QWidget *w = this; - const QWidget *p = w->parentWidget(); - while (!w->isWindow() && p) { - w = p; - p = p->parentWidget(); - offset += w->data->crect.topLeft(); - } - - const QWidgetPrivate *wd = w->d_func(); - QTLWExtra *tlw = wd->topData(); - if (!tlw->embedded) - return pos + offset; - - return d->mapToGlobal(pos); -} - -QPoint QWidget::mapFromGlobal(const QPoint &pos) const -{ - Q_D(const QWidget); - QPoint offset = data->crect.topLeft(); - const QWidget *w = this; - const QWidget *p = w->parentWidget(); - while (!w->isWindow() && p) { - w = p; - p = p->parentWidget(); - offset += w->data->crect.topLeft(); - } - - const QWidgetPrivate *wd = w->d_func(); - QTLWExtra *tlw = wd->topData(); - if (!tlw->embedded) - return pos - offset; - - return d->mapFromGlobal(pos); -} - -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) { - if (QX11Info::isCompositingManagerRunning() - && q->testAttribute(Qt::WA_TranslucentBackground) - && !(q->parent())) - XSetWindowBackground(X11->display, q->internalWinId(), - QColormap::instance(xinfo.screen()).pixel(Qt::transparent)); - else - 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.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; - - // preparing images to set the _NET_WM_ICON property - QIcon icon = q->windowIcon(); - QVector<long> icon_data; - Qt::HANDLE pixmap_handle = 0; - 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)); - } - 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.byteCount()); - } 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()) { - 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 QPixmap(qt_toX11Pixmap(QBitmap(icon.pixmap(QSize(64,64))))); - pixmap_handle = 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)))); - pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data.data())->x11ConvertToDefaultDepth(); - } - } - } - - if (!q->internalWinId()) - return; - - if (!icon_data.isEmpty()) { - XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON), XA_CARDINAL, 32, - PropModeReplace, (unsigned char *) icon_data.data(), - icon_data.size()); - } else { - XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON)); - } - - XWMHints *h = XGetWMHints(X11->display, q->internalWinId()); - XWMHints wm_hints; - if (!h) { - memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy - h = &wm_hints; - } - - if (pixmap_handle) { - h->icon_pixmap = pixmap_handle; - h->flags |= IconPixmapHint; - } else { - h->icon_pixmap = 0; - h->flags &= ~(IconPixmapHint | IconMaskHint); - } - - 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() -{ - 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); - - if (X11->isSupportedByWM(ATOM(_NET_ACTIVE_WINDOW)) - && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)) { - XEvent e; - e.xclient.type = ClientMessage; - e.xclient.message_type = ATOM(_NET_ACTIVE_WINDOW); - e.xclient.display = X11->display; - e.xclient.window = tlw->internalWinId(); - e.xclient.format = 32; - e.xclient.data.l[0] = 1; // 1 == application - e.xclient.data.l[1] = X11->userTime; - if (QWidget *aw = QApplication::activeWindow()) - e.xclient.data.l[2] = aw->internalWinId(); - else - e.xclient.data.l[2] = XNone; - e.xclient.data.l[3] = 0; - e.xclient.data.l[4] = 0; - XSendEvent(X11->display, RootWindow(X11->display, tlw->x11Info().screen()), - false, SubstructureNotifyMask | SubstructureRedirectMask, &e); - } else { - if (!qt_widget_private(tlw)->topData()->waitingForMapNotify) - XSetInputFocus(X11->display, tlw->internalWinId(), XRevertToParent, X11->time); - } - } -} - -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; - } - - if (mwmhints.decorations == MWM_DECOR_ALL) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - mwmhints.decorations = (MWM_DECOR_BORDER - | MWM_DECOR_TITLE - | MWM_DECOR_MENU); - } else { - mwmhints.decorations &= ~MWM_DECOR_RESIZEH; - } - - if (q->windowFlags() & Qt::WindowMinimizeButtonHint) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - mwmhints.decorations |= MWM_DECOR_MINIMIZE; - mwmhints.functions |= MWM_FUNC_MINIMIZE; - } - if (q->windowFlags() & Qt::WindowMaximizeButtonHint) { - mwmhints.flags |= MWM_HINTS_DECORATIONS; - 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"; - if (!netWmState.contains(ATOM(_NET_WM_STATE_ABOVE))) - netWmState.append(ATOM(_NET_WM_STATE_ABOVE)); - if (!netWmState.contains(ATOM(_NET_WM_STATE_STAYS_ON_TOP))) - netWmState.append(ATOM(_NET_WM_STATE_STAYS_ON_TOP)); - } else if (flags & Qt::WindowStaysOnBottomHint) { - if (!netWmState.contains(ATOM(_NET_WM_STATE_BELOW))) - netWmState.append(ATOM(_NET_WM_STATE_BELOW)); - } - if (q->isFullScreen()) { - if (!netWmState.contains(ATOM(_NET_WM_STATE_FULLSCREEN))) - netWmState.append(ATOM(_NET_WM_STATE_FULLSCREEN)); - } - if (q->isMaximized()) { - if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))) - netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)); - if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))) - netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_VERT)); - } - if (data.window_modality != Qt::NonModal) { - if (!netWmState.contains(ATOM(_NET_WM_STATE_MODAL))) - 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); - -#ifndef QT_NO_XSYNC - if (!topData()->syncUpdateCounter) { - XSyncValue value; - XSyncIntToValue(&value, 0); - topData()->syncUpdateCounter = XSyncCreateCounter(X11->display, value); - - XChangeProperty(X11->display, q->internalWinId(), - ATOM(_NET_WM_SYNC_REQUEST_COUNTER), - XA_CARDINAL, - 32, PropModeReplace, - (uchar *) &topData()->syncUpdateCounter, 1); - - topData()->newCounterValueHi = 0; - topData()->newCounterValueLo = 0; - } -#endif - - 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, const QRect &) -{ - 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(); - else if (!valid_rect) - dirty.translate(dx, dy); - - 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->compress_events = true; - extra->xDndProxy = 0; -} - -void QWidgetPrivate::deleteSysExtra() -{ -} - -void QWidgetPrivate::createTLSysExtra() -{ - extra->topextra->spont_unmapped = 0; - extra->topextra->dnd = 0; - extra->topextra->validWMState = 0; - extra->topextra->waitingForMapNotify = 0; - extra->topextra->parentWinId = 0; - extra->topextra->userTimeWindow = 0; -#ifndef QT_NO_XSYNC - extra->topextra->syncUpdateCounter = 0; - extra->topextra->syncRequestTimestamp = 0; - extra->topextra->newCounterValueHi = 0; - extra->topextra->newCounterValueLo = 0; -#endif -} - -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 ®ion) -{ - 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); -} - -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()); -} - -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 = qrand() % 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); -} - -void QWidgetPrivate::updateX11AcceptFocus() -{ - Q_Q(QWidget); - if (!q->isWindow() || !q->internalWinId()) - return; - - XWMHints *h = XGetWMHints(X11->display, q->internalWinId()); - XWMHints wm_hints; - if (!h) { - memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy - h = &wm_hints; - } - h->flags |= InputHint; - h->input = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : True; - - XSetWMHints(X11->display, q->internalWinId(), h); - if (h != &wm_hints) - XFree((char *)h); -} - -QT_END_NAMESPACE diff --git a/src/gui/kernel/qwidgetcreate_x11.cpp b/src/gui/kernel/qwidgetcreate_x11.cpp deleted file mode 100644 index 16bd6abf9a..0000000000 --- a/src/gui/kernel/qwidgetcreate_x11.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwidget.h" -#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/qwinnativepangesturerecognizer_win.cpp b/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp deleted file mode 100644 index 0d13bafc0c..0000000000 --- a/src/gui/kernel/qwinnativepangesturerecognizer_win.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "private/qwinnativepangesturerecognizer_win_p.h" - -#include "qevent.h" -#include "qgraphicsitem.h" -#include "qgesture.h" - -#include "private/qgesture_p.h" -#include "private/qevent_p.h" -#include "private/qapplication_p.h" -#include "private/qwidget_p.h" - -#ifndef QT_NO_GESTURES - -QT_BEGIN_NAMESPACE - -#if !defined(QT_NO_NATIVE_GESTURES) - -QWinNativePanGestureRecognizer::QWinNativePanGestureRecognizer() -{ -} - -QGesture *QWinNativePanGestureRecognizer::create(QObject *target) -{ - if (!target) - return new QPanGesture; // a special case - if (!target->isWidgetType()) - return 0; - if (qobject_cast<QGraphicsObject *>(target)) - return 0; - - QWidget *q = static_cast<QWidget *>(target); - QWidgetPrivate *d = q->d_func(); - d->nativeGesturePanEnabled = true; - d->winSetupGestures(); - - return new QPanGesture; -} - -QGestureRecognizer::Result QWinNativePanGestureRecognizer::recognize(QGesture *state, - QObject *, - QEvent *event) -{ - QPanGesture *q = static_cast<QPanGesture*>(state); - QPanGesturePrivate *d = q->d_func(); - - QGestureRecognizer::Result result = QGestureRecognizer::Ignore; - if (event->type() == QEvent::NativeGesture) { - QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); - switch(ev->gestureType) { - case QNativeGestureEvent::GestureBegin: - break; - case QNativeGestureEvent::Pan: - result = QGestureRecognizer::TriggerGesture; - event->accept(); - break; - case QNativeGestureEvent::GestureEnd: - if (q->state() == Qt::NoGesture) - return QGestureRecognizer::Ignore; // some other gesture has ended - result = QGestureRecognizer::FinishGesture; - break; - default: - return QGestureRecognizer::Ignore; - } - if (q->state() == Qt::NoGesture) { - d->lastOffset = d->offset = QPointF(); - d->startPosition = ev->position; - } else { - d->lastOffset = d->offset; - d->offset = QPointF(ev->position.x() - d->startPosition.x(), - ev->position.y() - d->startPosition.y()); - } - } - return result; -} - -void QWinNativePanGestureRecognizer::reset(QGesture *state) -{ - QPanGesture *pan = static_cast<QPanGesture*>(state); - QPanGesturePrivate *d = pan->d_func(); - - d->lastOffset = d->offset = QPointF(); - d->startPosition = QPoint(); - d->acceleration = 0; - - QGestureRecognizer::reset(state); -} - -#endif // QT_NO_NATIVE_GESTURES - -QT_END_NAMESPACE - -#endif // QT_NO_GESTURES diff --git a/src/gui/kernel/qwinnativepangesturerecognizer_win_p.h b/src/gui/kernel/qwinnativepangesturerecognizer_win_p.h deleted file mode 100644 index 6d23e41ce3..0000000000 --- a/src/gui/kernel/qwinnativepangesturerecognizer_win_p.h +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H -#define QWINNATIVEPANGESTURERECOGNIZER_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. -// - -#include <QGestureRecognizer> - -#ifndef QT_NO_GESTURES - -QT_BEGIN_NAMESPACE - -#if !defined(QT_NO_NATIVE_GESTURES) - -class QWinNativePanGestureRecognizer : public QGestureRecognizer -{ -public: - QWinNativePanGestureRecognizer(); - - QGesture *create(QObject *target); - QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); - void reset(QGesture *state); -}; - -#endif // QT_NO_NATIVE_GESTURES - -QT_END_NAMESPACE - -#endif // QT_NO_GESTURES - -#endif // QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp deleted file mode 100644 index 49a819469e..0000000000 --- a/src/gui/kernel/qx11embed_x11.cpp +++ /dev/null @@ -1,1808 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" -#include "qx11embed_x11.h" -#include <qapplication.h> -#include <qevent.h> -#include <qpainter.h> -#include <qlayout.h> -#include <qstyle.h> -#include <qstyleoption.h> -#include <qelapsedtimer.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 corresponding KDE 3 - 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 - corresponding KDE 3 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; - XFree(retval); - if (state == 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); - - long 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(x11Info().screen()), 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(x11Info().screen())) - ? 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(x11Info().screen())) { - 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 (((long * )prop_return)[1] & XEMBED_MAPPED) { - XMapWindow(x11Info().display(), internalWinId()); - } else { - XUnmapWindow(x11Info().display(), internalWinId()); - } - } - if (prop_return) - XFree(prop_return); - } - } - - 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->createWinId(); - 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(x11Info().screen()), 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 whether 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. - */ - QElapsedTimer 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->clientIsXEmbed) { - sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE); - } else { - d->checkGrab(); - if (hasFocus()) - XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time()); - } - if (!d->isEmbedded()) - d->moveInputToProxy(); - } - 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(x11Info().screen()), 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) { - long 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) { - long 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(q->x11Info().screen()), 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; - - long *p = (long *)prop_return; - if (nitems_return >= 2) - clientversion = (unsigned int)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); - // Following Owen Taylor's advice from the XEmbed specification to - // always use CurrentTime when no explicit user action is involved. - XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); -} - -/*! \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 deleted file mode 100644 index 30929f7ba9..0000000000 --- a/src/gui/kernel/qx11embed_x11.h +++ /dev/null @@ -1,132 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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 deleted file mode 100644 index f52443befc..0000000000 --- a/src/gui/kernel/qx11info_x11.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwidget.h" -#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->ref = 0; - 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 deleted file mode 100644 index ece85740d2..0000000000 --- a/src/gui/kernel/qx11info_x11.h +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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/painting/qcolormap_mac.cpp b/src/gui/painting/qcolormap_mac.cpp deleted file mode 100644 index 28589f41b8..0000000000 --- a/src/gui/painting/qcolormap_mac.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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_s60.cpp b/src/gui/painting/qcolormap_s60.cpp deleted file mode 100644 index 2c634db8a5..0000000000 --- a/src/gui/painting/qcolormap_s60.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcolormap.h" -#include "qcolor.h" - -QT_BEGIN_NAMESPACE - -class QColormapPrivate -{ -public: - inline QColormapPrivate() - : ref(1) - { } - - QAtomicInt ref; -}; - -void QColormap::initialize() -{ -} - -void QColormap::cleanup() -{ -} - -QColormap QColormap::instance(int) -{ - return QColormap(); -} - -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_win.cpp b/src/gui/painting/qcolormap_win.cpp deleted file mode 100644 index 1773f717c0..0000000000 --- a/src/gui/painting/qcolormap_win.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcolor.h" -#include "qcolormap.h" -#include "qvector.h" -#include "qt_windows.h" - -#if defined(Q_WS_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) -{ - Q_ASSERT_X(screenMap, "QColormap", - "A QApplication object needs to be constructed before QColormap is used."); - 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 deleted file mode 100644 index 05eefa455b..0000000000 --- a/src/gui/painting/qcolormap_x11.cpp +++ /dev/null @@ -1,670 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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; - -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; - } -} - -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/qpaintdevice_mac.cpp b/src/gui/painting/qpaintdevice_mac.cpp deleted file mode 100644 index 245408a0b0..0000000000 --- a/src/gui/painting/qpaintdevice_mac.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 - *****************************************************************************/ - -/*! \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); - uint flags = kCGImageAlphaPremultipliedFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - flags |= kCGBitmapByteOrder32Host; -#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.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_win.cpp b/src/gui/painting/qpaintdevice_win.cpp deleted file mode 100644 index 3dbe97492a..0000000000 --- a/src/gui/painting/qpaintdevice_win.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 - -HDC QPaintDevice::getDC() const -{ - return 0; -} - -void QPaintDevice::releaseDC(HDC) const -{ -} - -QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintdevice_x11.cpp b/src/gui/painting/qpaintdevice_x11.cpp deleted file mode 100644 index 690dea99d7..0000000000 --- a/src/gui/painting/qpaintdevice_x11.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 - -/*! \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; -} - -QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_mac.cpp b/src/gui/painting/qpaintengine_mac.cpp deleted file mode 100644 index c6d061dea8..0000000000 --- a/src/gui/painting/qpaintengine_mac.cpp +++ /dev/null @@ -1,1751 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <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/qfontengine_coretext_p.h> -#include <private/qfontengine_mac_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 || - devType == QInternal::Image)) { - - extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice); - CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice()); - uint flags = kCGImageAlphaPremultipliedFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - flags |= kCGBitmapByteOrder32Host; -#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(); - QTransform native = p->deviceTransform(); - QTransform logical = p->combinedTransform(); - - if (p->hasClipping()) { - QRegion r = p->clipRegion(); - r.translate(native.dx(), native.dy()); - if (clip.isEmpty()) - clip = r; - else - clip &= r; - } - qt_mac_clip_cg(context, clip, 0); - - CGContextTranslateCTM(context, native.dx(), native.dy()); - } - } else { - 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() -{ - return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent - & ~QPaintEngine::PerspectiveTransform - & ~QPaintEngine::ConicalGradientFill - & ~QPaintEngine::LinearGradientFill - & ~QPaintEngine::RadialGradientFill - & ~QPaintEngine::BrushStroke); -} - -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(r.x(), r.y(), r.width(), 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) { - QCFType<CGImageRef> img = pm.toMacCGImageRef(); - image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height()))); - } 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); - - uint cgflags = kCGImageAlphaNone; - 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 defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#endif - QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image, - static_cast<const QImage *>(image)->bits(), - image->byteCount(), - 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) - cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), - sr.width(), sr.height())); - 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); - static const CGFloat ScaleFactor = qt_mac_get_scalefactor(); - if (ScaleFactor > 1.) { - CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh); - } else { - CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? - kCGInterpolationHigh : kCGInterpolationNone); - } - bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; - if (!textAntialiasing || d->disabledSmoothFonts) { - d->disabledSmoothFonts = !textAntialiasing; - CGContextSetShouldSmoothFonts(d->hd, 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(4); - linedashes.append(2); - } else if(pen.style() == Qt::DotLine) { - linedashes.append(1); - linedashes.append(2); - } else if(pen.style() == Qt::DashDotLine) { - linedashes.append(4); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - } else if(pen.style() == Qt::DashDotDotLine) { - linedashes.append(4); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - linedashes.append(1); - linedashes.append(2); - } - 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 *>(¤t.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 deleted file mode 100644 index 3e71d6ca6b..0000000000 --- a/src/gui/painting/qpaintengine_mac_p.h +++ /dev/null @@ -1,254 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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 "private/qfont_p.h" -#include "QtCore/qhash.h" - -typedef struct CGColorSpace *CGColorSpaceRef; -QT_BEGIN_NAMESPACE - -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 ®ion, 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 *); - friend void qt_color_profile_changed(CFNotificationCenterRef center, void *, - CFStringRef , const void *, CFDictionaryRef); - 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), disabledSmoothFonts(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; - bool disabledSmoothFonts; - 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_s60.cpp b/src/gui/painting/qpaintengine_s60.cpp deleted file mode 100644 index ca303be03d..0000000000 --- a/src/gui/painting/qpaintengine_s60.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include <private/qpaintengine_s60_p.h> -#include <private/qpixmap_s60_p.h> -#include <private/qt_s60_p.h> -#include <private/qvolatileimage_p.h> - -QT_BEGIN_NAMESPACE - -class QS60PaintEnginePrivate : public QRasterPaintEnginePrivate -{ -public: - QS60PaintEnginePrivate() {} -}; - -QS60PaintEngine::QS60PaintEngine(QPaintDevice *device, QS60PixmapData *data) - : QRasterPaintEngine(*(new QS60PaintEnginePrivate), device), pixmapData(data) -{ -} - -bool QS60PaintEngine::begin(QPaintDevice *device) -{ - Q_D(QS60PaintEngine); - - if (pixmapData->classId() == QPixmapData::RasterClass) { - pixmapData->beginDataAccess(); - bool ret = QRasterPaintEngine::begin(device); - // Make sure QPaintEngine::paintDevice() returns the proper device. - // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPixmapData - // which is incorrect in Symbian. - d->pdev = device; - return ret; - } - - return QRasterPaintEngine::begin(device); -} - -bool QS60PaintEngine::end() -{ - if (pixmapData->classId() == QPixmapData::RasterClass) { - bool ret = QRasterPaintEngine::end(); - pixmapData->endDataAccess(); - return ret; - } - return QRasterPaintEngine::end(); -} - -void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) -{ - if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawPixmap(p, pm); - srcData->endDataAccess(); - } else { - void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); - if (nativeData) { - QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); - img->beginDataAccess(); - QRasterPaintEngine::drawImage(p, img->imageRef()); - img->endDataAccess(true); - } else { - QRasterPaintEngine::drawPixmap(p, pm); - } - } -} - -void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) -{ - if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawPixmap(r, pm, sr); - srcData->endDataAccess(); - } else { - void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); - if (nativeData) { - QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); - img->beginDataAccess(); - QRasterPaintEngine::drawImage(r, img->imageRef(), sr); - img->endDataAccess(true); - } else { - QRasterPaintEngine::drawPixmap(r, pm, sr); - } - } -} - -void QS60PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr) -{ - if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { - QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); - srcData->beginDataAccess(); - QRasterPaintEngine::drawTiledPixmap(r, pm, sr); - srcData->endDataAccess(); - } else { - QRasterPaintEngine::drawTiledPixmap(r, pm, sr); - } -} - -void QS60PaintEngine::prepare(QImage *image) -{ - QRasterBuffer *buffer = d_func()->rasterBuffer.data(); - if (buffer) - buffer->prepare(image); -} - -QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_s60_p.h b/src/gui/painting/qpaintengine_s60_p.h deleted file mode 100644 index a62bdac97c..0000000000 --- a/src/gui/painting/qpaintengine_s60_p.h +++ /dev/null @@ -1,84 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPAINTENGINE_S60_P_H -#define QPAINTENGINE_S60_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/qpaintengine_raster_p.h" - -QT_BEGIN_NAMESPACE - -class QS60PaintEnginePrivate; -class QS60PixmapData; - -class QS60PaintEngine : public QRasterPaintEngine -{ - Q_DECLARE_PRIVATE(QS60PaintEngine) - -public: - QS60PaintEngine(QPaintDevice *device, QS60PixmapData* data); - bool begin(QPaintDevice *device); - bool end(); - - void drawPixmap(const QPointF &p, const QPixmap &pm); - void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); - void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr); - - void prepare(QImage* image); - -private: - QS60PixmapData *pixmapData; -}; - -QT_END_NAMESPACE - -#endif // QPAINTENGINE_S60_P_H diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp deleted file mode 100644 index 1256996491..0000000000 --- a/src/gui/painting/qpaintengine_x11.cpp +++ /dev/null @@ -1,2507 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformdefs.h" - -#include "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 <private/qpainterpath_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 - -#include <private/qhexstring_p.h> - -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_GUI_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 = QLatin1Literal("$qt-alpha-brush$") - % HexString<uchar>(alpha) - % HexString<int>(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 = q_check_ptr((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 = qrand() % 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.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 ®ion); - 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.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) { - d->brush_pm.detach(); - QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data.data()); - brushData->convertToARGB32(); - } -#endif - vals.tile = (d->brush_pm.depth() == d->pdev_depth - ? d->brush_pm.handle() - : static_cast<QX11PixmapData*>(d->brush_pm.data.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) -{ - if (rect.isEmpty()) { - drawRects(&rect, 1); - return; - } - - 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 - XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64); - if (!d->has_pen) // make smoother outline - XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 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) -{ - Q_Q(QX11PaintEngine); - - 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 > 200000) { - QPolygon poly; - for (int i = 0; i < clippedCount; ++i) - poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y)); - - const QRect bounds = poly.boundingRect(); - const QRect aligned = bounds - & QRect(QPoint(), QSize(pdev->width(), pdev->height())); - - QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied); - img.fill(0); - - QPainter painter(&img); - painter.translate(-aligned.x(), -aligned.y()); - painter.setPen(Qt::NoPen); - painter.setBrush(fill); - if (gcMode == BrushGC) - painter.setBrushOrigin(q->painter()->brushOrigin()); - painter.drawPolygon(poly); - painter.end(); - - q->drawImage(aligned, img, img.rect(), Qt::AutoColor); - } else 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; - QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height()); - // necessary to get aliased alphablended primitives to be drawn correctly - if (d->cpen.isCosmetic() || d->has_scaling_xform) { - if (d->cpen.isCosmetic()) - stroker.setWidth(width == 0 ? 1 : width); - else - stroker.setWidth(width * d->xform_scale); - stroker.d_ptr->stroker.setClipRect(deviceRect); - stroke = stroker.createStroke(path * d->matrix); - if (stroke.isEmpty()) - return; - stroke.setFillRule(Qt::WindingFill); - d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false); - } else { - stroker.setWidth(width); - stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect)); - 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; - int image_byte_order = ImageByteOrder(X11->display); - if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout)) - || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) - || (image_byte_order == LSBFirst && bgr_layout)) - { - 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 && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - while (p < end) { - *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff); - p++; - } - } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) - || (image_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 ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) - || (image_byte_order == LSBFirst && bgr_layout)) - { - 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(pixmap.isNull()) - return; - - 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.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.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.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 if (mono_dst && !mono_src) { - QBitmap bitmap(pixmap); - XCopyArea(d->dpy, bitmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y); - } 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.data())->x11_mask; - Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data.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.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.constData(), glyphs.size(), positions.constData(), 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 = 0; - if (ft->xglyph_format != PictStandardA1) - 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; - - QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy())); - QRect clip(d->polygonClipper.boundingRect()); - 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; - - QRect rect = QRect(x + delta.x(), y + delta.y(), w, h); - rect = rect.intersected(clip); - if (rect.isEmpty()) - continue; - - rects[num_rects].x = short(rect.x()); - rects[num_rects].y = short(rect.y()); - rects[num_rects].width = ushort(rect.width()); - rects[num_rects].height = ushort(rect.height()); - ++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 deleted file mode 100644 index 897c69f122..0000000000 --- a/src/gui/painting/qpaintengine_x11_p.h +++ /dev/null @@ -1,246 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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 ®ion, 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; - xform_scale = 1; -#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/qprintengine_mac.mm b/src/gui/painting/qprintengine_mac.mm deleted file mode 100644 index 1dce30301f..0000000000 --- a/src/gui/painting/qprintengine_mac.mm +++ /dev/null @@ -1,911 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/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); - -QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate)) -{ - Q_D(QMacPrintEngine); - d->mode = mode; - d->initialize(); -} - -bool QMacPrintEngine::begin(QPaintDevice *dev) -{ - Q_D(QMacPrintEngine); - - Q_ASSERT(dev && dev->devType() == QInternal::Printer); - if (!static_cast<QPrinter *>(dev)->isValid()) - return false; - - 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 - status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format) - : PMSessionBeginCGDocument(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) { - // We dont need the paint engine to call restoreGraphicsState() - static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0; - static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0; - } - d->paintEngine->end(); - if (d->state != QPrinter::Idle) - d->releaseSession(); - 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 (!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()); - } -} - -void QMacPrintEnginePrivate::releaseSession() -{ -#ifndef QT_MAC_USE_COCOA - if (shouldSuppressStatus()) { - PMSessionEndPageNoDialog(session); - PMSessionEndDocumentNoDialog(session); - } else { - PMSessionEndPage(session); - PMSessionEndDocument(session); - } - PMRelease(session); -#else - PMSessionEndPageNoDialog(session); - PMSessionEndDocumentNoDialog(session); - [printInfo release]; -#endif - printInfo = 0; - session = 0; -} - -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; - err = PMSessionGetCGGraphicsContext(session, &cgContext); - 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_CopyCount: // fallthrough - 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: { - bool printerNameSet = false; - 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); - printerNameSet = true; - break; - } - } - } - if (status != noErr) - qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status)); - if (!printerNameSet) { - qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString())); - d->releaseSession(); - d->state = QPrinter::Idle; - } - 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_CopyCount: { - UInt32 copies = 1; - PMGetCopies(d->settings, &copies); - ret = (uint) copies; - break; - } - case PPK_SupportsMultipleCopies: - ret = true; - 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, ¯ect) == 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, ¯ect) == 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: { - PMPrinter printer; - OSStatus status = PMSessionGetCurrentPrinter(d->session, &printer); - if (status != noErr) - qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status)); - if (printer) - ret = QCFString::toQString(PMPrinterGetName(printer)); - 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 deleted file mode 100644 index 511705d26f..0000000000 --- a/src/gui/painting/qprintengine_mac_p.h +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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(); - void releaseSession(); - 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_win.cpp b/src/gui/painting/qprintengine_win.cpp deleted file mode 100644 index 07d66f5bd0..0000000000 --- a/src/gui/painting/qprintengine_win.cpp +++ /dev/null @@ -1,1776 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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 ®ion); - -// #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; - - d->devMode->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; - } - - 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) { - 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; - 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 - || d->txop >= QTransform::TxProject - || 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) { - wchar_t n[64]; - GetTextFace(d->hdc, 64, n); - fallBack = QString::fromWCharArray(n) - != QString::fromWCharArray(fe->logfont.lfFaceName); - } - } - - - if (fallBack) { - QPaintEngine::drawTextItem(p, textItem); - return ; - } - - // We only want to convert the glyphs to text if the entire string is compatible with ASCII - // and if we actually have access to the chars. - bool convertToText = ti.chars != 0; - for (int i=0; i < ti.num_chars; ++i) { - if (ti.chars[i].unicode() >= 0x80) { - convertToText = false; - break; - } - - if (ti.logClusters[i] != i) { - convertToText = 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, convertToText, 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 = QTransform::fromScale(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); - LOGBRUSH brush; - brush.lbStyle = BS_SOLID; - brush.lbColor = RGB(color.red(), color.green(), color.blue()); - DWORD capStyle = PS_ENDCAP_SQUARE; - DWORD joinStyle = PS_JOIN_BEVEL; - if (pen.capStyle() == Qt::FlatCap) - capStyle = PS_ENDCAP_FLAT; - else if (pen.capStyle() == Qt::RoundCap) - capStyle = PS_ENDCAP_ROUND; - - if (pen.joinStyle() == Qt::MiterJoin) - joinStyle = PS_JOIN_MITER; - else if (pen.joinStyle() == Qt::RoundJoin) - joinStyle = PS_JOIN_ROUND; - - HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC) - | PS_SOLID | capStyle | joinStyle, - (penWidth == 0) ? 1 : penWidth, &brush, 0, 0); - - 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")); - wchar_t buffer[256]; - GetProfileString(L"windows", L"device", - reinterpret_cast<const wchar_t *>(noPrinters.utf16()), - buffer, 256); - QString output = QString::fromWCharArray(buffer); - if (output.isEmpty() || output == noPrinters) // no printers - return; - - QStringList info = output.split(QLatin1Char(',')); - int infoSize = info.size(); - if (infoSize > 0) { - if (name.isEmpty()) - name = info.at(0); - if (program.isEmpty() && infoSize > 1) - program = info.at(1); - if (port.isEmpty() && infoSize > 2) - 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 = OpenPrinter((LPWSTR)name.utf16(), (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; - GetPrinter(hPrinter, 2, NULL, 0, &infoSize); - hMem = GlobalAlloc(GHND, infoSize); - pInfo = (PRINTER_INFO_2*) GlobalLock(hMem); - ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes); - - if (!ok) { - qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed"); - GlobalUnlock(pInfo); - GlobalFree(hMem); - ClosePrinter(hPrinter); - pInfo = 0; - hMem = 0; - hPrinter = 0; - return; - } - - devMode = pInfo->pDevMode; - hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()), - reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode); - - Q_ASSERT(hPrinter); - Q_ASSERT(pInfo); - - if (devMode) { - num_copies = devMode->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. - QList<QVariant> list; - - DWORD 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; - - LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG)); - DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()), - reinterpret_cast<const wchar_t *>(port.utf16()), - DC_ENUMRESOLUTIONS, (LPWSTR)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; - d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE; - d->doReinit(); - } - break; - - case PPK_ColorMode: - { - if (!d->devMode) - break; - d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME; - 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_CopyCount: // fallthrough - case PPK_NumberOfCopies: - if (!d->devMode) - break; - d->num_copies = value.toInt(); - d->devMode->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 = d->devMode->dmOrientation; - d->devMode->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; - d->devMode->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())); - - d->devMode->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; - d->devMode->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 = d->devMode->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->devMode->dmPaperSize = i + 1; - break; - } - } - } - free(forms); - } - 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).toReal()*25.4/72.0) * 100; - top = (margins.at(1).toReal()*25.4/72.0) * 100; - right = (margins.at(2).toReal()*25.4/72.0) * 100; - bottom = (margins.at(3).toReal()*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 { - value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale; - } - } - break; - - case PPK_DocumentName: - value = d->docName; - break; - - case PPK_FullPage: - value = d->fullPage; - break; - - case PPK_CopyCount: - value = d->num_copies; - break; - - case PPK_SupportsMultipleCopies: - value = true; - break; - - case PPK_NumberOfCopies: - value = 1; - break; - - case PPK_Orientation: - { - if (!d->devMode) { - value = QPrinter::Portrait; - } else { - value = (d->devMode->dmOrientation == 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->devPhysicalPageRect : d->devPageRect); - } - break; - - case PPK_PaperSize: - if (d->has_custom_paper_size) { - value = QPrinter::Custom; - } else { - if (!d->devMode) { - value = QPrinter::A4; - } else { - value = mapDevmodePaperSize(d->devMode->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 { - value = mapDevmodePaperSource(d->devMode->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 { - value = d->devMode->dmPaperSize; - } - break; - - case PPK_PaperSources: - { - int available = DeviceCapabilities((const wchar_t *)d->name.utf16(), - (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode); - - if (available <= 0) - break; - - wchar_t *data = new wchar_t[available]; - int count = DeviceCapabilities((const wchar_t *)d->name.utf16(), - (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode); - - QList<QVariant> out; - for (int i=0; i<count; ++i) { - QPrinter::PaperSource src = mapDevmodePaperSource(data[i]); - if (src != -1) - out << (int) src; - } - value = out; - - delete [] 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() -{ - 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(wchar_t); - 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::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(), -// QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(), -// QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1()); - - return hGlobal; -} - -void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames) -{ - if (globalDevnames) { - DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames); - name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset); - port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset); - program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset); - GlobalUnlock(globalDevnames); - } -} - -void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode) -{ - if (globalDevmode) { - 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 = devMode->dmCopies; - if (!OpenPrinter((wchar_t*)name.utf16(), &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) -{ - 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; - - if (winfe) { - hfont = winfe->hfont; - ttf = winfe->ttf; - } 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; - - 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. - 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); -#endif - - if (fast) { - // fast path - QVarLengthArray<wchar_t> g(glyphs.numGlyphs); - for (int i = 0; i < glyphs.numGlyphs; ++i) - g[i] = glyphs.glyphs[i]; - ExtTextOut(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 = QTransform::fromTranslate(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 = _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]; - ExtTextOut(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]; - - ExtTextOut(hdc, qRound(positions[i].x), - qRound(positions[i].y), options, 0, - convertToText ? convertedGlyphs + i : &g, 1, 0); - ++i; - } - } - } - -#if !defined(Q_OS_WINCE) - 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); -#endif - - SelectObject(hdc, old_font); -} - - -void QWin32PrintEnginePrivate::updateCustomPaperSize() -{ - uint paperSize = devMode->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; - } -} - -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 deleted file mode 100644 index b4d0670e7b..0000000000 --- a/src/gui/painting/qprintengine_win_p.h +++ /dev/null @@ -1,261 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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; -}; - -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 bool resetDC() { - hdc = ResetDC(hdc, devMode); - 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; - DEVMODE *devMode; - PRINTER_INFO_2 *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/qprinterinfo_mac.cpp b/src/gui/painting/qprinterinfo_mac.cpp deleted file mode 100644 index b24ab70267..0000000000 --- a/src/gui/painting/qprinterinfo_mac.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qprinterinfo.h" -#include "qprinterinfo_p.h" - -#include "private/qt_mac_p.h" - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_PRINTER - -extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &size); - -QList<QPrinterInfo> QPrinterInfo::availablePrinters() -{ - QList<QPrinterInfo> printers; - - QCFType<CFArrayRef> array; - if (PMServerCreatePrinterList(kPMServerLocal, &array) == noErr) { - CFIndex count = CFArrayGetCount(array); - for (int i = 0; i < count; ++i) { - PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); - QString printerName = QCFString::toQString(PMPrinterGetName(printer)); - - QPrinterInfo printerInfo(printerName); - if (PMPrinterIsDefault(printer)) - printerInfo.d_ptr->isDefault = true; - printers.append(printerInfo); - } - } - - return printers; -} - -QPrinterInfo QPrinterInfo::defaultPrinter() -{ - QList<QPrinterInfo> printers = availablePrinters(); - foreach (const QPrinterInfo &printerInfo, printers) { - if (printerInfo.isDefault()) - return printerInfo; - } - - return printers.value(0); -} - -QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const -{ - const Q_D(QPrinterInfo); - - QList<QPrinter::PaperSize> paperSizes; - if (isNull()) - return paperSizes; - - PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->name)); - if (!cfPrn) - return paperSizes; - - CFArrayRef array; - if (PMPrinterGetPaperList(cfPrn, &array) != noErr) { - PMRelease(cfPrn); - return paperSizes; - } - - int count = CFArrayGetCount(array); - for (int i = 0; i < count; ++i) { - PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); - double width, height; - if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) { - QSizeF size(width * 0.3527, height * 0.3527); - paperSizes.append(qSizeFTopaperSize(size)); - } - } - - PMRelease(cfPrn); - - return paperSizes; -} - -#endif // QT_NO_PRINTER - -QT_END_NAMESPACE diff --git a/src/gui/painting/qprinterinfo_win.cpp b/src/gui/painting/qprinterinfo_win.cpp deleted file mode 100644 index 2c4014d8dc..0000000000 --- a/src/gui/painting/qprinterinfo_win.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qprinterinfo.h" -#include "qprinterinfo_p.h" - -#include <qstringlist.h> - -#include <qt_windows.h> - -QT_BEGIN_NAMESPACE - -#ifndef QT_NO_PRINTER - -extern QPrinter::PaperSize mapDevmodePaperSize(int s); - -QList<QPrinterInfo> QPrinterInfo::availablePrinters() -{ - QList<QPrinterInfo> printers; - - DWORD needed = 0; - DWORD returned = 0; - if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) { - LPBYTE buffer = new BYTE[needed]; - if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) { - PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer); - QPrinterInfo defPrn = defaultPrinter(); - for (uint i = 0; i < returned; ++i) { - QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); - - QPrinterInfo printerInfo(printerName); - if (printerInfo.printerName() == defPrn.printerName()) - printerInfo.d_ptr->isDefault = true; - printers.append(printerInfo); - } - } - delete [] buffer; - } - - return printers; -} - -QPrinterInfo QPrinterInfo::defaultPrinter() -{ - QString noPrinters(QLatin1String("qt_no_printers")); - wchar_t buffer[256]; - GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256); - QString output = QString::fromWCharArray(buffer); - if (output != noPrinters) { - // Filter out the name of the printer, which should be everything before a comma. - QString printerName = output.split(QLatin1Char(',')).value(0); - QPrinterInfo printerInfo(printerName); - printerInfo.d_ptr->isDefault = true; - return printerInfo; - } - - return QPrinterInfo(); -} - -QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const -{ - const Q_D(QPrinterInfo); - - QList<QPrinter::PaperSize> paperSizes; - if (isNull()) - return paperSizes; - - DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), - NULL, DC_PAPERS, NULL, NULL); - if ((int)size != -1) { - wchar_t *papers = new wchar_t[size]; - size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), - NULL, DC_PAPERS, papers, NULL); - for (int c = 0; c < (int)size; ++c) - paperSizes.append(mapDevmodePaperSize(papers[c])); - delete [] papers; - } - - return paperSizes; -} - -#endif // QT_NO_PRINTER - -QT_END_NAMESPACE diff --git a/src/gui/painting/qregion_mac.cpp b/src/gui/painting/qregion_mac.cpp deleted file mode 100644 index 50fd783df4..0000000000 --- a/src/gui/painting/qregion_mac.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/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; -} - -/*! - \internal - Create's a RegionHandle, it's the caller's responsibility to release. - Returns 0 if the QRegion overflows. -*/ -RgnHandle QRegion::toQDRgnForUpdate_sys() 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--) { - - // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion - // in QWidgetPrivate::update_sys(). - enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value - if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) { - qt_mac_dispose_rgn(tmp_rgn); - qt_mac_dispose_rgn(rgnHandle); - return 0; - } - - 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_s60.cpp b/src/gui/painting/qregion_s60.cpp deleted file mode 100644 index eafff1b965..0000000000 --- a/src/gui/painting/qregion_s60.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qbitmap.h" -#include "qbuffer.h" -#include "qimage.h" -#include "qpolygon.h" -#include "qregion.h" - -QT_BEGIN_NAMESPACE - -QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 }; - -QT_END_NAMESPACE diff --git a/src/gui/painting/qregion_win.cpp b/src/gui/painting/qregion_win.cpp deleted file mode 100644 index 3466b62cbd..0000000000 --- a/src/gui/painting/qregion_win.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qatomic.h" -#include "qbitmap.h" -#include "qbuffer.h" -#include "qimage.h" -#include "qpolygon.h" -#include "qregion.h" -#include "qt_windows.h" -#include "qpainterpath.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 = 0; - 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; -} - -QRegion qt_region_from_HRGN(HRGN rgn) -{ - int numBytes = GetRegionData(rgn, 0, 0); - if (numBytes == 0) - return QRegion(); - - char *buf = new char[numBytes]; - if (buf == 0) - return QRegion(); - - RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); - if (GetRegionData(rgn, numBytes, rd) == 0) { - delete [] buf; - return QRegion(); - } - - QRegion region; - RECT *r = reinterpret_cast<RECT*>(rd->Buffer); - for (uint i = 0; i < rd->rdh.nCount; ++i) { - QRect rect; - rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1); - ++r; - region |= rect; - } - - delete [] buf; - - return region; -} - -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; - } - DeleteObject(rgn); - } -} - -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 deleted file mode 100644 index ef4e844bfa..0000000000 --- a/src/gui/painting/qregion_x11.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/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/platforms/mac/qapplication_mac.mm b/src/gui/platforms/mac/qapplication_mac.mm new file mode 100644 index 0000000000..f607a72e92 --- /dev/null +++ b/src/gui/platforms/mac/qapplication_mac.mm @@ -0,0 +1,3134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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 "qurl.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 + +#define kCMDeviceUnregisteredNotification CFSTR("CMDeviceUnregisteredNotification") +#define kCMDefaultDeviceNotification CFSTR("CMDefaultDeviceNotification") +#define kCMDeviceProfilesNotification CFSTR("CMDeviceProfilesNotification") +#define kCMDefaultDeviceProfileNotification CFSTR("CMDefaultDeviceProfileNotification") + +QT_BEGIN_NAMESPACE + +//for qt_mac.h +QPaintDevice *qt_mac_safe_pdev = 0; +QList<QMacWindowChangeEvent*> *QMacWindowChangeEvent::change_events = 0; +QPointer<QWidget> topLevelAt_cache = 0; + +/***************************************************************************** + 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 +QWidget * qt_button_down; // widget got last button-down +QPointer<QWidget> qt_last_mouse_receiver; +#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 +#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; +#endif +static AEEventHandlerUPP app_proc_ae_handlerUPP = NULL; +static EventHandlerRef tablet_proximity_handler = 0; +static EventHandlerUPP tablet_proximity_UPP = 0; +bool QApplicationPrivate::native_modal_dialog_active; + +Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +/***************************************************************************** + 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 *); //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 +extern void qt_mac_update_cursor(); // qcursor_mac.mm + +// Forward Decls +void onApplicationWindowChangedActivation( QWidget*widget, bool activated ); +void onApplicationChangedActivation( bool activated ); + +static void qt_mac_read_fontsmoothing_settings() +{ + qt_applefontsmoothing_enabled = true; + int w = 10, h = 10; + QImage image(w, h, QImage::Format_RGB32); + image.fill(0xffffffff); + QPainter p(&image); + p.drawText(0, h, "X\\"); + p.end(); + + const int *bits = (const int *) ((const QImage &) image).bits(); + int bpl = image.bytesPerLine() / 4; + for (int y=0; y<w; ++y) { + for (int x=0; x<h; ++x) { + int r = qRed(bits[x]); + int g = qGreen(bits[x]); + int b = qBlue(bits[x]); + if (r != g || r != b) { + qt_applefontsmoothing_enabled = true; + return; + } + } + bits += bpl; + } + qt_applefontsmoothing_enabled = false; +} + +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", kThemeMenuItemFont), // 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; + FontHash *hash = qt_app_fonts_hash(); + if (!hash->isEmpty()) { + FontHash::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; + PaletteHash *phash = qt_app_palettes_hash(); + if (!phash->isEmpty()) { + PaletteHash::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) +{ + // 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 + } + } +} + +struct QMacAppleEventTypeSpec { + AEEventClass mac_class; + AEEventID mac_id; +} app_apple_events[] = { + { kCoreEventClass, kAEQuitApplication }, + { kCoreEventClass, kAEOpenDocuments }, + { kInternetEventClass, kAEGetURL }, +}; + +#ifndef QT_MAC_USE_COCOA + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) +enum +{ + kEventMouseScroll = 11, + kEventParamMouseWheelSmoothVerticalDelta = 'saxy', + kEventParamMouseWheelSmoothHorizontalDelta = 'saxx', +}; +#endif + +/* watched events */ +static EventTypeSpec app_events[] = { + { kEventClassQt, kEventQtRequestWindowChange }, + { kEventClassQt, kEventQtRequestShowSheet }, + { kEventClassQt, kEventQtRequestContext }, + { kEventClassQt, kEventQtRequestActivate }, + { kEventClassQt, kEventQtRequestMenubarUpdate }, + + { kEventClassWindow, kEventWindowActivated }, + { kEventClassWindow, kEventWindowDeactivated }, + + { kEventClassMouse, kEventMouseScroll }, + { 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 +} + +void qt_color_profile_changed(CFNotificationCenterRef, void *, CFStringRef, const void *, + CFDictionaryRef) +{ + QCoreGraphicsPaintEngine::cleanUpMacColorSpaces(); +} +/* platform specific implementations */ +void qt_init(QApplicationPrivate *priv, int) +{ + if (qt_is_gui_used) { + CGDisplayRegisterReconfigurationCallback(qt_mac_display_change_callbk, 0); + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, + kCMDeviceUnregisteredNotification, 0, + CFNotificationSuspensionBehaviorDeliverImmediately); + CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, + kCMDefaultDeviceNotification, 0, + CFNotificationSuspensionBehaviorDeliverImmediately); + CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, + kCMDeviceProfilesNotification, 0, + CFNotificationSuspensionBehaviorDeliverImmediately); + CFNotificationCenterAddObserver(center, qApp, qt_color_profile_changed, + kCMDefaultDeviceProfileNotification, 0, + CFNotificationSuspensionBehaviorDeliverImmediately); + 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 something) + // 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(); + } + +#endif + if (!app_proc_ae_handlerUPP && !QApplication::testAttribute(Qt::AA_MacPluginApplication)) { + app_proc_ae_handlerUPP = AEEventHandlerUPP(QApplicationPrivate::globalAppleEventProcessor); + for(uint i = 0; i < sizeof(app_apple_events) / sizeof(QMacAppleEventTypeSpec); ++i) { + // Install apple event handler, but avoid overwriting an already + // existing handler (it means a 3rd party application has installed one): + SRefCon refCon = 0; + AEEventHandlerUPP current_handler = NULL; + AEGetEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id, ¤t_handler, &refCon, false); + if (!current_handler) + AEInstallEventHandler(app_apple_events[i].mac_class, app_apple_events[i].mac_id, + app_proc_ae_handlerUPP, SRefCon(qApp), false); + } + } + + 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 = [QNSApplication sharedApplication]; + qt_redirectNSApplicationSendEvent(); + + QMacCocoaAutoReleasePool pool; + id 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 + // 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_mac_read_fontsmoothing_settings(); +} + +void qt_release_apple_event_handler() +{ + 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 = 0; + } +} + +/***************************************************************************** + qt_cleanup() - cleans up when the application is finished + *****************************************************************************/ + +void qt_cleanup() +{ + CGDisplayRemoveReconfigurationCallback(qt_mac_display_change_callbk, 0); + CFNotificationCenterRef center = CFNotificationCenterGetDistributedCenter(); + CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceUnregisteredNotification, 0); + CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceNotification, 0); + CFNotificationCenterRemoveObserver(center, qApp, kCMDeviceProfilesNotification, 0); + CFNotificationCenterRemoveObserver(center, qApp, kCMDefaultDeviceProfileNotification, 0); + +#ifndef QT_MAC_USE_COCOA + qt_release_app_proc_handler(); + if (app_proc_handlerUPP) { + DisposeEventHandlerUPP(app_proc_handlerUPP); + app_proc_handlerUPP = 0; + } +#endif + qt_release_apple_event_handler(); + 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); + +#ifdef QT_MAC_USE_COCOA + qt_mac_update_cursor(); +#else + if (qApp && qApp->activeWindow()) + qt_mac_set_cursor(&qApp->d_func()->cursor_list.first()); +#endif +} + +void QApplication::restoreOverrideCursor() +{ + if (qApp->d_func()->cursor_list.isEmpty()) + return; + qApp->d_func()->cursor_list.removeFirst(); + +#ifdef QT_MAC_USE_COCOA + qt_mac_update_cursor(); +#else + 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()); + } +#endif +} +#endif // QT_NO_CURSOR + +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 + // Use a cache to avoid iterate through the whole list of windows for all + // calls to to topLevelAt. We e.g. do this for each and every mouse + // move since we need to find the widget under mouse: + if (topLevelAt_cache && topLevelAt_cache->frameGeometry().contains(p)) + return topLevelAt_cache; + + // INVARIANT: Cache miss. Go through the list if windows instead: + QMacCocoaAutoReleasePool pool; + NSPoint cocoaPoint = flipPoint(p); + NSInteger windowCount; + NSCountWindows(&windowCount); + if (windowCount <= 0) + return 0; // There's no window to find! + + QVarLengthArray<NSInteger> windowList(windowCount); + NSWindowList(windowCount, windowList.data()); + int firstQtWindowFound = -1; + for (int i = 0; i < windowCount; ++i) { + NSWindow *window = [NSApp windowWithWindowNumber:windowList[i]]; + if (window) { + QWidget *candidateWindow = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; + if (candidateWindow && firstQtWindowFound == -1) + firstQtWindowFound = i; + + if (NSPointInRect(cocoaPoint, [window frame])) { + // 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; + else + return candidateWindow; + } else { + if (i == firstQtWindowFound) { + // The cache will only work when the window under mouse is + // top most (that is, not partially obscured by other windows. + // And we only set it if no mask is present to optimize for the common case: + topLevelAt_cache = candidateWindow; + } + return candidateWindow; + } + } + } + } + + topLevelAt_cache = 0; + return 0; +#endif +} + +/***************************************************************************** + 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_last_mouse_receiver); + qt_last_mouse_receiver = 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)) + QEventDispatcherMacPrivate::beginModalSession(widget); +#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_last_mouse_receiver); // send synthetic enter event + qt_last_mouse_receiver = w; + } +#ifdef QT_MAC_USE_COCOA + if (!qt_mac_is_macsheet(widget)) + QEventDispatcherMacPrivate::endModalSession(widget); +#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(); +} + +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; +} + +#ifndef QT_MAC_USE_COCOA +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; +} +#endif + +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 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 kEventMouseScroll: edesc = "MouseWheelScroll"; 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); + if (ekind != kEventMouseWheelMoved) + buttons = qt_mac_get_buttons(mac_buttons); + else + buttons = QApplication::mouseButtons(); + } + + int wheel_deltaX = 0; + int wheel_deltaY = 0; + static EventRef compatibilityEvent = 0; + + if (ekind == kEventMouseScroll) { + // kEventMouseScroll is the new way of dealing with mouse wheel + // events (kEventMouseWheelMoved was the old). kEventMouseScroll results + // in much smoother scrolling when using Mighty Mouse or TrackPad. For + // compatibility with older applications, carbon will also send us + // kEventMouseWheelMoved events if we dont eat this event + // (actually two events; one for horizontal and one for vertical). + // As a results of this, and to make sure we dont't receive duplicate events, + // we try to detect when this happend by checking the 'compatibilityEvent'. + // Since delta is delivered as pixels rather than degrees, we need to + // convert from pixels to degrees in a sensible manner. + // It looks like 1/4 degrees per pixel behaves most native. + // (NB: Qt expects the unit for delta to be 8 per degree): + const int pixelsToDegrees = 2; + SInt32 mdelt = 0; + GetEventParameter(event, kEventParamMouseWheelSmoothHorizontalDelta, typeSInt32, 0, + sizeof(mdelt), 0, &mdelt); + wheel_deltaX = mdelt * pixelsToDegrees; + mdelt = 0; + GetEventParameter(event, kEventParamMouseWheelSmoothVerticalDelta, typeSInt32, 0, + sizeof(mdelt), 0, &mdelt); + wheel_deltaY = mdelt * pixelsToDegrees; + GetEventParameter(event, kEventParamEventRef, typeEventRef, 0, + sizeof(compatibilityEvent), 0, &compatibilityEvent); + } else if (ekind == kEventMouseWheelMoved) { + if (event != compatibilityEvent) { + compatibilityEvent = 0; + int mdelt = 0; + GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0, + sizeof(mdelt), 0, &mdelt); + EventMouseWheelAxis axis; + GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, 0, + sizeof(axis), 0, &axis); + + // Remove acceleration, and use either -120 or 120 as delta: + if (axis == kEventMouseWheelAxisX) + wheel_deltaX = qBound(-120, int(mdelt * 10000), 120); + else + wheel_deltaY = qBound(-120, int(mdelt * 10000), 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); + } + + //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) && t != QEvent::TabletRelease) { + // Never discard TabletRelease events as they may be delivered *after* TabletLeaveProximity events + qWarning("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 (t == QEvent::TabletPress) { + qt_button_down = widget; + } else if (t == QEvent::TabletRelease) { + qt_button_down = 0; + } +#if defined(DEBUG_MOUSE_MAPS) + qDebug("Bail out early due to tablet 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_last_mouse_receiver != 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_last_mouse_receiver ? qt_last_mouse_receiver->metaObject()->className() : "none", + qt_last_mouse_receiver ? qt_last_mouse_receiver->objectName().toLocal8Bit().constData() : ""); +#endif + + QWidget * const mouseGrabber = QWidget::mouseGrabber(); + + if (inPopupMode) { + QWidget *enter = enterLeaveWidget; + QWidget *leave = qt_last_mouse_receiver; + 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_last_mouse_receiver = enter; + } + } else { + QApplicationPrivate::dispatchEnterLeave(enter, leave); + qt_last_mouse_receiver = enter; + } + } else if ((!qt_button_down || !qt_last_mouse_receiver) && !mouseGrabber && !leaveAfterRelease) { + QApplicationPrivate::dispatchEnterLeave(enterLeaveWidget, qt_last_mouse_receiver); + qt_last_mouse_receiver = 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_deltaX || wheel_deltaY) { +#ifndef QT_NO_WHEELEVENT + if (wheel_deltaX) { + QWheelEvent qwe(plocal, p, wheel_deltaX, buttons, modifiers, Qt::Horizontal); + QApplication::sendSpontaneousEvent(widget, &qwe); + if (!qwe.isAccepted() && QApplicationPrivate::focus_widget && QApplicationPrivate::focus_widget != widget) { + QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(p), p, + wheel_deltaX, buttons, modifiers, Qt::Horizontal); + QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); + if (!qwe2.isAccepted()) + handled_event = false; + } + } + if (wheel_deltaY) { + QWheelEvent qwe(plocal, p, wheel_deltaY, buttons, modifiers, 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_deltaY, buttons, modifiers, Qt::Vertical); + QApplication::sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2); + if (!qwe2.isAccepted()) + handled_event = false; + } + } +#endif // QT_NO_WHEELEVENT + } 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_last_mouse_receiver = 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_deltaX); +#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, ©_cmd); + copy_cmd.menu.menuRef = GetApplicationDockTileMenu(); + SetEventParameter(copy, kEventParamDirectObject, typeHICommand, sizeof(copy_cmd), ©_cmd); + if(SendEventToMenu(copy, copy_cmd.menu.menuRef) == noErr) + handled_event = true; + } + if(!handled_event) { + if(cmd.commandID == kHICommandQuit) { + // Quitting the application is not Qt's responsibility if + // used in a plugin or just embedded into a native application. + // In that case, let the event pass down to the native apps event handler. + if (!QApplication::testAttribute(Qt::AA_MacPluginApplication)) { + 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 +} + +#ifdef QT_MAC_USE_COCOA +void QApplicationPrivate::qt_initAfterNSAppStarted() +{ + setupAppleEvents(); + qt_mac_update_cursor(); +} + +void QApplicationPrivate::setupAppleEvents() +{ + // This function is called from the event dispatcher when NSApplication has + // finished initialization, which appears to be just after [NSApplication run] has + // started to execute. By setting up our apple events handlers this late, we override + // the ones set up by NSApplication. + + // If Qt is used as a plugin, we let the 3rd party application handle events + // like quit and open file events. Otherwise, if we install our own handlers, we + // easily end up breaking functionallity the 3rd party application depend on: + if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) + return; + + QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) *newDelegate = [QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) sharedDelegate]; + NSAppleEventManager *eventManager = [NSAppleEventManager sharedAppleEventManager]; + [eventManager setEventHandler:newDelegate andSelector:@selector(appleEventQuit:withReplyEvent:) + forEventClass:kCoreEventClass andEventID:kAEQuitApplication]; + [eventManager setEventHandler:newDelegate andSelector:@selector(getUrl:withReplyEvent:) + forEventClass:kInternetEventClass andEventID:kAEGetURL]; +} +#endif + +// In Carbon this is your one stop for apple events. +// In Cocoa, it ISN'T. This is the catch-all Apple Event handler that exists +// for the time between instantiating the NSApplication, but before the +// NSApplication has installed it's OWN Apple Event handler. When Cocoa has +// that set up, we remove this. So, if you are debugging problems, you likely +// want to check out QCocoaApplicationDelegate instead. +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 (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; + } + } else if (aeClass == kInternetEventClass) { + switch (aeID) { + case kAEGetURL: { + char urlData[1024]; + Size actualSize; + if (AEGetParamPtr(ae, keyDirectObject, typeChar, 0, urlData, + sizeof(urlData) - 1, &actualSize) == noErr) { + urlData[actualSize] = 0; + QFileOpenEvent ev(QUrl(QString::fromUtf8(urlData))); + QApplication::sendSpontaneousEvent(app, &ev); + } + 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 used under Mac OS X, and behaves different + depending on if Qt is based on Carbon or Cocoa. + + For the Carbon port, 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. + + For the Cocoa port, If you create an application that inherits QApplication and reimplement + this function, you get direct access to all Cocoa Events that Qt receives + from Mac OS X with this function being called with the \a caller being 0 and + the \a event being an NSEvent pointer: + + NSEvent *e = reinterpret_cast<NSEvent *>(event); + + Return true if you want to stop the event from being processed. + Return false for normal event dispatching. The default + implementation returns false. + + \sa macEventFilter(void *nsevent) +*/ +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; +} + +#ifndef QT_NO_WHEELEVENT +void QApplication::setWheelScrollLines(int n) +{ + QApplicationPrivate::wheel_scroll_lines = n; +} + +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} +#endif + +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); + +#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"); + + 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 + if (!widget) + return; + + if (activated) { + if (QApplicationPrivate::app_style) { + QEvent ev(QEvent::Style); + qt_sendSpontaneousEvent(QApplicationPrivate::app_style, &ev); + } + qApp->setActiveWindow(widget); + } else { // deactivated + if (QApplicationPrivate::active_window == widget) + qApp->setActiveWindow(0); + } + + QMenuBar::macUpdateMenuBar(); + qt_mac_update_cursor(); +#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()) { + OSWindowRef wp = [NSApp keyWindow]; + if (QWidget *tmp_w = qt_mac_find_window(wp)) + app->setActiveWindow(tmp_w); + } + QMenuBar::macUpdateMenuBar(); + qt_mac_update_cursor(); + } 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 +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ } +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qclipboard_mac.cpp b/src/gui/platforms/mac/qclipboard_mac.cpp new file mode 100644 index 0000000000..4a8bc56e41 --- /dev/null +++ b/src/gui/platforms/mac/qclipboard_mac.cpp @@ -0,0 +1,634 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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> +#include "qt_cocoa_helpers_mac_p.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(); + +#ifdef QT_MAC_USE_COCOA + // QMimeData sub classes reimplementing the formats() might not expose the + // temporary "application/x-qt-mime-type-name" mimetype. So check the existence + // of this mime type while doing drag and drop. + QString dummyMimeType(QLatin1String("application/x-qt-mime-type-name")); + if (!formats.contains(dummyMimeType)) { + QByteArray dummyType = mime_src->data(dummyMimeType); + if (!dummyType.isEmpty()) { + formats.append(dummyMimeType); + } + } +#endif + 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. + bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text") + || c_flavor == QLatin1String("public.utf8-plain-text")); + if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) { + // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped + // correctly (as '\n') in this data. The 'public.utf16-plain-text' type + // usually maps newlines to '\r' instead. + QString str = qt_mac_get_pasteboardString(paste); + if (!str.isEmpty()) + return str; + } + if (checkForUtf16 && 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: Synchronize!"); +#endif + return fromGlobal; +} + + + + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qcocoaapplication_mac.mm b/src/gui/platforms/mac/qcocoaapplication_mac.mm new file mode 100644 index 0000000000..872f31dec7 --- /dev/null +++ b/src/gui/platforms/mac/qcocoaapplication_mac.mm @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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> +#include <private/qcocoaintrospection_p.h> + +QT_USE_NAMESPACE + +@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; +} + +- (void)qt_sendPostedMessage:(NSEvent *)event +{ + // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! + // That is why we need to split the address in two parts: + quint64 lower = [event data1]; + quint64 upper = [event data2]; + QCocoaPostMessageArgs *args = reinterpret_cast<QCocoaPostMessageArgs *>(lower | (upper << 32)); + // Special case for convenience: if the argument is an NSNumber, we unbox it directly. + // Use NSValue instead if this behaviour is unwanted. + id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1; + id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2; + switch (args->argCount) { + case 0: + [args->target performSelector:args->selector]; + break; + case 1: + [args->target performSelector:args->selector withObject:a1]; + break; + case 3: + [args->target performSelector:args->selector withObject:a1 withObject:a2]; + break; + } + + delete args; +} + +- (BOOL)qt_filterEvent:(NSEvent *)event +{ + if (qApp->macEventFilter(0, reinterpret_cast<EventRef>(event))) + return true; + + if ([event type] == NSApplicationDefined) { + switch ([event subtype]) { + case QtCocoaEventSubTypePostMessage: + [NSApp qt_sendPostedMessage:event]; + return true; + default: + break; + } + } + return false; +} + +@end + +@implementation QNSApplication + +- (void)qt_sendEvent_original:(NSEvent *)event +{ + Q_UNUSED(event); + // This method will only be used as a signature + // template for the method we add into NSApplication + // containing the original [NSApplication sendEvent:] implementation +} + +- (void)qt_sendEvent_replacement:(NSEvent *)event +{ + // This method (or its implementation to be precise) will + // be called instead of sendEvent if redirection occurs. + // 'self' will then be an instance of NSApplication + // (and not QNSApplication) + if (![NSApp qt_filterEvent:event]) + [self qt_sendEvent_original:event]; +} + +- (void)sendEvent:(NSEvent *)event +{ + // This method will be called if + // no redirection occurs + if (![NSApp qt_filterEvent:event]) + [super sendEvent:event]; +} + +- (void)qtDispatcherToQAction:(id)sender +{ + // Forward actions sendt from the menu bar (e.g. quit) to the menu loader. + // Having this method here means that we are the last stop in the responder + // chain, and that we are able to handle menu actions even when no window is + // visible on screen. Note: If Qt is used as a plugin, Qt will not use a + // native menu bar. Hence, we will also not need to do any redirection etc. as + // we do with sendEvent. + [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; +} + +@end + +QT_BEGIN_NAMESPACE + +void qt_redirectNSApplicationSendEvent() +{ + if ([NSApp isMemberOfClass:[QNSApplication class]]) { + // No need to change implementation since Qt + // already controls a subclass of NSApplication + return; + } + + // Change the implementation of [NSApplication sendEvent] to the + // implementation of qt_sendEvent_replacement found in QNSApplication. + // And keep the old implementation that gets overwritten inside a new + // method 'qt_sendEvent_original' that we add to NSApplication + qt_cocoa_change_implementation( + [NSApplication class], + @selector(sendEvent:), + [QNSApplication class], + @selector(qt_sendEvent_replacement:), + @selector(qt_sendEvent_original:)); + } + +QT_END_NAMESPACE +#endif diff --git a/src/gui/platforms/mac/qcocoaapplication_mac_p.h b/src/gui/platforms/mac/qcocoaapplication_mac_p.h new file mode 100644 index 0000000000..0c3f5e442d --- /dev/null +++ b/src/gui/platforms/mac/qcocoaapplication_mac_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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; + +- (void)qt_sendPostedMessage:(NSEvent *)event; +- (BOOL)qt_filterEvent:(NSEvent *)event; +@end + +@interface QNSApplication : NSApplication { +} +@end + +QT_BEGIN_NAMESPACE + +void qt_redirectNSApplicationSendEvent(); + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/platforms/mac/qcocoaapplicationdelegate_mac.mm b/src/gui/platforms/mac/qcocoaapplicationdelegate_mac.mm new file mode 100644 index 0000000000..77cd8902c3 --- /dev/null +++ b/src/gui/platforms/mac/qcocoaapplicationdelegate_mac.mm @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** + ** + ** 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> +#import <private/qcocoaapplication_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 <qurl.h> +#include <qapplication.h> + +QT_BEGIN_NAMESPACE +extern void onApplicationChangedActivation(bool); // qapplication_mac.mm +extern void qt_release_apple_event_handler(); //qapplication_mac.mm +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm +extern QPointer<QWidget> qt_button_down; // qapplication_mac.cpp + +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]; + if (self) + inLaunch = true; + 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]; +} + +// This function will only be called when NSApp is actually running. Before +// that, the kAEQuitApplication Apple event will be sent to +// QApplicationPrivate::globalAppleEventProcessor in qapplication_mac.mm +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + Q_UNUSED(sender); + // The reflection delegate gets precedence + if (reflectionDelegate + && [reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) { + return [reflectionDelegate applicationShouldTerminate:sender]; + } + + if (qtPrivate->canQuit()) { + if (!startedQuit) { + startedQuit = true; + qAppInstance()->quit(); + startedQuit = false; + } + } + + if (qtPrivate->threadData->eventLoops.size() == 0) { + // INVARIANT: No event loop is executing. This probably + // means that Qt is used as a plugin, or as a part of a native + // Cocoa application. In any case it should be fine to + // terminate now: + return NSTerminateNow; + } + + return NSTerminateCancel; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + Q_UNUSED(aNotification); + inLaunch = false; + qt_release_apple_event_handler(); +} + +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames +{ + for (NSString *fileName in filenames) { + QString qtFileName = qt_mac_NSStringToQString(fileName); + if (inLaunch) { + // We need to be careful because Cocoa will be nice enough to take + // command line arguments and send them to us as events. Given the history + // of Qt Applications, this will result in behavior people don't want, as + // they might be doing the opening themselves with the command line parsing. + if (qApp->arguments().contains(qtFileName)) + continue; + } + QFileOpenEvent foe(qtFileName); + qt_sendSpontaneousEvent(qAppInstance(), &foe); + } + + 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); + + if (!QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, 0); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; + } +} + +- (void)applicationDidResignActive:(NSNotification *)notification +{ + if (reflectionDelegate + && [reflectionDelegate respondsToSelector:@selector(applicationDidResignActive:)]) + [reflectionDelegate applicationDidResignActive:notification]; + + onApplicationChangedActivation(false); + + if (!QWidget::mouseGrabber()) + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; + qt_button_down = 0; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *)notification +{ + Q_UNUSED(notification); + QDesktopWidgetImplementation::instance()->onResize(); +} + +- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)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]; +} + +- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + Q_UNUSED(replyEvent); + + NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue]; + QUrl url(qt_mac_NSStringToQString(urlString)); + QFileOpenEvent qtEvent(url); + qt_sendSpontaneousEvent(qAppInstance(), &qtEvent); +} + +- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent +{ + Q_UNUSED(event); + Q_UNUSED(replyEvent); + [NSApp terminate:self]; +} + +- (void)qtDispatcherToQAction:(id)sender +{ + [[NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)] qtDispatcherToQAction:sender]; +} + +@end +#endif diff --git a/src/gui/platforms/mac/qcocoaapplicationdelegate_mac_p.h b/src/gui/platforms/mac/qcocoaapplicationdelegate_mac_p.h new file mode 100644 index 0000000000..714c046f48 --- /dev/null +++ b/src/gui/platforms/mac/qcocoaapplicationdelegate_mac_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/**************************************************************************** + ** + ** 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> +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; +- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames; +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender; +- (void)applicationDidBecomeActive:(NSNotification *)notification; +- (void)applicationDidResignActive:(NSNotification *)notification; +@end + +#endif + +@interface QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) : NSObject <NSApplicationDelegate> { + bool startedQuit; + QApplicationPrivate *qtPrivate; + NSMenu *dockMenu; + QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *qtMenuLoader; + NSObject <NSApplicationDelegate> *reflectionDelegate; + bool inLaunch; +} ++ (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 <NSApplicationDelegate> *)oldDelegate; +- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; +@end +#endif diff --git a/src/gui/platforms/mac/qcocoaintrospection_mac.mm b/src/gui/platforms/mac/qcocoaintrospection_mac.mm new file mode 100644 index 0000000000..70c893aeec --- /dev/null +++ b/src/gui/platforms/mac/qcocoaintrospection_mac.mm @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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/qcocoaintrospection_p.h> + +QT_BEGIN_NAMESPACE + +void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel, SEL backupSel) +{ +#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 + // The following code replaces the _implementation_ for the selector we want to hack + // (originalSel) with the implementation found in proxyClass. Then it creates + // a new 'backup' method inside baseClass containing the old, original, + // implementation (fakeSel). You can let the proxy implementation of originalSel + // call fakeSel if needed (similar approach to calling a super class implementation). + // fakeSel must also be implemented in proxyClass, as the signature is used + // as template for the method one we add into baseClass. + // NB: You will typically never create any instances of proxyClass; we use it + // only for stealing its contents and put it into baseClass. + if (!replacementSel) + replacementSel = originalSel; + + Method originalMethod = class_getInstanceMethod(baseClass, originalSel); + Method replacementMethod = class_getInstanceMethod(proxyClass, replacementSel); + IMP originalImp = method_setImplementation(originalMethod, method_getImplementation(replacementMethod)); + + if (backupSel) { + Method backupMethod = class_getInstanceMethod(proxyClass, backupSel); + class_addMethod(baseClass, backupSel, originalImp, method_getTypeEncoding(backupMethod)); + } +#endif + } +} + +void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel) +{ +#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 backupMethodInBaseClass = class_getInstanceMethod(baseClass, backupSel); + method_setImplementation(originalMethod, method_getImplementation(backupMethodInBaseClass)); +#endif + } +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qcocoaintrospection_p.h b/src/gui/platforms/mac/qcocoaintrospection_p.h new file mode 100644 index 0000000000..1c7d6ac13c --- /dev/null +++ b/src/gui/platforms/mac/qcocoaintrospection_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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> +#import <objc/objc-class.h> + +QT_BEGIN_NAMESPACE + +void qt_cocoa_change_implementation(Class baseClass, SEL originalSel, Class proxyClass, SEL replacementSel = 0, SEL backupSel = 0); +void qt_cocoa_change_back_implementation(Class baseClass, SEL originalSel, SEL backupSel); + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qcocoamenuloader_mac.mm b/src/gui/platforms/mac/qcocoamenuloader_mac.mm new file mode 100644 index 0000000000..71ff011069 --- /dev/null +++ b/src/gui/platforms/mac/qcocoamenuloader_mac.mm @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmacdefines_mac.h" +#ifdef QT_MAC_USE_COCOA +#include <qaction.h> +#include <qcoreapplication.h> +#include <private/qcocoamenuloader_mac_p.h> +#include <private/qapplication_p.h> +#include <private/qt_mac_p.h> +#include <private/qmenubar_p.h> +#include <qmenubar.h> +#include <private/qt_cocoa_helpers_mac_p.h> + +QT_FORWARD_DECLARE_CLASS(QCFString) +QT_FORWARD_DECLARE_CLASS(QString) + +#ifndef QT_NO_TRANSLATION + QT_BEGIN_NAMESPACE + extern QString qt_mac_applicationmenu_string(int type); + QT_END_NAMESPACE +#endif + +QT_USE_NAMESPACE + +@implementation QT_MANGLE_NAMESPACE(QCocoaMenuLoader) + +- (void)awakeFromNib +{ + servicesItem = [[appMenu itemWithTitle:@"Services"] retain]; + hideAllOthersItem = [[appMenu itemWithTitle:@"Hide Others"] retain]; + showAllItem = [[appMenu itemWithTitle:@"Show All"] retain]; + + // Get the names in the nib to match the app name set by Qt. + const NSString *appName = reinterpret_cast<const NSString*>(QCFString::toCFStringRef(qAppName())); + [quitItem setTitle:[[quitItem title] stringByReplacingOccurrencesOfString:@"NewApplication" + withString:const_cast<NSString *>(appName)]]; + [hideItem setTitle:[[hideItem title] stringByReplacingOccurrencesOfString:@"NewApplication" + withString:const_cast<NSString *>(appName)]]; + [aboutItem setTitle:[[aboutItem title] stringByReplacingOccurrencesOfString:@"NewApplication" + withString:const_cast<NSString *>(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 +{ + // The application menu is the menu in the menu bar that contains the + // 'Quit' item. When changing menu bar (e.g when switching between + // windows with different menu bars), we never recreate this menu, but + // instead pull it out the current menu bar and place into the new one: + NSMenu *mainMenu = [NSApp mainMenu]; + if ([NSApp mainMenu] == menu) + return; // nothing to do (menu is the current menu bar)! + +#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)removeActionsFromAppMenu +{ + for (NSMenuItem *item in [appMenu itemArray]) + [item setTag:nil]; +} + +- (void)dealloc +{ + [servicesItem release]; + [hideAllOthersItem release]; + [showAllItem release]; + + [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]; +} + +- (void)qtUpdateMenubar +{ + QMenuBarPrivate::macUpdateMenuBarImmediatly(); +} + +- (void)qtTranslateApplicationMenu +{ +#ifndef QT_NO_TRANSLATION + [servicesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(0))]; + [hideItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(1).arg(qAppName()))]; + [hideAllOthersItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(2))]; + [showAllItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(3))]; + [preferencesItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(4))]; + [quitItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(5).arg(qAppName()))]; + [aboutItem setTitle: qt_mac_QStringToNSString(qt_mac_applicationmenu_string(6).arg(qAppName()))]; +#endif +} + +- (IBAction)qtDispatcherToQAction:(id)sender +{ + QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData); + 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(); + } +} + + - (void)orderFrontCharacterPalette:(id)sender + { + [NSApp orderFrontCharacterPalette:sender]; + } +@end +#endif // QT_MAC_USE_COCOA diff --git a/src/gui/platforms/mac/qcocoamenuloader_mac_p.h b/src/gui/platforms/mac/qcocoamenuloader_mac_p.h new file mode 100644 index 0000000000..cfcc7e00c6 --- /dev/null +++ b/src/gui/platforms/mac/qcocoamenuloader_mac_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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; + NSMenuItem *servicesItem; + NSMenuItem *hideAllOthersItem; + NSMenuItem *showAllItem; +} +- (void)ensureAppMenuInMenu:(NSMenu *)menu; +- (void)removeActionsFromAppMenu; +- (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; +- (void)qtUpdateMenubar; +- (void)orderFrontCharacterPalette:(id)sender; +@end + +#endif // QT_MAC_USE_COCOA +#endif // QCOCOAMENULOADER_P_H diff --git a/src/gui/platforms/mac/qcocoapanel_mac.mm b/src/gui/platforms/mac/qcocoapanel_mac.mm new file mode 100644 index 0000000000..67a12e25f8 --- /dev/null +++ b/src/gui/platforms/mac/qcocoapanel_mac.mm @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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/qcocoawindowdelegate_mac_p.h> +#import <private/qcocoaview_mac_p.h> +#import <private/qcocoawindowcustomthemeframe_mac_p.h> +#import <private/qcocoaapplication_mac_p.h> +#import <private/qmultitouch_mac_p.h> +#import <private/qapplication_p.h> +#import <private/qbackingstore_p.h> +#import <private/qdnd_p.h> + +#include <QtGui/QWidget> + +QT_FORWARD_DECLARE_CLASS(QWidget); +QT_USE_NAMESPACE + +@implementation QT_MANGLE_NAMESPACE(QCocoaPanel) + +/*********************************************************************** + Copy and Paste between QCocoaWindow and QCocoaPanel + This is a bit unfortunate, but thanks to the dynamic dispatch we + have to duplicate this code or resort to really silly forwarding methods +**************************************************************************/ +#include "qcocoasharedwindowmethods_mac_p.h" + +@end +#endif diff --git a/src/gui/platforms/mac/qcocoapanel_mac_p.h b/src/gui/platforms/mac/qcocoapanel_mac_p.h new file mode 100644 index 0000000000..542615903e --- /dev/null +++ b/src/gui/platforms/mac/qcocoapanel_mac_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 QCOCOAPANEL_MAC_P +#define QCOCOAPANEL_MAC_P + +#include "qmacdefines_mac.h" +#ifdef QT_MAC_USE_COCOA +#import <Cocoa/Cocoa.h> + +QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); + +@interface NSPanel (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end + +@interface QT_MANGLE_NAMESPACE(QCocoaPanel) : NSPanel { + QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; +} + ++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; +- (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; + +@end +#endif + +#endif diff --git a/src/gui/platforms/mac/qcocoasharedwindowmethods_mac_p.h b/src/gui/platforms/mac/qcocoasharedwindowmethods_mac_p.h new file mode 100644 index 0000000000..ee1115bd4e --- /dev/null +++ b/src/gui/platforms/mac/qcocoasharedwindowmethods_mac_p.h @@ -0,0 +1,610 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** + NB: This is not a header file, dispite the file name suffix. This file is + included directly into the source code of qcocoawindow_mac.mm and + qcocoapanel_mac.mm to avoid manually doing copy and paste of the exact + same code needed at both places. This solution makes it more difficult + to e.g fix a bug in qcocoawindow_mac.mm, but forget to do the same in + qcocoapanel_mac.mm. + The reason we need to do copy and paste in the first place, rather than + resolve to method overriding, is that QCocoaPanel needs to inherit from + NSPanel, while QCocoaWindow needs to inherit NSWindow rather than NSPanel). +****************************************************************************/ + +// WARNING: Don't include any header files from within this file. Put them +// directly into qcocoawindow_mac_p.h and qcocoapanel_mac_p.h + +QT_BEGIN_NAMESPACE +extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp +extern const QStringList& qEnabledDraggedTypes(); // qmime_mac.cpp +extern void qt_event_request_window_change(QWidget *); // qapplication_mac.mm +extern void qt_mac_send_posted_gl_updates(QWidget *widget); // qapplication_mac.mm + +Q_GLOBAL_STATIC(QPointer<QWidget>, currentDragTarget); +QT_END_NAMESPACE + +- (id)initWithContentRect:(NSRect)contentRect + styleMask:(NSUInteger)windowStyle + backing:(NSBackingStoreType)bufferingType + defer:(BOOL)deferCreation +{ + self = [super initWithContentRect:contentRect styleMask:windowStyle + backing:bufferingType defer:deferCreation]; + if (self) { + currentCustomDragTypes = 0; + } + return self; +} + +- (void)dealloc +{ + delete currentCustomDragTypes; + [super dealloc]; +} + +- (BOOL)canBecomeKeyWindow +{ + QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; + if (!widget) + return NO; // This should happen only for qt_root_win + if (QApplicationPrivate::isBlockedByModal(widget)) + return NO; + + bool isToolTip = (widget->windowType() == Qt::ToolTip); + bool isPopup = (widget->windowType() == Qt::Popup); + return !(isPopup || isToolTip); +} + +- (BOOL)canBecomeMainWindow +{ + QWidget *widget = [self QT_MANGLE_NAMESPACE(qt_qwidget)]; + if (!widget) + return NO; // This should happen only for qt_root_win + if ([self isSheet]) + return NO; + + bool isToolTip = (widget->windowType() == Qt::ToolTip); + bool isPopup = (widget->windowType() == Qt::Popup); + bool isTool = (widget->windowType() == Qt::Tool); + return !(isPopup || isToolTip || isTool); +} + +- (void)becomeMainWindow +{ + [super becomeMainWindow]; + // Cocoa sometimes tell a hidden window to become the + // main window (and as such, show it). This can e.g + // happend when the application gets activated. If + // this is the case, we tell it to hide again: + if (![self isVisible]) + [self orderOut:self]; +} + +- (void)toggleToolbarShown:(id)sender +{ + macSendToolbarChangeEvent([self QT_MANGLE_NAMESPACE(qt_qwidget)]); + [super toggleToolbarShown:sender]; +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + qt_dispatchModifiersChanged(theEvent, [self QT_MANGLE_NAMESPACE(qt_qwidget)]); + [super flagsChanged:theEvent]; +} + + +- (void)tabletProximity:(NSEvent *)tabletEvent +{ + qt_dispatchTabletProximityEvent(tabletEvent); +} + +- (void)terminate:(id)sender +{ + // This function is called from the quit item in the menubar when this window + // is in the first responder chain (see also qtDispatcherToQAction above) + [NSApp terminate:sender]; +} + +- (void)setLevel:(NSInteger)windowLevel +{ + // Cocoa will upon activating/deactivating applications level modal + // windows up and down, regardsless of any explicit set window level. + // To ensure that modal stays-on-top dialogs actually stays on top after + // the application is activated (and therefore stacks in front of + // other stays-on-top windows), we need to add this little special-case override: + QWidget *widget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; + if (widget && widget->isModal() && (widget->windowFlags() & Qt::WindowStaysOnTopHint)) + [super setLevel:NSPopUpMenuWindowLevel]; + else + [super setLevel:windowLevel]; +} + +- (void)sendEvent:(NSEvent *)event +{ + [self retain]; + + bool handled = false; + switch([event type]) { + case NSMouseMoved: + // Cocoa sends move events to a parent and all its children under the mouse, much + // like Qt handles hover events. But we only want to handle the move event once, so + // to optimize a bit (since we subscribe for move event for all views), we handle it + // here before this logic happends. Note: it might be tempting to do this shortcut for + // all mouse events. The problem is that Cocoa does more than just find the correct view + // when sending the event, like raising windows etc. So avoid it as much as possible: + handled = qt_mac_handleMouseEvent(event, QEvent::MouseMove, Qt::NoButton, 0); + break; + default: + break; + } + + if (!handled) { + [super sendEvent:event]; + qt_mac_handleNonClientAreaMouseEvent(self, event); + } + [self release]; +} + +- (void)setInitialFirstResponder:(NSView *)view +{ + // This method is called the first time the window is placed on screen and + // is the earliest point in time we can connect OpenGL contexts to NSViews. + QWidget *qwidget = [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] qt_qwidgetForWindow:self]; + if (qwidget) { + qt_event_request_window_change(qwidget); + qt_mac_send_posted_gl_updates(qwidget); + } + + [super setInitialFirstResponder:view]; +} + +- (BOOL)makeFirstResponder:(NSResponder *)responder +{ + // For some reason Cocoa wants to flip the first responder + // when Qt doesn't want to, sorry, but "No" :-) + if (responder == nil && qApp->focusWidget()) + return NO; + return [super makeFirstResponder:responder]; +} + ++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask +{ + if (styleMask & QtMacCustomizeWindow) + return [QT_MANGLE_NAMESPACE(QCocoaWindowCustomThemeFrame) class]; + return [super frameViewClassForStyleMask:styleMask]; +} + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +- (void)touchesBeganWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesMovedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesEndedWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} + +- (void)touchesCancelledWithEvent:(NSEvent *)event; +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetTouch = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, 0, &widgetToGetTouch); + if (!widgetToGetTouch) + return; + + bool all = widgetToGetTouch->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + qt_translateRawTouchEvent(widgetToGetTouch, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all)); +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +-(void)registerDragTypes +{ + // Calling registerForDraggedTypes below is slow, so only do + // it once for each window, or when the custom types change. + QMacCocoaAutoReleasePool pool; + const QStringList& customTypes = qEnabledDraggedTypes(); + if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { + if (currentCustomDragTypes == 0) + currentCustomDragTypes = new QStringList(); + *currentCustomDragTypes = customTypes; + 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. + for (int i = 0; i < customTypes.size(); i++) { + [supportedTypes addObject:qt_mac_QStringToNSString(customTypes[i])]; + } + [self registerForDraggedTypes:supportedTypes]; + } +} + +- (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); +} + +- (void)changeDraggingCursor:(NSDragOperation)newOperation +{ + static SEL action = nil; + static bool operationSupported = false; + if (action == nil) { + action = NSSelectorFromString(@"operationNotAllowedCursor"); + if ([NSCursor respondsToSelector:action]) { + operationSupported = true; + } + } + if (operationSupported) { + NSCursor *notAllowedCursor = [NSCursor performSelector:action]; + bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor); + if (newOperation == NSDragOperationNone && !isNotAllowedCursor) { + [notAllowedCursor push]; + } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) { + [notAllowedCursor pop]; + } + + } +} + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender +{ + // The user dragged something into the window. Send a draggingEntered message + // to the QWidget under the mouse. As the drag moves over the window, and over + // different widgets, we will handle enter and leave events from within + // draggingUpdated below. The reason why we handle this ourselves rather than + // subscribing for drag events directly in QCocoaView is that calling + // registerForDraggedTypes on the views will severly degrade initialization time + // for an application that uses a lot of drag subscribing widgets. + + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + *currentDragTarget() = qwidget; + if (!qwidget) + return [super draggingEntered:sender]; + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + [self addDropData:sender]; + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + if ([sender draggingSource] != nil) { + // modifier flags might have changed, update it here since we don't send any input events. + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + // when the source is from another application the above technique will not work. + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + // send the drag enter event to the widget. + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDragEnterEvent qDEEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + QApplication::sendEvent(qwidget, &qDEEvent); + + if (!qDEEvent.isAccepted()) { + // The enter event was not accepted. We mark this by removing + // the drop data so we don't send subsequent drag move events: + [self removeDropData]; + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } else { + // Send a drag move event immediately after a drag enter event (as per documentation). + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + 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. + nsActions = NSDragOperationNone; + // Save as ignored in the answer rect. + qDMEvent.setDropAction(Qt::IgnoreAction); + } else { + nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction()); + } + + QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent); + [self changeDraggingCursor:nsActions]; + return nsActions; + } + } + +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender +{ + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = QApplication::widgetAt(globalPoint); + if (!qwidget) + return [super draggingEntered:sender]; + + // First, check if the widget under the mouse has changed since the + // last drag move events. If so, we need to change target, and dispatch + // syntetic drag enter/leave events: + if (qwidget != *currentDragTarget()) { + if (*currentDragTarget() && dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(*currentDragTarget(), &de); + [self removeDropData]; + } + return [self draggingEntered:sender]; + } + + if (qwidget->testAttribute(Qt::WA_DropSiteRegistered) == false) + return NSDragOperationNone; + + // If we have no drop data (which will be assigned inside draggingEntered), it means + // that the current drag target did not accept the enter event. If so, we ignore + // subsequent move events as well: + if (dropData == 0) { + [self changeDraggingCursor:NSDragOperationNone]; + return NSDragOperationNone; + } + + // If the mouse is still within the accepted rect (provided by + // the application on a previous event), we follow the optimization + // and just return the answer given at that point: + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + if (qt_mac_mouse_inside_answer_rect(localPoint) + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) { + NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction)); + [self changeDraggingCursor:operation]; + return operation; + } + + QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions; + Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions); + Qt::KeyboardModifiers modifiers = Qt::NoModifier; + + // Update modifiers: + if ([sender draggingSource] != nil) { + QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]); + modifiers = QApplication::keyboardModifiers(); + } else { + modifiers = qt_cocoaDragOperation2QtModifiers(nsActions); + } + + QMimeData *mimeData = dropData; + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + + // Insert the same drop action on the event according to + // what the application told us it should be on the previous event: + QDragMoveEvent qDMEvent(localPoint, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers); + if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons() + && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers()) + qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction); + + // Now, end the drag move event to the widget: + qDMEvent.accept(); + QApplication::sendEvent(qwidget, &qDMEvent); + + NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction()); + if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) { + // Ignore this event (we will still receive further + // notifications), save as ignored in the answer rect: + operation = NSDragOperationNone; + qDMEvent.setDropAction(Qt::IgnoreAction); + } + + qt_mac_copy_answer_rect(qDMEvent); + [self changeDraggingCursor:operation]; + + return operation; +} + +- (void)draggingExited:(id <NSDraggingInfo>)sender +{ + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) + return [super draggingExited:sender]; + + if (dropData) { + QDragLeaveEvent de; + QApplication::sendEvent(qwidget, &de); + [self removeDropData]; + } + + // Clean-up: + [self removeDropData]; + *currentDragTarget() = 0; + [self changeDraggingCursor:NSDragOperationEvery]; +} + +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender +{ + QWidget *qwidget = *currentDragTarget(); + if (!qwidget) + return NO; + + *currentDragTarget() = 0; + NSPoint nswindowPoint = [sender draggingLocation]; + NSPoint nsglobalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:nswindowPoint]; + QPoint globalPoint = flipPoint(nsglobalPoint).toPoint(); + + [self addDropData:sender]; + + NSDragOperation nsActions = [sender draggingSourceOperationMask]; + Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions); + QMimeData *mimeData = dropData; + + if (QDragManager::self()->source()) + mimeData = QDragManager::self()->dragPrivate()->data; + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->target = qwidget; + + QPoint localPoint(qwidget->mapFromGlobal(globalPoint)); + QDropEvent de(localPoint, qtAllowed, mimeData, + QApplication::mouseButtons(), QApplication::keyboardModifiers()); + QApplication::sendEvent(qwidget, &de); + + if (QDragManager::self()->object) + QDragManager::self()->dragPrivate()->executed_action = de.dropAction(); + + return de.isAccepted(); +} + +// This is a hack and it should be removed once we find the real cause for +// the painting problems. +// We have a static variable that signals if we have been called before or not. +static bool firstDrawingInvocation = true; + +// The method below exists only as a workaround to draw/not draw the baseline +// in the title bar. This is to support unifiedToolbar look. + +// This method is very special. To begin with, it is a +// method that will get called only if we enable documentMode. +// Furthermore, it won't get called as a normal method, we swap +// this method with the normal implementation of drawRect in +// _NSThemeFrame. When this method is active, its mission is to +// first call the original drawRect implementation so the widget +// gets proper painting. After that, it needs to detect if there +// is a toolbar or not, in order to decide how to handle the unified +// look. The distinction is important since the presence and +// visibility of a toolbar change the way we enter into unified mode. +// When there is a toolbar and that toolbar is visible, the problem +// is as simple as to tell the toolbar not to draw its baseline. +// However when there is not toolbar or the toolbar is not visible, +// we need to draw a line on top of the baseline, because the baseline +// in that case will belong to the title. For this case we need to draw +// a line on top of the baseline. +// As usual, there is a special case. When we first are called, we might +// need to repaint ourselves one more time. We only need that if we +// didn't get the activation, i.e. when we are launched via the command +// line. And this only if the toolbar is visible from the beginning, +// so we have a special flag that signals if we need to repaint or not. +- (void)drawRectSpecial:(NSRect)rect +{ + // Call the original drawing method. + [id(self) drawRectOriginal:rect]; + NSWindow *window = [id(self) window]; + NSToolbar *toolbar = [window toolbar]; + if(!toolbar) { + // There is no toolbar, we have to draw a line on top of the line drawn by Cocoa. + macDrawRectOnTop((void *)window); + } else { + if([toolbar isVisible]) { + // We tell Cocoa to avoid drawing the line at the end. + if(firstDrawingInvocation) { + firstDrawingInvocation = false; + macSyncDrawingOnFirstInvocation((void *)window); + } else + [toolbar setShowsBaselineSeparator:NO]; + } else { + // There is a toolbar but it is not visible so + // we have to draw a line on top of the line drawn by Cocoa. + macDrawRectOnTop((void *)window); + } + } +} + +- (void)drawRectOriginal:(NSRect)rect +{ + Q_UNUSED(rect) + // This method implementation is here to silenct the compiler. + // See drawRectSpecial for information. +} + diff --git a/src/gui/platforms/mac/qcocoaview_mac.mm b/src/gui/platforms/mac/qcocoaview_mac.mm new file mode 100644 index 0000000000..e885d1552c --- /dev/null +++ b/src/gui/platforms/mac/qcocoaview_mac.mm @@ -0,0 +1,1388 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 <private/qevent_p.h> +#include <private/qbackingstore_p.h> +#include <private/qwindowsurface_raster_p.h> +#include <private/qunifiedtoolbarsurface_mac_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> + +@interface NSEvent (Qt_Compile_Leopard_DeviceDelta) + - (CGFloat)deviceDeltaX; + - (CGFloat)deviceDeltaY; + - (CGFloat)deviceDeltaZ; +@end + +@interface NSEvent (Qt_Compile_Leopard_Gestures) + - (CGFloat)magnification; +@end + +QT_BEGIN_NAMESPACE + +extern void qt_mac_update_cursor(); // qcursor_mac.mm +extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; // qapplication_mac.cpp +extern QPointer<QWidget> qt_last_native_mouse_receiver; // qt_cocoa_helpers_mac.mm +extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); // qwidget_mac.mm +extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp +extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); +extern QWidget *mac_mouse_grabber; +extern bool qt_mac_clearDirtyOnWidgetInsideDrawWidget; // qwidget.cpp + +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_FORWARD_DECLARE_CLASS(QCursor) +QT_USE_NAMESPACE +extern "C" { + extern NSString *NSTextInputReplacementRangeAttributeName; +} + +//#define ALIEN_DEBUG 1 +#ifdef ALIEN_DEBUG +static int qCocoaViewCount = 0; +#endif + +@implementation QT_MANGLE_NAMESPACE(QCocoaView) + +- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate +{ + self = [super init]; + if (self) { + [self finishInitWithQWidget:widget widgetPrivate:widgetprivate]; + } + [self setFocusRingType:NSFocusRingTypeNone]; + composingText = new QString(); + +#ifdef ALIEN_DEBUG + ++qCocoaViewCount; + qDebug() << "Alien: create native view for" << widget << ". qCocoaViewCount is:" << qCocoaViewCount; +#endif + + composing = false; + sendKeyEvents = true; + fromKeyDownEvent = false; + alienTouchCount = 0; + + [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)dealloc +{ + QMacCocoaAutoReleasePool pool; + delete composingText; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + +#ifdef ALIEN_DEBUG + --qCocoaViewCount; + qDebug() << "Alien: widget deallocated. qCocoaViewCount is:" << qCocoaViewCount; +#endif + + [super dealloc]; +} + +- (BOOL)isOpaque +{ + if (!qwidgetprivate) + return [super isOpaque]; + return qwidgetprivate->isOpaque; +} + +- (BOOL)isFlipped +{ + return YES; +} + +// We preserve the content of the view if WA_StaticContents is defined. +// +// More info in the Cocoa documentation: +// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html +- (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]; + } + + // Make sure the opengl context is updated on resize. + if (qwidgetprivate && qwidgetprivate->isGLWidget && [self window]) { + qwidgetprivate->needWindowChange = true; + QEvent event(QEvent::MacGLWindowChange); + qApp->sendEvent(qwidget, &event); + } +} + +// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint. +// During the resize, the top of the widget is repainted, probably because of the +// change of coordinate space (Quartz vs Qt). This is then followed by this message: +// -[NSView _setNeedsDisplayIfTopLeftChanged] +// which force a full repaint by sending the message 'setNeedsDisplay:'. +// That is what we are preventing here. +- (void)setNeedsDisplay:(BOOL)flag { + if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) { + [super setNeedsDisplay:flag]; + } +} + +- (void)drawRect:(NSRect)aRect +{ + if (!qwidget) + return; + + // Getting context. + CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + qt_mac_retain_graphics_context(context); + + // We use a different graphics system. + // + // Widgets that are set to paint on screen, specifically QGLWidget, + // requires the native engine to execute in order to be drawn. + if (QApplicationPrivate::graphicsSystem() != 0 && !qwidget->testAttribute(Qt::WA_PaintOnScreen)) { + + // Raster engine. + if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) { + + if (!qwidgetprivate->isInUnifiedToolbar) { + + // Qt handles the painting occuring inside the window. + // Cocoa also keeps track of all widgets as NSView and therefore might + // ask for a repainting of a widget even if Qt is already taking care of it. + // + // The only valid reason for Cocoa to call drawRect: is for window manipulation + // (ie. resize, ...). + // + // Qt will then forward the update to the children. + if (!qwidget->isWindow()) { + qt_mac_release_graphics_context(context); + return; + } + + QRasterWindowSurface *winSurface = dynamic_cast<QRasterWindowSurface *>(qwidget->windowSurface()); + if (!winSurface || !winSurface->needsFlush) { + qt_mac_release_graphics_context(context); + return; + } + + // Clip to region. + const QVector<QRect> &rects = winSurface->regionToFlush.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 = winSurface->regionToFlush.boundingRect(); + const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height()); + + qt_mac_draw_image(context, winSurface->imageContext(), area, area); + + winSurface->needsFlush = false; + winSurface->regionToFlush = QRegion(); + + } else { + + QUnifiedToolbarSurface *unifiedSurface = qwidgetprivate->unifiedSurface; + if (!unifiedSurface) { + qt_mac_release_graphics_context(context); + return; + } + + int areaX = qwidgetprivate->toolbar_offset.x(); + int areaY = qwidgetprivate->toolbar_offset.y(); + int areaWidth = qwidget->geometry().width(); + int areaHeight = qwidget->geometry().height(); + const CGRect area = CGRectMake(areaX, areaY, areaWidth, areaHeight); + const CGRect drawingArea = CGRectMake(0, 0, areaWidth, areaHeight); + + qt_mac_draw_image(context, unifiedSurface->imageContext(), area, drawingArea); + + qwidgetprivate->flushRequested = false; + + } + + CGContextFlush(context); + qt_mac_release_graphics_context(context); + return; + } + + // Qt handles the painting occuring inside the window. + // Cocoa also keeps track of all widgets as NSView and therefore might + // ask for a repainting of a widget even if Qt is already taking care of it. + // + // The only valid reason for Cocoa to call drawRect: is for window manipulation + // (ie. resize, ...). + // + // Qt will then forward the update to the children. + if (qwidget->isWindow()) { + qwidgetprivate->syncBackingStore(qwidget->rect()); + } + } + + // Native engine. + qwidgetprivate->hd = context; + + 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; + + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + for (int i = 0; i < count; ++i) { + QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height); + qrgn += tmpRect; + } + + 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 (context, 0, widgetRect.size.height); + CGContextScaleCTM(context, 1, -1); + if (qwidget->isWindow()) + CGContextClearRect(context, widgetRect); + CGContextClipToMask(context, widgetRect, qwidgetprivate->extra->imageMask); + CGContextScaleCTM(context, 1, -1); + CGContextTranslateCTM (context, 0, -widgetRect.size.height); + } + + if (qwidget->isWindow() && !qwidgetprivate->isOpaque + && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) { + CGContextClearRect(context, NSRectToCGRect(aRect)); + } + + qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); + QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget); + + // We specify that we want to draw the widget itself, and + // all its children recursive. But we skip native children, because + // they will receive drawRect calls by themselves as needed: + int flags = QWidgetPrivate::DrawPaintOnScreen + | QWidgetPrivate::DrawRecursive + | QWidgetPrivate::DontDrawNativeChildren; + + if (qwidget->isWindow()) + flags |= QWidgetPrivate::DrawAsRoot; + + // Start to draw: + qt_mac_clearDirtyOnWidgetInsideDrawWidget = true; + qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), flags, 0); + qt_mac_clearDirtyOnWidgetInsideDrawWidget = false; + + if (!redirectionOffset.isNull()) + QPainter::restoreRedirected(qwidget); + 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; + qt_mac_release_graphics_context(context); +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent +{ + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::MouseButtonPress, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) + return NO; + + return !widgetToGetMouse->testAttribute(Qt::WA_MacNoClickThrough); +} + +- (NSView *)hitTest:(NSPoint)aPoint +{ + if (!qwidget) + return [super hitTest:aPoint]; + + if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents)) + return nil; // You cannot hit a transparent for mouse event widget. + return [super hitTest:aPoint]; +} + +- (void)updateTrackingAreas +{ + if (!qwidget) + return; + + // [NSView addTrackingArea] is slow, so bail out early if we can: + if (NSIsEmptyRect([self visibleRect])) + return; + + 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]; + } + } + + // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should + // only be turned on if mouseTracking, hover is on or a tool tip is set. + // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to + // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of + // mouse moves delivered to it (Apple recommends keeping it OFF because there + // is a performance hit). So it goes. + NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp + | NSTrackingInVisibleRect | 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 +{ + // Cocoa will not send a move event on mouseEnter. But since + // Qt expect this, we fake one now. See also mouseExited below + // for info about enter/leave event handling + NSEvent *nsmoveEvent = [NSEvent + mouseEventWithType:NSMouseMoved + location:[[self window] mouseLocationOutsideOfEventStream] + modifierFlags: [event modifierFlags] + timestamp: [event timestamp] + windowNumber: [event windowNumber] + context: [event context] + eventNumber: [event eventNumber] + clickCount: 0 + pressure: 0]; + + // Important: Cocoa sends us mouseEnter on all views under the mouse + // and not just the one on top. Therefore, to we cannot use qwidget + // as native widget for this case. Instead, we let qt_mac_handleMouseEvent + // resolve it (last argument set to 0): + qt_mac_handleMouseEvent(nsmoveEvent, QEvent::MouseMove, Qt::NoButton, 0); +} + +- (void)mouseExited:(NSEvent *)event +{ + // Note: normal enter/leave handling is done from within mouseMove. This handler + // catches the case when the mouse moves out of the window (which mouseMove do not). + // Updating the mouse cursor follows the same logic as enter/leave. And we update + // neither if a grab exists (even if the grab points to this widget, it seems, ref X11) + Q_UNUSED(event); + if (self == [[self window] contentView] && !qt_button_down && !QWidget::mouseGrabber()) { + qt_mac_update_cursor(); + // If the mouse exits the content view, but qt_mac_getTargetForMouseEvent still + // reports a target, it means that either there is a grab involved, or the mouse + // hovered over another window in the application. In both cases, move events will + // cause qt_mac_handleMouseEvent to be called, which will handle enter/leave. + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Leave, qlocal, qglobal, qwidget, &widgetUnderMouse); + + if (widgetUnderMouse == 0) { + QApplicationPrivate::dispatchEnterLeave(0, qt_last_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; + } + } +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + qt_dispatchModifiersChanged(theEvent, widgetToGetKey); + [super flagsChanged:theEvent]; +} + +- (void)mouseMoved:(NSEvent *)theEvent +{ + // Important: this method will only be called when the view's window is _not_ inside + // QCocoaWindow/QCocoaPanel. Otherwise, [QCocoaWindow sendEvent] will handle the event + // before it ends up here. So, this method is added for supporting QMacNativeWidget. + // TODO: Cocoa send move events to all views under the mouse. So make sure we only + // handle the event for the widget on top when using QMacNativeWidget. + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::LeftButton, qwidget); + // 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 +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::LeftButton, qwidget); +} + +- (void)rightMouseDown:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, Qt::RightButton, qwidget); +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, Qt::RightButton, qwidget); +} + +- (void)otherMouseDown:(NSEvent *)theEvent +{ + Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonPress, mouseButton, qwidget); +} + +- (void)otherMouseUp:(NSEvent *)theEvent +{ + Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]); + qt_mac_handleMouseEvent(theEvent, QEvent::MouseButtonRelease, mouseButton, qwidget); +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); +} + +- (void)rightMouseDragged:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); +} + +- (void)otherMouseDragged:(NSEvent *)theEvent +{ + qt_mac_handleMouseEvent(theEvent, QEvent::MouseMove, Qt::NoButton, qwidget); +} + +- (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]; + } + + Qt::MouseButtons buttons = QApplication::mouseButtons(); + Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); + + // Find the widget that should receive the event: + QPoint qlocal, qglobal; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(theEvent, QEvent::Wheel, qlocal, qglobal, qwidget, 0); + if (!widgetToGetMouse) + return; + + int deltaX = 0; + int deltaY = 0; + int deltaZ = 0; + + const EventRef carbonEvent = (EventRef)[theEvent eventRef]; + const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0; + const bool scrollEvent = carbonEventKind == kEventMouseScroll; + + if (scrollEvent) { + // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad). + // Since deviceDelta is delivered as pixels rather than degrees, we need to + // convert from pixels to degrees in a sensible manner. + // It looks like 1/4 degrees per pixel behaves most native. + // (NB: Qt expects the unit for delta to be 8 per degree): + const int pixelsToDegrees = 2; // 8 * 1/4 + deltaX = [theEvent deviceDeltaX] * pixelsToDegrees; + deltaY = [theEvent deviceDeltaY] * pixelsToDegrees; + deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees; + } else { + // carbonEventKind == kEventMouseWheelMoved + // Remove acceleration, and use either -120 or 120 as delta: + deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120); + deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120); + deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120); + } + +#ifndef QT_NO_WHEELEVENT + // ### Qt 5: Send one QWheelEvent with dx, dy and dz + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::initDelayedScroll(); + + if (deltaX != 0) { + QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal); + qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); + } + + if (deltaY != 0) { + QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical); + qt_sendSpontaneousEvent(widgetToGetMouse, &qwe); + } + + if (deltaZ != 0) { + // 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(widgetToGetMouse, &qwe); + } + + if (deltaX != 0 && deltaY != 0) + QMacScrollOptimization::performDelayedScroll(); +#endif //QT_NO_WHEELEVENT +} + +- (void)tabletProximity:(NSEvent *)tabletEvent +{ + qt_dispatchTabletProximityEvent(tabletEvent); +} + +- (void)tabletPoint:(NSEvent *)tabletEvent +{ + if (!qt_mac_handleTabletEvent(self, tabletEvent)) + [super tabletPoint:tabletEvent]; +} + +- (void)magnifyWithEvent:(NSEvent *)event +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) + return; + +#ifndef QT_NO_GESTURES + QNativeGestureEvent qNGEvent; + qNGEvent.gestureType = QNativeGestureEvent::Zoom; + NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; + qNGEvent.position = flipPoint(p).toPoint(); + qNGEvent.percentage = [event magnification]; + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); +#endif // QT_NO_GESTURES +} + +- (void)rotateWithEvent:(NSEvent *)event +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) + return; + +#ifndef QT_NO_GESTURES + QNativeGestureEvent qNGEvent; + qNGEvent.gestureType = QNativeGestureEvent::Rotate; + NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; + qNGEvent.position = flipPoint(p).toPoint(); + qNGEvent.percentage = -[event rotation]; + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); +#endif // QT_NO_GESTURES +} + +- (void)swipeWithEvent:(NSEvent *)event +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) + return; + +#ifndef QT_NO_GESTURES + QNativeGestureEvent qNGEvent; + qNGEvent.gestureType = QNativeGestureEvent::Swipe; + NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; + qNGEvent.position = flipPoint(p).toPoint(); + if ([event deltaX] == 1) + qNGEvent.angle = 180.0f; + else if ([event deltaX] == -1) + qNGEvent.angle = 0.0f; + else if ([event deltaY] == 1) + qNGEvent.angle = 90.0f; + else if ([event deltaY] == -1) + qNGEvent.angle = 270.0f; + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); +#endif // QT_NO_GESTURES +} + +- (void)beginGestureWithEvent:(NSEvent *)event +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) + return; + +#ifndef QT_NO_GESTURES + QNativeGestureEvent qNGEvent; + qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; + NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; + qNGEvent.position = flipPoint(p).toPoint(); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); +#endif // QT_NO_GESTURES +} + +- (void)endGestureWithEvent:(NSEvent *)event +{ + QPoint qlocal, qglobal; + QWidget *widgetToGetGesture = 0; + qt_mac_getTargetForMouseEvent(event, QEvent::Gesture, qlocal, qglobal, qwidget, &widgetToGetGesture); + if (!widgetToGetGesture) + return; + if (!QApplicationPrivate::tryModalHelper(widgetToGetGesture, 0)) + return; + +#ifndef QT_NO_GESTURES + QNativeGestureEvent qNGEvent; + qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; + NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]]; + qNGEvent.position = flipPoint(p).toPoint(); + qt_sendSpontaneousEvent(widgetToGetGesture, &qNGEvent); +} +#endif // QT_NO_GESTURES + +- (void)frameDidChange:(NSNotification *)note +{ + Q_UNUSED(note); + if (!qwidget) + return; + 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 && qwidget->isEnabled() != flag) + qwidget->setEnabled(flag); +} + ++ (Class)cellClass +{ + return [NSActionCell class]; +} + +- (BOOL)acceptsFirstResponder +{ + if (!qwidget) + return NO; + + // Disabled widget shouldn't get focus even if it's a window. + // hence disabled windows will not get any key or mouse events. + if (!qwidget->isEnabled()) + return NO; + + if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded) { + QWidget *focusWidget = qApp->focusWidget(); + if (!focusWidget) { + // There is no focus widget, but we still want to receive key events + // for shortcut handling etc. So we accept first responer for the + // content view as a last resort: + return YES; + } + if (!focusWidget->internalWinId() && focusWidget->nativeParentWidget() == qwidget) { + // The current focus widget is alien, and hence, cannot get acceptsFirstResponder + // calls. Since the focus widget is a child of qwidget, we let this view say YES: + return YES; + } + if (focusWidget->window() != qwidget) { + // The current focus widget is in another window. Since cocoa + // suggest that this window should be key now, we accept: + return YES; + } + } + + return qwidget->focusPolicy() != Qt::NoFocus; +} + +- (BOOL)resignFirstResponder +{ + if (!qwidget) + return YES; + + // Seems like the following test only triggers if this + // view is inside a QMacNativeWidget: +// if (QWidget *fw = QApplication::focusWidget()) { +// if (qwidget == fw || qwidget == fw->nativeParentWidget()) +// fw->clearFocus(); +// } + return YES; +} + +- (BOOL)becomeFirstResponder +{ + // see the comment in the acceptsFirstResponder - if the window "stole" focus + // let it become the responder, but don't tell Qt + if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded + && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus) + qwidget->setFocus(Qt::OtherFocusReason); + return YES; +} + +- (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); + macCurrentDnDParameters()->performedAction = operation; + if (QDragManager::self()->object + && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) { + macCurrentDnDParameters()->performedAction = + qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action); + } +} + +- (QWidget *)qt_qwidget +{ + return qwidget; +} + +- (void) qt_clearQWidget +{ + qwidget = 0; + qwidgetprivate = 0; +} + +- (void)keyDown:(NSEvent *)theEvent +{ + if (!qwidget) + return; + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + sendKeyEvents = true; + + if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled) + && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly + || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly + || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) { + fromKeyDownEvent = true; + [qt_mac_nativeview_for(qwidget) interpretKeyEvents:[NSArray arrayWithObject: theEvent]]; + fromKeyDownEvent = false; + } + + if (sendKeyEvents && !composing) { + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. + QWidget *toplevel = qwidget->window(); + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyDown:theEvent]; + } + } + } + } +} + + +- (void)keyUp:(NSEvent *)theEvent +{ + if (sendKeyEvents) { + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return; + + bool keyEventEaten = qt_dispatchKeyEvent(theEvent, widgetToGetKey); + if (!keyEventEaten && qwidget) { + // The event is not yet eaten, and if Qt is embedded inside a native + // cocoa application, send it to first responder not owned by Qt. + // The exception is if widgetToGetKey was redirected to a popup. + QWidget *toplevel = qwidget->window(); + if (toplevel == widgetToGetKey->window()) { + if (qt_widget_private(toplevel)->topData()->embedded) { + if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview]) + [w keyUp:theEvent]; + } + } + } + } +} + +- (void)viewWillMoveToWindow:(NSWindow *)window +{ + if (qwidget == 0) + return; + + if (qwidget->windowFlags() & Qt::MSWindowsOwnDC + && (window != [self window])) { // OpenGL Widget + QEvent event(QEvent::MacGLClearDrawable); + qApp->sendEvent(qwidget, &event); + } +} + +- (void)viewDidMoveToWindow +{ + if (qwidget == 0) + return; + + if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) { + // call update paint event + qwidgetprivate->needWindowChange = true; + QEvent event(QEvent::MacGLWindowChange); + qApp->sendEvent(qwidget, &event); + } +} + + +// NSTextInput Protocol implementation + +- (void) insertText:(id)aString +{ + QString commitText; + if ([aString length]) { + if ([aString isKindOfClass:[NSAttributedString class]]) { + commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string])); + } else { + commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString)); + }; + } + + // When entering characters through Character Viewer or Keyboard Viewer, the text is passed + // through this insertText method. Since we dont receive a keyDown Event in such cases, the + // composing flag will be false. + if (([aString length] && composing) || !fromKeyDownEvent) { + // Send the commit string to the widget. + composing = false; + sendKeyEvents = false; + QInputMethodEvent e; + e.setCommitString(commitText); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + } else { + // The key sequence "`q" on a French Keyboard will generate two calls to insertText before + // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept + // the "`" key. The last keyDown event needs to be processed by the widget to get the + // character "q". The string parameter is ignored for the second call. + sendKeyEvents = true; + } + + composingText->clear(); +} + +- (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 + selRange.length, 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); + } + *composingText = qtText; + + QInputMethodEvent e(qtText, attrs); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + + if (!composingLength) + composing = false; +} + +- (void) unmarkText +{ + if (composing) { + QInputMethodEvent e; + e.setCommitString(*composingText); + if (QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget)) + qt_sendSpontaneousEvent(widgetToGetKey, &e); + } + composingText->clear(); + 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:const_cast<NSString *>(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. + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return NSZeroRect; + + QRect mr(widgetToGetKey->inputMethodQuery(Qt::ImMicroFocus).toRect()); + QPoint mp(widgetToGetKey->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 +{ + QWidget *widgetToGetKey = qt_mac_getTargetForKeyEvent(qwidget); + if (!widgetToGetKey) + return nil; + + if (!widgetToGetKey->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_effectiveview_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_effectiveview_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("dummy")); + 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 = macCurrentDnDParameters(); + QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(dndParams->view); + + // Save supported actions: + [theView setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)]; + QPoint pointInView = [theView qt_qwidget]->mapFromGlobal(dndParams->globalPoint); + NSPoint imageLoc = {pointInView.x() - hotspot.x(), pointInView.y() + pix.height() - hotspot.y()}; + NSSize mouseOffset = {0.0, 0.0}; + NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + dragPrivate()->executed_action = Qt::ActionMask; + + // Execute the drag: + [theView retain]; + [theView dragImage:image + at:imageLoc + offset:mouseOffset + event:dndParams->theEvent + pasteboard:pboard + source:theView + slideBack:YES]; + + // Reset the implicit grab widget when drag ends because we will not + // receive the mouse release event when DND is active: + qt_button_down = 0; + [theView release]; + [image release]; + if (dragPrivate()) + dragPrivate()->executed_action = Qt::IgnoreAction; + 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()); + } + } + } + + // Clean-up: + o->setMimeData(0); + o->deleteLater(); + return performedAction; +} + +QT_END_NAMESPACE + +#endif // QT_MAC_USE_COCOA diff --git a/src/gui/platforms/mac/qcocoaview_mac_p.h b/src/gui/platforms/mac/qcocoaview_mac_p.h new file mode 100644 index 0000000000..cc79b6705b --- /dev/null +++ b/src/gui/platforms/mac/qcocoaview_mac_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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(QString); +QT_FORWARD_DECLARE_CLASS(QStringList); + +Q_GUI_EXPORT +@interface QT_MANGLE_NAMESPACE(QCocoaView) : NSControl <NSTextInput> { + QWidget *qwidget; + QWidgetPrivate *qwidgetprivate; + NSDragOperation supportedActions; + bool composing; + int composingLength; + bool sendKeyEvents; + bool fromKeyDownEvent; + QString *composingText; + @public int alienTouchCount; +} +- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; +- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate; +- (void)frameDidChange:(NSNotification *)note; +- (void)setSupportedActions:(NSDragOperation)actions; +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; +- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation; +- (BOOL)isComposing; +- (QWidget *)qt_qwidget; +- (void) qt_clearQWidget; + +@end +#endif diff --git a/src/gui/platforms/mac/qcocoawindow_mac.mm b/src/gui/platforms/mac/qcocoawindow_mac.mm new file mode 100644 index 0000000000..6e5023aaca --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindow_mac.mm @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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> +#import <private/qcocoaapplication_mac_p.h> +#import <private/qdnd_p.h> +#import <private/qmultitouch_mac_p.h> + +#include <QtGui/QWidget> + +QT_FORWARD_DECLARE_CLASS(QWidget); +QT_USE_NAMESPACE + +@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) + +/*********************************************************************** + Copy and Paste between QCocoaWindow and QCocoaPanel + This is a bit unfortunate, but thanks to the dynamic dispatch we + have to duplicate this code or resort to really silly forwarding methods +**************************************************************************/ +#include "qcocoasharedwindowmethods_mac_p.h" + +@end +#endif diff --git a/src/gui/platforms/mac/qcocoawindow_mac_p.h b/src/gui/platforms/mac/qcocoawindow_mac_p.h new file mode 100644 index 0000000000..d567cab244 --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindow_mac_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 QCOCOAWINDOW_MAC_P +#define QCOCOAWINDOW_MAC_P + +#ifdef QT_MAC_USE_COCOA +#include "qmacdefines_mac.h" +#import <Cocoa/Cocoa.h> +#include <private/qapplication_p.h> +#include <private/qbackingstore_p.h> + +enum { QtMacCustomizeWindow = 1 << 21 }; // This will one day be run over by + +QT_FORWARD_DECLARE_CLASS(QWidget); +QT_FORWARD_DECLARE_CLASS(QStringList); +QT_FORWARD_DECLARE_CLASS(QCocoaDropData); + +@interface NSWindow (QtCoverForHackWithCategory) ++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; +@end + +@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 NSWindow (QtIntegration) +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender; +- (void)draggingExited:(id <NSDraggingInfo>)sender; +- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; +@end + +@interface QT_MANGLE_NAMESPACE(QCocoaWindow) : NSWindow { + QStringList *currentCustomDragTypes; + QCocoaDropData *dropData; + NSInteger dragEnterSequence; +} + ++ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; +- (void)registerDragTypes; +- (void)drawRectOriginal:(NSRect)rect; + +@end +#endif + +#endif diff --git a/src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac.mm b/src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac.mm new file mode 100644 index 0000000000..b761934c01 --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac.mm @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h b/src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h new file mode 100644 index 0000000000..09b40875f6 --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindowcustomthemeframe_mac_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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/platforms/mac/qcocoawindowdelegate_mac.mm b/src/gui/platforms/mac/qcocoawindowdelegate_mac.mm new file mode 100644 index 0000000000..1faf068a12 --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindowdelegate_mac.mm @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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> +#include <QMainWindow> +#include <QToolBar> +#include <private/qmainwindowlayout_p.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); + if (qwidget->testAttribute(Qt::WA_PendingResizeEvent)) { + qwidget->setAttribute(Qt::WA_PendingResizeEvent, false); + QApplication::sendEvent(qwidget, &qre); + } else { + 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); + 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); + } else { + widgetData->window_state = widgetData->window_state & ~Qt::WindowMaximized; + QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state + | Qt::WindowMaximized)); + qt_sendSpontaneousEvent(qwidget, &e); + } + 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]; + } + + // We force the repaint to be synchronized with the resize of the window. + // Otherwise, the resize looks sluggish because we paint one event loop later. + if ([[window contentView] inLiveResize]) { + qwidget->repaint(); + + // We need to repaint the toolbar as well. + QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window()); + if (mWindow) { + QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout()); + QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList; + + for (int i = 0; i < toolbarList.size(); ++i) { + QToolBar* toolbar = toolbarList.at(i); + toolbar->repaint(); + } + } + } +} + +- (void)windowDidMove:(NSNotification *)notification +{ + // The code underneath needs to translate the window location + // from bottom left (which is the origin used by Cocoa) to + // upper left (which is the origin used by Qt): + NSWindow *window = [notification object]; + NSRect newRect = [window frame]; + QWidget *qwidget = m_windowHash->value(window); + 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); + } + } +} + +-(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); + 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); + 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); +} + +- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame +{ + Q_UNUSED(newFrame); + // saving the current window geometry before the window is maximized + QWidget *qwidget = m_windowHash->value(window); + QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget); + if (qwidget->isWindow()) { + if(qwidget->windowState() & Qt::WindowMaximized) { + // Restoring + widgetPrivate->topData()->wasMaximized = false; + } else { + // Maximizing + widgetPrivate->topData()->normalGeometry = qwidget->geometry(); + // If the window was maximized we need to update the coordinates since now it will start at 0,0. + // We do this in a special field that is only used when not restoring but manually resizing the window. + // Since the coordinates are fixed we just set a boolean flag. + widgetPrivate->topData()->wasMaximized = true; + } + } + return YES; +} + +- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame +{ + NSRect frameToReturn = defaultFrame; + QWidget *qwidget = m_windowHash->value(window); + QSizeF size = qwidget->maximumSize(); + NSRect windowFrameRect = [window frame]; + NSRect viewFrameRect = [[window contentView] frame]; + // consider additional size required for titlebar & frame + frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width, + size.width()+(windowFrameRect.size.width - viewFrameRect.size.width)); + frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height, + size.height()+(windowFrameRect.size.height - viewFrameRect.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); +} + +- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu +{ + Q_UNUSED(menu); + QWidget *qwidget = m_windowHash->value(window); + if (qwidget && !qwidget->windowFilePath().isEmpty()) { + return YES; + } + return NO; +} + +- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event + from:(NSPoint)dragImageLocation + withPasteboard:(NSPasteboard *)pasteboard +{ + Q_UNUSED(event); + Q_UNUSED(dragImageLocation); + Q_UNUSED(pasteboard); + QWidget *qwidget = m_windowHash->value(window); + if (qwidget && !qwidget->windowFilePath().isEmpty()) { + return YES; + } + return NO; +} + +- (void)syncContentViewFrame: (NSNotification *)notification +{ + NSView *cView = [notification object]; + if (cView) { + NSWindow *window = [cView window]; + QWidget *qwidget = m_windowHash->value(window); + if (qwidget) { + QWidgetData *widgetData = qt_qwidget_data(qwidget); + NSRect rect = [cView frame]; + const QSize newSize(rect.size.width, rect.size.height); + const QSize &oldSize = widgetData->crect.size(); + if (newSize != oldSize) { + [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize]; + } + } + + } +} + +@end +#endif// QT_MAC_USE_COCOA diff --git a/src/gui/platforms/mac/qcocoawindowdelegate_mac_p.h b/src/gui/platforms/mac/qcocoawindowdelegate_mac_p.h new file mode 100644 index 0000000000..638ce2df9a --- /dev/null +++ b/src/gui/platforms/mac/qcocoawindowdelegate_mac_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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) + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 +@protocol NSWindowDelegate <NSObject> +- (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; +- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu; +- (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event from:(NSPoint)dragImageLocation withPasteboard:(NSPasteboard *)pasteboard; +- (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame; +@end + +@protocol NSDrawerDelegate <NSObject> +- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize; +@end + +#endif + + + +@interface QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) : NSObject<NSWindowDelegate, NSDrawerDelegate> { + 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; +- (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)syncContentViewFrame: (NSNotification *)notification; +@end +#endif diff --git a/src/gui/platforms/mac/qcolormap_mac.cpp b/src/gui/platforms/mac/qcolormap_mac.cpp new file mode 100644 index 0000000000..28589f41b8 --- /dev/null +++ b/src/gui/platforms/mac/qcolormap_mac.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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/platforms/mac/qcursor_mac.mm b/src/gui/platforms/mac/qcursor_mac.mm new file mode 100644 index 0000000000..0afa3ee4f0 --- /dev/null +++ b/src/gui/platforms/mac/qcursor_mac.mm @@ -0,0 +1,689 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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> +#include <private/qapplication_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 +extern QPointer<QWidget> qt_button_down; //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(); + */ + } + } +}; + +inline void *qt_mac_nsCursorForQCursor(const QCursor &c) +{ + c.d->update(); + return [[static_cast<NSCursor *>(c.d->curs.cp.nscursor) retain] autorelease]; +} + +static QCursorData *currentCursor = 0; //current cursor + +void qt_mac_set_cursor(const QCursor *c) +{ +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(*c)) set]; +#else + if (!c) { + currentCursor = 0; + return; + } + c->handle(); //force the cursor to get loaded, if it's not + + if(currentCursor && currentCursor->type == QCursorData::TYPE_ThemeCursor + && currentCursor->curs.tc.anim) + currentCursor->curs.tc.anim->stop(); + if(c->d->type == QCursorData::TYPE_ImageCursor) { + [static_cast<NSCursor *>(c->d->curs.cp.nscursor) set]; + } else if(c->d->type == QCursorData::TYPE_ThemeCursor) { + 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); + } + } + + currentCursor = c->d; +#endif +} + +static QPointer<QWidget> lastWidgetUnderMouse = 0; +static QPointer<QWidget> lastMouseCursorWidget = 0; +static bool qt_button_down_on_prev_call = false; +static QCursor *grabCursor = 0; + +void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse) +{ + QCursor cursor(Qt::ArrowCursor); + if (qt_button_down) { + // The widget that is currently pressed + // grabs the mouse cursor: + widgetUnderMouse = qt_button_down; + qt_button_down_on_prev_call = true; + } else if (qt_button_down_on_prev_call) { + // Grab has been released, so do + // a full check: + qt_button_down_on_prev_call = false; + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + } + + if (QApplication::overrideCursor()) { + cursor = *QApplication::overrideCursor(); + } else if (grabCursor) { + cursor = *grabCursor; + } else if (widgetUnderMouse) { + if (widgetUnderMouse == lastWidgetUnderMouse) { + // Optimization that should hit when the widget under + // the mouse does not change as the mouse moves: + if (lastMouseCursorWidget) + cursor = lastMouseCursorWidget->cursor(); + } else { + QWidget *w = widgetUnderMouse; + for (; w; w = w->parentWidget()) { + if (w->testAttribute(Qt::WA_SetCursor)) { + cursor = w->cursor(); + break; + } + if (w->isWindow()) + break; + } + // One final check in case we ran out of parents in the loop: + if (w && !w->testAttribute(Qt::WA_SetCursor)) + w = 0; + + lastWidgetUnderMouse = widgetUnderMouse; + lastMouseCursorWidget = w; + } + } + +#ifdef QT_MAC_USE_COCOA + cursor.d->update(); + NSCursor *nsCursor = static_cast<NSCursor *>(cursor.d->curs.cp.nscursor); + if ([NSCursor currentCursor] != nsCursor) { + QMacCocoaAutoReleasePool pool; + [nsCursor set]; + } +#else + qt_mac_set_cursor(&cursor); +#endif +} + +void qt_mac_update_cursor() +{ + // This function is similar to qt_mac_updateCursorWithWidgetUnderMouse + // except that is clears the optimization cache, and finds the widget + // under mouse itself. Clearing the cache is useful in cases where the + // application has been deactivated/activated etc. + // NB: since we dont have any true native widget, the call to + // qt_mac_getTargetForMouseEvent will fail when the mouse is over QMacNativeWidgets. +#ifdef QT_MAC_USE_COCOA + lastWidgetUnderMouse = 0; + lastMouseCursorWidget = 0; + QWidget *widgetUnderMouse = 0; + + if (qt_button_down) { + widgetUnderMouse = qt_button_down; + } else { + QPoint localPoint; + QPoint globalPoint; + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); + } + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); +#else + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(QCursor::pos())); +#endif +} + +void qt_mac_setMouseGrabCursor(bool set, QCursor *const cursor = 0) +{ + if (grabCursor) { + delete grabCursor; + grabCursor = 0; + } + if (set) { + if (cursor) + grabCursor = new QCursor(*cursor); + else if (lastMouseCursorWidget) + grabCursor = new QCursor(lastMouseCursorWidget->cursor()); + else + grabCursor = new QCursor(Qt::ArrowCursor); + } + qt_mac_update_cursor(); +} + +#ifndef QT_MAC_USE_COCOA +void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos) +{ + qt_mac_updateCursorWithWidgetUnderMouse(QApplication::widgetAt(globalPos)); +} +#endif + +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) +{ +#ifdef QT_MAC_USE_COCOA + CGPoint pos; + pos.x = x; + pos.y = y; + + CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); +#else + 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); + } +#endif +} + +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; + case Qt::DragCopyCursor: + type = QCursorData::TYPE_ThemeCursor; + if ([NSCursor respondsToSelector:@selector(dragCopyCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragCopyCursor)]; + break; + case Qt::DragMoveCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.cp.nscursor = [NSCursor arrowCursor]; + break; + case Qt::DragLinkCursor: + type = QCursorData::TYPE_ThemeCursor; + if ([NSCursor respondsToSelector:@selector(dragLinkCursor)]) + curs.cp.nscursor = [NSCursor performSelector:@selector(dragLinkCursor)]; + 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; + case Qt::DragMoveCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeArrowCursor; + break; + case Qt::DragCopyCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeCopyArrowCursor; + break; + case Qt::DragLinkCursor: + type = QCursorData::TYPE_ThemeCursor; + curs.tc.curs = kThemeAliasArrowCursor; + 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/platforms/mac/qdesktopwidget_mac.mm b/src/gui/platforms/mac/qdesktopwidget_mac.mm new file mode 100644 index 0000000000..0b529c9843 --- /dev/null +++ b/src/gui/platforms/mac/qdesktopwidget_mac.mm @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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) +{ + onResize(); +} + +QDesktopWidgetImplementation::~QDesktopWidgetImplementation() +{ +} + +QDesktopWidgetImplementation *QDesktopWidgetImplementation::instance() +{ + return qdesktopWidgetImplementation(); +} + +QRect QDesktopWidgetImplementation::availableRect(int screenIndex) const +{ + if (screenIndex < 0 || screenIndex >= screenCount) + screenIndex = appScreen; + + return availableRects[screenIndex].toRect(); +} + +QRect QDesktopWidgetImplementation::screenRect(int screenIndex) const +{ + if (screenIndex < 0 || screenIndex >= screenCount) + screenIndex = appScreen; + + return screenRects[screenIndex].toRect(); +} + +void QDesktopWidgetImplementation::onResize() +{ + QMacCocoaAutoReleasePool pool; + NSArray *displays = [NSScreen screens]; + screenCount = [displays count]; + + screenRects.clear(); + availableRects.clear(); + NSRect primaryRect = [[displays objectAtIndex:0] frame]; + for (int i = 0; i<screenCount; i++) { + NSRect r = [[displays objectAtIndex:i] frame]; + int flippedY = - r.origin.y + // account for position offset and + primaryRect.size.height - r.size.height; // height difference. + screenRects.append(QRectF(r.origin.x, flippedY, + r.size.width, r.size.height)); + + r = [[displays objectAtIndex:i] visibleFrame]; + flippedY = - r.origin.y + // account for position offset and + primaryRect.size.height - r.size.height; // height difference. + availableRects.append(QRectF(r.origin.x, flippedY, + r.size.width, r.size.height)); + } +} + + + +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(); + + const int oldScreenCount = d->screenCount; + const QVector<QRectF> oldRects(d->screenRects); + const QVector<QRectF> oldWorks(d->availableRects); + + d->onResize(); + + for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { + if (oldRects.at(i) != d->screenRects.at(i)) + emit resized(i); + } + for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { + if (oldWorks.at(i) != d->availableRects.at(i)) + emit workAreaResized(i); + } + + if (oldScreenCount != d->screenCount) + emit screenCountChanged(d->screenCount); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qdesktopwidget_mac_p.h b/src/gui/platforms/mac/qdesktopwidget_mac_p.h new file mode 100644 index 0000000000..ac638ea7d8 --- /dev/null +++ b/src/gui/platforms/mac/qdesktopwidget_mac_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 <qrect.h> + +QT_BEGIN_NAMESPACE + +class QDesktopWidgetImplementation +{ +public: + QDesktopWidgetImplementation(); + ~QDesktopWidgetImplementation(); + static QDesktopWidgetImplementation *instance(); + + int appScreen; + int screenCount; + + QVector<QRectF> availableRects; + QVector<QRectF> screenRects; + + QRect availableRect(int screenIndex) const; + QRect screenRect(int screenIndex) const; + void onResize(); +}; + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qdnd_mac.mm b/src/gui/platforms/mac/qdnd_mac.mm new file mode 100644 index 0000000000..3af2ba007c --- /dev/null +++ b/src/gui/platforms/mac/qdnd_mac.mm @@ -0,0 +1,753 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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, ¤tActions); + 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, ¤tAction); + 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 interested 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 sent immediately 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); //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); + } + + //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); + o->setMimeData(0); + o->deleteLater(); + return action; + } + } + + DragActions ret = kDragActionNothing; + GetDragDropAction(dragRef, &ret); + DisposeDrag(dragRef); //cleanup + o->setMimeData(0); + o->deleteLater(); + 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/platforms/mac/qeventdispatcher_mac.mm b/src/gui/platforms/mac/qeventdispatcher_mac.mm new file mode 100644 index 0000000000..677a7368b4 --- /dev/null +++ b/src/gui/platforms/mac/qeventdispatcher_mac.mm @@ -0,0 +1,1200 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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" +#endif + +QT_BEGIN_NAMESPACE + +QT_USE_NAMESPACE + +/***************************************************************************** + 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. + + + if (blockSendPostedEvents) { + QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id)); + } else { + 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); + + // There is a race condition that happen where we disable the notifier and + // the kernel still has a notification to pass on. We then get this + // notification after we've successfully disabled the CFSocket, but our Qt + // notifier is now gone. The upshot is we have to check the notifier + // everytime. + if (callbackType == kCFSocketReadCallBack) { + if (socketInfo->readNotifier) + QApplication::sendEvent(socketInfo->readNotifier, ¬ifierEvent); + } else if (callbackType == kCFSocketWriteCallBack) { + if (socketInfo->writeNotifier) + QApplication::sendEvent(socketInfo->writeNotifier, ¬ifierEvent); + } +} + +/* + 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: +#ifndef QT_NO_GESTURES +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + case NSEventTypeGesture: // touch events + case NSEventTypeMagnify: + case NSEventTypeSwipe: + case NSEventTypeRotate: + case NSEventTypeBeginGesture: + case NSEventTypeEndGesture: +#endif +#endif // QT_NO_GESTURES + result = true; + break; + + default: + break; + } + return result; +} +#endif + +static inline void qt_mac_waitForMoreEvents() +{ +#ifndef QT_MAC_USE_COCOA + while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e20, true) == kCFRunLoopRunTimedOut) ; +#else + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +#endif +} + +#ifdef QT_MAC_USE_COCOA +static inline void qt_mac_waitForMoreModalSessionEvents() +{ + // If no event exist in the cocoa event que, wait + // (and free up cpu time) until at least one event occur. + // This implementation is a bit on the edge, but seems to + // work fine: + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSModalPanelRunLoopMode + dequeue:YES]; + if (event) + [NSApp postEvent:event atStart:YES]; +} +#endif + +bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) +{ + Q_D(QEventDispatcherMac); + d->interrupt = false; + +#ifdef QT_MAC_USE_COCOA + bool interruptLater = false; + QtMacInterruptDispatcherHelp::cancelInterruptLater(); +#endif + + // 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(); + + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; + bool retVal = false; + forever { + if (d->interrupt) + break; + +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + NSEvent* event = 0; + + // First, send all previously excluded input events, if any: + if (!excludeUserEvents) { + while (!d->queuedUserInputEvents.isEmpty()) { + event = static_cast<NSEvent *>(d->queuedUserInputEvents.takeFirst()); + if (!filterEvent(event)) { + qt_mac_send_event(flags, event, 0); + retVal = true; + } + [event release]; + } + } + + // If Qt is used as a plugin, or as an extension in a native cocoa + // application, we should not run or stop NSApplication; This will be + // done from the application itself. And if processEvents is called + // manually (rather than from a QEventLoop), we cannot enter a tight + // loop and block this call, but instead we need to return after one flush. + // Finally, if we are to exclude user input events, we cannot call [NSApp run] + // as we then loose control over which events gets dispatched: + const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; + const bool canExec_Qt = !excludeUserEvents && + (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; + + if (canExec_Qt && canExec_3rdParty) { + // We can use exec-mode, meaning that we can stay in a tight loop until + // interrupted. This is mostly an optimization, but it allow us to use + // [NSApp run], which is the normal code path for cocoa applications. + if (NSModalSession session = d->currentModalSession()) { + QBoolBlocker execGuard(d->currentExecIsNSAppRun, false); + while ([NSApp runModalSession:session] == NSRunContinuesResponse && !d->interrupt) + qt_mac_waitForMoreModalSessionEvents(); + + if (!d->interrupt && session == d->currentModalSessionCached) { + // Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + } else { + d->nsAppRunCalledByQt = true; + QBoolBlocker execGuard(d->currentExecIsNSAppRun, true); + [NSApp run]; + } + retVal = true; + } else { + // We cannot block the thread (and run in a tight loop). + // Instead we will process all current pending events and return. + d->ensureNSAppInitialized(); + if (NSModalSession session = d->currentModalSession()) { + // INVARIANT: a modal window is executing. + if (!excludeUserEvents) { + // Since we can dispatch all kinds of events, we choose + // to use cocoa's native way of running modal sessions: + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + } else do { + // Dispatch all non-user events (but que non-user events up for later). In + // this case, we need more control over which events gets dispatched, and + // cannot use [NSApp runModalSession:session]: + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSModalPanelRunLoopMode + dequeue: YES]; + + if (event) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + } else do { + // INVARIANT: No modal window is executing. + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + + if (event) { + if (flags & QEventLoop::ExcludeUserInputEvents) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + + // Be sure to flush the Qt posted events when not using exec mode + // (exec mode will always do this call from the event loop source): + if (!d->interrupt) + QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + + // Since the window that holds modality might have changed while processing + // events, we we need to interrupt when we return back the previous process + // event recursion to ensure that we spin the correct modal session. + // We do the interruptLater at the end of the function to ensure that we don't + // disturb the 'wait for more events' below (as deleteLater will post an event): + interruptLater = true; + } +#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. + qt_mac_waitForMoreEvents(); + flags &= ~QEventLoop::WaitForMoreEvents; + } else { + // Done with event processing for now. + // Leave the function: + break; + } + } + + // If we're interrupted, we need to interrupt the _current_ + // recursion as well to check if it is still supposed to be + // executing. This way we wind down the stack until we land + // on a recursion that again calls processEvents (typically + // from QEventLoop), and set interrupt to false: + if (d->interrupt) + interrupt(); + +#ifdef QT_MAC_USE_COCOA + if (interruptLater) + QtMacInterruptDispatcherHelp::interruptLater(); +#endif + + 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; +bool QEventDispatcherMacPrivate::interrupt = false; + +#ifdef QT_MAC_USE_COCOA +QStack<QCocoaModalSessionInfo> QEventDispatcherMacPrivate::cocoaModalSessionStack; +bool QEventDispatcherMacPrivate::currentExecIsNSAppRun = false; +bool QEventDispatcherMacPrivate::nsAppRunCalledByQt = false; +bool QEventDispatcherMacPrivate::cleanupModalSessionsNeeded = false; +NSModalSession QEventDispatcherMacPrivate::currentModalSessionCached = 0; + +void QEventDispatcherMacPrivate::ensureNSAppInitialized() +{ + // Some elements in Cocoa require NSApplication to be running before + // they get fully initialized, in particular the menu bar. This + // function is intended for cases where a dialog is told to execute before + // QApplication::exec is called, or the application spins the events loop + // manually rather than calling QApplication:exec. + // The function makes sure that NSApplication starts running, but stops + // it again as soon as the send posted events callback is called. That way + // we let Cocoa finish the initialization it seems to need. We'll only + // apply this trick at most once for any application, and we avoid doing it + // for the common case where main just starts QApplication::exec. + if (nsAppRunCalledByQt || [NSApp isRunning]) + return; + nsAppRunCalledByQt = true; + QBoolBlocker block1(interrupt, true); + QBoolBlocker block2(currentExecIsNSAppRun, true); + [NSApp run]; +} + +void QEventDispatcherMacPrivate::temporarilyStopAllModalSessions() +{ + // Flush, and Stop, all created modal session, and as + // such, make them pending again. The next call to + // currentModalSession will recreate them again. The + // reason to stop all session like this is that otherwise + // a call [NSApp stop] would not stop NSApp, but rather + // the current modal session. So if we need to stop NSApp + // we need to stop all the modal session first. To avoid changing + // the stacking order of the windows while doing so, we put + // up a block that is used in QCocoaWindow and QCocoaPanel: + int stackSize = cocoaModalSessionStack.size(); + for (int i=0; i<stackSize; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.session) { + [NSApp endModalSession:info.session]; + info.session = 0; + } + } + currentModalSessionCached = 0; +} + +NSModalSession QEventDispatcherMacPrivate::currentModalSession() +{ + // If we have one or more modal windows, this function will create + // a session for each of those, and return the one for the top. + if (currentModalSessionCached) + return currentModalSessionCached; + + if (cocoaModalSessionStack.isEmpty()) + return 0; + + int sessionCount = cocoaModalSessionStack.size(); + for (int i=0; i<sessionCount; ++i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (!info.widget) + continue; + if (info.widget->testAttribute(Qt::WA_DontShowOnScreen)) + continue; + if (!info.session) { + QMacCocoaAutoReleasePool pool; + NSWindow *window = qt_mac_window_for(info.widget); + if (!window) + continue; + + ensureNSAppInitialized(); + QBoolBlocker block1(blockSendPostedEvents, true); + info.nswindow = window; + [(NSWindow*) info.nswindow retain]; + int levelBeforeEnterModal = [window level]; + info.session = [NSApp beginModalSessionForWindow:window]; + // Make sure we don't stack the window lower that it was before + // entering modal, in case it e.g. had the stays-on-top flag set: + if (levelBeforeEnterModal > [window level]) + [window setLevel:levelBeforeEnterModal]; + } + currentModalSessionCached = info.session; + cleanupModalSessionsNeeded = false; + } + return currentModalSessionCached; +} + +static void setChildrenWorksWhenModal(QWidget *widget, bool worksWhenModal) +{ + // For NSPanels (but not NSWindows, sadly), we can set the flag + // worksWhenModal, so that they are active even when they are not modal. + 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:worksWhenModal]; + if (worksWhenModal && [window isVisible]){ + [window orderFront:window]; + } + } + } +} + +void QEventDispatcherMacPrivate::updateChildrenWorksWhenModal() +{ + // Make the dialog children of the widget + // active. And make the dialog children of + // the previous modal dialog unactive again: + QMacCocoaAutoReleasePool pool; + int size = cocoaModalSessionStack.size(); + if (size > 0){ + if (QWidget *prevModal = cocoaModalSessionStack[size-1].widget) + setChildrenWorksWhenModal(prevModal, true); + if (size > 1){ + if (QWidget *prevModal = cocoaModalSessionStack[size-2].widget) + setChildrenWorksWhenModal(prevModal, false); + } + } +} + +void QEventDispatcherMacPrivate::cleanupModalSessions() +{ + // Go through the list of modal sessions, and end those + // that no longer has a widget assosiated; no widget means + // the the session has logically ended. The reason we wait like + // this to actually end the sessions for real (rather than at the + // point they were marked as stopped), is that ending a session + // when no other session runs below it on the stack will make cocoa + // drop some events on the floor. + QMacCocoaAutoReleasePool pool; + int stackSize = cocoaModalSessionStack.size(); + + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.widget) { + // This session has a widget, and is therefore not marked + // as stopped. So just make it current. There might still be other + // stopped sessions on the stack, but those will be stopped on + // a later "cleanup" call. + currentModalSessionCached = info.session; + break; + } + cocoaModalSessionStack.remove(i); + currentModalSessionCached = 0; + if (info.session) { + [NSApp endModalSession:info.session]; + [(NSWindow *)info.nswindow release]; + } + } + + updateChildrenWorksWhenModal(); + cleanupModalSessionsNeeded = false; +} + +void QEventDispatcherMacPrivate::beginModalSession(QWidget *widget) +{ + // Add a new, empty (null), NSModalSession to the stack. + // It will become active the next time QEventDispatcher::processEvents is called. + // A QCocoaModalSessionInfo is considered pending to become active if the widget pointer + // is non-zero, and the session pointer is zero (it will become active upon a call to + // currentModalSession). A QCocoaModalSessionInfo is considered pending to be stopped if + // the widget pointer is zero, and the session pointer is non-zero (it will be fully + // stopped in cleanupModalSessions()). + QCocoaModalSessionInfo info = {widget, 0, 0}; + cocoaModalSessionStack.push(info); + updateChildrenWorksWhenModal(); + currentModalSessionCached = 0; +} + +void QEventDispatcherMacPrivate::endModalSession(QWidget *widget) +{ + // Mark all sessions attached to widget as pending to be stopped. We do this + // by setting the widget pointer to zero, but leave the session pointer. + // We don't tell cocoa to stop any sessions just yet, because cocoa only understands + // when we stop the _current_ modal session (which is the session on top of + // the stack, and might not belong to 'widget'). + int stackSize = cocoaModalSessionStack.size(); + for (int i=stackSize-1; i>=0; --i) { + QCocoaModalSessionInfo &info = cocoaModalSessionStack[i]; + if (info.widget == widget) { + info.widget = 0; + if (i == stackSize-1) { + // The top sessions ended. Interrupt the event dispatcher + // to start spinning the correct session immidiatly: + currentModalSessionCached = 0; + cleanupModalSessionsNeeded = true; + QEventDispatcherMac::instance()->interrupt(); + } + } + } +} + +#endif + +QEventDispatcherMacPrivate::QEventDispatcherMacPrivate() +{ +} + +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); + + /* The first cycle in the loop adds the source and the events of the source + are not processed. + We use an observer to process the posted events for the first + execution of the loop. */ + CFRunLoopObserverContext firstTimeObserverContext; + bzero(&firstTimeObserverContext, sizeof(CFRunLoopObserverContext)); + firstTimeObserverContext.info = d; + d->firstTimeObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, + kCFRunLoopEntry, + /* repeats = */ false, + 0, + QEventDispatcherMacPrivate::firstLoopEntry, + &firstTimeObserverContext); + CFRunLoopAddObserver(mainRunLoop(), d->firstTimeObserver, 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; +} + +inline static void processPostedEvents(QEventDispatcherMacPrivate *const d, const bool blockSendPostedEvents) +{ + if (blockSendPostedEvents) { + // We're told to not send posted events (because the event dispatcher + // is currently working on setting up the correct session to run). But + // we still need to make sure that we don't fall asleep until pending events + // are sendt, so we just signal this need, and return: + CFRunLoopSourceSignal(d->postedEventsSource); + return; + } + +#ifdef QT_MAC_USE_COCOA + if (d->cleanupModalSessionsNeeded) + d->cleanupModalSessions(); +#endif + + if (d->interrupt) { +#ifdef QT_MAC_USE_COCOA + if (d->currentExecIsNSAppRun) { + // The event dispatcher has been interrupted. But since + // [NSApplication run] is running the event loop, we + // delayed stopping it until now (to let cocoa process + // pending cocoa events first). + if (d->currentModalSessionCached) + d->temporarilyStopAllModalSessions(); + [NSApp stop:NSApp]; + d->cancelWaitForMoreEvents(); + } +#endif + return; + } + + if (!d->threadData->canWait || (d->serialNumber != d->lastSerial)) { + d->lastSerial = d->serialNumber; + QApplicationPrivate::sendPostedEvents(0, 0, d->threadData); + } +} + +void QEventDispatcherMacPrivate::firstLoopEntry(CFRunLoopObserverRef ref, + CFRunLoopActivity activity, + void *info) +{ + Q_UNUSED(ref); + Q_UNUSED(activity); +#ifdef QT_MAC_USE_COCOA + QApplicationPrivate::qt_initAfterNSAppStarted(); +#endif + processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); +} + +void QEventDispatcherMacPrivate::postedEventsSourcePerformCallback(void *info) +{ + processPostedEvents(static_cast<QEventDispatcherMacPrivate *>(info), blockSendPostedEvents); +} + +#ifdef QT_MAC_USE_COCOA +void QEventDispatcherMacPrivate::cancelWaitForMoreEvents() +{ + // In case the event dispatcher is waiting for more + // events somewhere, we post a dummy event to wake it up: + QMacCocoaAutoReleasePool pool; + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint + modifierFlags:0 timestamp:0. windowNumber:0 context:0 + subtype:QtCocoaEventSubTypeWakeup 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 + // We do nothing more here than setting d->interrupt = true, and + // poke the event loop if it is sleeping. Actually stopping + // NSApp, or the current modal session, is done inside the send + // posted events callback. We do this to ensure that all current pending + // cocoa events gets delivered before we stop. Otherwise, if we now stop + // the last event loop recursion, cocoa will just drop pending posted + // events on the floor before we get a chance to reestablish a new session. + d->cancelWaitForMoreEvents(); +#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); + + CFRunLoopObserverInvalidate(d->firstTimeObserver); + CFRelease(d->firstTimeObserver); +} + +#ifdef QT_MAC_USE_COCOA + +QtMacInterruptDispatcherHelp* QtMacInterruptDispatcherHelp::instance = 0; + +QtMacInterruptDispatcherHelp::QtMacInterruptDispatcherHelp() : cancelled(false) +{ + // The whole point of this class is that we enable a way to interrupt + // the event dispatcher when returning back to a lower recursion level + // than where interruptLater was called. This is needed to detect if + // [NSApp run] should still be running at the recursion level it is at. + // Since the interrupt is canceled if processEvents is called before + // this object gets deleted, we also avoid interrupting unnecessary. + deleteLater(); +} + +QtMacInterruptDispatcherHelp::~QtMacInterruptDispatcherHelp() +{ + if (cancelled) + return; + instance = 0; + QEventDispatcherMac::instance()->interrupt(); +} + +void QtMacInterruptDispatcherHelp::cancelInterruptLater() +{ + if (!instance) + return; + instance->cancelled = true; + delete instance; + instance = 0; +} + +void QtMacInterruptDispatcherHelp::interruptLater() +{ + cancelInterruptLater(); + instance = new QtMacInterruptDispatcherHelp; +} + +#endif + +QT_END_NAMESPACE + diff --git a/src/gui/platforms/mac/qeventdispatcher_mac_p.h b/src/gui/platforms/mac/qeventdispatcher_mac_p.h new file mode 100644 index 0000000000..12fcafbb01 --- /dev/null +++ b/src/gui/platforms/mac/qeventdispatcher_mac_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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; + void *nswindow; +} 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 currentExecIsNSAppRun; + static bool nsAppRunCalledByQt; + static bool cleanupModalSessionsNeeded; + static NSModalSession currentModalSessionCached; + static NSModalSession currentModalSession(); + static void updateChildrenWorksWhenModal(); + static void temporarilyStopAllModalSessions(); + static void beginModalSession(QWidget *widget); + static void endModalSession(QWidget *widget); + static void cancelWaitForMoreEvents(); + static void cleanupModalSessions(); + static void ensureNSAppInitialized(); +#endif + + MacSocketHash macSockets; + QList<void *> queuedUserInputEvents; // List of EventRef in Carbon, and NSEvent * in Cocoa + CFRunLoopSourceRef postedEventsSource; + CFRunLoopObserverRef waitingObserver; + CFRunLoopObserverRef firstTimeObserver; + QAtomicInt serialNumber; + int lastSerial; + static 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); + static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); +}; + +#ifdef QT_MAC_USE_COCOA +class QtMacInterruptDispatcherHelp : public QObject +{ + static QtMacInterruptDispatcherHelp *instance; + bool cancelled; + + QtMacInterruptDispatcherHelp(); + ~QtMacInterruptDispatcherHelp(); + + public: + static void interruptLater(); + static void cancelInterruptLater(); +}; +#endif + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_MAC_P_H diff --git a/src/gui/platforms/mac/qfont_mac.cpp b/src/gui/platforms/mac/qfont_mac.cpp new file mode 100644 index 0000000000..daf68c03ea --- /dev/null +++ b/src/gui/platforms/mac/qfont_mac.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfont.h" +#include "qfont_p.h" +#include "qfontengine_p.h" +#include "qfontengine_mac_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::Monospace: + return QString::fromLatin1("Courier"); + case QFont::Decorative: + return QString::fromLatin1("Bookman Old Style"); + case QFont::Cursive: + return QString::fromLatin1("Apple Chancery"); + case QFont::Fantasy: + return QString::fromLatin1("Papyrus"); + 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/platforms/mac/qfontdatabase_mac.cpp b/src/gui/platforms/mac/qfontdatabase_mac.cpp new file mode 100644 index 0000000000..5ba236b5f7 --- /dev/null +++ b/src/gui/platforms/mac/qfontdatabase_mac.cpp @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qt_mac_p.h> +#include "qfontengine_p.h" +#include <qfile.h> +#include <qabstractfileengine.h> +#include <stdlib.h> +#include <qendian.h> +#include <private/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_p.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 + +#ifndef QT_MAC_USE_COCOA +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 = qt_determine_writing_systems_from_truetype_bits(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; +} +#endif + +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 QT_MAC_USE_COCOA + 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; +} + +static inline float weightToFloat(unsigned int weight) +{ + return (weight - 50) / 100.0; +} + +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 = familyList(req); + + 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(); + +#if defined(QT_MAC_USE_COCOA) + QCFString fontName = NULL, familyName = NULL; +#else + ATSFontFamilyRef familyRef = 0; + ATSFontRef fontRef = 0; +#endif + + 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(); +#if defined(QT_MAC_USE_COCOA) + QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); + if (ctFont) { + fontName = CTFontCopyFullName(ctFont); + familyName = CTFontCopyFamilyName(ctFont); + goto FamilyFound; + } +#else + familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + if (familyRef) { + fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); + goto FamilyFound; + } +#endif + } + } + } +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); + +#ifdef QT_MAC_USE_COCOA + fontDef.family = familyName; + QFontEngine *engine = new QCoreTextFontEngineMulti(fontName, fontDef, d->kerning); +#else + QCFString actualName; + if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) + fontDef.family = actualName; + QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); +#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(); +#if defined(QT_MAC_USE_COCOA) + // Make sure that the family name set on the font matches what + // kCTFontFamilyNameAttribute returns in initializeDb(). + // So far the best solution seems find the installed font + // using CoreText and get the family name from it. + // (ATSFontFamilyGetName appears to be the correct API, but also + // returns the font display name.) + for(int i = 0; i < containedFonts.size(); ++i) { + QCFString fontPostScriptName; + ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName); + QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14); + QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + fnt->families.append(familyName); + } +#else + for(int i = 0; i < containedFonts.size(); ++i) { + QCFString family; + ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family); + fnt->families.append(family); + } +#endif + + 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/platforms/mac/qfontengine_coretext.mm b/src/gui/platforms/mac/qfontengine_coretext.mm new file mode 100644 index 0000000000..d4df2183ed --- /dev/null +++ b/src/gui/platforms/mac/qfontengine_coretext.mm @@ -0,0 +1,880 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_coretext_p.h" + +#include <QtCore/qendian.h> +#include <QtCore/qsettings.h> + +#include <private/qimage_p.h> + +#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +QT_BEGIN_NAMESPACE + +static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90); + +static void loadAdvancesForGlyphs(CTFontRef ctfont, + QVarLengthArray<CGGlyph> &cgGlyphs, + QGlyphLayout *glyphs, int len, + QTextEngine::ShaperFlags flags, + const QFontDef &fontDef) +{ + Q_UNUSED(flags); + QVarLengthArray<CGSize> advances(len); + CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len); + + for (int i = 0; i < len; ++i) { + if (glyphs->glyphs[i] & 0xff000000) + continue; + glyphs->advances_x[i] = QFixed::fromReal(advances[i].width); + glyphs->advances_y[i] = QFixed::fromReal(advances[i].height); + } + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < len; ++i) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } + } +} + +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning) + : 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; + } + + transform = CGAffineTransformIdentity; + if (fontDef.stretch != 100) { + transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); + } + + QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize); + QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform); + ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, 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); + } + init(kerning); +} + +QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CGFontRef cgFontRef, const QFontDef &fontDef, bool kerning) + : QFontEngineMulti(0) +{ + this->fontDef = fontDef; + + transform = CGAffineTransformIdentity; + if (fontDef.stretch != 100) { + transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); + } + + ctfont = CTFontCreateWithGraphicsFont(cgFontRef, fontDef.pixelSize, &transform, NULL); + init(kerning); +} + +QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti() +{ + CFRelease(ctfont); +} + +void QCoreTextFontEngineMulti::init(bool kerning) +{ + Q_ASSERT(ctfont != NULL); + attributeDict = CFDictionaryCreateMutable(0, 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont); + if (!kerning) { + float zero = 0.0; + QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero); + CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern); + } + + QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef); + fe->ref.ref(); + engines.append(fe); +} + +uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef font) const +{ + for (int i = 0; i < engines.count(); ++i) { + if (CFEqual(engineAt(i)->ctfont, font)) + return i; + } + + QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this); + QCoreTextFontEngine *fe = new QCoreTextFontEngine(font, fontDef); + 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 flags, + unsigned short *logClusters, const HB_CharAttributes *, + QScriptItem *si) const +{ + QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0, + reinterpret_cast<const UniChar *>(str), + len, kCFAllocatorNull); + QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict); + QCFType<CTTypesetterRef> typeSetter; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (flags & QTextEngine::RightToLeft) { + const void *optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; + const short rtlForcedEmbeddingLevelValue = 1; + const void *rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) }; + QCFType<CFDictionaryRef> options = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, 1, + &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + typeSetter = CTTypesetterCreateWithAttributedStringAndOptions(attributedString, options); + } else +#else + Q_UNUSED(flags); +#endif + 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) { + // CoreText failed to shape the text we gave it, so we assume one glyph + // per character and build a list of invalid glyphs with zero advance + *nglyphs = len; + for (int i = 0; i < len; ++i) { + outGlyphs[i] = 0; + if (logClusters) + logClusters[i] = i; + outAdvances_x[i] = QFixed(); + outAdvances_y[i] = QFixed(); + outAttributes[i].clusterStart = true; + } + return true; + } + + 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); + CFRange stringRange = CTRunGetStringRange(run); + int prepend = 0; +#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 + UniChar beginGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location); + QChar dir = QChar::direction(beginGlyph); + bool beginWithOverride = dir == QChar::DirLRO || dir == QChar::DirRLO || dir == QChar::DirLRE || dir == QChar::DirRLE; + if (beginWithOverride) { + logClusters[stringRange.location] = 0; + outGlyphs[0] = 0xFFFF; + outAdvances_x[0] = 0; + outAdvances_y[0] = 0; + outAttributes[0].clusterStart = true; + outAttributes[0].dontPrint = true; + outGlyphs++; + outAdvances_x++; + outAdvances_y++; + outAttributes++; + prepend = 1; + } +#endif + UniChar endGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location + stringRange.length - 1); + bool endWithPDF = QChar::direction(endGlyph) == QChar::DirPDF; + if (endWithPDF) + glyphCount++; + + 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)); + uint fontIndex = fontIndexForFont(runFont); + const QFontEngine *engine = engineAt(fontIndex); + fontIndex <<= 24; + si->ascent = qMax(engine->ascent(), si->ascent); + si->descent = qMax(engine->descent(), si->descent); + si->leading = qMax(engine->leading(), si->leading); + //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont)); + if (endWithPDF) + glyphCount--; + + 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 + prepend; + (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) { + if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location + prepend) { + 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); + // Use negative y advance for flipped coordinate system + outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i].y - tmpPoints[i + 1].y); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + outAdvances_x[idx] = outAdvances_x[idx].round(); + outAdvances_y[idx] = outAdvances_y[idx].round(); + } + } + CGSize lastGlyphAdvance; + CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); + + outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; + outAdvances_x[rtl ? 0 : (glyphCount - 1)] = + (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(lastGlyphAdvance.width).round() + : QFixed::fromReal(lastGlyphAdvance.width); + + if (endWithPDF) { + logClusters[stringRange.location + stringRange.length - 1] = glyphCount + prepend; + outGlyphs[glyphCount] = 0xFFFF; + outAdvances_x[glyphCount] = 0; + outAdvances_y[glyphCount] = 0; + outAttributes[glyphCount].clusterStart = true; + outAttributes[glyphCount].dontPrint = true; + glyphCount++; + } + } + 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 +{ + *nglyphs = len; + QCFType<CFStringRef> cfstring; + + QVarLengthArray<CGGlyph> cgGlyphs(len); + CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); + + for (int i = 0; i < len; ++i) { + if (cgGlyphs[i]) { + glyphs->glyphs[i] = cgGlyphs[i]; + } else { + if (!cfstring) + cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull); + QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1)); + CGGlyph substituteGlyph = 0; + CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1); + if (substituteGlyph) { + const uint fontIndex = (fontIndexForFont(substituteFont) << 24); + glyphs->glyphs[i] = substituteGlyph | fontIndex; + if (!(flags & QTextEngine::GlyphIndicesOnly)) { + CGSize advance; + CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1); + glyphs->advances_x[i] = QFixed::fromReal(advance.width); + glyphs->advances_y[i] = QFixed::fromReal(advance.height); + } + } + } + } + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); + return true; +} + +void QCoreTextFontEngineMulti::loadEngine(int) +{ + // Do nothing + Q_ASSERT(false); +} + +extern int qt_antialiasing_threshold; // from qapplication.cpp + +static inline CGAffineTransform transformFromFontDef(const QFontDef &fontDef) +{ + CGAffineTransform transform = CGAffineTransformIdentity; + if (fontDef.stretch != 100) + transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); + return transform; +} + +QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def) +{ + fontDef = def; + transform = transformFromFontDef(fontDef); + ctfont = font; + CFRetain(ctfont); + cgFont = CTFontCopyGraphicsFont(font, NULL); + init(); +} + +QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def) +{ + fontDef = def; + transform = transformFromFontDef(fontDef); + cgFont = font; + // Keep reference count balanced + CFRetain(cgFont); + ctfont = CTFontCreateWithGraphicsFont(font, fontDef.pixelSize, &transform, NULL); + init(); +} + +QCoreTextFontEngine::~QCoreTextFontEngine() +{ + CFRelease(cgFont); + CFRelease(ctfont); +} + +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp + +int getTraitValue(CFDictionaryRef allTraits, CFStringRef trait) +{ + if (CFDictionaryContainsKey(allTraits, trait)) { + CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait); + float v = 0; + CFNumberGetValue(traitNum, kCFNumberFloatType, &v); + // the value we get from CFNumberRef is from -1.0 to 1.0 + int value = v * 500 + 500; + return value; + } + + return 0; +} + +void QCoreTextFontEngine::init() +{ + Q_ASSERT(ctfont != NULL); + Q_ASSERT(cgFont != NULL); + + QCFString family = CTFontCopyFamilyName(ctfont); + fontDef.family = family; + + synthesisFlags = 0; + CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont); + if (traits & kCTFontItalicTrait) + fontDef.style = QFont::StyleItalic; + + CFDictionaryRef allTraits = CTFontCopyTraits(ctfont); + fontDef.weight = weightFromInteger(getTraitValue(allTraits, kCTFontWeightTrait)); + int slant = getTraitValue(allTraits, kCTFontSlantTrait); + if (slant > 500 && !(traits & kCTFontItalicTrait)) + fontDef.style = QFont::StyleOblique; + CFRelease(allTraits); + + if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) + synthesisFlags |= SynthesizedBold; + // XXX: we probably don't need to synthesis italic for oblique font + if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) + synthesisFlags |= SynthesizedItalic; + + avgCharWidth = 0; + QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + unsigned emSize = CTFontGetUnitsPerEm(ctfont); + if (os2Table.size() >= 10) { + fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8)); + // qAbs is a workaround for weird fonts like Lucida Grande + qint16 width = qAbs(qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 2))); + avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize); + } else + avgCharWidth = QFontEngine::averageCharWidth(); + + ctMaxCharWidth = ctMinLeftBearing = ctMinRightBearing = 0; + QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a')); + if (hheaTable.size() >= 16) { + quint16 width = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 10)); + ctMaxCharWidth = width * fontDef.pixelSize / emSize; + qint16 bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 12)); + ctMinLeftBearing = bearing * fontDef.pixelSize / emSize; + bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 14)); + ctMinRightBearing = bearing * fontDef.pixelSize / emSize; + } +} + +bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + *nglyphs = len; + QCFType<CFStringRef> cfstring; + + QVarLengthArray<CGGlyph> cgGlyphs(len); + CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); + + for (int i = 0; i < len; ++i) + if (cgGlyphs[i]) + glyphs->glyphs[i] = cgGlyphs[i]; + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); + return true; +} + +glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) +{ + QFixed w; + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } + return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), 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); + if (synthesisFlags & QFontEngine::SynthesizedItalic) { + rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW; + } + 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); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + ret.xoff = ret.xoff.round(); + ret.yoff = ret.yoff.round(); + } + + return ret; +} + +QFixed QCoreTextFontEngine::ascent() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() + : QFixed::fromReal(CTFontGetAscent(ctfont)); +} +QFixed QCoreTextFontEngine::descent() const +{ + QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + d = d.round(); + + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). + // Fix in Qt 5. + return d - 1; +} +QFixed QCoreTextFontEngine::leading() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() + : QFixed::fromReal(CTFontGetLeading(ctfont)); +} +QFixed QCoreTextFontEngine::xHeight() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() + : QFixed::fromReal(CTFontGetXHeight(ctfont)); +} + +QFixed QCoreTextFontEngine::averageCharWidth() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? avgCharWidth.round() : avgCharWidth; +} + +qreal QCoreTextFontEngine::maxCharWidth() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(ctMaxCharWidth) : ctMaxCharWidth; +} + +qreal QCoreTextFontEngine::minLeftBearing() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(ctMinLeftBearing) : ctMinLeftBearing; +} + +qreal QCoreTextFontEngine::minRightBearing() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(ctMinRightBearing) : ctMinLeftBearing; +} + +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, -SYNTHETIC_ITALIC_SKEW, 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, -SYNTHETIC_ITALIC_SKEW, 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::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int /*margin*/, bool aa) +{ + 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 = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + uint cgflags = kCGImageAlphaNoneSkipFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#endif + CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(), + 8, im.bytesPerLine(), colorspace, + cgflags); + CGContextSetFontSize(ctx, fontDef.pixelSize); + CGContextSetShouldAntialias(ctx, aa || + (fontDef.pointSize > qt_antialiasing_threshold + && !(fontDef.styleStrategy & QFont::NoAntialias))); + CGContextSetShouldSmoothFonts(ctx, aa); + 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, SYNTHETIC_ITALIC_SKEW, 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() + subPixelPosition.toReal(); + qreal pos_y = im.height() + br.y.toReal() - 1; + 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); + + return im; +} + +QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + QImage im = imageForGlyph(glyph, subPixelPosition, 0, false); + + 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; +} + +QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, const QTransform &x) +{ + if (x.type() >= QTransform::TxScale) + return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, margin, x); + + QImage im = imageForGlyph(glyph, subPixelPosition, margin, true); + qGamma_correct_back_to_linear_cs(&im); + return im; +} + +void QCoreTextFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + int i, numGlyphs = glyphs->numGlyphs; + QVarLengthArray<CGGlyph> cgGlyphs(numGlyphs); + + for (i = 0; i < numGlyphs; ++i) { + if (glyphs->glyphs[i] & 0xff000000) + cgGlyphs[i] = 0; + else + cgGlyphs[i] = glyphs->glyphs[i]; + } + + loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, numGlyphs, flags, fontDef); +} + +QFontEngine::FaceId QCoreTextFontEngine::faceId() const +{ + return QFontEngine::FaceId(); +} + +bool QCoreTextFontEngine::canRender(const QChar *string, int len) +{ + QVarLengthArray<CGGlyph> cgGlyphs(len); + return CTFontGetGlyphsForCharacters(ctfont, (const UniChar *) string, cgGlyphs.data(), len); +} + +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 *) +{ + // ### +} + +QFixed QCoreTextFontEngine::emSquareSize() const +{ + return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); +} + +QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const +{ + QFontDef newFontDef = fontDef; + newFontDef.pixelSize = pixelSize; + newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + + return new QCoreTextFontEngine(cgFont, fontDef); +} + +QT_END_NAMESPACE + +#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + diff --git a/src/gui/platforms/mac/qfontengine_coretext_p.h b/src/gui/platforms/mac/qfontengine_coretext_p.h new file mode 100644 index 0000000000..bb80a9b2f3 --- /dev/null +++ b/src/gui/platforms/mac/qfontengine_coretext_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_CORETEXT_P_H +#define QFONTENGINE_CORETEXT_P_H + +#include <private/qfontengine_p.h> + +#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +class QRawFontPrivate; +class QCoreTextFontEngineMulti; +class QCoreTextFontEngine : public QFontEngine +{ +public: + QCoreTextFontEngine(CTFontRef font, const QFontDef &def); + QCoreTextFontEngine(CGFontRef font, const QFontDef &def); + ~QCoreTextFontEngine(); + + 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 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 bool supportsSubPixelPositions() const { return true; } + + 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, QFixed subPixelPosition); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + virtual qreal minRightBearing() const; + virtual qreal minLeftBearing() const; + virtual QFixed emSquareSize() const; + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + +private: + friend class QRawFontPrivate; + + void init(); + QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool colorful); + CTFontRef ctfont; + CGFontRef cgFont; + int synthesisFlags; + CGAffineTransform transform; + QFixed avgCharWidth; + qreal ctMaxCharWidth; + qreal ctMinLeftBearing; + qreal ctMinRightBearing; + friend class QCoreTextFontEngineMulti; +}; + +class QCoreTextFontEngineMulti : public QFontEngineMulti +{ +public: + QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning); + QCoreTextFontEngineMulti(CGFontRef cgFontRef, 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, + QScriptItem *si) const; + + virtual const char *name() const { return "CoreText"; } +protected: + virtual void loadEngine(int at); + +private: + void init(bool kerning); + inline const QCoreTextFontEngine *engineAt(int i) const + { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); } + + uint fontIndexForFont(CTFontRef font) const; + CTFontRef ctfont; + mutable QCFType<CFMutableDictionaryRef> attributeDict; + CGAffineTransform transform; + friend class QFontDialogPrivate; +}; + +#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + +#endif // QFONTENGINE_CORETEXT_P_H diff --git a/src/gui/platforms/mac/qfontengine_mac.mm b/src/gui/platforms/mac/qfontengine_mac.mm new file mode 100644 index 0000000000..9f094ad7d1 --- /dev/null +++ b/src/gui/platforms/mac/qfontengine_mac.mm @@ -0,0 +1,1236 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_mac_p.h" + +#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 <qglobal.h> +#include <qpixmap.h> +#include <qpixmapcache.h> +#include <qvarlengtharray.h> +#include <qdebug.h> +#include <qendian.h> +#include <qmath.h> +#include <private/qimage_p.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; +} + + +#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; + unsigned int styleStrategy; +}; + +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); + } +#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 (nfo->styleStrategy & QFont::ForceIntegerMetrics) { + yAdvance = yAdvance.round(); + xAdvance = xAdvance.round(); + } + + if (glyphId != 0xffff || i == 0) { + if (i < nfo->glyphs->numGlyphs) + { + 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, QScriptItem *) 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; + nfo.styleStrategy = fontDef.styleStrategy; + + int prevNumGlyphs = *nglyphs; + + 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 (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); + if (prevNumGlyphs < *nfo.numGlyphs) + return false; + 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; +} + +// Not used directly for shaping, only used to calculate m_averageCharWidth +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); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = glyphs->advances_y[i].round(); + } + } +} + +glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs) +{ + QFixed w; + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() + : glyphs.effectiveAdvance(i); + } + return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), 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); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + gm.x = gm.x.floor(); + gm.y = gm.y.floor(); + gm.xoff = gm.xoff.round(); + gm.yoff = gm.yoff.round(); + } + + return gm; +} + +QFixed QFontEngineMac::ascent() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_ascent.round() + : m_ascent; +} + +QFixed QFontEngineMac::descent() const +{ + // subtract a pixel to even out the historical +1 in QFontMetrics::height(). + // Fix in Qt 5. + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_descent.round() - 1 + : m_descent; +} + +QFixed QFontEngineMac::leading() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_leading.round() + : m_leading; +} + +qreal QFontEngineMac::maxCharWidth() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? qRound(m_maxCharWidth) + : m_maxCharWidth; +} + +QFixed QFontEngineMac::xHeight() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_xHeight.round() + : m_xHeight; +} + +QFixed QFontEngineMac::averageCharWidth() const +{ + return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + ? m_averageCharWidth.round() + : 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); +} + + +/*! + Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for + the subpixel antialiasing... +*/ +QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful) +{ + const glyph_metrics_t br = boundingBox(glyph); + QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32); + im.fill(0xff000000); + + CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace(); + uint cgflags = kCGImageAlphaNoneSkipFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#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, colorful); + 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); + + return im; +} + +QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) +{ + QImage im = imageForGlyph(glyph, 2, false); + + 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; +} + +QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) +{ + QImage im = imageForGlyph(glyph, margin, true); + + if (t.type() >= QTransform::TxScale) { + im = im.transformed(t); + } + + qGamma_correct_back_to_linear_cs(&im); + + return im; +} + + +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) +if (QSysInfo::MacintoshVersion >= QSysInfo::MV_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 +#endif +{ + 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()); +} + 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 = QFontEngine::convertToPostscriptFontFamilyName(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/platforms/mac/qfontengine_mac_p.h b/src/gui/platforms/mac/qfontengine_mac_p.h new file mode 100644 index 0000000000..292ea98d9a --- /dev/null +++ b/src/gui/platforms/mac/qfontengine_mac_p.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_MAC_P_H +#define QFONTENGINE_MAC_P_H + +#include <private/qfontengine_p.h> + +#ifndef QT_MAC_USE_COCOA +class QFontEngineMacMulti; +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); + virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); + +private: + QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); + + 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, QScriptItem *) 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 // QFONTENGINE_MAC_P_H diff --git a/src/gui/platforms/mac/qkeymapper_mac.cpp b/src/gui/platforms/mac/qkeymapper_mac.cpp new file mode 100644 index 0000000000..d3bbf89711 --- /dev/null +++ b/src/gui/platforms/mac/qkeymapper_mac.cpp @@ -0,0 +1,1023 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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> +#include <private/qmacinputcontext_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); + } + } + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + Qt::KeyboardModifiers oldModifiers = ret; + ret &= ~(Qt::MetaModifier | Qt::ControlModifier); + if (oldModifiers & Qt::ControlModifier) + ret |= Qt::MetaModifier; + if (oldModifiers & Qt::MetaModifier) + ret |= Qt::ControlModifier; + } + 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; + } + } + + if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + int oldModifiers = ret; + ret &= ~(controlKeyBit | cmdKeyBit); + if (oldModifiers & controlKeyBit) + ret |= cmdKeyBit; + if (oldModifiers & cmdKeyBit) + ret |= controlKeyBit; + } + 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 qt_mac_enum_mapper qt_mac_private_unicode[] = { + { 0xF700, QT_MAC_MAP_ENUM(Qt::Key_Up) }, //NSUpArrowFunctionKey + { 0xF701, QT_MAC_MAP_ENUM(Qt::Key_Down) }, //NSDownArrowFunctionKey + { 0xF702, QT_MAC_MAP_ENUM(Qt::Key_Left) }, //NSLeftArrowFunctionKey + { 0xF703, QT_MAC_MAP_ENUM(Qt::Key_Right) }, //NSRightArrowFunctionKey + { 0xF727, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertFunctionKey + { 0xF728, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteFunctionKey + { 0xF729, QT_MAC_MAP_ENUM(Qt::Key_Home) }, //NSHomeFunctionKey + { 0xF72B, QT_MAC_MAP_ENUM(Qt::Key_End) }, //NSEndFunctionKey + { 0xF72C, QT_MAC_MAP_ENUM(Qt::Key_PageUp) }, //NSPageUpFunctionKey + { 0xF72D, QT_MAC_MAP_ENUM(Qt::Key_PageDown) }, //NSPageDownFunctionKey + { 0xF72F, QT_MAC_MAP_ENUM(Qt::Key_ScrollLock) }, //NSScrollLockFunctionKey + { 0xF730, QT_MAC_MAP_ENUM(Qt::Key_Pause) }, //NSPauseFunctionKey + { 0xF731, QT_MAC_MAP_ENUM(Qt::Key_SysReq) }, //NSSysReqFunctionKey + { 0xF735, QT_MAC_MAP_ENUM(Qt::Key_Menu) }, //NSMenuFunctionKey + { 0xF738, QT_MAC_MAP_ENUM(Qt::Key_Print) }, //NSPrintFunctionKey + { 0xF73A, QT_MAC_MAP_ENUM(Qt::Key_Clear) }, //NSClearDisplayFunctionKey + { 0xF73D, QT_MAC_MAP_ENUM(Qt::Key_Insert) }, //NSInsertCharFunctionKey + { 0xF73E, QT_MAC_MAP_ENUM(Qt::Key_Delete) }, //NSDeleteCharFunctionKey + { 0xF741, QT_MAC_MAP_ENUM(Qt::Key_Select) }, //NSSelectFunctionKey + { 0xF742, QT_MAC_MAP_ENUM(Qt::Key_Execute) }, //NSExecuteFunctionKey + { 0xF746, QT_MAC_MAP_ENUM(Qt::Key_Help) }, //NSHelpFunctionKey + { 0xF747, QT_MAC_MAP_ENUM(Qt::Key_Mode_switch) }, //NSModeSwitchFunctionKey + { 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; + } + } + + // check if they belong to key codes in private unicode range + if (key >= 0xf700 && key <= 0xf747) { + if (key >= 0xf704 && key <= 0xf726) { + return Qt::Key_F1 + (key.unicode() - 0xf704) ; + } + for (int i = 0; qt_mac_private_unicode[i].qt_code; i++) { + if (qt_mac_private_unicode[i].mac_code == key) { + return qt_mac_private_unicode[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; + if (er) + 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() +{ + deleteLayouts(); +} + +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)); + keyboardInputDirection = keyboardInputLocale.textDirection(); + } else { + keyboardInputLocale = QLocale::c(); + keyboardInputDirection = Qt::LeftToRight; + } + return true; +} + +void +QKeyMapperPrivate::deleteLayouts() +{ + keyboard_mode = NullMode; + for (int i = 0; i < 255; ++i) { + if (keyLayout[i]) { + delete keyLayout[i]; + keyLayout[i] = 0; + } + } +} + +void +QKeyMapperPrivate::clearMappings() +{ + deleteLayouts(); + 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; + } + + QInputContext *currentContext = qApp->inputContext(); + if (currentContext && currentContext->isComposing()) { + if (ekind == kEventRawKeyDown) { + QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); + if (context) + context->setLastKeydownEvent(event); + } + return false; + } + // Once we process the key down , we don't need to send the saved event again from + // kEventTextInputUnicodeForKeyEvent, so clear it. + if (currentContext && ekind == kEventRawKeyDown) { + QMacInputContext *context = qobject_cast<QMacInputContext*>(currentContext); + if (context) + context->setLastKeydownEvent(0); + } + + //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 unfortunately it is the best that can be + done for now because of the Control/Meta mapping problems */ + if (modifiers & (Qt::ControlModifier | Qt::MetaModifier) + && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + 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; + if (er) + 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); +#ifdef QT_MAC_USE_COCOA + // The unicode characters in the range 0xF700-0xF747 are reserved + // by Mac OS X for transient use as keyboard function keys. We + // wont send 'text' for such key events. This is done to match + // behavior on other platforms. + unsigned int *unicodeKey = (unsigned int*)info; + if (*unicodeKey >= 0xf700 && *unicodeKey <= 0xf747) + text = QString(); + bool isAccepted; +#endif + 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 + ,&isAccepted +#endif + ); +#ifdef QT_MAC_USE_COCOA + *unicodeKey = (unsigned int)isAccepted; +#endif + } + return handled_event; +} + +void +QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void * +#if defined(QT_MAC_USE_COCOA) + unicodeKey // unicode character from NSEvent (modifiers applied) +#endif + ) +{ + 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; + } +#ifndef Q_WS_MAC32 + else { + const QChar unicode(*((UniChar *)unicodeKey)); + int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey); + if (qtkey == Qt::Key_unknown) + qtkey = unicode.unicode(); + keyLayout[macVirtualKey]->qtKey[i] = qtkey; + } +#endif +#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/platforms/mac/qmacdefines_mac.h b/src/gui/platforms/mac/qmacdefines_mac.h new file mode 100644 index 0000000000..d6ccb93593 --- /dev/null +++ b/src/gui/platforms/mac/qmacdefines_mac.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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> + +#ifdef qDebug +# define old_qDebug qDebug +# undef qDebug +#endif + +#ifdef __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 + +#ifdef old_qDebug +# undef qDebug +# define qDebug QT_NO_QDEBUG_MACRO +# undef old_qDebug +#endif diff --git a/src/gui/platforms/mac/qmacgesturerecognizer_mac.mm b/src/gui/platforms/mac/qmacgesturerecognizer_mac.mm new file mode 100644 index 0000000000..6a4f0bb445 --- /dev/null +++ b/src/gui/platforms/mac/qmacgesturerecognizer_mac.mm @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmacgesturerecognizer_mac_p.h" +#include "qgesture.h" +#include "qgesture_p.h" +#include "qevent.h" +#include "qevent_p.h" +#include "qwidget.h" +#include "qdebug.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +QMacSwipeGestureRecognizer::QMacSwipeGestureRecognizer() +{ +} + +QGesture *QMacSwipeGestureRecognizer::create(QObject * /*target*/) +{ + return new QSwipeGesture; +} + +QGestureRecognizer::Result +QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) { + QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); + switch (ev->gestureType) { + case QNativeGestureEvent::Swipe: { + QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); + g->setSwipeAngle(ev->angle); + g->setHotSpot(ev->position); + return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; + break; } + default: + break; + } + } + + return QGestureRecognizer::Ignore; +} + +void QMacSwipeGestureRecognizer::reset(QGesture *gesture) +{ + QSwipeGesture *g = static_cast<QSwipeGesture *>(gesture); + g->setSwipeAngle(0); + QGestureRecognizer::reset(gesture); +} + +//////////////////////////////////////////////////////////////////////// + +QMacPinchGestureRecognizer::QMacPinchGestureRecognizer() +{ +} + +QGesture *QMacPinchGestureRecognizer::create(QObject * /*target*/) +{ + return new QPinchGesture; +} + +QGestureRecognizer::Result +QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::NativeGesture && obj->isWidgetType()) { + QPinchGesture *g = static_cast<QPinchGesture *>(gesture); + QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); + switch(ev->gestureType) { + case QNativeGestureEvent::GestureBegin: + reset(gesture); + g->setStartCenterPoint(static_cast<QWidget*>(obj)->mapFromGlobal(ev->position)); + g->setCenterPoint(g->startCenterPoint()); + g->setChangeFlags(QPinchGesture::CenterPointChanged); + g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); + return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint; + case QNativeGestureEvent::Rotate: { + g->setLastScaleFactor(g->scaleFactor()); + g->setLastRotationAngle(g->rotationAngle()); + g->setRotationAngle(g->rotationAngle() + ev->percentage); + g->setChangeFlags(QPinchGesture::RotationAngleChanged); + g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); + return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; + } + case QNativeGestureEvent::Zoom: + g->setLastScaleFactor(g->scaleFactor()); + g->setLastRotationAngle(g->rotationAngle()); + g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage)); + g->setChangeFlags(QPinchGesture::ScaleFactorChanged); + g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); + return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; + case QNativeGestureEvent::GestureEnd: + return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; + default: + break; + } + } + + return QGestureRecognizer::Ignore; +} + +void QMacPinchGestureRecognizer::reset(QGesture *gesture) +{ + QPinchGesture *g = static_cast<QPinchGesture *>(gesture); + g->setChangeFlags(0); + g->setTotalChangeFlags(0); + g->setScaleFactor(1.0f); + g->setTotalScaleFactor(1.0f); + g->setLastScaleFactor(1.0f); + g->setRotationAngle(0.0f); + g->setTotalRotationAngle(0.0f); + g->setLastRotationAngle(0.0f); + g->setCenterPoint(QPointF()); + g->setStartCenterPoint(QPointF()); + g->setLastCenterPoint(QPointF()); + QGestureRecognizer::reset(gesture); +} + +//////////////////////////////////////////////////////////////////////// + +#if defined(QT_MAC_USE_COCOA) + +QMacPanGestureRecognizer::QMacPanGestureRecognizer() : _panCanceled(true) +{ +} + +QGesture *QMacPanGestureRecognizer::create(QObject *target) +{ + if (!target) + return new QPanGesture; + + if (QWidget *w = qobject_cast<QWidget *>(target)) { + w->setAttribute(Qt::WA_AcceptTouchEvents); + w->setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + return new QPanGesture; + } + return 0; +} + +QGestureRecognizer::Result +QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent *event) +{ + const int panBeginDelay = 300; + const int panBeginRadius = 3; + + QPanGesture *g = static_cast<QPanGesture *>(gesture); + + switch (event->type()) { + case QEvent::TouchBegin: { + const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); + if (ev->touchPoints().size() == 1) { + reset(gesture); + _startPos = QCursor::pos(); + _panTimer.start(panBeginDelay, target); + _panCanceled = false; + return QGestureRecognizer::MayBeGesture; + } + break;} + case QEvent::TouchEnd: { + if (_panCanceled) + break; + + const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); + if (ev->touchPoints().size() == 1) + return QGestureRecognizer::FinishGesture; + break;} + case QEvent::TouchUpdate: { + if (_panCanceled) + break; + + const QTouchEvent *ev = static_cast<const QTouchEvent*>(event); + if (ev->touchPoints().size() == 1) { + if (_panTimer.isActive()) { + // INVARIANT: Still in maybeGesture. Check if the user + // moved his finger so much that it makes sense to cancel the pan: + const QPointF p = QCursor::pos(); + if ((p - _startPos).manhattanLength() > panBeginRadius) { + _panCanceled = true; + _panTimer.stop(); + return QGestureRecognizer::CancelGesture; + } + } else { + const QPointF p = QCursor::pos(); + const QPointF posOffset = p - _startPos; + g->setLastOffset(g->offset()); + g->setOffset(QPointF(posOffset.x(), posOffset.y())); + g->setHotSpot(_startPos); + return QGestureRecognizer::TriggerGesture; + } + } else if (_panTimer.isActive()) { + // I only want to cancel the pan if the user is pressing + // more than one finger, and the pan hasn't started yet: + _panCanceled = true; + _panTimer.stop(); + return QGestureRecognizer::CancelGesture; + } + break;} + case QEvent::Timer: { + QTimerEvent *ev = static_cast<QTimerEvent *>(event); + if (ev->timerId() == _panTimer.timerId()) { + _panTimer.stop(); + if (_panCanceled) + break; + // Begin new pan session! + _startPos = QCursor::pos(); + g->setHotSpot(_startPos); + return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; + } + break; } + default: + break; + } + + return QGestureRecognizer::Ignore; +} + +void QMacPanGestureRecognizer::reset(QGesture *gesture) +{ + QPanGesture *g = static_cast<QPanGesture *>(gesture); + _startPos = QPointF(); + _panCanceled = true; + g->setOffset(QPointF(0, 0)); + g->setLastOffset(QPointF(0, 0)); + g->setAcceleration(qreal(1)); + QGestureRecognizer::reset(gesture); +} +#endif // QT_MAC_USE_COCOA + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/src/gui/platforms/mac/qmacgesturerecognizer_mac_p.h b/src/gui/platforms/mac/qmacgesturerecognizer_mac_p.h new file mode 100644 index 0000000000..465f6a2ac8 --- /dev/null +++ b/src/gui/platforms/mac/qmacgesturerecognizer_mac_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMACSWIPEGESTURERECOGNIZER_MAC_P_H +#define QMACSWIPEGESTURERECOGNIZER_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 "qtimer.h" +#include "qpoint.h" +#include "qgesturerecognizer.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +class QMacSwipeGestureRecognizer : public QGestureRecognizer +{ +public: + QMacSwipeGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); + void reset(QGesture *gesture); +}; + +class QMacPinchGestureRecognizer : public QGestureRecognizer +{ +public: + QMacPinchGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); + void reset(QGesture *gesture); +}; + +#if defined(QT_MAC_USE_COCOA) + +class QMacPanGestureRecognizer : public QObject, public QGestureRecognizer +{ +public: + QMacPanGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *gesture, QObject *watched, QEvent *event); + void reset(QGesture *gesture); +private: + QPointF _startPos; + QBasicTimer _panTimer; + bool _panCanceled; +}; + +#endif + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES + +#endif // QMACSWIPEGESTURERECOGNIZER_MAC_P_H diff --git a/src/gui/platforms/mac/qmime_mac.cpp b/src/gui/platforms/mac/qmime_mac.cpp new file mode 100644 index 0000000000..d6f6222c23 --- /dev/null +++ b/src/gui/platforms/mac/qmime_mac.cpp @@ -0,0 +1,1310 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 converts between a MIME type and a + \l{http://developer.apple.com/macosx/uniformtypeidentifiers.html}{Uniform + Type Identifier (UTI)} format. + \since 4.2 + + \ingroup draganddrop + + Qt's drag and drop and clipboard facilities use the MIME + standard. On X11, this maps trivially to the Xdnd protocol. On + Mac, although some applications use MIME to describe clipboard + contents, it is more common to use Apple's UTI format. + + QMacPasteboardMime's role is to bridge the gap between MIME and UTI; + By subclasses this class, one can extend Qt's drag and drop + and clipboard handling to convert to and from unsupported, or proprietary, UTI formats. + + A subclass of QMacPasteboardMime will automatically be registered, and active, upon instantiation. + + Qt has predefined support for the following UTIs: + \list + \i public.utf8-plain-text - converts to "text/plain" + \i public.utf16-plain-text - converts to "text/plain" + \i public.html - converts to "text/html" + \i public.url - converts to "text/uri-list" + \i public.file-url - converts to "text/uri-list" + \i public.tiff - converts to "application/x-qt-image" + \i public.vcard - converts to "text/plain" + \i com.apple.traditional-mac-plain-text - converts to "text/plain" + \i com.apple.pict - converts to "application/x-qt-image" + \endlist + + When working with MIME data, Qt will interate through all instances of QMacPasteboardMime to + find an instance that can convert to, or from, a specific MIME type. It will do this by calling + canConvert() on each instance, starting with (and choosing) the last created instance first. + The actual conversions will be done by using convertToMime() and convertFromMime(). + + \note The API uses the term "flavor" in some cases. This is for backwards + compatibility reasons, and should now be understood as UTIs. +*/ + +/*! \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(QLatin1Char('/'), 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(QLatin1Char(';')); + 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(reinterpret_cast<const QChar *>(firstData.constData()), + firstData.size() / sizeof(QChar)); + } 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 + +// This can be removed once 10.6 is the minimum (or we have to require 64-bit) whichever comes first. + +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 //Q_WS_MAC32 + +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; + QCFType<CFDataRef> tiffData = CFDataCreateWithBytesNoCopy(0, + reinterpret_cast<const UInt8 *>(a.constData()), + a.size(), kCFAllocatorNull); + QCFType<CGImageSourceRef> imageSource = CGImageSourceCreateWithData(tiffData, 0); + image = CGImageSourceCreateImageAtIndex(imageSource, 0, 0); + + 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; +} + +class QMacPasteboardMimeUrl : public QMacPasteboardMime { +public: + QMacPasteboardMimeUrl() : 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 QMacPasteboardMimeUrl::convertorName() +{ + return QLatin1String("URL"); +} + +QString QMacPasteboardMimeUrl::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("text/uri-list"))) + return QLatin1String("public.url"); + return QString(); +} + +QString QMacPasteboardMimeUrl::mimeFor(QString flav) +{ + if(flav == QLatin1String("public.url")) + return QLatin1String("text/uri-list"); + return QString(); +} + +bool QMacPasteboardMimeUrl::canConvert(const QString &mime, QString flav) +{ + return flav == QLatin1String("public.url") + && mime == QLatin1String("text/uri-list"); +} + +QVariant QMacPasteboardMimeUrl::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> QMacPasteboardMimeUrl::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; +} + +class QMacPasteboardMimeVCard : public QMacPasteboardMime +{ +public: + QMacPasteboardMimeVCard() : 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 QMacPasteboardMimeVCard::convertorName() +{ + return QString("VCard"); +} + +bool QMacPasteboardMimeVCard::canConvert(const QString &mime, QString flav) +{ + return mimeFor(flav) == mime; +} + +QString QMacPasteboardMimeVCard::flavorFor(const QString &mime) +{ + if(mime.startsWith(QLatin1String("text/plain"))) + return QLatin1String("public.vcard"); + return QString(); +} + +QString QMacPasteboardMimeVCard::mimeFor(QString flav) +{ + if (flav == QLatin1String("public.vcard")) + return QLatin1String("text/plain"); + return QString(); +} + +QVariant QMacPasteboardMimeVCard::convertToMime(const QString &mime, QList<QByteArray> data, QString) +{ + QByteArray cards; + if (mime == QLatin1String("text/plain")) { + for (int i=0; i<data.size(); ++i) + cards += data[i]; + } + return QVariant(cards); +} + +QList<QByteArray> QMacPasteboardMimeVCard::convertFromMime(const QString &mime, QVariant data, QString) +{ + QList<QByteArray> ret; + if (mime == QLatin1String("text/plain")) + ret.append(data.toString().toUtf8()); + 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> ®istry, 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 + // 10.6 does automatic synthesis to and from PICT to standard image types (like TIFF), + // so don't bother doing it ourselves, especially since it's not available in 64-bit. + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) + new QMacPasteboardMimePict; +#endif + new QMacPasteboardMimeUnicodeText; + new QMacPasteboardMimePlainText; + new QMacPasteboardMimeHTMLText; + new QMacPasteboardMimeFileUri; + new QMacPasteboardMimeUrl; + new QMacPasteboardMimeTypeName; + new QMacPasteboardMimeVCard; + //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/platforms/mac/qmultitouch_mac.mm b/src/gui/platforms/mac/qmultitouch_mac.mm new file mode 100644 index 0000000000..d9e845a01c --- /dev/null +++ b/src/gui/platforms/mac/qmultitouch_mac.mm @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qmultitouch_mac_p.h> +#include <qcursor.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA + +QHash<qint64, QCocoaTouch*> QCocoaTouch::_currentTouches; +QPointF QCocoaTouch::_screenReferencePos; +QPointF QCocoaTouch::_trackpadReferencePos; +int QCocoaTouch::_idAssignmentCount = 0; +int QCocoaTouch::_touchCount = 0; +bool QCocoaTouch::_updateInternalStateOnly = true; + +QCocoaTouch::QCocoaTouch(NSTouch *nstouch) +{ + if (_currentTouches.size() == 0) + _idAssignmentCount = 0; + + _touchPoint.setId(_idAssignmentCount++); + _touchPoint.setPressure(1.0); + _identity = qint64([nstouch identity]); + _currentTouches.insert(_identity, this); + updateTouchData(nstouch, NSTouchPhaseBegan); +} + +QCocoaTouch::~QCocoaTouch() +{ + _currentTouches.remove(_identity); +} + +void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) +{ + if (_touchCount == 1) + _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary); + else + _touchPoint.setState(toTouchPointState(phase)); + + // From the normalized position on the trackpad, calculate + // where on screen the touchpoint should be according to the + // reference position: + NSPoint npos = [nstouch normalizedPosition]; + QPointF qnpos = QPointF(npos.x, 1 - npos.y); + _touchPoint.setNormalizedPos(qnpos); + + if (_touchPoint.id() == 0 && phase == NSTouchPhaseBegan) { + _trackpadReferencePos = qnpos; + _screenReferencePos = QCursor::pos(); + } + + NSSize dsize = [nstouch deviceSize]; + float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width; + float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height; + QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY); + _touchPoint.setScreenPos(_screenReferencePos - relativePos); +} + +QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch) +{ + qint64 identity = qint64([nstouch identity]); + if (_currentTouches.contains(identity)) + return _currentTouches.value(identity); + return 0; +} + +Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState) +{ + Qt::TouchPointState qtState = Qt::TouchPointReleased; + switch (nsState) { + case NSTouchPhaseBegan: + qtState = Qt::TouchPointPressed; + break; + case NSTouchPhaseMoved: + qtState = Qt::TouchPointMoved; + break; + case NSTouchPhaseStationary: + qtState = Qt::TouchPointStationary; + break; + case NSTouchPhaseEnded: + case NSTouchPhaseCancelled: + qtState = Qt::TouchPointReleased; + break; + default: + break; + } + return qtState; +} + +QList<QTouchEvent::TouchPoint> +QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) +{ + QMap<int, QTouchEvent::TouchPoint> touchPoints; + NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil]; + NSSet *active = [event + touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary + inView:nil]; + _touchCount = [active count]; + + // First: remove touches that were ended by the user. If we are + // currently not accepting single touches, a corresponding 'begin' + // has never been send to the app for these events. + // So should therefore not send the following removes either. + + for (int i=0; i<int([ended count]); ++i) { + NSTouch *touch = [[ended allObjects] objectAtIndex:i]; + QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch); + if (qcocoaTouch) { + qcocoaTouch->updateTouchData(touch, [touch phase]); + if (!_updateInternalStateOnly) + touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); + delete qcocoaTouch; + } + } + + bool wasUpdateInternalStateOnly = _updateInternalStateOnly; + _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2; + + // Next: update, or create, existing touches. + // We always keep track of all touch points, even + // when not accepting single touches. + + for (int i=0; i<int([active count]); ++i) { + NSTouch *touch = [[active allObjects] objectAtIndex:i]; + QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch); + if (!qcocoaTouch) + qcocoaTouch = new QCocoaTouch(touch); + else + qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]); + if (!_updateInternalStateOnly) + touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); + } + + // Next: sadly, we need to check that our touch hash is in + // sync with cocoa. This is typically not the case after a system + // gesture happend (like a four-finger-swipe to show expose). + + if (_touchCount != _currentTouches.size()) { + // Remove all instances, and basically start from scratch: + touchPoints.clear(); + foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) { + if (!_updateInternalStateOnly) { + qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); + touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); + } + delete qcocoaTouch; + } + _currentTouches.clear(); + _updateInternalStateOnly = !acceptSingleTouch; + return touchPoints.values(); + } + + // Finally: If this call _started_ to reject single + // touches, we need to fake a relase for the remaining + // touch now (and refake a begin for it later, if needed). + + if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) { + QCocoaTouch *qcocoaTouch = _currentTouches.values().first(); + qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); + touchPoints.insert(qcocoaTouch->_touchPoint.id(), qcocoaTouch->_touchPoint); + // Since this last touch also will end up beeing the first + // touch (if the user adds a second finger without lifting + // the first), we promote it to be the primary touch: + qcocoaTouch->_touchPoint.setId(0); + _idAssignmentCount = 1; + } + + return touchPoints.values(); +} + +#endif + +QT_END_NAMESPACE + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + diff --git a/src/gui/platforms/mac/qmultitouch_mac_p.h b/src/gui/platforms/mac/qmultitouch_mac_p.h new file mode 100644 index 0000000000..16be930d0a --- /dev/null +++ b/src/gui/platforms/mac/qmultitouch_mac_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 QMULTITOUCH_MAC_P_H +#define QMULTITOUCH_MAC_P_H + +#ifdef QT_MAC_USE_COCOA +#import <Cocoa/Cocoa.h> +#endif + +#include <qevent.h> +#include <qhash.h> +#include <QtCore> + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA + +class QCocoaTouch +{ + public: + static QList<QTouchEvent::TouchPoint> getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch); + static void setMouseInDraggingState(bool inDraggingState); + + private: + static QHash<qint64, QCocoaTouch*> _currentTouches; + static QPointF _screenReferencePos; + static QPointF _trackpadReferencePos; + static int _idAssignmentCount; + static int _touchCount; + static bool _updateInternalStateOnly; + + QTouchEvent::TouchPoint _touchPoint; + qint64 _identity; + + QCocoaTouch(NSTouch *nstouch); + ~QCocoaTouch(); + + void updateTouchData(NSTouch *nstouch, NSTouchPhase phase); + static QCocoaTouch *findQCocoaTouch(NSTouch *nstouch); + static Qt::TouchPointState toTouchPointState(NSTouchPhase nsState); +}; + +#endif // QT_MAC_USE_COCOA + +QT_END_NAMESPACE + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + +#endif // QMULTITOUCH_MAC_P_H + diff --git a/src/gui/platforms/mac/qnsframeview_mac_p.h b/src/gui/platforms/mac/qnsframeview_mac_p.h new file mode 100644 index 0000000000..6ec3f64efa --- /dev/null +++ b/src/gui/platforms/mac/qnsframeview_mac_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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/platforms/mac/qnsthemeframe_mac_p.h b/src/gui/platforms/mac/qnsthemeframe_mac_p.h new file mode 100644 index 0000000000..2cb4916c06 --- /dev/null +++ b/src/gui/platforms/mac/qnsthemeframe_mac_p.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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; // collides with hittest in qcocoasharedwindowmethods_mac_p.h +- (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/platforms/mac/qnstitledframe_mac_p.h b/src/gui/platforms/mac/qnstitledframe_mac_p.h new file mode 100644 index 0000000000..4eb5332194 --- /dev/null +++ b/src/gui/platforms/mac/qnstitledframe_mac_p.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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/platforms/mac/qpaintdevice_mac.cpp b/src/gui/platforms/mac/qpaintdevice_mac.cpp new file mode 100644 index 0000000000..245408a0b0 --- /dev/null +++ b/src/gui/platforms/mac/qpaintdevice_mac.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 + *****************************************************************************/ + +/*! \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); + uint flags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + flags |= kCGBitmapByteOrder32Host; +#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.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/platforms/mac/qpaintengine_mac.cpp b/src/gui/platforms/mac/qpaintengine_mac.cpp new file mode 100644 index 0000000000..c6d061dea8 --- /dev/null +++ b/src/gui/platforms/mac/qpaintengine_mac.cpp @@ -0,0 +1,1751 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <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/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_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 || + devType == QInternal::Image)) { + + extern CGColorSpaceRef qt_mac_colorSpaceForDeviceType(const QPaintDevice *paintDevice); + CGColorSpaceRef colorspace = qt_mac_colorSpaceForDeviceType(pe->paintDevice()); + uint flags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + flags |= kCGBitmapByteOrder32Host; +#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(); + QTransform native = p->deviceTransform(); + QTransform logical = p->combinedTransform(); + + if (p->hasClipping()) { + QRegion r = p->clipRegion(); + r.translate(native.dx(), native.dy()); + if (clip.isEmpty()) + clip = r; + else + clip &= r; + } + qt_mac_clip_cg(context, clip, 0); + + CGContextTranslateCTM(context, native.dx(), native.dy()); + } + } else { + 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() +{ + return QPaintEngine::PaintEngineFeatures(QPaintEngine::AllFeatures & ~QPaintEngine::PaintOutsidePaintEvent + & ~QPaintEngine::PerspectiveTransform + & ~QPaintEngine::ConicalGradientFill + & ~QPaintEngine::LinearGradientFill + & ~QPaintEngine::RadialGradientFill + & ~QPaintEngine::BrushStroke); +} + +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(r.x(), r.y(), r.width(), 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) { + QCFType<CGImageRef> img = pm.toMacCGImageRef(); + image = CGImageCreateWithImageInRect(img, CGRectMake(qRound(sr.x()), qRound(sr.y()), qRound(sr.width()), qRound(sr.height()))); + } 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); + + uint cgflags = kCGImageAlphaNone; + 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 defined(kCGBitmapByteOrder32Host) //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#endif + QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(image, + static_cast<const QImage *>(image)->bits(), + image->byteCount(), + 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) + cgimage = CGImageCreateWithImageInRect(cgimage, CGRectMake(sr.x(), sr.y(), + sr.width(), sr.height())); + 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); + static const CGFloat ScaleFactor = qt_mac_get_scalefactor(); + if (ScaleFactor > 1.) { + CGContextSetInterpolationQuality(d->hd, kCGInterpolationHigh); + } else { + CGContextSetInterpolationQuality(d->hd, (hints & QPainter::SmoothPixmapTransform) ? + kCGInterpolationHigh : kCGInterpolationNone); + } + bool textAntialiasing = (hints & QPainter::TextAntialiasing) == QPainter::TextAntialiasing; + if (!textAntialiasing || d->disabledSmoothFonts) { + d->disabledSmoothFonts = !textAntialiasing; + CGContextSetShouldSmoothFonts(d->hd, 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(4); + linedashes.append(2); + } else if(pen.style() == Qt::DotLine) { + linedashes.append(1); + linedashes.append(2); + } else if(pen.style() == Qt::DashDotLine) { + linedashes.append(4); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + } else if(pen.style() == Qt::DashDotDotLine) { + linedashes.append(4); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + linedashes.append(1); + linedashes.append(2); + } + 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 *>(¤t.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/platforms/mac/qpaintengine_mac_p.h b/src/gui/platforms/mac/qpaintengine_mac_p.h new file mode 100644 index 0000000000..3e71d6ca6b --- /dev/null +++ b/src/gui/platforms/mac/qpaintengine_mac_p.h @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 "private/qfont_p.h" +#include "QtCore/qhash.h" + +typedef struct CGColorSpace *CGColorSpaceRef; +QT_BEGIN_NAMESPACE + +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 ®ion, 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 *); + friend void qt_color_profile_changed(CFNotificationCenterRef center, void *, + CFStringRef , const void *, CFDictionaryRef); + 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), disabledSmoothFonts(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; + bool disabledSmoothFonts; + 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/platforms/mac/qpixmap_mac.cpp b/src/gui/platforms/mac/qpixmap_mac.cpp new file mode 100644 index 0000000000..72e2aa6e04 --- /dev/null +++ b/src/gui/platforms/mac/qpixmap_mac.cpp @@ -0,0 +1,1195 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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> +#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.data())->pixels; +} + +Q_GUI_EXPORT int qt_mac_pixmap_get_bytes_per_line(const QPixmap *pix) +{ + return static_cast<QMacPixmapData*>(pix->data.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); + + uint cgflags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#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), has_alpha(0), has_mask(0), + uninit(true), pixels(0), pixelsSize(0), pixelsToFree(0), + bytesPerRow(0), cg_data(0), cg_dataBeingReleased(0), cg_mask(0), + pengine(0) +{ +} + +QPixmapData *QMacPixmapData::createCompatiblePixmapData() const +{ + return new QMacPixmapData(pixelType()); +} + +#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; + is_null = (w <= 0 || h <= 0); + d = (pixelType() == BitmapType ? 1 : 32); + bool make_null = w <= 0 || h <= 0; // create null pixmap + if (make_null || d == 0) { + w = 0; + h = 0; + is_null = true; + 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(); + is_null = (w <= 0 || h <= 0); + 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.colorCount() == 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) = 0xFF000000; + else + *(drow+x) = 0xFFFFFFFF; + } + break; + } + case QImage::Format_Indexed8: { + int numColors = image.numColors(); + if (numColors > 0) { + for (int x = 0; x < w; ++x) { + int index = *(srow + x); + *(drow+x) = PREMUL(image.color(qMin(index, numColors))); + } + } + } 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.colorCount(); 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->colorCount();loopc++) { + if(qi->color(loopc)==mycol) + return loopc; + } + qi->setColorCount(qi->colorCount()+1); + qi->setColor(qi->colorCount(),mycol); + return qi->colorCount(); +} + +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.setColorCount(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; + } + } + + // If we had an alpha channel from before, don't + // switch it off. Only go from no alpha to alpha: + if (fillColor.alpha() != 255) + macSetHasAlpha(true); +} + +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.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.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); + 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; + macReleaseCGImageRef(); +} + +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); + uint cgflags = kCGImageAlphaPremultipliedFirst; +#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version + cgflags |= kCGBitmapByteOrder32Host; +#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, pixelsSize); + pixels = base_pixels; + pixelsSize = numBytes; +} + +#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 +} + +// 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); + QCFType<CGImageRef> image = CGBitmapContextCreateImage(bitmap); + 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. + \warning As of Qt 4.6, this function \e{always} returns zero. +*/ + +Qt::HANDLE QPixmap::macQDHandle() const +{ + return 0; +} + +/*! \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. + \warning As of Qt 4.6, this function \e{always} returns zero. +*/ + +Qt::HANDLE QPixmap::macQDAlphaHandle() const +{ + 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 (isNull()) + return 0; + + if (data->classId() == QPixmapData::MacClass) { + QMacPixmapData *d = static_cast<QMacPixmapData *>(data.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.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.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; + + //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 + +/*! \internal */ +QPaintEngine* QMacPixmapData::paintEngine() const +{ + if (!pengine) { + QMacPixmapData *that = const_cast<QMacPixmapData*>(this); + that->pengine = new QCoreGraphicsPaintEngine(); + } + 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; +} + +bool QMacPixmapData::scroll(int dx, int dy, const QRect &rect) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); + Q_UNUSED(rect); + return false; +} + +/*! + \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); + ret.fill(Qt::transparent); + 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/platforms/mac/qpixmap_mac_p.h b/src/gui/platforms/mac/qpixmap_mac_p.h new file mode 100644 index 0000000000..307e38aceb --- /dev/null +++ b/src/gui/platforms/mac/qpixmap_mac_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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(); + + QPixmapData *createCompatiblePixmapData() const; + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, 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: + + 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; + uint pixelsSize; + quint32 *pixelsToFree; + uint bytesPerRow; + QRectF cg_mask_rect; + CGImageRef cg_data, cg_dataBeingReleased, cg_mask; + 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/platforms/mac/qprintengine_mac.mm b/src/gui/platforms/mac/qprintengine_mac.mm new file mode 100644 index 0000000000..1dce30301f --- /dev/null +++ b/src/gui/platforms/mac/qprintengine_mac.mm @@ -0,0 +1,911 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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); + +QMacPrintEngine::QMacPrintEngine(QPrinter::PrinterMode mode) : QPaintEngine(*(new QMacPrintEnginePrivate)) +{ + Q_D(QMacPrintEngine); + d->mode = mode; + d->initialize(); +} + +bool QMacPrintEngine::begin(QPaintDevice *dev) +{ + Q_D(QMacPrintEngine); + + Q_ASSERT(dev && dev->devType() == QInternal::Printer); + if (!static_cast<QPrinter *>(dev)->isValid()) + return false; + + 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 + status = d->shouldSuppressStatus() ? PMSessionBeginCGDocumentNoDialog(d->session, d->settings, d->format) + : PMSessionBeginCGDocument(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) { + // We dont need the paint engine to call restoreGraphicsState() + static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->stackCount = 0; + static_cast<QCoreGraphicsPaintEngine*>(d->paintEngine)->d_func()->hd = 0; + } + d->paintEngine->end(); + if (d->state != QPrinter::Idle) + d->releaseSession(); + 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 (!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()); + } +} + +void QMacPrintEnginePrivate::releaseSession() +{ +#ifndef QT_MAC_USE_COCOA + if (shouldSuppressStatus()) { + PMSessionEndPageNoDialog(session); + PMSessionEndDocumentNoDialog(session); + } else { + PMSessionEndPage(session); + PMSessionEndDocument(session); + } + PMRelease(session); +#else + PMSessionEndPageNoDialog(session); + PMSessionEndDocumentNoDialog(session); + [printInfo release]; +#endif + printInfo = 0; + session = 0; +} + +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; + err = PMSessionGetCGGraphicsContext(session, &cgContext); + 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_CopyCount: // fallthrough + 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: { + bool printerNameSet = false; + 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); + printerNameSet = true; + break; + } + } + } + if (status != noErr) + qWarning("QMacPrintEngine::setPrinterName: Error setting printer: %ld", long(status)); + if (!printerNameSet) { + qWarning("QMacPrintEngine::setPrinterName: Failed to set printer named '%s'.", qPrintable(value.toString())); + d->releaseSession(); + d->state = QPrinter::Idle; + } + 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_CopyCount: { + UInt32 copies = 1; + PMGetCopies(d->settings, &copies); + ret = (uint) copies; + break; + } + case PPK_SupportsMultipleCopies: + ret = true; + 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, ¯ect) == 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, ¯ect) == 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: { + PMPrinter printer; + OSStatus status = PMSessionGetCurrentPrinter(d->session, &printer); + if (status != noErr) + qWarning("QMacPrintEngine::printerName: Failed getting current PMPrinter: %ld", long(status)); + if (printer) + ret = QCFString::toQString(PMPrinterGetName(printer)); + 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/platforms/mac/qprintengine_mac_p.h b/src/gui/platforms/mac/qprintengine_mac_p.h new file mode 100644 index 0000000000..511705d26f --- /dev/null +++ b/src/gui/platforms/mac/qprintengine_mac_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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(); + void releaseSession(); + 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/platforms/mac/qprinterinfo_mac.cpp b/src/gui/platforms/mac/qprinterinfo_mac.cpp new file mode 100644 index 0000000000..b24ab70267 --- /dev/null +++ b/src/gui/platforms/mac/qprinterinfo_mac.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qprinterinfo.h" +#include "qprinterinfo_p.h" + +#include "private/qt_mac_p.h" + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +extern QPrinter::PaperSize qSizeFTopaperSize(const QSizeF &size); + +QList<QPrinterInfo> QPrinterInfo::availablePrinters() +{ + QList<QPrinterInfo> printers; + + QCFType<CFArrayRef> array; + if (PMServerCreatePrinterList(kPMServerLocal, &array) == noErr) { + CFIndex count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPrinter printer = static_cast<PMPrinter>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + QString printerName = QCFString::toQString(PMPrinterGetName(printer)); + + QPrinterInfo printerInfo(printerName); + if (PMPrinterIsDefault(printer)) + printerInfo.d_ptr->isDefault = true; + printers.append(printerInfo); + } + } + + return printers; +} + +QPrinterInfo QPrinterInfo::defaultPrinter() +{ + QList<QPrinterInfo> printers = availablePrinters(); + foreach (const QPrinterInfo &printerInfo, printers) { + if (printerInfo.isDefault()) + return printerInfo; + } + + return printers.value(0); +} + +QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const +{ + const Q_D(QPrinterInfo); + + QList<QPrinter::PaperSize> paperSizes; + if (isNull()) + return paperSizes; + + PMPrinter cfPrn = PMPrinterCreateFromPrinterID(QCFString::toCFStringRef(d->name)); + if (!cfPrn) + return paperSizes; + + CFArrayRef array; + if (PMPrinterGetPaperList(cfPrn, &array) != noErr) { + PMRelease(cfPrn); + return paperSizes; + } + + int count = CFArrayGetCount(array); + for (int i = 0; i < count; ++i) { + PMPaper paper = static_cast<PMPaper>(const_cast<void *>(CFArrayGetValueAtIndex(array, i))); + double width, height; + if (PMPaperGetWidth(paper, &width) == noErr && PMPaperGetHeight(paper, &height) == noErr) { + QSizeF size(width * 0.3527, height * 0.3527); + paperSizes.append(qSizeFTopaperSize(size)); + } + } + + PMRelease(cfPrn); + + return paperSizes; +} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qrawfont_mac.cpp b/src/gui/platforms/mac/qrawfont_mac.cpp new file mode 100644 index 0000000000..1ed4185a5d --- /dev/null +++ b/src/gui/platforms/mac/qrawfont_mac.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_RAWFONT) + +#include "qrawfont_p.h" +#include "qfontengine_coretext_p.h" + +QT_BEGIN_NAMESPACE + +void QRawFontPrivate::platformCleanUp() +{ +} + +extern int qt_defaultDpi(); + +void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, + int pixelSize, + QFont::HintingPreference hintingPreference) +{ + // Mac OS X ignores it + Q_UNUSED(hintingPreference); + + QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(NULL, + fontData.constData(), fontData.size(), NULL); + + CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider); + + if (cgFont == NULL) { + qWarning("QRawFont::platformLoadFromData: CGFontCreateWithDataProvider failed"); + } else { + QFontDef def; + def.pixelSize = pixelSize; + def.pointSize = pixelSize * 72.0 / qt_defaultDpi(); + fontEngine = new QCoreTextFontEngine(cgFont, def); + CFRelease(cgFont); + fontEngine->ref.ref(); + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/platforms/mac/qregion_mac.cpp b/src/gui/platforms/mac/qregion_mac.cpp new file mode 100644 index 0000000000..50fd783df4 --- /dev/null +++ b/src/gui/platforms/mac/qregion_mac.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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; +} + +/*! + \internal + Create's a RegionHandle, it's the caller's responsibility to release. + Returns 0 if the QRegion overflows. +*/ +RgnHandle QRegion::toQDRgnForUpdate_sys() 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--) { + + // detect overflow. Tested for use with HIViewSetNeedsDisplayInRegion + // in QWidgetPrivate::update_sys(). + enum { HIViewSetNeedsDisplayInRegionOverflow = 10000 }; // empirically determined conservative value + if (qt_r->right() > HIViewSetNeedsDisplayInRegionOverflow || qt_r->bottom() > HIViewSetNeedsDisplayInRegionOverflow) { + qt_mac_dispose_rgn(tmp_rgn); + qt_mac_dispose_rgn(rgnHandle); + return 0; + } + + 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/platforms/mac/qsound_mac.mm b/src/gui/platforms/mac/qsound_mac.mm new file mode 100644 index 0000000000..5a9af135b0 --- /dev/null +++ b/src/gui/platforms/mac/qsound_mac.mm @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <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 + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 +@protocol NSSoundDelegate <NSObject> +-(void)sound:(NSSound *)sound didFinishPlaying:(BOOL)aBool; +@end +#endif + +QT_USE_NAMESPACE + +@interface QT_MANGLE_NAMESPACE(QMacSoundDelegate) : NSObject<NSSoundDelegate> { + QSound *qSound; // may be null. + QAuServerMac* server; +} +-(id)initWithQSound:(QSound*)sound:(QAuServerMac*)server; +@end + +@implementation QT_MANGLE_NAMESPACE(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]; + QT_MANGLE_NAMESPACE(QMacSoundDelegate) * const delegate = [[QT_MANGLE_NAMESPACE(QMacSoundDelegate) alloc] initWithQSound:qSound:this]; + [nsSound setDelegate:delegate]; + [nsFileName release]; + 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/platforms/mac/qt_cocoa_helpers_mac.mm b/src/gui/platforms/mac/qt_cocoa_helpers_mac.mm new file mode 100644 index 0000000000..32123ee682 --- /dev/null +++ b/src/gui/platforms/mac/qt_cocoa_helpers_mac.mm @@ -0,0 +1,1824 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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 <qaction.h> +#include <qwidget.h> +#include <qdesktopwidget.h> +#include <qevent.h> +#include <qpixmapcache.h> +#include <qvarlengtharray.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/qcocoaapplication_mac_p.h> +#include <private/qcocoawindow_mac_p.h> +#include <private/qcocoaview_mac_p.h> +#include <private/qkeymapper_p.h> +#include <private/qwidget_p.h> +#include <private/qcocoawindow_mac_p.h> + +QT_BEGIN_NAMESPACE + +#ifdef QT_MAC_USE_COCOA +// Cmd + left mousebutton should produce a right button +// press (mainly for mac users with one-button mice): +static bool qt_leftButtonIsRightButton = false; +#endif + +Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader); + +QMacWindowFader::QMacWindowFader() + : m_duration(0.250) +{ +} + +QMacWindowFader *QMacWindowFader::currentFader() +{ + return macwindowFader(); +} + +void QMacWindowFader::registerWindowToFade(QWidget *window) +{ + m_windowsToFade.append(window); +} + +void QMacWindowFader::performFade() +{ + const QWidgetList myWidgetsToFade = m_windowsToFade; + const int widgetCount = myWidgetsToFade.count(); +#if QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + [NSAnimationContext beginGrouping]; + [[NSAnimationContext currentContext] setDuration:NSTimeInterval(m_duration)]; +#endif + + for (int i = 0; i < widgetCount; ++i) { + QWidget *widget = m_windowsToFade.at(i); + OSWindowRef window = qt_mac_window_for(widget); +#if QT_MAC_USE_COCOA + [[window animator] setAlphaValue:0.0]; + QTimer::singleShot(qRound(m_duration * 1000), widget, SLOT(hide())); +#else + TransitionWindowOptions options = {0, m_duration, 0, 0}; + TransitionWindowWithOptions(window, kWindowFadeTransitionEffect, kWindowHideTransitionAction, + 0, 1, &options); +#endif + } +#if QT_MAC_USE_COCOA + [NSAnimationContext endGrouping]; +#endif + m_duration = 0.250; + m_windowsToFade.clear(); +} + +extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp; +extern QWidget * mac_mouse_grabber; +extern QWidget *qt_button_down; //qapplication_mac.cpp +extern QPointer<QWidget> qt_last_mouse_receiver; +extern OSViewRef qt_mac_effectiveview_for(const QWidget *w); +extern void qt_mac_updateCursorWithWidgetUnderMouse(QWidget *widgetUnderMouse); // qcursor_mac.mm + +void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) +{ +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; +#endif + OSWindowRef wnd = static_cast<OSWindowRef>(window); + if (wnd) { + QWidget *widget; +#if QT_MAC_USE_COCOA + widget = [wnd QT_MANGLE_NAMESPACE(qt_qwidget)]; +#else + const UInt32 kWidgetCreatorQt = kEventClassQt; + enum { + kWidgetPropertyQWidget = 'QWId' //QWidget * + }; + if (GetWindowProperty(static_cast<WindowRef>(window), kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(widget), 0, &widget) != noErr) + widget = 0; +#endif + if (widget) { + QMacWindowFader::currentFader()->setFadeDuration(durationSeconds); + QMacWindowFader::currentFader()->registerWindowToFade(widget); + QMacWindowFader::currentFader()->performFade(); + } + } +} +struct dndenum_mapper +{ + NSDragOperation mac_code; + Qt::DropAction qt_code; + bool Qt2Mac; +}; + +#if defined(QT_MAC_USE_COCOA) && defined(__OBJC__) + +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 } +}; + +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; +} + +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; +} + +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; +} + +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; +} + +Q_GLOBAL_STATIC(DnDParams, currentDnDParameters); +DnDParams *macCurrentDnDParameters() +{ + return currentDnDParameters(); +} +#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, ¤tAttributes); + 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 + if (NSToolbar *toolbar = [wnd toolbar]) { + QMacCocoaAutoReleasePool pool; + if (show != [toolbar isVisible]) { + [toolbar setVisible:show]; + } else { + // The toolbar may be in sync, but we are not, update our framestrut. + qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); + } + } +#else + qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); + 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 macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ) +{ + OSWindowRef wnd = static_cast<OSWindowRef>(window); +#if QT_MAC_USE_COCOA + if (NSToolbar *toolbar = [wnd 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->effectiveWinId()) 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; +} + +void macSendToolbarChangeEvent(QWidget *widget) +{ + QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey)); + qt_sendSpontaneousEvent(widget, &ev); +} + +Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash) +QMacTabletHash *qt_mac_tablet_hash() +{ + return tablet_hash(); +} + +#ifdef QT_MAC_USE_COCOA + +// Clears the QWidget pointer that each QCocoaView holds. +void qt_mac_clearCocoaViewQWidgetPointers(QWidget *widget) +{ + QT_MANGLE_NAMESPACE(QCocoaView) *cocoaView = reinterpret_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(qt_mac_nativeview_for(widget)); + if (cocoaView && [cocoaView respondsToSelector:@selector(qt_qwidget)]) { + [cocoaView qt_clearQWidget]; + } +} + +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); +} + +// 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; +} + +bool operator<(const Qt::Key &key, const KeyPair &entry) +{ + return key < entry.qtKey; +} + +bool operator<(const KeyPair &entry, const Qt::Key &key) +{ + return entry.qtKey < key; +} + +static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2) +{ + return entry1.qtKey < entry2.qtKey; +} + +static const int NumEntries = 59; +static const KeyPair entries[NumEntries] = { + { NSEnterCharacter, Qt::Key_Enter }, + { NSBackspaceCharacter, Qt::Key_Backspace }, + { NSTabCharacter, Qt::Key_Tab }, + { NSNewlineCharacter, Qt::Key_Return }, + { NSCarriageReturnCharacter, Qt::Key_Return }, + { NSBackTabCharacter, Qt::Key_Backtab }, + { kEscapeCharCode, Qt::Key_Escape }, + // Cocoa sends us delete when pressing backspace! + // (NB when we reverse this list in qtKey2CocoaKey, there + // will be two indices of Qt::Key_Backspace. But is seems to work + // ok for menu shortcuts (which uses that function): + { 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; + +QChar qtKey2CocoaKey(Qt::Key key) +{ + // The first time this function is called, create a reverse + // looup table sorted on Qt Key rather than Cocoa key: + static QVector<KeyPair> rev_entries(NumEntries); + static bool mustInit = true; + if (mustInit){ + mustInit = false; + for (int i=0; i<NumEntries; ++i) + rev_entries[i] = entries[i]; + qSort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan); + } + const QVector<KeyPair>::iterator i + = qBinaryFind(rev_entries.begin(), rev_entries.end(), key); + if (i == rev_entries.end()) + return QChar(); + return i->cocoaKey; +} + +#ifdef QT_MAC_USE_COCOA +static Qt::Key cocoaKey2QtKey(QChar keyCode) +{ + 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; +} + +NSString *qt_mac_removePrivateUnicode(NSString* string) +{ + int len = [string length]; + if (len) { + QVarLengthArray <unichar, 10> characters(len); + bool changed = false; + for (int i = 0; i<len; i++) { + characters[i] = [string characterAtIndex:i]; + // check if they belong to key codes in private unicode range + // currently we need to handle only the NSDeleteFunctionKey + if (characters[i] == NSDeleteFunctionKey) { + characters[i] = NSDeleteCharacter; + changed = true; + } + } + if (changed) + return [NSString stringWithCharacters:characters.data() length:len]; + } + return string; +} + +Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations) +{ + Qt::KeyboardModifiers qtMods =Qt::NoModifier; + if (dragOperations & NSDragOperationLink) + qtMods |= Qt::MetaModifier; + if (dragOperations & NSDragOperationGeneric) + qtMods |= Qt::ControlModifier; + if (dragOperations & NSDragOperationCopy) + qtMods |= Qt::AltModifier; + 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! + bool ignoreText = false; + Qt::Key qtKey = Qt::Key_unknown; + if (keyLength == 1) { + QChar ch([keyChars characterAtIndex:0]); + if (ch.isLower()) + ch = ch.toUpper(); + qtKey = cocoaKey2QtKey(ch); + // Do not set the text for Function-Key Unicodes characters (0xF700–0xF8FF). + ignoreText = (ch.unicode() >= 0xF700 && ch.unicode() <= 0xF8FF); + } + 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 (!ignoreText && !(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]); + return qt_sendSpontaneousEvent(widgetToGetEvent, &ke) && ke.isAccepted(); +} +#endif + +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; +} + +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); + unsigned int info = 0; + + if ([event type] == NSKeyDown) { + NSString *characters = [event characters]; + if ([characters length]) { + unichar value = [characters characterAtIndex:0]; + qt_keymapper_private()->updateKeyMap(0, key_event, (void *)&value); + info = value; + } + } + + if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event)) + return true; + + if (mustUseCocoaKeyEvent()) + return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent); + + bool consumed = qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &info, true); + return consumed && (info != 0); +#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())); +} + +#if QT_MAC_USE_COCOA && __OBJC__ + +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event) +{ + QWidget *widgetToGetEvent = [window QT_MANGLE_NAMESPACE(qt_qwidget)]; + if (widgetToGetEvent == 0) + return; + + 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]; + qglobalPoint = QPoint(flipPoint(globalPoint).toPoint()); + QWidget *w = widgetToGetEvent->childAt(widgetToGetEvent->mapFromGlobal(qglobalPoint)); + // check that the mouse pointer is on the non-client area and + // there are not widgets in it. + if (fakeNCEvents || (!NSMouseInRect(globalPoint, contentRect, NO) && !w)) { + 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) + // or if someone already got an explicit or implicit grab + if (mac_mouse_grabber || qt_button_down || + (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; + qt_leftButtonIsRightButton = true; + } + } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) { + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { + button = Qt::RightButton; + qt_leftButtonIsRightButton = false; + } + } + + Qt::MouseButtons buttons = 0; + { + UInt32 mac_buttons; + if (GetEventParameter((EventRef)[event eventRef], kEventParamMouseChord, typeUInt32, 0, + sizeof(mac_buttons), 0, &mac_buttons) == noErr) + buttons = qt_mac_get_buttons(mac_buttons); + } + + QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods); + qt_sendSpontaneousEvent(widgetToGetEvent, &qme); + + // We don't need to set the implicit grab widget here because we won't + // reach this point if then event type is Press over a Qt widget. + // However we might need to unset it if the event is Release. + if (eventType == QEvent::MouseButtonRelease) + qt_button_down = 0; +} + +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent) +{ + if (QWidget *popup = QApplication::activePopupWidget()) { + QWidget *focusInPopup = popup->focusWidget(); + return focusInPopup ? focusInPopup : popup; + } + + QWidget *widgetToGetKey = qApp->focusWidget(); + if (!widgetToGetKey) + widgetToGetKey = widgetThatReceivedEvent; + + return widgetToGetKey; +} + +// This function will find the widget that should receive the +// mouse event. Because of explicit/implicit mouse grabs, popups, +// etc, this might not end up being the same as the widget under +// the mouse (which is more interresting when handling enter/leave +// events +QWidget *qt_mac_getTargetForMouseEvent( + // You can call this function without providing an event. + NSEvent *event, + QEvent::Type eventType, + QPoint &returnLocalPoint, + QPoint &returnGlobalPoint, + QWidget *nativeWidget, + QWidget **returnWidgetUnderMouse) +{ + Q_UNUSED(event); + NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation]; + returnGlobalPoint = flipPoint(nsglobalpoint).toPoint(); + QWidget *mouseGrabber = QWidget::mouseGrabber(); + bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); + QWidget *popup = QApplication::activePopupWidget(); + + // Resolve the widget under the mouse: + QWidget *widgetUnderMouse = 0; + if (popup || qt_button_down || !nativeWidget || !nativeWidget->isVisible()) { + // Using QApplication::widgetAt for finding the widget under the mouse + // is most safe, since it ignores cocoas own mouse down redirections (which + // we need to be prepared for when using nativeWidget as starting point). + // (the only exception is for QMacNativeWidget, where QApplication::widgetAt fails). + // But it is also slower (I guess), so we try to avoid it and use nativeWidget if we can: + widgetUnderMouse = QApplication::widgetAt(returnGlobalPoint); + } + + if (!widgetUnderMouse && nativeWidget) { + // Entering here should be the common case. We + // also handle the QMacNativeWidget fallback case. + QPoint p = nativeWidget->mapFromGlobal(returnGlobalPoint); + widgetUnderMouse = nativeWidget->childAt(p); + if (!widgetUnderMouse && nativeWidget->rect().contains(p)) + widgetUnderMouse = nativeWidget; + } + + if (widgetUnderMouse) { + // Check if widgetUnderMouse is blocked by a modal + // window, or the mouse if over the frame strut: + if (widgetUnderMouse == qt_button_down) { + // Small optimization to avoid an extra call to isBlockedByModal: + if (buttonDownNotBlockedByModal == false) + widgetUnderMouse = 0; + } else if (QApplicationPrivate::isBlockedByModal(widgetUnderMouse)) { + widgetUnderMouse = 0; + } + + if (widgetUnderMouse && widgetUnderMouse->isWindow()) { + // Exclude the titlebar (and frame strut) when finding widget under mouse: + QPoint p = widgetUnderMouse->mapFromGlobal(returnGlobalPoint); + if (!widgetUnderMouse->rect().contains(p)) + widgetUnderMouse = 0; + } + } + if (returnWidgetUnderMouse) + *returnWidgetUnderMouse = widgetUnderMouse; + + // Resolve the target for the mouse event. Default will be + // widgetUnderMouse, except if there is a grab (popup/mouse/button-down): + if (popup && !mouseGrabber) { + // We special case handling of popups, since they have an implicitt mouse grab. + QWidget *candidate = buttonDownNotBlockedByModal ? qt_button_down : widgetUnderMouse; + if (!popup->isAncestorOf(candidate)) { + // INVARIANT: we have a popup, but the candidate is not + // in it. But the popup will grab the mouse anyway, + // except if the user scrolls: + if (eventType == QEvent::Wheel) + return 0; + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else if (popup == candidate) { + // INVARIANT: The candidate is the popup itself, and not a child: + returnLocalPoint = popup->mapFromGlobal(returnGlobalPoint); + return popup; + } else { + // INVARIANT: The candidate is a child inside the popup: + returnLocalPoint = candidate->mapFromGlobal(returnGlobalPoint); + return candidate; + } + } + + QWidget *target = mouseGrabber; + if (!target && buttonDownNotBlockedByModal) + target = qt_button_down; + if (!target) + target = widgetUnderMouse; + if (!target) + return 0; + + returnLocalPoint = target->mapFromGlobal(returnGlobalPoint); + return target; +} + +QPointer<QWidget> qt_last_native_mouse_receiver = 0; + +static inline void qt_mac_checkEnterLeaveForNativeWidgets(QWidget *maybeEnterWidget) +{ + // Dispatch enter/leave for the cases where QApplicationPrivate::sendMouseEvent do + // not. This will in general be the cases when alien widgets are not involved: + // 1. from a native widget to another native widget or + // 2. from a native widget to no widget + // 3. from no widget to a native or alien widget + + if (qt_button_down || QWidget::mouseGrabber()) + return; + + if ((maybeEnterWidget == qt_last_native_mouse_receiver) && qt_last_native_mouse_receiver) + return; + if (maybeEnterWidget) { + if (!qt_last_native_mouse_receiver) { + // case 3 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, 0); + qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); + } else if (maybeEnterWidget->internalWinId()) { + // case 1 + QApplicationPrivate::dispatchEnterLeave(maybeEnterWidget, qt_last_native_mouse_receiver); + qt_last_native_mouse_receiver = maybeEnterWidget->internalWinId() ? maybeEnterWidget : maybeEnterWidget->nativeParentWidget(); + } // else at lest one of the widgets are alien, so enter/leave will be handled in QApplicationPrivate + } else { + if (qt_last_native_mouse_receiver) { + // case 2 + QApplicationPrivate::dispatchEnterLeave(0, qt_last_native_mouse_receiver); + qt_last_mouse_receiver = 0; + qt_last_native_mouse_receiver = 0; + } + } +} + +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget) +{ + // Give the Input Manager a chance to process the mouse events. + NSInputManager *currentIManager = [NSInputManager currentInputManager]; + if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { + [currentIManager handleMouseEvent:event]; + } + + // Find the widget that should receive the event, and the widget under the mouse. Those + // can differ if an implicit or explicit mouse grab is active: + QWidget *widgetUnderMouse = 0; + QPoint localPoint, globalPoint; + QWidget *widgetToGetMouse = qt_mac_getTargetForMouseEvent(event, eventType, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); + if (!widgetToGetMouse) + return false; + + // From here on, we let nativeWidget actually be the native widget under widgetUnderMouse. The reason + // for this, is that qt_mac_getTargetForMouseEvent will set cocoa's mouse event redirection aside when + // determining which widget is under the mouse (in other words, it will usually ignore nativeWidget). + // nativeWidget will be used in QApplicationPrivate::sendMouseEvent to correctly dispatch enter/leave events. + if (widgetUnderMouse) + nativeWidget = widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget(); + if (!nativeWidget) + return false; + NSView *view = qt_mac_effectiveview_for(nativeWidget); + + // Handle tablet events (if any) first. + if (qt_mac_handleTabletEvent(view, event)) { + // Tablet event was handled. In Qt we aren't supposed to send the mouse event. + return true; + } + + EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([event eventRef])); + if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent)) + return true; + + // Keep previousButton to make sure we don't send double click + // events when the user double clicks using two different buttons: + static Qt::MouseButton previousButton = Qt::NoButton; + + Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); + NSInteger clickCount = [event 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); + } + + // Send enter/leave events for the cases when QApplicationPrivate::sendMouseEvent do not: + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + + switch (eventType) { + default: + qWarning("not handled! %d", eventType); + break; + case QEvent::MouseMove: + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) + button = Qt::RightButton; + break; + case QEvent::MouseButtonPress: + qt_button_down = widgetUnderMouse; + if (clickCount % 2 == 0 && (previousButton == Qt::NoButton || previousButton == button)) + eventType = QEvent::MouseButtonDblClick; + if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { + button = Qt::RightButton; + qt_leftButtonIsRightButton = true; + } + break; + case QEvent::MouseButtonRelease: + if (button == Qt::LeftButton && qt_leftButtonIsRightButton) { + button = Qt::RightButton; + qt_leftButtonIsRightButton = false; + } + qt_button_down = 0; + break; + } + + qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); + + DnDParams *dndParams = currentDnDParameters(); + dndParams->view = view; + dndParams->theEvent = event; + dndParams->globalPoint = globalPoint; + + // Send the mouse event: + QMouseEvent qme(eventType, localPoint, globalPoint, button, buttons, keyMods); + QApplicationPrivate::sendMouseEvent( + widgetToGetMouse, &qme, widgetUnderMouse, nativeWidget, + &qt_button_down, qt_last_mouse_receiver, true); + + if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) { + QContextMenuEvent qcme(QContextMenuEvent::Mouse, localPoint, globalPoint, keyMods); + qt_sendSpontaneousEvent(widgetToGetMouse, &qcme); + } + + if (eventType == QEvent::MouseButtonRelease) { + // A mouse button was released, which means that the implicit grab was + // released. We therefore need to re-check if should send (delayed) enter leave events: + // qt_button_down has now become NULL since the call at the top of the function. Also, since + // the relase might have closed a window, we dont give the nativeWidget hint + qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, nativeWidget, &widgetUnderMouse); + qt_mac_checkEnterLeaveForNativeWidgets(widgetUnderMouse); + } + + previousButton = button; + return true; +} +#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 +} + +#if QT_MAC_USE_COCOA +void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget) +{ + QMacCocoaAutoReleasePool pool; + OSWindowRef theWindow = static_cast<OSWindowRef>(window); + if(!theWindow) + return; + id theClass = [[[theWindow contentView] superview] class]; + // What we do here is basically to add a new selector to NSThemeFrame called + // "drawRectOriginal:" which will contain the original implementation of + // "drawRect:". After that we get the new implementation from QCocoaWindow + // and exchange them. The new implementation is called drawRectSpecial. + // We cannot just add the method because it might have been added before and since + // we cannot remove a method once it has been added we need to ask QCocoaWindow if + // we did the swap or not. + if(!widget->drawRectOriginalAdded) { + Method m2 = class_getInstanceMethod(theClass, @selector(drawRect:)); + if(!m2) { + // This case is pretty extreme, no drawRect means no drawing! + return; + } + class_addMethod(theClass, @selector(drawRectOriginal:), method_getImplementation(m2), method_getTypeEncoding(m2)); + widget->drawRectOriginalAdded = true; + } + if(widget->originalDrawMethod) { + Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:)); + if(!m0) { + // Ok, this means the methods were never swapped. Just ignore + return; + } + Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:)); + if(!m1) { + // Ok, this means the methods were never swapped. Just ignore + return; + } + // We have the original method here. Proceed and swap the methods. + method_exchangeImplementations(m1, m0); + widget->originalDrawMethod = false; + [theWindow display]; + } +} + +void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget) +{ + QMacCocoaAutoReleasePool pool; + OSWindowRef theWindow = static_cast<OSWindowRef>(window); + id theClass = [[[theWindow contentView] superview] class]; + // Now we need to revert the methods to their original state. + // We cannot remove the method, so we just keep track of it in QCocoaWindow. + Method m0 = class_getInstanceMethod([theWindow class], @selector(drawRectSpecial:)); + if(!m0) { + // Ok, this means the methods were never swapped. Just ignore + return; + } + Method m1 = class_getInstanceMethod(theClass, @selector(drawRect:)); + if(!m1) { + // Ok, this means the methods were never swapped. Just ignore + return; + } + method_exchangeImplementations(m1, m0); + widget->originalDrawMethod = true; + [theWindow display]; +} +#endif // QT_MAC_USE_COCOA + +#if QT_MAC_USE_COCOA +void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show) +{ + if(!window) + return; + QMacCocoaAutoReleasePool pool; + OSWindowRef theWindow = static_cast<OSWindowRef>(window); + NSToolbar *macToolbar = [theWindow toolbar]; + [macToolbar setShowsBaselineSeparator:show]; +} +#endif // QT_MAC_USE_COCOA + +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; +} + +#if QT_MAC_USE_COCOA +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]; +} +#endif // QT_MAC_USE_COCOA + +// 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; +} + +void qt_mac_dispatchPendingUpdateRequests(QWidget *widget) +{ + if (!widget) + return; +#ifndef QT_MAC_USE_COCOA + HIViewRender(qt_mac_nativeview_for(widget)); +#else + [qt_mac_nativeview_for(widget) displayIfNeeded]; +#endif +} + +CGFloat qt_mac_get_scalefactor() +{ +#ifndef QT_MAC_USE_COCOA + return HIGetScaleFactor(); +#else + return [[NSScreen mainScreen] userSpaceScaleFactor]; +#endif +} + +QString qt_mac_get_pasteboardString(OSPasteboardRef paste) +{ + QMacCocoaAutoReleasePool pool; + NSPasteboard *pb = nil; + CFStringRef pbname; + if (PasteboardCopyName(paste, &pbname) == noErr) { + pb = [NSPasteboard pasteboardWithName:const_cast<NSString *>(reinterpret_cast<const NSString *>(pbname))]; + CFRelease(pbname); + } else { + pb = [NSPasteboard generalPasteboard]; + } + if (pb) { + NSString *text = [pb stringForType:NSStringPboardType]; + if (text) + return qt_mac_NSStringToQString(text); + } + return QString(); +} + +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; +} + +void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon) +{ + 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 + } +} + +#ifdef QT_MAC_USE_COCOA +void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) +{ + QMacCocoaAutoReleasePool pool; + OSMenuRef menu = static_cast<OSMenuRef>(theMenu); + if (collapse) { + bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. + NSMenuItem *previousItem = nil; + + NSArray *itemArray = [menu itemArray]; + for (unsigned int i = 0; i < [itemArray count]; ++i) { + NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); + if ([item isSeparatorItem]) { + [item setHidden:previousIsSeparator]; + } + + if (![item isHidden]) { + previousItem = item; + previousIsSeparator = ([previousItem isSeparatorItem]); + } + } + + // We now need to check the final item since we don't want any separators at the end of the list. + if (previousItem && previousIsSeparator) + [previousItem setHidden:YES]; + } else { + NSArray *itemArray = [menu itemArray]; + for (unsigned int i = 0; i < [itemArray count]; ++i) { + NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); + if (QAction *action = reinterpret_cast<QAction *>([item tag])) + [item setHidden:!action->isVisible()]; + } + } +} + +class CocoaPostMessageAfterEventLoopExitHelp : public QObject +{ + id target; + SEL selector; + int argCount; + id arg1; + id arg2; +public: + CocoaPostMessageAfterEventLoopExitHelp(id target, SEL selector, int argCount, id arg1, id arg2) + : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2){ + deleteLater(); + } + + ~CocoaPostMessageAfterEventLoopExitHelp() + { + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + } +}; + +void qt_cocoaPostMessage(id target, SEL selector, int argCount, id arg1, id arg2) +{ + // WARNING: data1 and data2 is truncated to from 64-bit to 32-bit on OS 10.5! + // That is why we need to split the address in two parts: + QCocoaPostMessageArgs *args = new QCocoaPostMessageArgs(target, selector, argCount, arg1, arg2); + quint32 lower = quintptr(args); + quint32 upper = quintptr(args) >> 32; + NSEvent *e = [NSEvent otherEventWithType:NSApplicationDefined + location:NSZeroPoint modifierFlags:0 timestamp:0 windowNumber:0 + context:nil subtype:QtCocoaEventSubTypePostMessage data1:lower data2:upper]; + [NSApp postEvent:e atStart:NO]; +} + +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount, id arg1, id arg2) +{ + if (QApplicationPrivate::instance()->threadData->eventLoops.size() <= 1) + qt_cocoaPostMessage(target, selector, argCount, arg1, arg2); + else + new CocoaPostMessageAfterEventLoopExitHelp(target, selector, argCount, arg1, arg2); +} + +#endif + +QMacCocoaAutoReleasePool::QMacCocoaAutoReleasePool() +{ +#ifndef QT_MAC_USE_COCOA + NSApplicationLoad(); +#endif + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +QMacCocoaAutoReleasePool::~QMacCocoaAutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + +void qt_mac_post_retranslateAppMenu() +{ +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + qt_cocoaPostMessage([NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)], @selector(qtTranslateApplicationMenu)); +#endif +} + +QWidgetPrivate *QMacScrollOptimization::_target = 0; +bool QMacScrollOptimization::_inWheelEvent = false; +int QMacScrollOptimization::_dx = 0; +int QMacScrollOptimization::_dy = 0; +QRect QMacScrollOptimization::_scrollRect = QRect(0, 0, -1, -1); + +#ifdef QT_MAC_USE_COCOA +// This method implements the magic for the drawRectSpecial method. +// We draw a line at the upper edge of the content view in order to +// override the title baseline. +void macDrawRectOnTop(void * /*OSWindowRef */window) +{ + OSWindowRef theWindow = static_cast<OSWindowRef>(window); + NSView *contentView = [theWindow contentView]; + if(!contentView) + return; + // Get coordinates of the content view + NSRect contentRect = [contentView frame]; + // Draw a line on top of the already drawn line. + // We need to check if we are active or not to use the proper color. + if([theWindow isKeyWindow] || [theWindow isMainWindow]) { + [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; + } else { + [[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0] set]; + } + NSPoint origin = NSMakePoint(0, contentRect.size.height); + NSPoint end = NSMakePoint(contentRect.size.width, contentRect.size.height); + [NSBezierPath strokeLineFromPoint:origin toPoint:end]; +} + +// This method will (or at least should) get called only once. +// Its mission is to find out if we are active or not. If we are active +// we assume that we were launched via finder, otherwise we assume +// we were called from the command line. The distinction is important, +// since in the first case we don't need to trigger a paintEvent, while +// in the second case we do. +void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window) +{ + OSWindowRef theWindow = static_cast<OSWindowRef>(window); + NSApplication *application = [NSApplication sharedApplication]; + NSToolbar *toolbar = [theWindow toolbar]; + if([application isActive]) { + // Launched from finder + [toolbar setShowsBaselineSeparator:NO]; + } else { + // Launched from commandline + [toolbar setVisible:false]; + [toolbar setShowsBaselineSeparator:NO]; + [toolbar setVisible:true]; + [theWindow display]; + } +} + +void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *childWidget) +{ + if (!childWidget) + return; + + QWidget *parent = childWidget->parentWidget(); + if (childWidget->isWindow() && parent) { + if ([[qt_mac_window_for(parent) childWindows] containsObject:qt_mac_window_for(childWidget)]) { + QWidgetPrivate *d = qt_widget_private(childWidget); + d->setSubWindowStacking(false); + d->setSubWindowStacking(true); + } + } +} + +void qt_mac_display(QWidget *widget) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + [theNSView display]; +} + +void qt_mac_setNeedsDisplay(QWidget *widget) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + [theNSView setNeedsDisplay:YES]; +} + +void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region) +{ + NSView *theNSView = qt_mac_nativeview_for(widget); + if (region.isEmpty()) { + [theNSView setNeedsDisplay:YES]; + return; + } + + QVector<QRect> rects = region.rects(); + for (int i = 0; i < rects.count(); ++i) { + const QRect &rect = rects.at(i); + NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + [theNSView setNeedsDisplayInRect:nsrect]; + } + +} + +#endif // QT_MAC_USE_COCOA + +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qt_cocoa_helpers_mac_p.h b/src/gui/platforms/mac/qt_cocoa_helpers_mac_p.h new file mode 100644 index 0000000000..a49753ae2f --- /dev/null +++ b/src/gui/platforms/mac/qt_cocoa_helpers_mac_p.h @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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 QT_COCOA_HELPERS_MAC_P_H +#define QT_COCOA_HELPERS_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 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 <private/qwidget_p.h> +#include <qtextdocument.h> +#include <qdebug.h> +#include <qpoint.h> +#include "private/qt_mac_p.h" + +struct HIContentBorderMetrics; + +#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 + +enum { + QtCocoaEventSubTypeWakeup = SHRT_MAX, + QtCocoaEventSubTypePostMessage = SHRT_MAX-1 +}; + +Qt::MouseButtons qt_mac_get_buttons(int buttons); +Qt::MouseButton qt_mac_get_button(EventMouseButton button); +void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds = 0.15); +bool macWindowIsTextured(void * /*OSWindowRef*/ window); +void macWindowToolbarShow(const QWidget *widget, bool show ); +void macWindowToolbarSet( void * /*OSWindowRef*/ window, void* toolbarRef ); +bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ); +void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow ); +void macWindowFlush(void * /*OSWindowRef*/ window); +void macSendToolbarChangeEvent(QWidget *widget); +void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics); +void qt_mac_replaceDrawRect(void * /*OSWindowRef */window, QWidgetPrivate *widget); +void qt_mac_replaceDrawRectOriginal(void * /*OSWindowRef */window, QWidgetPrivate *widget); +void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show); +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); +// These methods exists only for supporting unified mode. +void macDrawRectOnTop(void * /*OSWindowRef */ window); +void macSyncDrawingOnFirstInvocation(void * /*OSWindowRef */window); +void qt_cocoaStackChildWindowOnTopOfOtherChildren(QWidget *widget); +void qt_mac_menu_collapseSeparators(void * /*NSMenu */ menu, bool collapse); +#endif +bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent); +void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent); +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); +Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations); +QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height); +void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, + QStyle::StandardPixmap standardIcon = QStyle::SP_CustomBase); + +#if QT_MAC_USE_COCOA && __OBJC__ +struct DnDParams +{ + NSView *view; + NSEvent *theEvent; + QPoint globalPoint; + NSDragOperation performedAction; +}; + +DnDParams *macCurrentDnDParameters(); +NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); +NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); +Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); + +QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent); +QWidget *qt_mac_getTargetForMouseEvent(NSEvent *event, QEvent::Type eventType, + QPoint &returnLocalPoint, QPoint &returnGlobalPoint, QWidget *nativeWidget, QWidget **returnWidgetUnderMouse); +bool qt_mac_handleMouseEvent(NSEvent *event, QEvent::Type eventType, Qt::MouseButton button, QWidget *nativeWidget); +void qt_mac_handleNonClientAreaMouseEvent(NSWindow *window, NSEvent *event); +#endif + +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); + +CGFloat qt_mac_get_scalefactor(); +QString qt_mac_get_pasteboardString(OSPasteboardRef paste); + +#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]; } + +#ifdef QT_MAC_USE_COCOA +class QCocoaPostMessageArgs { +public: + id target; + SEL selector; + int argCount; + id arg1; + id arg2; + QCocoaPostMessageArgs(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0) + : target(target), selector(selector), argCount(argCount), arg1(arg1), arg2(arg2) + { + [target retain]; + [arg1 retain]; + [arg2 retain]; + } + + ~QCocoaPostMessageArgs() + { + [arg2 release]; + [arg1 release]; + [target release]; + } +}; +void qt_cocoaPostMessage(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); +void qt_cocoaPostMessageAfterEventLoopExit(id target, SEL selector, int argCount=0, id arg1=0, id arg2=0); +#endif + +#endif + +class QMacScrollOptimization { + // This class is made to optimize for the case when the user + // scrolls both horizontally and vertically at the same + // time. This will result in two QWheelEvents (one for each + // direction), which will typically result in two calls to + // QWidget::_scroll_sys. Rather than copying pixels twize on + // screen because of this, we add this helper class to try to + // get away with only one blit. + static QWidgetPrivate *_target; + static bool _inWheelEvent; + static int _dx; + static int _dy; + static QRect _scrollRect; + +public: + static void initDelayedScroll() + { + _inWheelEvent = true; + } + + static bool delayScroll(QWidgetPrivate *target, int dx, int dy, const QRect &scrollRect) + { + if (!_inWheelEvent) + return false; + if (_target && _target != target) + return false; + if (_scrollRect.width() != -1 && _scrollRect != scrollRect) + return false; + + _target = target; + _dx += dx; + _dy += dy; + _scrollRect = scrollRect; + return true; + } + + static void performDelayedScroll() + { + if (!_inWheelEvent) + return; + _inWheelEvent = false; + if (!_target) + return; + + _target->scroll_sys(_dx, _dy, _scrollRect); + + _target = 0; + _dx = 0; + _dy = 0; + _scrollRect = QRect(0, 0, -1, -1); + } +}; + +void qt_mac_post_retranslateAppMenu(); + +#ifdef QT_MAC_USE_COCOA +void qt_mac_display(QWidget *widget); +void qt_mac_setNeedsDisplay(QWidget *widget); +void qt_mac_setNeedsDisplayInRect(QWidget *widget, QRegion region); +#endif // QT_MAC_USE_COCOA + + +// Utility functions to ease the use of Core Graphics contexts. + +inline void qt_mac_retain_graphics_context(CGContextRef context) +{ + CGContextRetain(context); + CGContextSaveGState(context); +} + +inline void qt_mac_release_graphics_context(CGContextRef context) +{ + CGContextRestoreGState(context); + CGContextRelease(context); +} + +inline void qt_mac_draw_image(CGContextRef context, CGContextRef imageContext, CGRect area, CGRect drawingArea) +{ + CGImageRef image = CGBitmapContextCreateImage(imageContext); + CGImageRef subImage = CGImageCreateWithImageInRect(image, area); + + CGContextTranslateCTM (context, 0, drawingArea.origin.y + CGRectGetMaxY(drawingArea)); + CGContextScaleCTM(context, 1, -1); + CGContextDrawImage(context, drawingArea, subImage); + + CGImageRelease(subImage); + CGImageRelease(image); +} + +QT_END_NAMESPACE + +#endif // QT_COCOA_HELPERS_MAC_P_H diff --git a/src/gui/platforms/mac/qt_mac.cpp b/src/gui/platforms/mac/qt_mac.cpp new file mode 100644 index 0000000000..046bcf6a54 --- /dev/null +++ b/src/gui/platforms/mac/qt_mac.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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 if (model == kCGColorSpaceModelMonochrome) { + pc.setRgbF(components[0], components[0], components[0], components[1]); + } 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) +{ +#ifdef Q_OS_MAC32 + RGBColor c; + GetThemeTextColor(themeColor, 32, true, &c); + QColor color = QColor(c.red / 256, c.green / 256, c.blue / 256); + return color; +#else + // There is no equivalent to GetThemeTextColor in 64-bit and it was rather bad that + // I didn't file a request to implement this for Snow Leopard. So, in the meantime + // I've encoded the values from the GetThemeTextColor. This is not exactly ideal + // as if someone really wants to mess with themeing, these colors will be wrong. + // It also means that we need to make sure the values for differences between + // OS releases (and it will be likely that we are a step behind.) + switch (themeColor) { + case kThemeTextColorAlertActive: + case kThemeTextColorTabFrontActive: + case kThemeTextColorBevelButtonActive: + case kThemeTextColorListView: + case kThemeTextColorPlacardActive: + case kThemeTextColorPopupButtonActive: + case kThemeTextColorPopupLabelActive: + case kThemeTextColorPushButtonActive: + return Qt::black; + case kThemeTextColorAlertInactive: + case kThemeTextColorDialogInactive: + case kThemeTextColorPlacardInactive: + return QColor(69, 69, 69, 255); + case kThemeTextColorPopupButtonInactive: + case kThemeTextColorPopupLabelInactive: + case kThemeTextColorPushButtonInactive: + case kThemeTextColorTabFrontInactive: + case kThemeTextColorBevelButtonInactive: + return QColor(127, 127, 127, 255); + default: { + QNativeImage nativeImage(16,16, QNativeImage::systemFormat()); + CGRect cgrect = CGRectMake(0, 0, 16, 16); + HIThemeSetTextFill(themeColor, 0, nativeImage.cg, kHIThemeOrientationNormal); + CGContextFillRect(nativeImage.cg, cgrect); + QColor color = nativeImage.image.pixel(0,0); + return QColor(nativeImage.image.pixel(0 , 0)); + } + } +#endif +} +QT_END_NAMESPACE diff --git a/src/gui/platforms/mac/qt_mac_p.h b/src/gui/platforms/mac/qt_mac_p.h new file mode 100644 index 0000000000..b2bb804ff0 --- /dev/null +++ b/src/gui/platforms/mac/qt_mac_p.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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> +#ifdef QT_MAC_USE_COCOA +#include <objc/runtime.h> +#endif // QT_MAC_USE_COCOA +#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 for chaining to gether a bunch of fades. It pretty much is only used for qmenu fading. +class QMacWindowFader +{ + QWidgetList m_windowsToFade; + float m_duration; + Q_DISABLE_COPY(QMacWindowFader) +public: + QMacWindowFader(); // PLEASE DON'T CALL THIS. + static QMacWindowFader *currentFader(); + void registerWindowToFade(QWidget *window); + void setFadeDuration(float durationInSecs) { m_duration = durationInSecs; } + float fadeDuration() const { return m_duration; } + void performFade(); +}; + +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 ©) : 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 ©) { + 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 +extern QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt); //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; + unsigned int lastOperation; + void clear() { + rect = QRect(); + modifiers = Qt::NoModifier; + buttons = Qt::NoButton; + lastAction = Qt::IgnoreAction; + lastOperation = 0; + } +}; +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/platforms/mac/qtextengine_mac.cpp b/src/gui/platforms/mac/qtextengine_mac.cpp new file mode 100644 index 0000000000..2c6e579b45 --- /dev/null +++ b/src/gui/platforms/mac/qtextengine_mac.cpp @@ -0,0 +1,656 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtextengine_p.h" + +#include <private/qfontengine_coretext_p.h> +#include <private/qfontengine_mac_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_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, &si.leading); + 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); + } + + ensureSpace(num_glyphs); + num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; + + QGlyphLayout g = availableGlyphs(&si); + g.numGlyphs = num_glyphs; + unsigned short *log_clusters = logClusters(&si); + + bool stringToCMapFailed = false; + if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) { + ensureSpace(num_glyphs); + g = availableGlyphs(&si); + stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, + attributes(), &si); + } + + if (!stringToCMapFailed) { + heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); + + 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/platforms/mac/qwidget_mac.mm b/src/gui/platforms/mac/qwidget_mac.mm new file mode 100644 index 0000000000..354f05ba10 --- /dev/null +++ b/src/gui/platforms/mac/qwidget_mac.mm @@ -0,0 +1,5420 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/**************************************************************************** +** +** 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/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 "qfileinfo.h" +#include "qimage.h" +#include "qlayout.h" +#include "qmenubar.h" +#include <private/qbackingstore_p.h> +#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 "qevent_p.h" +#include "qdnd_p.h" +#include <QtGui/qgraphicsproxywidget.h> +#include "qmainwindow.h" + +QT_BEGIN_NAMESPACE + +// qmainwindow.cpp +extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window); + +#define XCOORD_MAX 16383 +#define WRECT_MAX 8191 + +#ifndef QT_MAC_USE_COCOA + +extern "C" { + extern OSStatus _HIViewScrollRectWithOptions(HIViewRef, const HIRect *, CGFloat, CGFloat, + OptionBits) __attribute__ ((weak)); +} +#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 QPointer<QWidget> qt_button_down; //qapplication_mac.cpp +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_last_mouse_receiver; //qapplication_mac.mm +extern QPointer<QWidget> qt_last_native_mouse_receiver; //qt_cocoa_helpers_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 +extern void qt_mac_setMouseGrabCursor(bool set, QCursor *cursor = 0); // qcursor_mac.mm +extern QPointer<QWidget> topLevelAt_cache; // qapplication_mac.mm +/***************************************************************************** + 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) +{ + NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->effectiveWinId()); + NSArray *windows = [NSApp windows]; + for (NSWindow *window in windows) { + NSArray *drawers = [window drawers]; + for (NSDrawer *drawer in drawers) { + if ([drawer contentView] == widgetView) + return drawer; + } + } + return 0; +} +#endif + +static void qt_mac_destructView(OSViewRef view) +{ +#ifdef QT_MAC_USE_COCOA + NSWindow *window = [view window]; + if ([window contentView] == view) + [window setContentView:[[NSView alloc] initWithFrame:[view bounds]]]; + [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; + return w->parentWidget() && (modality == Qt::WindowModal || w->windowType() == Qt::Sheet); +} + +bool qt_mac_is_macdrawer(const QWidget *w) +{ + return (w && w->parentWidget() && w->windowType() == Qt::Drawer); +} + +bool qt_mac_insideKeyWindow(const QWidget *w) +{ +#ifdef QT_MAC_USE_COCOA + return [[reinterpret_cast<NSView *>(w->effectiveWinId()) window] isKeyWindow]; +#else + Q_UNUSED(w); +#endif + return false; +} + +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.mm + if(qt_mac_app_fullscreen == b) + return; + qt_mac_app_fullscreen = b; + if (b) { + SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); + } else { + SetSystemUIMode(kUIModeNormal, 0); + } +} + +Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w) +{ + return reinterpret_cast<OSViewRef>(w->internalWinId()); +} + +Q_GUI_EXPORT OSViewRef qt_mac_effectiveview_for(const QWidget *w) +{ + // Get the first non-alien (parent) widget for + // w, and return its NSView (if it has one): + return reinterpret_cast<OSViewRef>(w->effectiveWinId()); +} + +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) +{ + if (!window) + return false; + + if (!window->internalWinId()) + return true; //alien + + return qt_isGenuineQWidget(OSViewRef(window->internalWinId())); +} + +Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w) +{ + if (OSViewRef hiview = qt_mac_effectiveview_for(w)) { + OSWindowRef window = qt_mac_window_for(hiview); + if (window) + return window; + + if (qt_isGenuineQWidget(hiview)) { + // This is a workaround for NSToolbar. When a widget is hidden + // by clicking the toolbar button, Cocoa reparents the widgets + // to another window (but Qt doesn't know about it). + // When we start showing them, it reparents back, + // but at this point it's window is nil, but the window it's being brought + // into (the Qt one) is for sure created. + // This stops the hierarchy moving under our feet. + QWidget *toplevel = w->window(); + if (toplevel != w) { + hiview = qt_mac_nativeview_for(toplevel); + if (OSWindowRef w = qt_mac_window_for(hiview)) + return w; + } + + toplevel->d_func()->createWindow_sys(); + // Reget the hiview since "create window" could potentially move the view (I guess). + hiview = qt_mac_nativeview_for(toplevel); + return qt_mac_window_for(hiview); + } + } + 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; + CGWindowLevel tmpLevel; + GetWindowGroupLevelOfType(GetWindowGroupOfClass(wclass), kWindowGroupLevelActive, &tmpLevel); + group_level = tmpLevel; + 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::macSetNeedsDisplay(QRegion region) +{ + Q_Q(QWidget); +#ifndef QT_MAC_USE_COCOA + if (region.isEmpty()) + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); + else if (RgnHandle rgnHandle = region.toQDRgnForUpdate_sys()) + HIViewSetNeedsDisplayInRegion(qt_mac_nativeview_for(q), QMacSmartQuickDrawRegion(rgnHandle), true); + else + HIViewSetNeedsDisplay(qt_mac_nativeview_for(q), true); // do a complete repaint on overflow. +#else + if (NSView *nativeView = qt_mac_nativeview_for(q)) { + // INVARIANT: q is _not_ alien. So we can optimize a little: + if (region.isEmpty()) { + [nativeView setNeedsDisplay:YES]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + NSRect nsrect = NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()); + [nativeView setNeedsDisplayInRect:nsrect]; + } + } + } else if (QWidget *effectiveWidget = q->nativeParentWidget()) { + // INVARIANT: q is alien, and effectiveWidget is native. + if (NSView *effectiveView = qt_mac_nativeview_for(effectiveWidget)) { + if (region.isEmpty()) { + const QRect &rect = q->rect(); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } else { + QVector<QRect> rects = region.rects(); + for (int i = 0; i<rects.count(); ++i) { + const QRect &rect = rects.at(i); + QPoint p = q->mapTo(effectiveWidget, rect.topLeft()); + NSRect nsrect = NSMakeRect(p.x(), p.y(), rect.width(), rect.height()); + [effectiveView setNeedsDisplayInRect:nsrect]; + } + } + } + } +#endif +} + +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; +} + +#ifndef QT_NO_GESTURES +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +/* We build the release package against the 10.4 SDK. + So, to enable gestures for applications running on + 10.6+, we define the missing constants here: */ +enum { + kEventClassGesture = 'gest', + kEventGestureStarted = 1, + kEventGestureEnded = 2, + kEventGestureMagnify = 4, + kEventGestureSwipe = 5, + kEventGestureRotate = 6, + kEventParamRotationAmount = 'rota', + kEventParamSwipeDirection = 'swip', + kEventParamMagnificationAmount = 'magn' +}; +#endif +#endif // QT_NO_GESTURES + +// window events +static EventTypeSpec window_events[] = { + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowExpanded }, + { kEventClassWindow, kEventWindowHidden }, + { kEventClassWindow, kEventWindowZoom }, + { kEventClassWindow, kEventWindowZoomed }, + { kEventClassWindow, kEventWindowCollapsed }, + { kEventClassWindow, kEventWindowToolbarSwitchMode }, + { kEventClassWindow, kEventWindowProxyBeginDrag }, + { kEventClassWindow, kEventWindowProxyEndDrag }, + { kEventClassWindow, kEventWindowResizeCompleted }, + { kEventClassWindow, kEventWindowBoundsChanging }, + { kEventClassWindow, kEventWindowGetRegion }, + { kEventClassWindow, kEventWindowGetClickModality }, + { kEventClassWindow, kEventWindowTransitionCompleted }, + { kEventClassGesture, kEventGestureStarted }, + { kEventClassGesture, kEventGestureEnded }, + { kEventClassGesture, kEventGestureMagnify }, + { kEventClassGesture, kEventGestureSwipe }, + { kEventClassGesture, kEventGestureRotate }, + { 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; + } 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; + } 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 == kEventWindowZoom) { + widget->d_func()->topData()->normalGeometry = widget->geometry(); + handled_event = false; + } 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); + } + 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); + qt_button_down = 0; + } else if(ekind == kEventWindowToolbarSwitchMode) { + macSendToolbarChangeEvent(widget); + 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) { + 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 if (ekind == kEventWindowHidden) { + // Make sure that we also hide any visible sheets on our window. + // Cocoa does the right thing for us. + const QObjectList children = widget->children(); + const int childCount = children.count(); + for (int i = 0; i < childCount; ++i) { + QObject *obj = children.at(i); + if (obj->isWidgetType()) { + QWidget *widget = static_cast<QWidget *>(obj); + if (qt_mac_is_macsheet(widget) && widget->isVisible()) + widget->hide(); + } + } + } 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; } + +#ifndef QT_NO_GESTURES + case kEventClassGesture: { + // First, find the widget that was under + // the mouse when the gesture happened: + HIPoint screenLocation; + if (GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0, + sizeof(screenLocation), 0, &screenLocation) != noErr) { + handled_event = false; + break; + } + QWidget *widget = QApplication::widgetAt(screenLocation.x, screenLocation.y); + if (!widget) { + handled_event = false; + break; + } + + QNativeGestureEvent qNGEvent; + qNGEvent.position = QPoint(screenLocation.x, screenLocation.y); + + switch (ekind) { + case kEventGestureStarted: + qNGEvent.gestureType = QNativeGestureEvent::GestureBegin; + break; + case kEventGestureEnded: + qNGEvent.gestureType = QNativeGestureEvent::GestureEnd; + break; + case kEventGestureRotate: { + CGFloat amount; + if (GetEventParameter(event, kEventParamRotationAmount, 'cgfl', 0, + sizeof(amount), 0, &amount) != noErr) { + handled_event = false; + break; + } + qNGEvent.gestureType = QNativeGestureEvent::Rotate; + qNGEvent.percentage = float(-amount); + break; } + case kEventGestureSwipe: { + HIPoint swipeDirection; + if (GetEventParameter(event, kEventParamSwipeDirection, typeHIPoint, 0, + sizeof(swipeDirection), 0, &swipeDirection) != noErr) { + handled_event = false; + break; + } + qNGEvent.gestureType = QNativeGestureEvent::Swipe; + if (swipeDirection.x == 1) + qNGEvent.angle = 180.0f; + else if (swipeDirection.x == -1) + qNGEvent.angle = 0.0f; + else if (swipeDirection.y == 1) + qNGEvent.angle = 90.0f; + else if (swipeDirection.y == -1) + qNGEvent.angle = 270.0f; + break; } + case kEventGestureMagnify: { + CGFloat amount; + if (GetEventParameter(event, kEventParamMagnificationAmount, 'cgfl', 0, + sizeof(amount), 0, &amount) != noErr) { + handled_event = false; + break; + } + qNGEvent.gestureType = QNativeGestureEvent::Zoom; + qNGEvent.percentage = float(amount); + break; } + } + + QApplication::sendSpontaneousEvent(widget, &qNGEvent); + break; } +#endif // QT_NO_GESTURES + + 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; + if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr) { + Q_ASSERT(false); + } + 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"); + 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())); + } + + 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); + widget->d_func()->paintBackground(&p, qrgn, 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), tint); + } + p.end(); + if (!redirectionOffset.isNull()) + widget->d_func()->restoreRedirected(); + } + + 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(); + + //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); + } 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; + } else if (ekind == kEventControlDragLeave) { + dropWidget = QDragManager::self()->currentTarget(); + if (dropWidget) { + dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragLeave, 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()))) { + if (widget == qt_button_down) + qt_button_down = 0; + } + } + } + break; } + case kEventClassMouse: { + bool send_to_app = false; + 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]; + +#ifdef ALIEN_DEBUG + qDebug() << "Creating NSView for" << widget; +#endif + + 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)) { + bool oldState = widget->testAttribute(Qt::WA_WState_ExplicitShowHide); + if(visible) { + if (!widget->testAttribute(Qt::WA_WState_ExplicitShowHide)) + widget->show(); + } else { + widget->hide(); + if(!oldState) + widget->setAttribute(Qt::WA_WState_ExplicitShowHide, false); + } + } + } +} + +/***************************************************************************** + QWidgetPrivate member functions + *****************************************************************************/ +bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up) +{ + // I'm not sure what "up" is + if(!w || !w->isWindow()) + return false; + + QTLWExtra *topData = w->d_func()->topData(); + QWExtra *extraData = w->d_func()->extraData(); + // topData->resizer is only 4 bits, so subtracting -1 from zero causes bad stuff + // to happen, prevent that here (you really want the thing hidden). + if (up >= 0 || topData->resizer != 0) + topData->resizer += up; + OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->effectiveWinId())); + { +#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 + QMacCocoaAutoReleasePool pool; + [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_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); +#if !defined(QT_NO_MAINWINDOW) && !defined(QT_NO_TOOLBAR) + // Make sure that QMainWindow has the MacWindowToolBarButtonHint when the + // unifiedTitleAndToolBarOnMac property is ON. This is to avoid reentry of + // setParent() triggered by the QToolBar::event(QEvent::ParentChange). + QMainWindow *mainWindow = qobject_cast<QMainWindow *>(q); + if (mainWindow && mainWindow->unifiedTitleAndToolBarOnMac()) { + data.window_flags |= Qt::MacWindowToolBarButtonHint; + } +#endif +#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) { + wattr |= kWindowNoTitleBarAttribute; + } else if(wclass == kFloatingWindowClass) { + wattr |= kWindowNoTitleBarAttribute; + } 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(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 if(q->testAttribute(Qt::WA_ShowModal)) + wclass = kMovableModalWindowClass; + 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; + } + } + 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 (wattr & kWindowHideOnSuspendAttribute) + HIWindowChangeAvailability(windowRef, kHIWindowExposeHidden, 0); + else + HIWindowChangeAvailability(windowRef, 0, kHIWindowExposeHidden); + 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); + applyMaxAndMinSizeOnWindow(); +} +#else // QT_MAC_USE_COCOA + +void QWidgetPrivate::setWindowLevel() +{ + Q_Q(QWidget); + const QWidget * const windowParent = q->window()->parentWidget(); + const QWidget * const primaryWindow = windowParent ? windowParent->window() : 0; + 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) { + // Correct modality level (NSModalPanelWindowLevel) will be + // set by cocoa when creating a modal session later. + winLevel = NSNormalWindowLevel; + } + + // 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; + [qt_mac_window_for(q) setLevel:winLevel]; +} + +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]; + } else { + [windowRef setHidesOnDeactivate:NO]; + } + if (q->testAttribute(Qt::WA_MacNoShadow)) + [windowRef setHasShadow:NO]; + else + [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; + if (!nsview) { + nsview = qt_mac_create_widget(q, this, 0); + setWinId(WId(nsview)); + } + [windowRef setContentView:nsview]; + [nsview setHidden:NO]; + transferChildren(); + + // Tell Cocoa explicit that we wan't the view to receive key events + // (regardless of focus policy) because this is how it works on other + // platforms (and in the carbon port): + [windowRef makeFirstResponder:nsview]; + + 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); + } + + // Its more performant to handle the mouse cursor + // ourselves, expecially when using alien widgets: + [windowRef disableCursorRects]; + + setWindowLevel(); + 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); + applyMaxAndMinSizeOnWindow(); +} + +#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 = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { + 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 Qt Support 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); + QMacCocoaAutoReleasePool pool; + + 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 { + 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 + // Alien note: a 'window' was supplied as argument, meaning this widget + // is not alien. So therefore the parent cannot be alien either. + 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 + +#ifdef QT_MAC_USE_COCOA + if (q->testAttribute(Qt::WA_NativeWindow) == false || q->internalWinId() != 0) { + // INVARIANT: q is Alien, and we should not create an NSView to back it up. + } else +#endif + 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); + if (q->isVisible()) { + // If q were Alien before, but now became native (e.g. if a call to + // winId was done from somewhere), we need to show the view immidiatly: + QMacCocoaAutoReleasePool pool; + [osview setHidden:NO]; + } +#endif + } + } + + updateIsOpaque(); + + if (q->testAttribute(Qt::WA_DropSiteRegistered)) + registerDropSite(true); + if (q->hasFocus()) + setFocus_sys(); + 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 qt_mac_repaintParentUnderAlienWidget(QWidget *alienWidget) +{ + QWidget *nativeParent = alienWidget->nativeParentWidget(); + if (!nativeParent) + return; + + QPoint globalPos = alienWidget->mapToGlobal(QPoint(0, 0)); + QRect dirtyRect = QRect(nativeParent->mapFromGlobal(globalPos), alienWidget->size()); + nativeParent->repaint(dirtyRect); +} + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + QMacCocoaAutoReleasePool pool; + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); + if (!internalWinId()) + qt_mac_repaintParentUnderAlienWidget(this); + d->deactivateWidgetCleanup(); + qt_mac_event_release(this); + if(testAttribute(Qt::WA_WState_Created)) { + 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(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); + } + } + QT_TRY { + d->setWinId(0); + } QT_CATCH (const std::bad_alloc &) { + // swallow - destructors must not throw + } + } +} + +void QWidgetPrivate::transferChildren() +{ + Q_Q(QWidget); + if (!q->internalWinId()) + 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->internalWinId()) { +#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(w) retain]; + [qt_mac_nativeview_for(w) removeFromSuperview]; + [qt_mac_nativeview_for(q) addSubview:qt_mac_nativeview_for(w)]; + [qt_mac_nativeview_for(w) release]; + w->setAttribute(Qt::WA_DropSiteRegistered, oldRegistered); +#endif + } + } + } + } +} + +#ifdef QT_MAC_USE_COCOA +void QWidgetPrivate::setSubWindowStacking(bool set) +{ + // After hitting too many unforeseen bugs trying to put Qt on top of the cocoa child + // window API, we have decided to revert this behaviour as much as we can. We + // therefore now only allow child windows to exist for children of modal dialogs. + static bool use_behaviour_qt473 = !qgetenv("QT_MAC_USE_CHILDWINDOWS").isEmpty(); + + // This will set/remove a visual relationship between parent and child on screen. + // The reason for doing this is to ensure that a child always stacks infront of + // its parent. Unfortunatly is turns out that [NSWindow addChildWindow] has + // several unwanted side-effects, one of them being the moving of a child when + // moving the parent, which we choose to accept. A way tougher side-effect is + // that Cocoa will hide the parent if you hide the child. And in the case of + // a tool window, since it will normally hide when you deactivate the + // application, Cocoa will hide the parent upon deactivate as well. The result often + // being no more visible windows on screen. So, to make a long story short, we only + // allow parent-child relationships between windows that both are either a plain window + // or a dialog. + + Q_Q(QWidget); + if (!q->isWindow()) + return; + NSWindow *qwin = [qt_mac_nativeview_for(q) window]; + if (!qwin) + return; + Qt::WindowType qtype = q->windowType(); + if (set && !(qtype == Qt::Window || qtype == Qt::Dialog)) + return; + if (set && ![qwin isVisible]) + return; + + if (QWidget *parent = q->parentWidget()) { + if (NSWindow *pwin = [qt_mac_nativeview_for(parent) window]) { + if (set) { + Qt::WindowType ptype = parent->window()->windowType(); + if ([pwin isVisible] + && (ptype == Qt::Window || ptype == Qt::Dialog) + && ![qwin parentWindow] + && (use_behaviour_qt473 || parent->windowModality() == Qt::ApplicationModal)) { + NSInteger level = [qwin level]; + [pwin addChildWindow:qwin ordered:NSWindowAbove]; + if ([qwin level] < level) + [qwin setLevel:level]; + } + } else { + [pwin removeChildWindow:qwin]; + } + } + } + + // Only set-up child windows for q if q is modal: + if (set && !use_behaviour_qt473 && q->windowModality() != Qt::ApplicationModal) + return; + + QObjectList widgets = q->children(); + for (int i=0; i<widgets.size(); ++i) { + QWidget *child = qobject_cast<QWidget *>(widgets.at(i)); + if (child && child->isWindow()) { + if (NSWindow *cwin = [qt_mac_nativeview_for(child) window]) { + if (set) { + Qt::WindowType ctype = child->window()->windowType(); + if ([cwin isVisible] && (ctype == Qt::Window || ctype == Qt::Dialog) && ![cwin parentWindow]) { + NSInteger level = [cwin level]; + [qwin addChildWindow:cwin ordered:NSWindowAbove]; + if ([cwin level] < level) + [cwin setLevel:level]; + } + } else { + [qwin removeChildWindow:qt_mac_window_for(child)]; + } + } + } + } +} +#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(effectiveRectFor(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 + if (qt_mac_is_macdrawer(q)) { + oldDrawer = qt_mac_drawer_for(q); + } + if (wasWindow) { + OSWindowRef oldWindow = qt_mac_window_for(old_id); + oldToolbar = [oldWindow toolbar]; + if (oldToolbar) { + [oldToolbar retain]; + oldToolbarVisible = [oldToolbar isVisible]; + [oldWindow setToolbar:nil]; + } + } +#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); + bool explicitlyHidden = q->testAttribute(Qt::WA_WState_Hidden) && q->testAttribute(Qt::WA_WState_ExplicitShowHide); + if (wasCreated && !qt_isGenuineQWidget(q)) + return; + + if (!q->testAttribute(Qt::WA_WState_WindowOpacitySet)) { + q->setWindowOpacity(1.0f); + q->setAttribute(Qt::WA_WState_WindowOpacitySet, false); + } + + 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, + // unless this is an alien widget. ) + const bool nonWindowWithCreatedParent = !q->isWindow() && parent->testAttribute(Qt::WA_WState_Created); + const bool nativeWidget = q->internalWinId() != 0; + if (wasCreated || (nativeWidget && nonWindowWithCreatedParent)) { + createWinId(); + if (q->isWindow()) { +#ifndef QT_MAC_USE_COCOA + // We do this down below for wasCreated, so avoid doing this twice + // (only for performance, it gets called a lot anyway). + if (!wasCreated) { + if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { + mwl->updateHIToolBarStatus(); + } + } +#else + // Simply transfer our toolbar over. Everything should stay put, unlike in Carbon. + if (oldToolbar && !(f & Qt::FramelessWindowHint)) { + OSWindowRef newWindow = qt_mac_window_for(q); + [newWindow setToolbar:oldToolbar]; + [oldToolbar release]; + [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(); +#ifndef QT_MAC_USE_COCOA + // If we were a unified window, We just transfered our toolbars out of the unified toolbar. + // So redo the status one more time. It apparently is not an issue with Cocoa. + if (q->isWindow()) { + if (QMainWindowLayout *mwl = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q))) { + mwl->updateHIToolBarStatus(); + } + } +#endif + + 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 (!internalWinId()) { + 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 (!internalWinId()) { + 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:qt_mac_QStringToNSString(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; + QFileInfo fi(filePath); + [qt_mac_window_for(q) setRepresentedFilename:fi.exists() ? qt_mac_QStringToNSString(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 (validRef) { + status = HIWindowSetProxyFSRef(qt_mac_window_for(q), &ref); + } 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; + if (icon.isNull()) + return; + NSButton *iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; + if (iconButton == nil) { + QCFString string(q->windowTitle()); + const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string); + [qt_mac_window_for(q) setRepresentedURL:[NSURL fileURLWithPath:const_cast<NSString *>(tmpString)]]; + iconButton = [qt_mac_window_for(q) standardWindowButton:NSWindowDocumentIconButton]; + } + if (icon.isNull()) { + [iconButton setImage:nil]; + } else { + QPixmap scaled = pm->scaled(QSize(16,16), Qt::KeepAspectRatio, Qt::SmoothTransformation); + NSImage *image = static_cast<NSImage *>(qt_mac_create_nsimage(scaled)); + [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:qt_mac_QStringToNSString(iconText)]; +#endif + } +} + +void QWidget::grabMouse() +{ + if(isVisible() && !qt_nograb()) { + if(mac_mouse_grabber) + mac_mouse_grabber->releaseMouse(); + mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true); + } +} + +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ + if(isVisible() && !qt_nograb()) { + if(mac_mouse_grabber) + mac_mouse_grabber->releaseMouse(); + mac_mouse_grabber=this; + qt_mac_setMouseGrabCursor(true, const_cast<QCursor *>(&cursor)); + } +} +#endif + +void QWidget::releaseMouse() +{ + if(!qt_nograb() && mac_mouse_grabber == this) { + mac_mouse_grabber = 0; + qt_mac_setMouseGrabCursor(false); + } +} + +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); + qApp->setActiveWindow(tlw); +#else + [win makeKeyWindow]; +#endif + } 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 (updateRedirectedToGraphicsProxyWidget(q, r)) + return; + dirtyOnWidget += r; + macSetNeedsDisplay(r != q->rect() ? r : QRegion()); +} + +void QWidgetPrivate::update_sys(const QRegion &rgn) +{ + Q_Q(QWidget); + if (updateRedirectedToGraphicsProxyWidget(q, rgn)) + return; + dirtyOnWidget += rgn; + macSetNeedsDisplay(rgn); +} + +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(); +#ifndef QT_MAC_USE_COCOA + if (realWindow && !q->testAttribute(Qt::WA_Moved)) { + if (qt_mac_is_macsheet(q)) + recreateMacWindow(); + q->createWinId(); + if (QWidget *p = q->parentWidget()) { + p->createWinId(); + RepositionWindow(qt_mac_window_for(q), qt_mac_window_for(p), kWindowCenterOnParentWindow); + } else { + RepositionWindow(qt_mac_window_for(q), 0, kWindowCenterOnMainScreen); + } + } +#endif + + data.fstrut_dirty = true; + if (realWindow) { + 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 + +#ifdef QT_MAC_USE_COCOA + // Make sure that we end up sending a repaint event to + // the widget if the window has been visible one before: + [qt_mac_get_contentview_for(window) setNeedsDisplay:YES]; +#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()]; + + QWidget *top = 0; + if (QApplicationPrivate::tryModalHelper(q, &top)) { + [window makeKeyAndOrderFront:window]; + // If this window is app modal, we need to start spinning + // a modal session for it. Interrupting + // the event dispatcher will make this happend: + if (data.window_modality == Qt::ApplicationModal) + QEventDispatcherMac::instance()->interrupt(); + } else { + // The window is modally shaddowed, so we need to make + // sure that we don't pop in front of the modal window: + [window orderFront:window]; + if (!top->testAttribute(Qt::WA_DontShowOnScreen)) { + if (NSWindow *modalWin = qt_mac_window_for(top)) + [modalWin orderFront:window]; + } + } + setSubWindowStacking(true); + qt_mac_update_cursor(); +#endif + if (q->windowType() == Qt::Popup) { + qt_button_down = 0; + 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)) { +#ifndef QT_MAC_USE_COCOA + qt_event_request_activate(q); +#endif + } + } else if(topData()->embedded || !q->parentWidget() || q->parentWidget()->isVisible()) { +#ifndef QT_MAC_USE_COCOA + HIViewSetVisible(qt_mac_nativeview_for(q), true); +#else + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just show the view: + [view setHidden:NO]; + } else { + // INVARIANT: q is alien. Repaint q instead: + q->repaint(); + } +#endif + } + +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Enter, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; + } +#endif + + topLevelAt_cache = 0; + qt_event_request_window_change(q); +} + +QPoint qt_mac_nativeMapFromParent(const QWidget *child, const QPoint &pt) +{ +#ifndef QT_MAC_USE_COCOA + CGPoint nativePoint = CGPointMake(pt.x(), pt.y()); + HIViewConvertPoint(&nativePoint, qt_mac_nativeview_for(child->parentWidget()), + qt_mac_nativeview_for(child)); +#else + NSPoint nativePoint = [qt_mac_nativeview_for(child) convertPoint:NSMakePoint(pt.x(), pt.y()) fromView:qt_mac_nativeview_for(child->parentWidget())]; +#endif + return QPoint(nativePoint.x, nativePoint.y); +} + + +void QWidgetPrivate::hide_sys() +{ + Q_Q(QWidget); + if((q->windowType() == Qt::Desktop)) //you can't hide the desktop! + return; + QMacCocoaAutoReleasePool pool; + if(q->isWindow()) { +#ifdef QT_MAC_USE_COCOA + setSubWindowStacking(false); +#endif + 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]; + // Unfortunately it is not as easy as just hiding the window, we need + // to find out if we were in full screen mode. If we were and this is + // the last window in full screen mode then we need to unset the full screen + // mode. If this is not the last visible window in full screen mode then we + // don't change the full screen mode. + if(q->isFullScreen()) + { + bool keepFullScreen = false; + QWidgetList windowList = qApp->topLevelWidgets(); + int windowCount = windowList.count(); + for(int i = 0; i < windowCount; i++) + { + QWidget *w = windowList[i]; + // If it is the same window, we don't need to check :-) + if(q == w) + continue; + // If they are not visible or if they are minimized then + // we just ignore them. + if(!w->isVisible() || w->isMinimized()) + continue; + // Is it full screen? + // Notice that if there is one window in full screen mode then we + // cannot switch the full screen mode off, therefore we just abort. + if(w->isFullScreen()) { + keepFullScreen = true; + break; + } + } + // No windows in full screen mode, so let just unset that flag. + if(!keepFullScreen) + qt_mac_set_fullscreen_mode(false); + } +#endif + toggleDrawers(false); + qt_mac_update_cursor(); +#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 + } +#ifndef QT_MAC_USE_COCOA + // If the window we now hide was the active window, we need + // to find, and activate another window on screen. NB: Cocoa takes care of this + // logic for us (and distinquishes between main windows and key windows) + if (q->isActiveWindow() && !(q->windowType() == Qt::Popup)) { + QWidget *w = 0; + if(q->parentWidget()) + w = q->parentWidget()->window(); + if(!w || (!w->isVisible() && !w->isMinimized())) { + for (WindowPtr wp = GetFrontWindowOfClass(kMovableModalWindowClass, true); + wp; wp = GetNextWindowOfClass(wp, kMovableModalWindowClass, true)) { + if((w = qt_mac_find_window(wp))) + break; + } + if (!w){ + 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; + } + } + } + if(w && w->isVisible() && !w->isMinimized()) { + qt_event_request_activate(w); + } + } +#endif + } else { + invalidateBuffer(q->rect()); +#ifndef QT_MAC_USE_COCOA + HIViewSetVisible(qt_mac_nativeview_for(q), false); +#else + if (NSView *view = qt_mac_nativeview_for(q)) { + // INVARIANT: q is native. Just hide the view: + [view setHidden:YES]; + } else { + // INVARIANT: q is alien. Repaint where q is placed instead: + qt_mac_repaintParentUnderAlienWidget(q); + } +#endif + } + +#ifdef QT_MAC_USE_COCOA + if ([NSApp isActive] && !qt_button_down && !QWidget::mouseGrabber()){ + // Update enter/leave immidiatly, don't wait for a move event. But only + // if no grab exists (even if the grab points to this widget, it seems, ref X11) + QPoint qlocal, qglobal; + QWidget *widgetUnderMouse = 0; + qt_mac_getTargetForMouseEvent(0, QEvent::Leave, qlocal, qglobal, 0, &widgetUnderMouse); + QApplicationPrivate::dispatchEnterLeave(widgetUnderMouse, qt_last_native_mouse_receiver); + qt_last_mouse_receiver = widgetUnderMouse; + qt_last_native_mouse_receiver = widgetUnderMouse ? + (widgetUnderMouse->internalWinId() ? widgetUnderMouse : widgetUnderMouse->nativeParentWidget()) : 0; + } +#endif + + topLevelAt_cache = 0; + 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(); + if(!qApp->desktop()->screenNumber(this)) + qt_mac_set_fullscreen_mode(false); + setParent(parentWidget(), d->topData()->savedFlags); + setGeometry(d->topData()->normalGeometry); + 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 && !(oldstate & Qt::WindowFullScreen)) { +#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 + } +} + +NSComparisonResult compareViews2Raise(id view1, id view2, void *context) +{ + id topView = reinterpret_cast<id>(context); + if (view1 == topView) + return NSOrderedDescending; + if (view2 == topView) + return NSOrderedAscending; + return NSOrderedSame; +} + +void QWidgetPrivate::raise_sys() +{ + Q_Q(QWidget); + if((q->windowType() == Qt::Desktop)) + return; + +#if QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; + if (isRealWindow()) { + // With the introduction of spaces it is not as simple as just raising the window. + // First we need to check if we are in the right space. If we are, then we just continue + // as usual. The problem comes when we are not in the active space. There are two main cases: + // 1. Our parent was moved to a new space. In this case we want the window to be raised + // in the same space as its parent. + // 2. We don't have a parent. For this case we will just raise the window and let Cocoa + // switch to the corresponding space. + // NOTICE: There are a lot of corner cases here. We are keeping this simple for now, if + // required we will introduce special handling for some of them. + if (!q->testAttribute(Qt::WA_DontShowOnScreen) && q->isVisible()) { + OSWindowRef window = qt_mac_window_for(q); + // isOnActiveSpace is available only from 10.6 onwards, so we need to check if it is + // available before calling it. + if([window respondsToSelector:@selector(isOnActiveSpace)]) { + if(![window performSelector:@selector(isOnActiveSpace)]) { + QWidget *parentWidget = q->parentWidget(); + if(parentWidget) { + OSWindowRef parentWindow = qt_mac_window_for(parentWidget); + if(parentWindow && [parentWindow respondsToSelector:@selector(isOnActiveSpace)]) { + if ([parentWindow performSelector:@selector(isOnActiveSpace)]) { + // The window was created in a different space. Therefore if we want + // to show it in the current space we need to recreate it in the new + // space. + recreateMacWindow(); + window = qt_mac_window_for(q); + } + } + } + } + } + [window orderFront:window]; + } + if (qt_mac_raise_process) { //we get to be the active process now + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly); + } + } else { + NSView *view = qt_mac_nativeview_for(q); + NSView *parentView = [view superview]; + [parentView sortSubviewsUsingFunction:compareViews2Raise context:reinterpret_cast<void *>(view)]; + } + topLevelAt_cache = 0; +#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 +} + +NSComparisonResult compareViews2Lower(id view1, id view2, void *context) +{ + id topView = reinterpret_cast<id>(context); + if (view1 == topView) + return NSOrderedAscending; + if (view2 == topView) + return NSOrderedDescending; + return NSOrderedSame; +} + +void QWidgetPrivate::lower_sys() +{ + Q_Q(QWidget); + if((q->windowType() == Qt::Desktop)) + return; +#ifdef QT_MAC_USE_COCOA + if (isRealWindow()) { + OSWindowRef window = qt_mac_window_for(q); + [window orderBack:window]; + } else { + NSView *view = qt_mac_nativeview_for(q); + NSView *parentView = [view superview]; + [parentView sortSubviewsUsingFunction:compareViews2Lower context:reinterpret_cast<void *>(view)]; + } + topLevelAt_cache = 0; +#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 +} + +NSComparisonResult compareViews2StackUnder(id view1, id view2, void *context) +{ + const QHash<NSView *, int> &viewOrder = *reinterpret_cast<QHash<NSView *, int> *>(context); + if (viewOrder[view1] < viewOrder[view2]) + return NSOrderedAscending; + if (viewOrder[view1] > viewOrder[view2]) + return NSOrderedDescending; + return NSOrderedSame; +} + +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. + NSView *myView = qt_mac_nativeview_for(q); + NSView *wView = qt_mac_nativeview_for(w); + + QHash<NSView *, int> viewOrder; + NSView *parentView = [myView superview]; + NSArray *subviews = [parentView subviews]; + NSUInteger index = 1; + // make a hash of view->zorderindex and make sure z-value is always odd, + // so that when we modify the order we create a new (even) z-value which + // will not interfere with others. + for (NSView *subview in subviews) { + viewOrder.insert(subview, index * 2); + ++index; + } + viewOrder[myView] = viewOrder[wView] - 1; + + [parentView sortSubviewsUsingFunction:compareViews2StackUnder context:reinterpret_cast<void *>(&viewOrder)]; +#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 +} + +#ifndef QT_MAC_USE_COCOA +/* + 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 + flag +*/ +static void qt_mac_update_widget_position(QWidget *q, QRect oldRect, QRect newRect) +{ + 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 ( + // always repaint on move. + (isMove) || + + // 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 || + + // we are on Panther (no HIViewSetNeedsDisplayInRect) + QSysInfo::MacintoshVersion < QSysInfo::MV_10_4 + ){ + 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); +} +#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)); + + if (!q->internalWinId() && QApplicationPrivate::graphicsSystem() != 0) { + // We have no view to move, and no paint engine that + // we can update dirty regions on. So just return: + return; + } + + QMacCocoaAutoReleasePool pool; + + /* + 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). + */ + + // wrect is the same as crect, except that it is + // clipped to fit inside parent (and screen): + QRect wrect; + + // wrectInParentCoordSys will be the same as wrect, except that it is + // originated in q's parent rather than q itself. It starts out in + // parent's Qt coord system, and ends up in parent's coordinate system: + QRect wrectInParentCoordSys = data.crect; + + // If q's parent has been clipped, parentWRect will + // be filled with the parents clipped crect: + QRect parentWRect; + + // Embedded have different meaning on each platform, and on + // Mac, it means that q is a QMacNativeWidget. + bool isEmbeddedWindow = (q->isWindow() && topData()->embedded); +#ifdef QT_MAC_USE_COCOA + NSView *nsview = qt_mac_nativeview_for(q); +#endif + if (!isEmbeddedWindow) { + parentWRect = q->parentWidget()->data->wrect; + } else { + // INVARIANT: q's parent view is not owned by Qt. So we need to + // do some extra calls to get the clipped rect of the parent view: +#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 { + const QRect wrectRange(-WRECT_MAX,-WRECT_MAX, 2*WRECT_MAX, 2*WRECT_MAX); + parentWRect = wrectRange; + } + } + + if (parentWRect.isValid()) { + // INVARIANT: q's parent has been clipped. + // So we fit our own wrects inside it: + if (!parentWRect.contains(wrectInParentCoordSys) && !isEmbeddedWindow) { + wrectInParentCoordSys &= parentWRect; + wrect = wrectInParentCoordSys; + // Make sure wrect is originated in q's coordinate system: + wrect.translate(-data.crect.topLeft()); + } + // // Make sure wrectInParentCoordSys originated in q's parent coordinate system: + wrectInParentCoordSys.translate(-parentWRect.topLeft()); + } else { + // INVARIANT: we dont know yet the clipping rect of q's parent. + // So we may or may not have to adjust our wrects: + + if (data.wrect.isValid() && QRect(QPoint(),data.crect.size()).contains(data.wrect)) { + // This is where the main optimization is: we have an old wrect from an earlier + // setGeometry call, and the new crect is smaller than it. If the final wrect is + // also inside the old wrect, we can just move q and its children to the new + // location without any clipping: + + // vrect will be the part of q that's will be visible inside + // q's parent. If it inside the old wrect, then we can just move: + QRect vrect = wrectInParentCoordSys & q->parentWidget()->rect(); + vrect.translate(-data.crect.topLeft()); + + if (data.wrect.contains(vrect)) { + wrectInParentCoordSys = data.wrect; + wrectInParentCoordSys.translate(data.crect.topLeft()); +#ifndef QT_MAC_USE_COCOA + HIRect bounds = CGRectMake(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + HIViewSetFrame(qt_mac_nativeview_for(q), &bounds); +#else + if (nsview) { + // INVARIANT: q is native. Set view frame: + NSRect bounds = NSMakeRect(wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + } else { + // INVARIANT: q is alien. Repaint wrect instead (includes old and new wrect): + QWidget *parent = q->parentWidget(); + QPoint globalPosWRect = parent->mapToGlobal(data.wrect.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + QRect dirtyWRect = QRect(nativeParent->mapFromGlobal(globalPosWRect), data.wrect.size()); + + nativeParent->update(dirtyWRect); + } +#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 + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; +#endif + } + } + return; + } + } + +#ifndef QT_MAC_USE_COCOA + const QRect validRange(-XCOORD_MAX,-XCOORD_MAX, 2*XCOORD_MAX, 2*XCOORD_MAX); + if (!validRange.contains(wrectInParentCoordSys)) { + // We're too big, and must clip: + QPoint screenOffset(0, 0); // offset of the part being on screen + const QWidget *parentWidget = q->parentWidget(); + while (parentWidget && !parentWidget->isWindow()) { + screenOffset -= parentWidget->data->crect.topLeft(); + parentWidget = parentWidget->parentWidget(); + } + QRect cropRect(screenOffset.x() - WRECT_MAX, + screenOffset.y() - WRECT_MAX, + 2*WRECT_MAX, + 2*WRECT_MAX); + + wrectInParentCoordSys &=cropRect; + wrect = wrectInParentCoordSys; + wrect.translate(-data.crect.topLeft()); + } +#endif //QT_MAC_USE_COCOA + } + + // unmap if we are outside the valid window system coord system + bool outsideRange = !wrectInParentCoordSys.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 + // If q is Alien, the following call does nothing: + [nsview setHidden:YES]; +#endif + q->setAttribute(Qt::WA_Mapped, false); + } else if (!q->isHidden()) { + mapWindow = true; + } + } + + if (outsideRange) + return; + + // Store the new clipped rect: + 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(); + } + } + +#ifndef QT_MAC_USE_COCOA + // Move the actual HIView: + qt_mac_update_widget_position(q, oldRect, wrectInParentCoordSys); + if (jump) + q->update(); +#else + if (nsview) { + // INVARIANT: q is native. Move the actual NSView: + NSRect bounds = NSMakeRect( + wrectInParentCoordSys.x(), wrectInParentCoordSys.y(), + wrectInParentCoordSys.width(), wrectInParentCoordSys.height()); + [nsview setFrame:bounds]; + if (jump) + q->update(); + } else if (QApplicationPrivate::graphicsSystem() == 0){ + // INVARIANT: q is alien and we use native paint engine. + // Schedule updates where q is moved from and to: + const QWidget *parent = q->parentWidget(); + const QPoint globalPosOldWRect = parent->mapToGlobal(oldRect.topLeft()); + const QPoint globalPosNewWRect = parent->mapToGlobal(wrectInParentCoordSys.topLeft()); + + QWidget *nativeParent = q->nativeParentWidget(); + const QRegion dirtyOldWRect = QRect(nativeParent->mapFromGlobal(globalPosOldWRect), oldRect.size()); + const QRegion dirtyNewWRect = QRect(nativeParent->mapFromGlobal(globalPosNewWRect), wrectInParentCoordSys.size()); + + const bool sizeUnchanged = oldRect.size() == wrectInParentCoordSys.size(); + const bool posUnchanged = oldRect.topLeft() == wrectInParentCoordSys.topLeft(); + + // Resolve/minimize the region that needs to update: + if (sizeUnchanged && q->testAttribute(Qt::WA_OpaquePaintEvent)) { + // INVARIANT: q is opaque, and is only moved (not resized). So in theory we only + // need to blit pixels, and skip a repaint. But we can only make this work if we + // had access to the backbuffer, so we need to update all: + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } else if (posUnchanged && q->testAttribute(Qt::WA_StaticContents)) { + // We only need to redraw exposed areas: + nativeParent->update(dirtyNewWRect - dirtyOldWRect); + } else { + nativeParent->update(dirtyOldWRect | dirtyNewWRect); + } + } +#endif + + if (mapWindow && !dontShow) { + q->setAttribute(Qt::WA_Mapped); +#ifndef QT_MAC_USE_COCOA + HIViewSetVisible(qt_mac_nativeview_for(q), true); +#else + // If q is Alien, the following call does nothing: + [nsview setHidden:NO]; +#endif + } +} + +void QWidgetPrivate::adjustWithinMaxAndMinSize(int &w, int &h) +{ + if (QWExtra *extra = extraData()) { + 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 (isRealWindow()) { + w = qMax(0, w); + h = qMax(0, h); + } +} + +void QWidgetPrivate::applyMaxAndMinSizeOnWindow() +{ + Q_Q(QWidget); + QMacCocoaAutoReleasePool pool; + + 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) setContentMinSize:min]; + [qt_mac_window_for(q) setContentMaxSize:max]; +#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 && !q->testAttribute(Qt::WA_DontShowOnScreen)){ + adjustWithinMaxAndMinSize(w, h); +#ifndef QT_MAC_USE_COCOA + if (w != 0 && h != 0) { + topData()->isSetGeometry = 1; + topData()->isMove = isMove; + Rect r; SetRect(&r, x, y, x + w, y + h); + SetWindowBounds(qt_mac_window_for(q), kWindowContentRgn, &r); + topData()->isSetGeometry = 0; + } else { + setGeometry_sys_helper(x, y, w, h, isMove); + } +#else + if (!isMove && !q->testAttribute(Qt::WA_Moved) && !q->isVisible()) { + // INVARIANT: The location of the window has not yet been set. The default will + // instead be to center it on the desktop, or over the parent, if any. Since we now + // resize the window, we need to adjust the top left position to keep the window + // centeralized. And we need to to this now (and before show) in case the positioning + // of other windows (e.g. sub-windows) depend on this position: + if (QWidget *p = q->parentWidget()) { + x = p->geometry().center().x() - (w / 2); + y = p->geometry().center().y() - (h / 2); + } else { + QRect availGeo = QApplication::desktop()->availableGeometry(q); + x = availGeo.center().x() - (w / 2); + y = availGeo.center().y() - (h / 2); + } + } + + QSize olds = q->size(); + const bool isResize = (olds != QSize(w, h)); + 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()); + // The setFrame call will trigger a 'windowDidResize' notification for the corresponding + // NSWindow. The pending flag is set, so that the resize event can be send as non-spontaneous. + if (isResize) + q->setAttribute(Qt::WA_PendingResizeEvent); + QPoint currTopLeft = data.crect.topLeft(); + if (currTopLeft.x() == x && currTopLeft.y() == y + && cocoaFrameRect.size.width != 0 + && cocoaFrameRect.size.height != 0) { + [window setFrame:cocoaFrameRect display:realWindow]; + } else { + // The window is moved and resized (or resized to zero). + // Since Cocoa usually only sends us a resize callback after + // setting a window frame, we issue an explicit move as + // well. To stop Cocoa from optimize away the move (since the move + // would have the same origin as the setFrame call) we shift the + // window back and forth inbetween. + cocoaFrameRect.origin.y += 1; + [window setFrame:cocoaFrameRect display:realWindow]; + cocoaFrameRect.origin.y -= 1; + [window setFrameOrigin:cocoaFrameRect.origin]; + } +#endif + } else { + setGeometry_sys_helper(x, y, w, h, isMove); + } + + topLevelAt_cache = 0; +} + +void QWidgetPrivate::setGeometry_sys_helper(int x, int y, int w, int h, bool isMove) +{ + Q_Q(QWidget); + bool realWindow = isRealWindow(); + + 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) + data.window_state = data.window_state & ~Qt::WindowMaximized; + + const bool visible = q->isVisible(); + // Apply size restrictions, applicable for Windows & Widgets. + if (QWExtra *extra = extraData()) { + w = qMin(w, extra->maxw); + h = qMin(h, extra->maxh); + w = qMax(w, extra->minw); + h = qMax(h, extra->minh); + } + data.crect = QRect(x, y, w, h); + + if (realWindow) { + adjustWithinMaxAndMinSize(w, h); + qt_mac_update_sizer(q); + +#ifndef QT_MAC_USE_COCOA + if (q->windowFlags() & Qt::WindowMaximizeButtonHint) { + 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); + } + } + 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_resizeHelper(oldp, olds); + } + + 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(); + applyMaxAndMinSizeOnWindow(); +} + +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()) { + // INVARIANT: Alien paint engine + 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 &qscrollRect) +{ + if (QMacScrollOptimization::delayScroll(this, dx, dy, qscrollRect)) + return; + + Q_Q(QWidget); + if (QApplicationPrivate::graphicsSystem() && !paintOnScreen()) { + // INVARIANT: Alien paint engine + scrollRect(qscrollRect, dx, dy); + return; + } + + static int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; + } + + // Scroll the whole widget if qscrollRect is not valid: + QRect validScrollRect = qscrollRect.isValid() ? qscrollRect : q->rect(); + validScrollRect &= clipRect(); + + // If q is overlapped by other widgets, we cannot just blit pixels since + // this will move overlapping widgets as well. In case we just update: + const bool overlapped = isOverlapped(validScrollRect.translated(data.crect.topLeft())); + const bool accelerateScroll = accelEnv && isOpaque && !overlapped; + const bool isAlien = (q->internalWinId() == 0); + const QPoint scrollDelta(dx, dy); + + // If qscrollRect is valid, we are _not_ supposed to scroll q's children (as documented). + // But we do scroll children (and the whole of q) if qscrollRect is invalid. This case is + // documented as undefined, but we exploit it to help factor our code into one function. + const bool scrollChildren = !qscrollRect.isValid(); + + if (!q->updatesEnabled()) { + // We are told not to update anything on q at this point. So unless + // we are supposed to scroll children, we bail out early: + if (!scrollChildren || q->children().isEmpty()) + return; + } + + if (!accelerateScroll) { + if (overlapped) { + QRegion region(validScrollRect); + subtractOpaqueSiblings(region); + update_sys(region); + }else { + update_sys(qscrollRect); + } + return; + } + +#ifdef QT_MAC_USE_COCOA + QMacCocoaAutoReleasePool pool; +#else + Q_UNUSED(isAlien); + // We're not sure what the following call is supposed to achive + // but until we see what it breaks, we don't bring it into the + // Cocoa port: + qt_event_request_window_change(q); +#endif + + // First move all native children. Alien children will indirectly be + // moved when the parent is scrolled. All directly or indirectly moved + // children will receive a move event before the function call returns. + QWidgetList movedChildren; + if (scrollChildren) { + QObjectList children = q->children(); + + for (int i=0; i<children.size(); i++) { + QObject *obj = children.at(i); + if (QWidget *w = qobject_cast<QWidget*>(obj)) { + if (!w->isWindow()) { + w->data->crect = QRect(w->pos() + scrollDelta, w->size()); +#ifndef QT_MAC_USE_COCOA + if (w->testAttribute(Qt::WA_WState_Created)) { + 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 = q->testAttribute(Qt::WA_OpaquePaintEvent); + + if (opaque) + HIViewSetDrawingEnabled(hiview, false); + HIViewSetFrame(hiview, &bounds); + if (opaque) + HIViewSetDrawingEnabled(hiview, true); + } +#else + if (NSView *view = qt_mac_nativeview_for(w)) { + // INVARIANT: w is not alien + [view setFrame:NSMakeRect( + w->data->crect.x(), w->data->crect.y(), + w->data->crect.width(), w->data->crect.height())]; + } +#endif + movedChildren.append(w); + } + } + } + } + + if (q->testAttribute(Qt::WA_WState_Created) && q->isVisible()) { + // Scroll q itself according to the qscrollRect, and + // call update on any exposed areas so that they get redrawn: + +#ifndef QT_MAC_USE_COCOA + OSViewRef view = qt_mac_nativeview_for(q); + HIRect scrollrect = CGRectMake(qscrollRect.x(), qscrollRect.y(), qscrollRect.width(), qscrollRect.height()); + OSStatus err = _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &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(qscrollRect.x(), 0), qMax(qscrollRect.y(), 0), + qMin(qscrollRect.width(), q->width()), qMin(qscrollRect.height(), q->height())); + _HIViewScrollRectWithOptions(view, qscrollRect.isValid() ? &scrollrect : 0, dx, dy, kHIViewScrollRectAdjustInvalid); + } +#else + + QWidget *nativeWidget = isAlien ? q->nativeParentWidget() : q; + if (!nativeWidget) + return; + OSViewRef view = qt_mac_nativeview_for(nativeWidget); + if (!view) + return; + + // Calculate the rectangles that needs to be redrawn + // after the scroll. This will be source rect minus destination rect: + QRect deltaXRect; + if (dx != 0) { + deltaXRect.setY(validScrollRect.y()); + deltaXRect.setHeight(validScrollRect.height()); + if (dx > 0) { + deltaXRect.setX(validScrollRect.x()); + deltaXRect.setWidth(dx); + } else { + deltaXRect.setX(validScrollRect.x() + validScrollRect.width() + dx); + deltaXRect.setWidth(-dx); + } + } + + QRect deltaYRect; + if (dy != 0) { + deltaYRect.setX(validScrollRect.x()); + deltaYRect.setWidth(validScrollRect.width()); + if (dy > 0) { + deltaYRect.setY(validScrollRect.y()); + deltaYRect.setHeight(dy); + } else { + deltaYRect.setY(validScrollRect.y() + validScrollRect.height() + dy); + deltaYRect.setHeight(-dy); + } + } + + if (isAlien) { + // Adjust the scroll rect to the location as seen from the native parent: + QPoint scrollTopLeftInsideNative = nativeWidget->mapFromGlobal(q->mapToGlobal(validScrollRect.topLeft())); + validScrollRect.moveTo(scrollTopLeftInsideNative); + } + + // Make the pixel copy rect within the validScrollRect bounds: + NSRect nsscrollRect = NSMakeRect( + validScrollRect.x() + (dx < 0 ? -dx : 0), + validScrollRect.y() + (dy < 0 ? -dy : 0), + validScrollRect.width() + (dx > 0 ? -dx : 0), + validScrollRect.height() + (dy > 0 ? -dy : 0)); + + NSSize deltaSize = NSMakeSize(dx, dy); + [view scrollRect:nsscrollRect by:deltaSize]; + + // Some areas inside the scroll rect might have been marked as dirty from before, which + // means that they are scheduled to be redrawn. But as we now scroll, those dirty rects + // should also move along to ensure that q receives repaints on the correct places. + // Since some of the dirty rects might lay outside, or only intersect with, the scroll + // rect, the old calls to setNeedsDisplay still makes sense. + // NB: Using [view translateRectsNeedingDisplayInRect:nsscrollRect by:deltaSize] have + // so far not been proven fruitful to solve this problem. + const QVector<QRect> &dirtyRectsToScroll = dirtyOnWidget.rects(); + for (int i=0; i<dirtyRectsToScroll.size(); ++i) { + QRect qdirtyRect = dirtyRectsToScroll[i]; + qdirtyRect.translate(dx, dy); + update_sys(qdirtyRect); + } + + // Update newly exposed areas. This will generate new dirty areas on + // q, and therefore, we do it after updating the old dirty rects above: + if (dx != 0) + update_sys(deltaXRect); + if (dy != 0) + update_sys(deltaYRect); + +#endif // QT_MAC_USE_COCOA + } + + for (int i=0; i<movedChildren.size(); i++) { + QWidget *w = movedChildren.at(i); + QMoveEvent e(w->pos(), w->pos() - scrollDelta); + QApplication::sendEvent(w, &e); + } +} + +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); + if(m == PdmWidth) + return (int)rect.size.width; + return (int)rect.size.height; } +#else + if (m == PdmWidth) + return data->crect.width(); + else + return data->crect.height(); +#endif + 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->resizer = 0; + extra->topextra->isSetGeometry = 0; + extra->topextra->isMove = 0; + extra->topextra->wattr = 0; + extra->topextra->wclass = 0; + extra->topextra->group = 0; + extra->topextra->windowIcon = 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; + } + if (extra->topextra->windowIcon) { + ReleaseIconRef(extra->topextra->windowIcon); + extra->topextra->windowIcon = 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 + NSWindow *win = qt_mac_window_for(q); + if (on) { + if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaWindow) class]]) + [static_cast<QT_MANGLE_NAMESPACE(QCocoaWindow) *>(win) registerDragTypes]; + else if ([win isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaPanel) class]]) + [static_cast<QT_MANGLE_NAMESPACE(QCocoaPanel) *>(win) registerDragTypes]; + } +#endif +} + +void QWidgetPrivate::registerTouchWindow(bool enable) +{ + Q_UNUSED(enable); +#ifdef QT_MAC_USE_COCOA +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_6) + return; + + Q_Q(QWidget); + if (enable == touchEventsEnabled) + return; + + QCocoaView *view = static_cast<QCocoaView *>(qt_mac_effectiveview_for(q)); + if (!view) + return; + + if (enable) { + ++view->alienTouchCount; + if (view->alienTouchCount == 1) { + touchEventsEnabled = true; + [view setAcceptsTouchEvents:YES]; + } + } else { + --view->alienTouchCount; + if (view->alienTouchCount == 0) { + touchEventsEnabled = false; + [view setAcceptsTouchEvents:NO]; + } + } +#endif +#endif +} + +void QWidgetPrivate::setMask_sys(const QRegion ®ion) +{ + Q_UNUSED(region); + Q_Q(QWidget); + +#ifndef QT_MAC_USE_COCOA + if (q->isWindow()) + ReshapeCustomWindow(qt_mac_window_for(q)); + else + HIViewReshapeStructure(qt_mac_nativeview_for(q)); +#else + if (!q->internalWinId()) + return; + + if (extra->mask.isEmpty()) { + extra->maskBits = QImage(); + finishCocoaMaskSetup(); + } else { + syncCocoaMask(); + } + + topLevelAt_cache = 0; +#endif +} + +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; + + OSWindowRef oswindow = qt_mac_window_for(q); +#if QT_MAC_USE_COCOA + [oswindow setAlphaValue:level]; +#else + SetWindowAlpha(oswindow, 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) { + if(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]; + } + macSetNeedsDisplay(QRegion()); +} +#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; + if (!pe) + pe = new QCoreGraphicsPaintEngine(); + if (pe->isActive()) { + QPaintEngine *engine = new QCoreGraphicsPaintEngine(); + engine->setAutoDestruct(true); + return engine; + } + 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 + QMacCocoaAutoReleasePool pool; + bool alreadySheet = [windowRef styleMask] & NSDocModalWindowMask; + + if (windowParent && q->windowModality() == Qt::WindowModal){ + // INVARIANT: Window should be window-modal (which implies a sheet). + if (!alreadySheet) { + // NB: the following call will call setModal_sys recursivly: + recreateMacWindow(); + windowRef = qt_mac_window_for(q); + } + 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 { + // INVARIANT: Window shold _not_ be window-modal (and as such, not a sheet). + if (alreadySheet){ + // NB: the following call will call setModal_sys recursivly: + recreateMacWindow(); + windowRef = qt_mac_window_for(q); + } + if (q->windowModality() == Qt::NonModal + && primaryWindow && primaryWindow->windowModality() == Qt::ApplicationModal) { + // INVARIANT: Our window has a parent that is application modal. + // This means that q is supposed to be on top of this window and + // not be modally shaddowed: + if ([windowRef isKindOfClass:[NSPanel class]]) + [static_cast<NSPanel *>(windowRef) setWorksWhenModal:YES]; + } + } + +#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 { + // Window should be application-modal (which implies NOT using a sheet). + if (old_wclass == kSheetWindowClass){ + // We cannot convert a sheet to a window. + // 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 = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q)); + if (q->testAttribute(Qt::WA_MacBrushedMetal)) { + if (layout) + layout->updateHIToolBarStatus(); + ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalAttribute, 0); + ChangeWindowAttributes(qt_mac_window_for(q), kWindowMetalNoContentSeparatorAttribute, 0); + } else { + 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/platforms/s60/qapplication_s60.cpp b/src/gui/platforms/s60/qapplication_s60.cpp new file mode 100644 index 0000000000..408c3b5883 --- /dev/null +++ b/src/gui/platforms/s60/qapplication_s60.cpp @@ -0,0 +1,2712 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication_p.h" +#include "qsessionmanager.h" +#include "qevent.h" +#include "qsymbianevent.h" +#include "qeventdispatcher_s60_p.h" +#include "qwidget.h" +#include "qdesktopwidget.h" +#include "private/qbackingstore_p.h" +#include "qt_s60_p.h" +#include "private/qevent_p.h" +#include "qstring.h" +#include "qdebug.h" +#include "qimage.h" +#include "qcombobox.h" +#include "private/qkeymapper_p.h" +#include "private/qfont_p.h" +#ifndef QT_NO_STYLE_S60 +#include "private/qs60style_p.h" +#endif +#include "private/qwindowsurface_s60_p.h" +#include "qpaintengine.h" +#include "private/qmenubar_p.h" +#include "private/qsoftkeymanager_p.h" +#ifdef QT_GRAPHICSSYSTEM_RUNTIME +#include "private/qgraphicssystem_runtime_p.h" +#endif + +#include "apgwgnam.h" // For CApaWindowGroupName +#include <mdaaudiotoneplayer.h> // For CMdaAudioToneUtility + +#if defined(Q_OS_SYMBIAN) +# include <private/qs60mainapplication_p.h> +# include <centralrepository.h> +# include "qs60mainappui.h" +# include "qinputcontext.h" +#endif + +#if defined(Q_WS_S60) +# if !defined(QT_NO_IM) +# include <private/qcoefepinputcontext_p.h> +# endif +#endif + +#include "private/qstylesheetstyle_p.h" + +#include <hal.h> +#include <hal_data.h> + +#ifdef Q_SYMBIAN_TRANSITION_EFFECTS +#include <graphics/wstfxconst.h> +#endif + +QT_BEGIN_NAMESPACE + +// Goom Events through Window Server +static const int KGoomMemoryLowEvent = 0x10282DBF; +static const int KGoomMemoryGoodEvent = 0x20026790; +// Split view open/close events from AVKON +static const int KSplitViewOpenEvent = 0x2001E2C0; +static const int KSplitViewCloseEvent = 0x2001E2C1; + +#if defined(QT_DEBUG) +static bool appNoGrab = false; // Grabbing enabled +#endif +static bool app_do_modal = false; // modal mode +Q_GLOBAL_STATIC(QS60Data, qt_s60Data); + +extern bool qt_sendSpontaneousEvent(QObject*,QEvent*); +extern QWidgetList *qt_modal_stack; // stack of modal widgets +extern QDesktopWidget *qt_desktopWidget; // qapplication.cpp + +QWidget *qt_button_down = 0; // widget got last button-down + +QSymbianControl *QSymbianControl::lastFocusedControl = 0; + +QS60Data* qGlobalS60Data() +{ + return qt_s60Data(); +} + +#ifdef Q_WS_S60 +void QS60Data::setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible) +{ + bool buttonGroupVisibilityChanged = false; + if (CEikButtonGroupContainer *const b = buttonGroupContainer()) { + buttonGroupVisibilityChanged = (b->IsVisible() != buttonGroupVisible); + b->MakeVisible(buttonGroupVisible); + } + bool statusPaneVisibilityChanged = false; + if (CEikStatusPane *const s = statusPane()) { + statusPaneVisibilityChanged = (s->IsVisible() != statusPaneVisible); + s->MakeVisible(statusPaneVisible); + } + if (buttonGroupVisibilityChanged || statusPaneVisibilityChanged) { + const QSize size = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()).size(); + const QSize oldSize; // note that QDesktopWidget::resizeEvent ignores the QResizeEvent contents + QResizeEvent event(size, oldSize); + QApplication::instance()->sendEvent(QApplication::desktop(), &event); + } + if (buttonGroupVisibilityChanged && !statusPaneVisibilityChanged && QApplication::activeWindow()) + // Ensure that control rectangle is updated + static_cast<QSymbianControl *>(QApplication::activeWindow()->winId())->handleClientAreaChange(); +} + +bool QS60Data::setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState) +{ + // Show statusbar: + // Topmost parent: Show unless fullscreen/minimized. + // Child windows: Follow topmost parent, unless fullscreen, in which case do not show statusbar + // Show CBA: + // Topmost parent: Show unless fullscreen/minimized. + // Exception: Show if fullscreen with Qt::WindowSoftkeysVisibleHint. + // Child windows: + // Minimized: Unclear if there is an use case for having focused minimized window at all. + // Always follow topmost parent just to be safe. + // Maximized and normal: follow topmost parent. + // Exception: If topmost parent is not showing CBA, show CBA if any softkey actions are + // defined. + // Fullscreen: Show only if Qt::WindowSoftkeysVisibleHint set. + + Qt::WindowStates comparisonState = newState; + QWidget *parentWindow = window->parentWidget(); + if (parentWindow) { + while (parentWindow->parentWidget()) + parentWindow = parentWindow->parentWidget(); + comparisonState = parentWindow->windowState(); + } else { + parentWindow = window; + } + + bool decorationsVisible = !(comparisonState & (Qt::WindowFullScreen | Qt::WindowMinimized)); + const bool parentIsFullscreen = comparisonState & Qt::WindowFullScreen; + const bool parentCbaVisibilityHint = parentWindow->windowFlags() & Qt::WindowSoftkeysVisibleHint; + bool buttonGroupVisibility = (decorationsVisible || (parentIsFullscreen && parentCbaVisibilityHint)); + + // Do extra checking for child windows + if (window->parentWidget()) { + if (newState & Qt::WindowFullScreen) { + decorationsVisible = false; + if (window->windowFlags() & Qt::WindowSoftkeysVisibleHint) + buttonGroupVisibility = true; + else + buttonGroupVisibility = false; + } else if (!(newState & Qt::WindowMinimized) && !buttonGroupVisibility) { + for (int i = 0; i < window->actions().size(); ++i) { + if (window->actions().at(i)->softKeyRole() != QAction::NoSoftKey) { + buttonGroupVisibility = true; + break; + } + } + } + } + + S60->setStatusPaneAndButtonGroupVisibility(decorationsVisible, buttonGroupVisibility); + + return decorationsVisible; +} +#endif + +void QS60Data::controlVisibilityChanged(CCoeControl *control, bool visible) +{ + if (QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control)) { + QWidget *const widget = QWidgetPrivate::mapper->value(control); + QWidget *const window = widget->window(); + if (QTLWExtra *topData = qt_widget_private(window)->maybeTopData()) { + QWidgetBackingStoreTracker &backingStore = topData->backingStore; + if (visible) { + if (backingStore.data()) { + backingStore.registerWidget(widget); + } else { + backingStore.create(window); + backingStore.registerWidget(widget); + qt_widget_private(widget)->invalidateBuffer(widget->rect()); + widget->repaint(); + } + } else { + // In certain special scenarios we may get an ENotVisible event + // without a previous EPartiallyVisible. The backingstore must + // still be destroyed, hence the registerWidget() call below. + if (backingStore.data() && widget->internalWinId() + && qt_widget_private(widget)->maybeBackingStore() == backingStore.data()) + backingStore.registerWidget(widget); + backingStore.unregisterWidget(widget); + // In order to ensure that any resources used by the window surface + // are immediately freed, we flush the WSERV command buffer. + S60->wsSession().Flush(); + } + } + } +} + +bool qt_nograb() // application no-grab option +{ +#if defined(QT_DEBUG) + return appNoGrab; +#else + return false; +#endif +} + +// Modified from http://www3.symbian.com/faq.nsf/0/0F1464EE96E737E780256D5E00503DD1?OpenDocument +class QS60Beep : public CBase, public MMdaAudioToneObserver +{ +public: + static QS60Beep* NewL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration); + void Play(); + ~QS60Beep(); +private: + void ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds iDuration); + void MatoPrepareComplete(TInt aError); + void MatoPlayComplete(TInt aError); +private: + typedef enum + { + EBeepNotPrepared, + EBeepPrepared, + EBeepPlaying + } TBeepState; +private: + CMdaAudioToneUtility* iToneUtil; + TBeepState iState; + TInt iFrequency; + TTimeIntervalMicroSeconds iDuration; +}; + +static QS60Beep* qt_S60Beep = 0; + +QS60Beep::~QS60Beep() +{ + if (iToneUtil) { + switch (iState) { + case EBeepPlaying: + iToneUtil->CancelPlay(); + break; + case EBeepNotPrepared: + iToneUtil->CancelPrepare(); + break; + } + } + delete iToneUtil; +} + +QS60Beep* QS60Beep::NewL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration) +{ + QS60Beep* self = new (ELeave) QS60Beep(); + CleanupStack::PushL(self); + self->ConstructL(aFrequency, aDuration); + CleanupStack::Pop(); + return self; +} + +void QS60Beep::ConstructL(TInt aFrequency, TTimeIntervalMicroSeconds aDuration) +{ + iToneUtil = CMdaAudioToneUtility::NewL(*this); + iState = EBeepNotPrepared; + iFrequency = aFrequency; + iDuration = aDuration; + iToneUtil->PrepareToPlayTone(iFrequency, iDuration); +} + +void QS60Beep::Play() +{ + if (iState == EBeepPlaying) { + iToneUtil->CancelPlay(); + iState = EBeepPrepared; + } + + iToneUtil->Play(); + iState = EBeepPlaying; +} + +void QS60Beep::MatoPrepareComplete(TInt aError) +{ + if (aError == KErrNone) { + iState = EBeepPrepared; + Play(); + } +} + +void QS60Beep::MatoPlayComplete(TInt aError) +{ + Q_UNUSED(aError); + iState = EBeepPrepared; +} + + +static Qt::KeyboardModifiers mapToQtModifiers(TUint s60Modifiers) +{ + Qt::KeyboardModifiers result = Qt::NoModifier; + + if (s60Modifiers & EModifierKeypad) + result |= Qt::KeypadModifier; + if (s60Modifiers & EModifierShift || s60Modifiers & EModifierLeftShift + || s60Modifiers & EModifierRightShift) + result |= Qt::ShiftModifier; + if (s60Modifiers & EModifierCtrl || s60Modifiers & EModifierLeftCtrl + || s60Modifiers & EModifierRightCtrl) + result |= Qt::ControlModifier; + if (s60Modifiers & EModifierAlt || s60Modifiers & EModifierLeftAlt + || s60Modifiers & EModifierRightAlt) + result |= Qt::AltModifier; + + return result; +} + +static void mapS60MouseEventTypeToQt(QEvent::Type *type, Qt::MouseButton *button, const TPointerEvent *pEvent) +{ + switch (pEvent->iType) { + case TPointerEvent::EButton1Down: + *type = QEvent::MouseButtonPress; + *button = Qt::LeftButton; + break; + case TPointerEvent::EButton1Up: + *type = QEvent::MouseButtonRelease; + *button = Qt::LeftButton; + break; + case TPointerEvent::EButton2Down: + *type = QEvent::MouseButtonPress; + *button = Qt::MidButton; + break; + case TPointerEvent::EButton2Up: + *type = QEvent::MouseButtonRelease; + *button = Qt::MidButton; + break; + case TPointerEvent::EButton3Down: + *type = QEvent::MouseButtonPress; + *button = Qt::RightButton; + break; + case TPointerEvent::EButton3Up: + *type = QEvent::MouseButtonRelease; + *button = Qt::RightButton; + break; + case TPointerEvent::EDrag: + *type = QEvent::MouseMove; + *button = Qt::NoButton; + break; + case TPointerEvent::EMove: + // Qt makes no distinction between move and drag + *type = QEvent::MouseMove; + *button = Qt::NoButton; + break; + default: + *type = QEvent::None; + *button = Qt::NoButton; + break; + } + if (pEvent->iModifiers & EModifierDoubleClick){ + *type = QEvent::MouseButtonDblClick; + } + + if (*type == QEvent::MouseButtonPress || *type == QEvent::MouseButtonDblClick) + QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons | (*button); + else if (*type == QEvent::MouseButtonRelease) + QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons &(~(*button)); + + QApplicationPrivate::mouse_buttons = QApplicationPrivate::mouse_buttons & Qt::MouseButtonMask; +} + +//### Can be replaced with CAknLongTapDetector if animation is required. +//NOTE: if CAknLongTapDetector is used make sure it gets variated out of 3.1 and 3.2,. +//also MLongTapObserver needs to be changed to MAknLongTapDetectorCallBack if CAknLongTapDetector is used. +class QLongTapTimer : public CTimer +{ +public: + static QLongTapTimer* NewL(QAbstractLongTapObserver *observer); + QLongTapTimer(QAbstractLongTapObserver *observer); + void ConstructL(); +public: + void PointerEventL(const TPointerEvent &event); + void RunL(); +protected: +private: + QAbstractLongTapObserver *m_observer; + TPointerEvent m_event; + QPoint m_pressedCoordinates; + int m_dragDistance; +}; + +QLongTapTimer* QLongTapTimer::NewL(QAbstractLongTapObserver *observer) +{ + QLongTapTimer* self = new QLongTapTimer(observer); + self->ConstructL(); + return self; +} +void QLongTapTimer::ConstructL() +{ + CTimer::ConstructL(); +} + +QLongTapTimer::QLongTapTimer(QAbstractLongTapObserver *observer):CTimer(CActive::EPriorityHigh) +{ + m_observer = observer; + m_dragDistance = qApp->startDragDistance(); + CActiveScheduler::Add(this); +} + +void QLongTapTimer::PointerEventL(const TPointerEvent& event) +{ + if ( event.iType == TPointerEvent::EDrag || event.iType == TPointerEvent::EButtonRepeat) + { + QPoint diff(QPoint(event.iPosition.iX,event.iPosition.iY) - m_pressedCoordinates); + if (diff.manhattanLength() < m_dragDistance) + return; + } + Cancel(); + m_event = event; + if (event.iType == TPointerEvent::EButton1Down) + { + m_pressedCoordinates = QPoint(event.iPosition.iX,event.iPosition.iY); + // must be same as KLongTapDelay in aknlongtapdetector.h + After(800000); + } +} +void QLongTapTimer::RunL() +{ + if (m_observer) + m_observer->HandleLongTapEventL(m_event.iPosition, m_event.iParentPosition); +} + +QSymbianControl::QSymbianControl(QWidget *w) + : CCoeControl() + , qwidget(w) + , m_longTapDetector(0) + , m_ignoreFocusChanged(0) + , m_symbianPopupIsOpen(0) + , m_inExternalScreenOverride(false) + , m_lastStatusPaneVisibility(0) +{ +} + +void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) +{ + if (!desktop) + { + if (isWindowOwning || !qwidget->parentWidget() + || qwidget->parentWidget()->windowType() == Qt::Desktop) { + RWindowGroup &wg(S60->windowGroup(qwidget)); + CreateWindowL(wg); + } else { + /** + * TODO: in order to avoid creating windows for all ancestors of + * this widget up to the root window, the parameter passed to + * CreateWindowL should be + * qwidget->parentWidget()->effectiveWinId(). However, if we do + * this, then we need to take care of re-parenting when a window + * is created for a widget between this one and the root window. + */ + CreateWindowL(qwidget->parentWidget()->winId()); + } + + // Necessary in order to be able to track the activation status of + // the control's window + qwidget->d_func()->createExtra(); + + SetFocusing(true); + m_longTapDetector = QLongTapTimer::NewL(this); + m_doubleClickTimer.invalidate(); + + DrawableWindow()->SetPointerGrab(ETrue); + } + +#ifdef Q_SYMBIAN_TRANSITION_EFFECTS + if (OwnsWindow()) { + TTfxWindowPurpose windowPurpose(ETfxPurposeNone); + switch (qwidget->windowType()) { + case Qt::Dialog: + windowPurpose = ETfxPurposeDialogWindow; + break; + case Qt::Popup: + windowPurpose = ETfxPurposePopupWindow; + break; + case Qt::Tool: + windowPurpose = ETfxPurposeToolWindow; + break; + case Qt::ToolTip: + windowPurpose = ETfxPurposeToolTipWindow; + break; + case Qt::SplashScreen: + windowPurpose = ETfxPurposeSplashScreenWindow; + break; + default: + windowPurpose = (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop) + ? ETfxPurposeWindow : ETfxPurposeChildWindow; + break; + } + Window().SetPurpose(windowPurpose); + } +#endif +} + +QSymbianControl::~QSymbianControl() +{ + // Ensure backing store is deleted before the top-level + // window is destroyed + qt_widget_private(qwidget)->topData()->backingStore.destroy(); + + if (S60->curWin == this) + S60->curWin = 0; + if (!QApplicationPrivate::is_app_closing) { + QT_TRY { + setFocusSafely(false); + } QT_CATCH(const std::exception&) { + // ignore exceptions, nothing can be done + } + } + S60->appUi()->RemoveFromStack(this); + delete m_longTapDetector; +} + +void QSymbianControl::setWidget(QWidget *w) +{ + qwidget = w; +} + +QPoint QSymbianControl::translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const +{ + QPoint pos(pointerEventPos.iX, pointerEventPos.iY); + if (qwidget->d_func()->fixNativeOrientationCalled) { + QSize wsize = qwidget->size(); + TSize size = Size(); + if (size.iWidth == wsize.height() && size.iHeight == wsize.width()) { + qreal x = pos.x(); + qreal y = pos.y(); + pos.setX(size.iHeight - y); + pos.setY(x); + } + } + return pos; +} + +TRect QSymbianControl::translateRectForFixedNativeOrientation(const TRect &controlRect) const +{ + TRect rect = controlRect; + if (qwidget->d_func()->fixNativeOrientationCalled) { + QPoint a = translatePointForFixedNativeOrientation(rect.iTl); + QPoint b = translatePointForFixedNativeOrientation(rect.iBr); + if (a.x() < b.x()) { + rect.iTl.iX = a.x(); + rect.iBr.iX = b.x(); + } else { + rect.iTl.iX = b.x(); + rect.iBr.iX = a.x(); + } + if (a.y() < b.y()) { + rect.iTl.iY = a.y(); + rect.iBr.iY = b.y(); + } else { + rect.iTl.iY = b.y(); + rect.iBr.iY = a.y(); + } + } + return rect; +} + +void QSymbianControl::HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ) +{ + QWidget *alienWidget; + QPoint widgetPos = translatePointForFixedNativeOrientation(aPenEventLocation); + QPoint globalPos = translatePointForFixedNativeOrientation(aPenEventScreenLocation); + alienWidget = qwidget->childAt(widgetPos); + if (!alienWidget) + alienWidget = qwidget; + +#if !defined(QT_NO_CONTEXTMENU) + QContextMenuEvent contextMenuEvent(QContextMenuEvent::Mouse, widgetPos, globalPos, Qt::NoModifier); + qt_sendSpontaneousEvent(alienWidget, &contextMenuEvent); +#endif +} + +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER +void QSymbianControl::translateAdvancedPointerEvent(const TAdvancedPointerEvent *event) +{ + QApplicationPrivate *d = QApplicationPrivate::instance(); + QPointF screenPos = qwidget->mapToGlobal(translatePointForFixedNativeOrientation(event->iPosition)); + qreal pressure; + if(d->pressureSupported + && event->Pressure() > 0) //workaround for misconfigured HAL + pressure = event->Pressure() / qreal(d->maxTouchPressure); + else + pressure = qreal(1.0); + processTouchEvent(event->PointerNumber(), event->iType, screenPos, pressure); +} +#endif + +void QSymbianControl::processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure) +{ + QRect screenGeometry = qApp->desktop()->screenGeometry(qwidget); + + QApplicationPrivate *d = QApplicationPrivate::instance(); + + QList<QTouchEvent::TouchPoint> points = d->appAllTouchPoints; + while (points.count() <= pointerNumber) + points.append(QTouchEvent::TouchPoint(points.count())); + + Qt::TouchPointStates allStates = 0; + for (int i = 0; i < points.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = points[i]; + + if (touchPoint.id() == pointerNumber) { + Qt::TouchPointStates state; + switch (type) { + case TPointerEvent::EButton1Down: +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + case TPointerEvent::EEnterHighPressure: +#endif + state = Qt::TouchPointPressed; + break; + case TPointerEvent::EButton1Up: +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + case TPointerEvent::EExitCloseProximity: +#endif + state = Qt::TouchPointReleased; + break; + case TPointerEvent::EDrag: + state = Qt::TouchPointMoved; + break; + default: + // how likely is this to happen? + state = Qt::TouchPointStationary; + break; + } + if (pointerNumber == 0) + state |= Qt::TouchPointPrimary; + touchPoint.setState(state); + + touchPoint.setScreenPos(screenPos); + touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(), + screenPos.y() / screenGeometry.height())); + + touchPoint.setPressure(pressure); + } else if (touchPoint.state() != Qt::TouchPointReleased) { + // all other active touch points should be marked as stationary + touchPoint.setState(Qt::TouchPointStationary); + } + + allStates |= touchPoint.state(); + } + + if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) { + // all touch points released + d->appAllTouchPoints.clear(); + } else { + d->appAllTouchPoints = points; + } + + QApplicationPrivate::translateRawTouchEvent(qwidget, + QTouchEvent::TouchScreen, + points); +} + +void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent) +{ +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + if (pEvent.IsAdvancedPointerEvent()) { + const TAdvancedPointerEvent *advancedPointerEvent = pEvent.AdvancedPointerEvent(); + translateAdvancedPointerEvent(advancedPointerEvent); + if (advancedPointerEvent->PointerNumber() != 0) { + // only send mouse events for the first touch point + return; + } + } +#endif + + m_longTapDetector->PointerEventL(pEvent); + QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent)); +} + +void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) +{ + QMouseEvent::Type type; + Qt::MouseButton button; + mapS60MouseEventTypeToQt(&type, &button, &pEvent); + Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); + + QPoint widgetPos = translatePointForFixedNativeOrientation(pEvent.iPosition); + TPoint controlScreenPos = PositionRelativeToScreen(); + QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos; + S60->lastCursorPos = globalPos; + S60->lastPointerEventPos = widgetPos; + + QWidget *mouseGrabber = QWidget::mouseGrabber(); + + QWidget *popupWidget = qApp->activePopupWidget(); + QWidget *popupReceiver = 0; + if (popupWidget) { + QWidget *popupChild = popupWidget->childAt(popupWidget->mapFromGlobal(globalPos)); + popupReceiver = popupChild ? popupChild : popupWidget; + } + + if (mouseGrabber) { + if (popupReceiver) { + sendMouseEvent(popupReceiver, type, globalPos, button, modifiers); + } else { + sendMouseEvent(mouseGrabber, type, globalPos, button, modifiers); + } + // No Enter/Leave events in grabbing mode. + return; + } + + QWidget *widgetUnderPointer = qwidget->childAt(widgetPos); + if (!widgetUnderPointer) + widgetUnderPointer = qwidget; + + QApplicationPrivate::dispatchEnterLeave(widgetUnderPointer, S60->lastPointerEventTarget); + S60->lastPointerEventTarget = widgetUnderPointer; + + QWidget *receiver; + if (!popupReceiver && S60->mousePressTarget && type != QEvent::MouseButtonPress) { + receiver = S60->mousePressTarget; + if (type == QEvent::MouseButtonRelease) + S60->mousePressTarget = 0; + } else { + receiver = popupReceiver ? popupReceiver : widgetUnderPointer; + if (type == QEvent::MouseButtonPress) + S60->mousePressTarget = receiver; + } + +#if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS) + if (S60->brokenPointerCursors) + qt_symbian_move_cursor_sprite(); +#endif + +//Generate single touch event for S60 5.0 (has touchscreen, does not have advanced pointers) +#ifndef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + if (S60->hasTouchscreen) { + processTouchEvent(0, pEvent.iType, QPointF(globalPos), 1.0); + } +#endif + + sendMouseEvent(receiver, type, globalPos, button, modifiers); +} + +#ifdef Q_WS_S60 +void QSymbianControl::HandleStatusPaneSizeChange() +{ + QS60MainAppUi *s60AppUi = static_cast<QS60MainAppUi *>(S60->appUi()); + s60AppUi->HandleStatusPaneSizeChange(); +} +#endif + +void QSymbianControl::sendMouseEvent( + QWidget *receiver, + QEvent::Type type, + const QPoint &globalPos, + Qt::MouseButton button, + Qt::KeyboardModifiers modifiers) +{ + Q_ASSERT(receiver); + QMouseEvent mEvent(type, receiver->mapFromGlobal(globalPos), globalPos, + button, QApplicationPrivate::mouse_buttons, modifiers); + QEventDispatcherS60 *dispatcher; + // It is theoretically possible for someone to install a different event dispatcher. + if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(receiver->d_func()->threadData->eventDispatcher)) != 0) { + if (dispatcher->excludeUserInputEvents()) { + dispatcher->saveInputEvent(this, receiver, new QMouseEvent(mEvent)); + return; + } + } + + sendMouseEvent(receiver, &mEvent); +} + +bool QSymbianControl::sendMouseEvent(QWidget *widget, QMouseEvent *mEvent) +{ + return qt_sendSpontaneousEvent(widget, mEvent); +} + +TKeyResponse QSymbianControl::OfferKeyEventL(const TKeyEvent& keyEvent, TEventCode type) +{ + TKeyResponse r = EKeyWasNotConsumed; + QT_TRYCATCH_LEAVING(r = OfferKeyEvent(keyEvent, type)); + return r; +} + +TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCode type) +{ + /* + S60 has a confusing way of delivering key events. There are three types of + events: EEventKey, EEventKeyDown and EEventKeyUp. When a key is pressed, + EEventKeyDown is first generated, followed by EEventKey. Then, when the key is + released, EEventKeyUp is generated. + However, it is possible that only the EEventKey is generated alone, typically + in relation to virtual keyboards. In that case we need to take care to + generate both press and release events in Qt, since applications expect that. + We do this by having three states for each used scan code, depending on the + events received. See the switch below for what happens in each state + transition. + */ + + if (type != EEventKeyDown) + if (handleVirtualMouse(keyEvent, type) == EKeyWasConsumed) + return EKeyWasConsumed; + + TKeyResponse ret = EKeyWasNotConsumed; +#define GET_RETURN(x) (ret = ((x) == EKeyWasConsumed) ? EKeyWasConsumed : ret) + + // This top level switch corresponds to the states, and the inner switches + // correspond to the transitions. + QS60Data::ScanCodeState &scanCodeState = S60->scanCodeStates[keyEvent.iScanCode]; + switch (scanCodeState) { + case QS60Data::Unpressed: + switch (type) { + case EEventKeyDown: + scanCodeState = QS60Data::KeyDown; + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + break; + case EEventKeyUp: + // No action. + break; + } + break; + case QS60Data::KeyDown: + switch (type) { + case EEventKeyDown: + // This should never happen, just stay in this state to be safe. + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + scanCodeState = QS60Data::KeyDownAndKey; + break; + case EEventKeyUp: + scanCodeState = QS60Data::Unpressed; + break; + } + break; + case QS60Data::KeyDownAndKey: + switch (type) { + case EEventKeyDown: + // This should never happen, just stay in this state to be safe. + break; + case EEventKey: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyPress)); + break; + case EEventKeyUp: + GET_RETURN(sendSymbianKeyEvent(keyEvent, QEvent::KeyRelease)); + scanCodeState = QS60Data::Unpressed; + break; + } + break; + } + return ret; + +#undef GET_RETURN +} + +TKeyResponse QSymbianControl::sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type) +{ + // Because S60 does not generate keysyms for EKeyEventDown and EKeyEventUp + // events, we need to cache the keysyms from the EKeyEvent events. This is what + // resolveS60ScanCode does. + TUint s60Keysym = QApplicationPrivate::resolveS60ScanCode(keyEvent.iScanCode, + keyEvent.iCode); + int keyCode; + if (s60Keysym == EKeyNull){ //some key events have 0 in iCode, for them iScanCode should be used + keyCode = qt_keymapper_private()->mapS60ScanCodesToQt(keyEvent.iScanCode); + } else if (s60Keysym >= 0x20 && s60Keysym < ENonCharacterKeyBase) { + // Normal characters keys. + keyCode = s60Keysym; + } else { + // Special S60 keys. + keyCode = qt_keymapper_private()->mapS60KeyToQt(s60Keysym); + } + + Qt::KeyboardModifiers mods = mapToQtModifiers(keyEvent.iModifiers); + QKeyEventEx qKeyEvent(type, keyCode, mods, qt_keymapper_private()->translateKeyEvent(keyCode, mods), + (keyEvent.iRepeats != 0), 1, keyEvent.iScanCode, s60Keysym, keyEvent.iModifiers); + QWidget *widget; + widget = QWidget::keyboardGrabber(); + if (!widget) { + if (QApplicationPrivate::popupWidgets != 0) { + widget = QApplication::activePopupWidget()->focusWidget(); + if (!widget) { + widget = QApplication::activePopupWidget(); + } + } else { + widget = QApplicationPrivate::focus_widget; + if (!widget) { + widget = qwidget; + } + } + } + + QEventDispatcherS60 *dispatcher; + // It is theoretically possible for someone to install a different event dispatcher. + if ((dispatcher = qobject_cast<QEventDispatcherS60 *>(widget->d_func()->threadData->eventDispatcher)) != 0) { + if (dispatcher->excludeUserInputEvents()) { + dispatcher->saveInputEvent(this, widget, new QKeyEventEx(qKeyEvent)); + return EKeyWasConsumed; + } + } + return sendKeyEvent(widget, &qKeyEvent); +} + +TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type) +{ +#ifndef QT_NO_CURSOR + if (S60->mouseInteractionEnabled && S60->virtualMouseRequired) { + //translate keys to pointer + if ((keyEvent.iScanCode >= EStdKeyLeftArrow && keyEvent.iScanCode <= EStdKeyDownArrow) || + (keyEvent.iScanCode >= EStdKeyDevice10 && keyEvent.iScanCode <= EStdKeyDevice13) || + keyEvent.iScanCode == EStdKeyDevice3) { + QPoint pos = QCursor::pos(); + TPointerEvent fakeEvent; + fakeEvent.iType = (TPointerEvent::TType)(-1); + fakeEvent.iModifiers = keyEvent.iModifiers; + TInt x = pos.x(); + TInt y = pos.y(); + if (type == EEventKeyUp) { + S60->virtualMouseAccelTimeout.start(); + switch (keyEvent.iScanCode) { + case EStdKeyLeftArrow: + S60->virtualMousePressedKeys &= ~QS60Data::Left; + break; + case EStdKeyRightArrow: + S60->virtualMousePressedKeys &= ~QS60Data::Right; + break; + case EStdKeyUpArrow: + S60->virtualMousePressedKeys &= ~QS60Data::Up; + break; + case EStdKeyDownArrow: + S60->virtualMousePressedKeys &= ~QS60Data::Down; + break; + // diagonal keys (named aliases don't exist in 3.1 SDK) + case EStdKeyDevice10: + S60->virtualMousePressedKeys &= ~QS60Data::LeftUp; + break; + case EStdKeyDevice11: + S60->virtualMousePressedKeys &= ~QS60Data::RightUp; + break; + case EStdKeyDevice12: + S60->virtualMousePressedKeys &= ~QS60Data::RightDown; + break; + case EStdKeyDevice13: + S60->virtualMousePressedKeys &= ~QS60Data::LeftDown; + break; + case EStdKeyDevice3: //select + if (S60->virtualMousePressedKeys & QS60Data::Select) + fakeEvent.iType = TPointerEvent::EButton1Up; + S60->virtualMousePressedKeys &= ~QS60Data::Select; + break; + } + } + else if (type == EEventKey) { + int dx = 0; + int dy = 0; + if (keyEvent.iScanCode != EStdKeyDevice3) { + m_doubleClickTimer.invalidate(); + //reset mouse accelleration after a short time with no moves + const int maxTimeBetweenKeyEventsMs = 500; + if (S60->virtualMouseAccelTimeout.isValid() && + S60->virtualMouseAccelTimeout.hasExpired(maxTimeBetweenKeyEventsMs)) { + S60->virtualMouseAccelDX = 0; + S60->virtualMouseAccelDY = 0; + } + S60->virtualMouseAccelTimeout.invalidate(); + } + switch (keyEvent.iScanCode) { + case EStdKeyLeftArrow: + S60->virtualMousePressedKeys |= QS60Data::Left; + dx = -1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyRightArrow: + S60->virtualMousePressedKeys |= QS60Data::Right; + dx = 1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyUpArrow: + S60->virtualMousePressedKeys |= QS60Data::Up; + dy = -1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDownArrow: + S60->virtualMousePressedKeys |= QS60Data::Down; + dy = 1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDevice10: + S60->virtualMousePressedKeys |= QS60Data::LeftUp; + dx = -1; + dy = -1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDevice11: + S60->virtualMousePressedKeys |= QS60Data::RightUp; + dx = 1; + dy = -1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDevice12: + S60->virtualMousePressedKeys |= QS60Data::RightDown; + dx = 1; + dy = 1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDevice13: + S60->virtualMousePressedKeys |= QS60Data::LeftDown; + dx = -1; + dy = 1; + fakeEvent.iType = TPointerEvent::EMove; + break; + case EStdKeyDevice3: + // Platform bug. If you start pressing several keys simultaneously (for + // example for drag'n'drop), Symbian starts producing spurious up and + // down messages for some keys. Therefore, make sure we have a clean slate + // of pressed keys before starting a new button press. + if (S60->virtualMousePressedKeys & QS60Data::Select) { + return EKeyWasConsumed; + } else { + S60->virtualMousePressedKeys |= QS60Data::Select; + fakeEvent.iType = TPointerEvent::EButton1Down; + if (m_doubleClickTimer.isValid() + && !m_doubleClickTimer.hasExpired(QApplication::doubleClickInterval())) { + fakeEvent.iModifiers |= EModifierDoubleClick; + m_doubleClickTimer.invalidate(); + } else { + m_doubleClickTimer.start(); + } + } + break; + } + if (dx) { + int cdx = S60->virtualMouseAccelDX; + //reset accel on change of sign, else double accel + if (dx * cdx <= 0) + cdx = dx; + else + cdx *= 4; + //cap accelleration + if (dx * cdx > S60->virtualMouseMaxAccel) + cdx = dx * S60->virtualMouseMaxAccel; + //move mouse position + x += cdx; + S60->virtualMouseAccelDX = cdx; + } + + if (dy) { + int cdy = S60->virtualMouseAccelDY; + if (dy * cdy <= 0) + cdy = dy; + else + cdy *= 4; + if (dy * cdy > S60->virtualMouseMaxAccel) + cdy = dy * S60->virtualMouseMaxAccel; + y += cdy; + S60->virtualMouseAccelDY = cdy; + } + } + //clip to screen size (window server allows a sprite hotspot to be outside the screen) + int screenNumber = S60->screenNumberForWidget(qwidget); + if (x < 0) + x = 0; + else if (x >= S60->screenWidthInPixelsForScreen[screenNumber]) + x = S60->screenWidthInPixelsForScreen[screenNumber] - 1; + if (y < 0) + y = 0; + else if (y >= S60->screenHeightInPixelsForScreen[screenNumber]) + y = S60->screenHeightInPixelsForScreen[screenNumber] - 1; + TPoint epos(x, y); + TPoint cpos = epos - PositionRelativeToScreen(); + fakeEvent.iPosition = cpos; + fakeEvent.iParentPosition = epos; + if(fakeEvent.iType != -1) + HandlePointerEvent(fakeEvent); + return EKeyWasConsumed; + } + } +#endif + + return EKeyWasNotConsumed; +} + +void QSymbianControl::sendInputEvent(QWidget *widget, QInputEvent *inputEvent) +{ + switch (inputEvent->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + sendKeyEvent(widget, static_cast<QKeyEvent *>(inputEvent)); + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + sendMouseEvent(widget, static_cast<QMouseEvent *>(inputEvent)); + break; + default: + // Shouldn't get here. + Q_ASSERT_X(0 == 1, "QSymbianControl::sendInputEvent()", "inputEvent->type() is unknown"); + break; + } +} + +TKeyResponse QSymbianControl::sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent) +{ +#if !defined(QT_NO_IM) && defined(Q_WS_S60) + if (widget && widget->isEnabled() && widget->testAttribute(Qt::WA_InputMethodEnabled)) { + QInputContext *qic = widget->inputContext(); + if (qic && qic->filterEvent(keyEvent)) + return EKeyWasConsumed; + } +#endif // !defined(QT_NO_IM) && defined(Q_OS_SYMBIAN) + + if (widget && qt_sendSpontaneousEvent(widget, keyEvent)) + if (keyEvent->isAccepted()) + return EKeyWasConsumed; + + return EKeyWasNotConsumed; +} + +#if !defined(QT_NO_IM) && defined(Q_WS_S60) +TCoeInputCapabilities QSymbianControl::InputCapabilities() const +{ + QWidget *w = 0; + + if (qwidget->hasFocus()) + w = qwidget; + else + w = qwidget->focusWidget(); + + QCoeFepInputContext *ic; + if (w && w->isEnabled() && w->testAttribute(Qt::WA_InputMethodEnabled) + && (ic = qobject_cast<QCoeFepInputContext *>(w->inputContext()))) { + return ic->inputCapabilities(); + } else { + return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0); + } +} +#endif + +void QSymbianControl::Draw(const TRect& controlRect) const +{ + // Set flag to avoid calling DrawNow in window surface + QWidget *window = qwidget->window(); + Q_ASSERT(window); + QTLWExtra *topExtra = window->d_func()->maybeTopData(); + Q_ASSERT(topExtra); + + TRect wcontrolRect = translateRectForFixedNativeOrientation(controlRect); + + if (!topExtra->inExpose) { + topExtra->inExpose = true; + if (!qwidget->isWindow()) { + // If we get here, then it means we have a native child window + // Since no content should ever be painted to these windows, we + // erase them with a transparent brush when they get an expose. + CWindowGc &gc = SystemGc(); + gc.SetBrushColor(TRgb(0, 0, 0, 0)); + gc.Clear(controlRect); + } + QRect exposeRect = qt_TRect2QRect(wcontrolRect); + qwidget->d_func()->syncBackingStore(exposeRect); + topExtra->inExpose = false; + } + + QWindowSurface *surface = qwidget->windowSurface(); + QPaintEngine *engine = surface ? surface->paintDevice()->paintEngine() : NULL; + + if (!engine) + return; + + const bool sendNativePaintEvents = qwidget->d_func()->extraData()->receiveNativePaintEvents; + if (sendNativePaintEvents) { + const QRect r = qt_TRect2QRect(wcontrolRect); + QMetaObject::invokeMethod(qwidget, "beginNativePaintEvent", Qt::DirectConnection, Q_ARG(QRect, r)); + } + + // Map source rectangle into coordinates of the backing store. + const QPoint controlBase(controlRect.iTl.iX, controlRect.iTl.iY); + const QPoint backingStoreBase = qwidget->mapTo(qwidget->window(), controlBase); + const TRect backingStoreRect(TPoint(backingStoreBase.x(), backingStoreBase.y()), controlRect.Size()); + + if (engine->type() == QPaintEngine::Raster) { + QS60WindowSurface *s60Surface; +#ifdef QT_GRAPHICSSYSTEM_RUNTIME + if (QApplicationPrivate::runtime_graphics_system) { + QRuntimeWindowSurface *rtSurface = + static_cast<QRuntimeWindowSurface*>(qwidget->windowSurface()); + s60Surface = static_cast<QS60WindowSurface *>(rtSurface->m_windowSurface.data()); + } else +#endif + s60Surface = static_cast<QS60WindowSurface *>(qwidget->windowSurface()); + + CFbsBitmap *bitmap = s60Surface->symbianBitmap(); + CWindowGc &gc = SystemGc(); + + QWExtra::NativePaintMode nativePaintMode = qwidget->d_func()->extraData()->nativePaintMode; + if(qwidget->d_func()->paintOnScreen()) + nativePaintMode = QWExtra::Disable; + + switch(nativePaintMode) { + case QWExtra::Disable: + // Do nothing + break; + case QWExtra::Blit: + case QWExtra::BlitWriteAlpha: + if (qwidget->d_func()->isOpaque || nativePaintMode == QWExtra::BlitWriteAlpha) + gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); + gc.BitBlt(controlRect.iTl, bitmap, backingStoreRect); + break; + case QWExtra::ZeroFill: + if (Window().DisplayMode() == EColor16MA + || Window().DisplayMode() == Q_SYMBIAN_ECOLOR16MAP) { + gc.SetBrushStyle(CGraphicsContext::ESolidBrush); + gc.SetDrawMode(CGraphicsContext::EDrawModeWriteAlpha); + gc.SetBrushColor(TRgb::Color16MA(0)); + gc.Clear(controlRect); + } else { + gc.SetBrushColor(TRgb(0x000000)); + gc.Clear(controlRect); + }; + break; + default: + Q_ASSERT(false); + } + } + + if (sendNativePaintEvents) { + const QRect r = qt_TRect2QRect(wcontrolRect); + // The draw ops aren't actually sent to WSERV until the graphics + // context is deactivated, which happens in the function calling + // this one. We therefore delay the delivery of endNativePaintEvent, + // to ensure that drawing has completed by the time the widget + // receives the event. Note that, if the widget needs to ensure + // that the draw ops have actually been executed into the output + // framebuffer, a call to RWsSession::Flush is required in the + // endNativePaintEvent implementation. + QMetaObject::invokeMethod(qwidget, "endNativePaintEvent", Qt::QueuedConnection, Q_ARG(QRect, r)); + } +} + +void QSymbianControl::qwidgetResize_helper(const QSize &newSize) +{ + QRect cr = qwidget->geometry(); + QSize oldSize(cr.size()); + cr.setSize(newSize); + qwidget->data->crect = cr; + if (qwidget->isVisible()) { + QTLWExtra *tlwExtra = qwidget->d_func()->maybeTopData(); + bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); + if (!slowResize && tlwExtra) + tlwExtra->inTopLevelResize = true; + QResizeEvent e(newSize, oldSize); + qt_sendSpontaneousEvent(qwidget, &e); + if (!qwidget->testAttribute(Qt::WA_StaticContents)) + qwidget->d_func()->syncBackingStore(); + if (!slowResize && tlwExtra) + tlwExtra->inTopLevelResize = false; + } else { + if (!qwidget->testAttribute(Qt::WA_PendingResizeEvent)) { + QResizeEvent *e = new QResizeEvent(newSize, oldSize); + QApplication::postEvent(qwidget, e); + } + } +} + +void QSymbianControl::SizeChanged() +{ + CCoeControl::SizeChanged(); + + // When FixNativeOrientation had been called, the RWindow/CCoeControl size + // and the surface/QWidget size have nothing to do with each other. + if (qwidget->d_func()->fixNativeOrientationCalled) + return; + + QSize oldSize = qwidget->size(); + QSize newSize(Size().iWidth, Size().iHeight); + + if (oldSize != newSize) { + // Enforce the proper size for fullscreen widgets on the secondary screen. + const bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen; + const int screenNumber = S60->screenNumberForWidget(qwidget); + if (!m_inExternalScreenOverride && isFullscreen && screenNumber > 0) { + int screenWidth = S60->screenWidthInPixelsForScreen[screenNumber]; + int screenHeight = S60->screenHeightInPixelsForScreen[screenNumber]; + TSize screenSize(screenWidth, screenHeight); + if (screenWidth > 0 && screenHeight > 0 && screenSize != Size()) { + m_inExternalScreenOverride = true; + SetExtent(TPoint(0, 0), screenSize); + return; + } + } + + qwidgetResize_helper(newSize); + } + + m_inExternalScreenOverride = false; + + // CCoeControl::SetExtent calls SizeChanged, but does not call + // PositionChanged, so we call it here to ensure that the widget's + // position is updated. + PositionChanged(); +} + +void QSymbianControl::PositionChanged() +{ + CCoeControl::PositionChanged(); + + QPoint oldPos = qwidget->geometry().topLeft(); + QPoint newPos(Position().iX, Position().iY); + + if (oldPos != newPos) { + QRect cr = qwidget->geometry(); + cr.moveTopLeft(newPos); + qwidget->data->crect = cr; + QTLWExtra *top = qwidget->d_func()->maybeTopData(); + if (top && (qwidget->windowState() & (~Qt::WindowActive)) == Qt::WindowNoState) + top->normalGeometry.moveTopLeft(newPos); + if (qwidget->isVisible()) { + QMoveEvent e(newPos, oldPos); + qt_sendSpontaneousEvent(qwidget, &e); + } else { + QMoveEvent * e = new QMoveEvent(newPos, oldPos); + QApplication::postEvent(qwidget, e); + } + } +} + +void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) +{ + if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) + return; + +#ifdef Q_WS_S60 + if (S60->splitViewLastWidget) + return; +#endif + + // Popups never get focused, but still receive the FocusChanged when they are hidden. + if (QApplicationPrivate::popupWidgets != 0 + || (qwidget->windowType() & Qt::Popup) == Qt::Popup) + return; + + if (IsFocused() && IsVisible()) { + if (m_symbianPopupIsOpen) { + QWidget *fw = QApplication::focusWidget(); + if (fw) { + QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason); + QCoreApplication::sendEvent(fw, &event); + } + m_symbianPopupIsOpen = false; + } + + QApplication::setActiveWindow(qwidget->window()); + qwidget->d_func()->setWindowIcon_sys(true); + qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); +#ifdef Q_WS_S60 + if (qwidget->isWindow()) + S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState()); +#endif + } else if (QApplication::activeWindow() == qwidget->window()) { + bool focusedControlFound = false; + WId winId = 0; + for (QWidget *w = qwidget->parentWidget(); w && (winId = w->internalWinId()); w = w->parentWidget()) { + if (winId->IsFocused() && winId->IsVisible()) { + focusedControlFound = true; + break; + } else if (w->isWindow()) + break; + } + if (!focusedControlFound) { + if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { + QWidget *fw = QApplication::focusWidget(); + if (fw) { + QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); + QCoreApplication::sendEvent(fw, &event); + } + m_symbianPopupIsOpen = true; + return; + } + + QApplication::setActiveWindow(0); + } + } + // else { We don't touch the active window unless we were explicitly activated or deactivated } +} + +void QSymbianControl::handleClientAreaChange() +{ + const bool cbaVisibilityHint = qwidget->windowFlags() & Qt::WindowSoftkeysVisibleHint; + if (qwidget->isFullScreen() && !cbaVisibilityHint) { + SetExtentToWholeScreen(); + } else if (qwidget->isMaximized() || (qwidget->isFullScreen() && cbaVisibilityHint)) { + TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); + SetExtent(r.iTl, r.Size()); + } else if (!qwidget->isMinimized()) { // Normal geometry + if (!qwidget->testAttribute(Qt::WA_Resized)) { + qwidget->adjustSize(); + qwidget->setAttribute(Qt::WA_Resized, false); //not a user resize + } + if (!qwidget->testAttribute(Qt::WA_Moved) && qwidget->windowType() != Qt::Dialog) { + TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); + SetPosition(r.iTl); + qwidget->setAttribute(Qt::WA_Moved, false); // not really an explicit position + } + } +} + +bool QSymbianControl::isSplitViewWidget(QWidget *widget) { + bool returnValue = true; + //Ignore events sent to non-active windows, not visible widgets and not parents of input widget. + if (!qwidget->isActiveWindow() + || !qwidget->isVisible() + || !qwidget->isAncestorOf(widget)) { + + returnValue = false; + } + return returnValue; +} + +void QSymbianControl::HandleResourceChange(int resourceType) +{ + switch (resourceType) { + case KSplitViewCloseEvent: //intentional fall-through + case KSplitViewOpenEvent: { +#if !defined(QT_NO_IM) && defined(Q_WS_S60) + + //Fetch widget getting the text input + QWidget *widget = QWidget::keyboardGrabber(); + if (!widget) { + if (QApplicationPrivate::popupWidgets) { + widget = QApplication::activePopupWidget()->focusWidget(); + if (!widget) { + widget = QApplication::activePopupWidget(); + } + } else { + widget = QApplicationPrivate::focus_widget; + if (!widget) { + widget = qwidget; + } + } + } + if (widget) { + QCoeFepInputContext *ic = qobject_cast<QCoeFepInputContext *>(widget->inputContext()); + if (!ic) { + ic = qobject_cast<QCoeFepInputContext *>(qApp->inputContext()); + } + if (ic && isSplitViewWidget(widget)) { + if (resourceType == KSplitViewCloseEvent) { + ic->resetSplitViewWidget(); + } else { + ic->ensureFocusWidgetVisible(widget); + } + } + } +#endif // !defined(QT_NO_IM) && defined(Q_WS_S60) + } + break; + case KInternalStatusPaneChange: + // When status pane is not visible, only handle client area change if status pane was + // previously visible, as size changes to hidden status pane should not affect + // client area. + if (S60->statusPane() && (S60->statusPane()->IsVisible() || m_lastStatusPaneVisibility)) { + m_lastStatusPaneVisibility = S60->statusPane()->IsVisible(); + handleClientAreaChange(); + } + if (IsFocused() && IsVisible()) { + qwidget->d_func()->setWindowIcon_sys(true); + qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); + } + break; + case KUidValueCoeFontChangeEvent: + // font change event + break; +#ifdef Q_WS_S60 + case KEikDynamicLayoutVariantSwitch: + { + handleClientAreaChange(); + // Send resize event to trigger desktopwidget workAreaResized signal + if (qt_desktopWidget) { + QResizeEvent e(qt_desktopWidget->size(), qt_desktopWidget->size()); + QApplication::sendEvent(qt_desktopWidget, &e); + } + break; + } +#endif + default: + break; + } + + CCoeControl::HandleResourceChange(resourceType); + +} +void QSymbianControl::CancelLongTapTimer() +{ + m_longTapDetector->Cancel(); +} + +TTypeUid::Ptr QSymbianControl::MopSupplyObject(TTypeUid id) +{ + if (id.iUid == ETypeId) + return id.MakePtr(this); + + return CCoeControl::MopSupplyObject(id); +} + +void QSymbianControl::setFocusSafely(bool focus) +{ + // The stack hack in here is very unfortunate, but it is the only way to ensure proper + // focus in Symbian. If this is not executed, the control which happens to be on + // the top of the stack may randomly be assigned focus by Symbian, for example + // when creating new windows (specifically in CCoeAppUi::HandleStackChanged()). + + // Close any popups. + CEikonEnv::Static()->EikAppUi()->StopDisplayingMenuBar(); + + if (focus) { + S60->appUi()->RemoveFromStack(this); + // Symbian doesn't automatically remove focus from the last focused control, so we need to + // remember it and clear focus ourselves. + if (lastFocusedControl && lastFocusedControl != this) + lastFocusedControl->SetFocus(false); + QT_TRAP_THROWING(S60->appUi()->AddToStackL(this, + ECoeStackPriorityDefault + 1, ECoeStackFlagStandard)); // Note the + 1 + lastFocusedControl = this; + this->SetFocus(true); + } else { + S60->appUi()->RemoveFromStack(this); + QT_TRAP_THROWING(S60->appUi()->AddToStackL(this, + ECoeStackPriorityDefault, ECoeStackFlagStandard)); + if(this == lastFocusedControl) + lastFocusedControl = 0; + this->SetFocus(false); + } +} + +bool QSymbianControl::isControlActive() +{ + return IsActivated() ? true : false; +} + +void QSymbianControl::ensureFixNativeOrientation() +{ +#if defined(Q_SYMBIAN_SUPPORTS_FIXNATIVEORIENTATION) + if (!qwidget->isWindow() || qwidget->windowType() == Qt::Desktop) + return; + if (S60->screenNumberForWidget(qwidget) > 0) + return; + const bool isFixed = qwidget->d_func()->fixNativeOrientationCalled; + const bool isFixEnabled = qwidget->testAttribute(Qt::WA_SymbianNoSystemRotation); + const bool isFullScreen = qwidget->windowState().testFlag(Qt::WindowFullScreen); + if (isFullScreen && isFixEnabled) { + const bool surfaceBasedGs = + QApplicationPrivate::graphics_system_name == QLatin1String("openvg") + || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"); + if (!surfaceBasedGs) + qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); + if (!isFixed && surfaceBasedGs) { + if (Window().FixNativeOrientation() == KErrNone) { + qwidget->d_func()->fixNativeOrientationCalled = true; + // The EGL window surface is now fixed to the native orientation + // of the device, no matter what size we pass when creating it. + // Enforce the same size for the QWidget too. For the underlying + // CCoeControl and RWindow it is up to the system to resize them + // when the standard auto-rotation mechanism is in use, we must not + // change that behavior by forcing any size for those. In practice + // this means that the QWidget and the underlying native control + // dimensions will be out of sync when FixNativeOrientation was + // called and the device is turned to the non-native (typically + // landscape) orientation. The pointer event handling and certain + // functions like Draw() will need to compensate for this. + QSize newSize(S60->nativeScreenWidthInPixels, S60->nativeScreenHeightInPixels); + if (qwidget->size() != newSize) + qwidgetResize_helper(newSize); + } else { + qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); + } + } + } else if (isFixed) { + qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); + qwidget->d_func()->fixNativeOrientationCalled = false; + qwidget->hide(); + qwidget->d_func()->create_sys(0, false, true); + qwidget->show(); + } +#else + qwidget->setAttribute(Qt::WA_SymbianNoSystemRotation, false); +#endif +} + +/*! + \typedef QApplication::QS60MainApplicationFactory + \since 4.6 + + This is a typedef for a pointer to a function with the following + signature: + + \snippet doc/src/snippets/code/src_corelib_global_qglobal.cpp 47 + + \sa QApplication::QApplication() +*/ + +/*! + \since 4.6 + + Creates an application using the application factory given in + \a factory, and using \a argc command line arguments in \a argv. + \a factory can be leaving, but the error will be converted to a + standard exception. + + This function is only available on S60. +*/ +QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, 0x040000)) +{ + Q_D(QApplication); + S60->s60ApplicationFactory = factory; + d->construct(); +} + +QApplication::QApplication(QApplication::QS60MainApplicationFactory factory, int &argc, char **argv, int _internal) + : QCoreApplication(*new QApplicationPrivate(argc, argv, GuiClient, _internal)) +{ + Q_D(QApplication); + S60->s60ApplicationFactory = factory; + d->construct(); + QApplicationPrivate::app_compile_version = _internal; +} + +void qt_init(QApplicationPrivate * /* priv */, int) +{ + if (!CCoeEnv::Static()) { + // The S60 framework creates a new trap handler which will render any existing traps + // invalid as long as it is active. This means that all code in main() that occurs after + // the QApplication construction needs to be surrounded by a new trap, despite having + // an outer one already. To avoid this, we save the original trap handler here, and set + // it back after the S60 framework is constructed. Then we restore it right before the S60 + // framework destruction. + TTrapHandler *origTrapHandler = User::TrapHandler(); + + // The S60 framework has not been initialized. We need to do it. + TApaApplicationFactory factory(S60->s60ApplicationFactory ? + S60->s60ApplicationFactory : newS60Application); + CApaCommandLine* commandLine = q_check_ptr(QCoreApplicationPrivate::symbianCommandLine()); + if (commandLine) { + // After this construction, CEikonEnv will be available from CEikonEnv::Static(). + // (much like our qApp). + QtEikonEnv* coe = new QtEikonEnv; + //not using QT_TRAP_THROWING, because coe owns the cleanupstack so it can't be pushed there. + TRAPD(err, coe->ConstructAppFromCommandLineL(factory, *commandLine)); + if(err != KErrNone) { + qWarning() << "qt_init: Eikon application construct failed (" + << err + << "), maybe missing resource file on S60 3.1?"; + delete coe; + qt_symbian_throwIfError(err); + } + } + + S60->s60InstalledTrapHandler = User::SetTrapHandler(origTrapHandler); + + S60->qtOwnsS60Environment = true; + } else { + S60->qtOwnsS60Environment = false; + } + +#ifdef QT_NO_DEBUG + if (!qgetenv("QT_S60_AUTO_FLUSH_WSERV").isEmpty()) +#endif + S60->wsSession().SetAutoFlush(ETrue); + +#ifdef Q_SYMBIAN_WINDOW_SIZE_CACHE + TRAP_IGNORE(S60->wsSession().EnableWindowSizeCacheL()); +#endif + + S60->updateScreenSize(); + + + TDisplayMode mode = S60->screenDevice()->DisplayMode(); + S60->screenDepth = TDisplayModeUtils::NumDisplayModeBitsPerPixel(mode); + + //NB: RWsSession::GetColorModeList tells you what window modes are supported, + //not what bitmap formats. + if(QSysInfo::symbianVersion() == QSysInfo::SV_9_2) + S60->supportsPremultipliedAlpha = 0; + else + S60->supportsPremultipliedAlpha = 1; + + RProcess me; + TSecureId securId = me.SecureId(); + S60->uid = securId.operator TUid(); + + // enable focus events - used to re-enable mouse after focus changed between mouse and non mouse app, + // and for dimming behind modal windows + S60->windowGroup().EnableFocusChangeEvents(); + + //Check if mouse interaction is supported (either EMouse=1 in the HAL, or EMachineUID is one of the phones known to support this) + const TInt KMachineUidSamsungI8510 = 0x2000C51E; + // HAL::Get(HALData::EPen, TInt& result) may set 'result' to 1 on some 3.1 systems (e.g. N95). + // But we know that S60 systems below 5.0 did not support touch. + static const bool touchIsUnsupportedOnSystem = + QSysInfo::s60Version() == QSysInfo::SV_S60_3_1 + || QSysInfo::s60Version() == QSysInfo::SV_S60_3_2; + TInt machineUID; + TInt mouse; + TInt touch; + TInt err; + err = HAL::Get(HALData::EMouse, mouse); + if (err != KErrNone) + mouse = 0; + err = HAL::Get(HALData::EMachineUid, machineUID); + if (err != KErrNone) + machineUID = 0; + err = HAL::Get(HALData::EPen, touch); + if (err != KErrNone || touchIsUnsupportedOnSystem) + touch = 0; +#ifdef __WINS__ + if(QSysInfo::symbianVersion() <= QSysInfo::SV_9_4) { + //for symbian SDK emulator, force values to match typical devices. + mouse = 0; + touch = touchIsUnsupportedOnSystem ? 0 : 1; + } +#endif + if (mouse || machineUID == KMachineUidSamsungI8510) { + S60->hasTouchscreen = false; + S60->virtualMouseRequired = false; + } + else if (!touch) { + S60->hasTouchscreen = false; + S60->virtualMouseRequired = true; + } + else { + S60->hasTouchscreen = true; + S60->virtualMouseRequired = false; + } + + S60->avkonComponentsSupportTransparency = false; + S60->menuBeingConstructed = false; + +#ifdef Q_WS_S60 + TUid KCRUidAvkon = { 0x101F876E }; + TUint32 KAknAvkonTransparencyEnabled = 0x0000000D; + + CRepository* repository = 0; + TRAP(err, repository = CRepository::NewL(KCRUidAvkon)); + + if(err == KErrNone) { + TInt value = 0; + err = repository->Get(KAknAvkonTransparencyEnabled, value); + if(err == KErrNone) { + S60->avkonComponentsSupportTransparency = (value==1) ? true : false; + } + } + delete repository; + repository = 0; +#endif + + qt_keymapper_private()->updateInputLanguage(); + +#ifdef QT_KEYPAD_NAVIGATION + if (touch) { + QApplicationPrivate::navigationMode = Qt::NavigationModeNone; + } else { + QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional; + } +#endif + +#ifndef QT_NO_CURSOR + //Check if window server pointer cursors are supported or not +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + //In generic binary, use the HAL and OS version + //Any other known good phones should be added here. + if (machineUID == KMachineUidSamsungI8510 || (QSysInfo::symbianVersion() != QSysInfo::SV_9_4 + && QSysInfo::symbianVersion() != QSysInfo::SV_9_3 && QSysInfo::symbianVersion() + != QSysInfo::SV_9_2)) { + S60->brokenPointerCursors = false; + qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup()); + } + else + S60->brokenPointerCursors = true; +#endif + + if (S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) { + qt_symbian_set_pointer_sprite(Qt::ArrowCursor); + qt_symbian_show_pointer_sprite(); + } + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); + } +#endif + + QFont systemFont; + systemFont.setFamily(systemFont.defaultFamily()); + QApplicationPrivate::setSystemFont(systemFont); + + QObject::connect(qApp, SIGNAL(aboutToQuit()), qApp, SLOT(_q_aboutToQuit())); + +#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = true; + + const TUid KIvePropertyCat = {0x2726beef}; + enum TIvePropertyChipType { + EVCBCM2727B1 = 0x00000000, + EVCBCM2763A0 = 0x04000100, + EVCBCM2763B0 = 0x04000102, + EVCBCM2763C0 = 0x04000103, + EVCBCM2763C1 = 0x04000104, + EVCBCMUnknown = 0x7fffffff + }; + + TInt chipType = EVCBCMUnknown; + if (RProperty::Get(KIvePropertyCat, 0 /*chip type*/, chipType) == KErrNone) { + if (chipType == EVCBCM2727B1) { + // We have only 32MB GPU memory. Use raster surfaces + // for transparent TLWs. + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; + } + } else { + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; + } + if (QApplicationPrivate::graphics_system_name == QLatin1String("raster")) + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; +#else + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; +#endif +/* + ### Commented out for now as parameter handling not needed in SOS(yet). Code below will break testlib with -o flag + int argc = priv->argc; + char **argv = priv->argv; + + // 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; + } + +#if defined(QT_DEBUG) + if (qstrcmp(argv[i], "-nograb") == 0) + appNoGrab = !appNoGrab; + else +#endif // QT_DEBUG + ; + } +*/ + + // Register WId with the metatype system. This is to enable + // QWidgetPrivate::create_sys to used delayed slot invocation in order + // to destroy WId objects during reparenting. + qRegisterMetaType<WId>("WId"); +} + +#ifdef QT_NO_FREETYPE +extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp +#endif + +/***************************************************************************** + qt_cleanup() - cleans up when the application is finished + *****************************************************************************/ +void qt_cleanup() +{ +#ifdef Q_WS_S60 + S60->setButtonGroupContainer(0); +#endif + if(qt_S60Beep) { + delete qt_S60Beep; + qt_S60Beep = 0; + } + QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles + QPixmapCache::clear(); // Has to happen now, since QS60PixmapData has FBS handles + +#ifdef QT_NO_FREETYPE + qt_cleanup_symbianFontDatabase(); +#endif +// S60 structure and window server session are freed in eventdispatcher destructor as they are needed there + + // It's important that this happens here, before the event dispatcher gets + // deleted, because the input context needs the event loop one last time before + // it dies. + delete QApplicationPrivate::inputContext; + QApplicationPrivate::inputContext = 0; + + //Change mouse pointer back + S60->wsSession().SetPointerCursorMode(EPointerCursorNone); + +#ifdef Q_WS_S60 + // Clear CBA + CEikonEnv::Static()->AppUiFactory()->SwapButtonGroup(0); + delete S60->buttonGroupContainer(); + S60->setButtonGroupContainer(0); +#endif + + // Call EndFullScreen() to prevent confusing the system effect state machine. + qt_endFullScreenEffect(); + + if (S60->qtOwnsS60Environment) { + // Restore the S60 framework trap handler. See qt_init(). + User::SetTrapHandler(S60->s60InstalledTrapHandler); + + CEikonEnv* coe = CEikonEnv::Static(); + coe->PrepareToExit(); + // The CEikonEnv itself is destroyed in here. + coe->DestroyEnvironment(); + } +} + +void QApplicationPrivate::initializeWidgetPaletteHash() +{ + // TODO: Implement QApplicationPrivate::initializeWidgetPaletteHash() + // Possibly a task fot the S60Style guys +} + +void QApplicationPrivate::createEventDispatcher() +{ + Q_Q(QApplication); + eventDispatcher = new QEventDispatcherS60(q); +} + +QString QApplicationPrivate::appName() const +{ + return QCoreApplicationPrivate::appName(); +} + +bool QApplicationPrivate::modalState() +{ + return app_do_modal; +} + +void QApplicationPrivate::enterModal_sys(QWidget *widget) +{ +#ifdef Q_SYMBIAN_TRANSITION_EFFECTS + S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeEnter); +#endif + if (widget) { + static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(ETrue); + // Modal partial screen dialogs (like queries) capture pointer events. + // ### FixMe: Add specialized behaviour for fullscreen modal dialogs + widget->effectiveWinId()->SetGloballyCapturing(ETrue); + widget->effectiveWinId()->SetPointerCapture(ETrue); + } + 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) +{ +#ifdef Q_SYMBIAN_TRANSITION_EFFECTS + S60->wsSession().SendEffectCommand(ETfxCmdAppModalModeExit); +#endif + if (widget) { + static_cast<QSymbianControl *>(widget->effectiveWinId())->FadeBehindPopup(EFalse); + // ### FixMe: Add specialized behaviour for fullscreen modal dialogs + widget->effectiveWinId()->SetGloballyCapturing(EFalse); + widget->effectiveWinId()->SetPointerCapture(EFalse); + } + 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; +} + +void QApplicationPrivate::openPopup(QWidget *popup) +{ + if (popup && qobject_cast<QComboBox *>(popup->parentWidget())) + static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(ETrue); + + if (!QApplicationPrivate::popupWidgets) + QApplicationPrivate::popupWidgets = new QWidgetList; + QApplicationPrivate::popupWidgets->append(popup); + + // Cancel focus widget pointer capture and long tap timer + if (QApplication::focusWidget()) { + static_cast<QSymbianControl*>(QApplication::focusWidget()->effectiveWinId())->CancelLongTapTimer(); + QApplication::focusWidget()->effectiveWinId()->SetPointerCapture(false); + } + + if (!qt_nograb()) { + // Cancel pointer capture and long tap timer for earlier popup + int popupCount = QApplicationPrivate::popupWidgets->count(); + if (popupCount > 1) { + QWidget* prevPopup = QApplicationPrivate::popupWidgets->at(popupCount-2); + static_cast<QSymbianControl*>(prevPopup->effectiveWinId())->CancelLongTapTimer(); + prevPopup->effectiveWinId()->SetPointerCapture(false); + } + + // Enable pointer capture for this (topmost) popup + Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); + WId id = popup->effectiveWinId(); + id->SetPointerCapture(true); + } + + // 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 + QWidget *fw = popup->focusWidget(); + if (fw) { + fw->setFocus(Qt::PopupFocusReason); + } else if (QApplicationPrivate::popupWidgets->count() == 1) { // this was the first popup + fw = QApplication::focusWidget(); + if (fw) { + QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); + q_func()->sendEvent(fw, &e); + } + } +} + +void QApplicationPrivate::closePopup(QWidget *popup) +{ + if (popup && qobject_cast<QComboBox *>(popup->parentWidget())) + static_cast<QSymbianControl *>(popup->effectiveWinId())->FadeBehindPopup(EFalse); + + if (!QApplicationPrivate::popupWidgets) + return; + QApplicationPrivate::popupWidgets->removeAll(popup); + + // Cancel pointer capture and long tap for this popup + WId id = popup->effectiveWinId(); + id->SetPointerCapture(false); + static_cast<QSymbianControl*>(id)->CancelLongTapTimer(); + + if (QApplicationPrivate::popupWidgets->isEmpty()) { // this was the last popup + delete QApplicationPrivate::popupWidgets; + QApplicationPrivate::popupWidgets = 0; + if (!qt_nograb()) { // grabbing not disabled + Q_ASSERT(popup->testAttribute(Qt::WA_WState_Created)); + if (QWidgetPrivate::mouseGrabber != 0) + QWidgetPrivate::mouseGrabber->grabMouse(); + + if (QWidgetPrivate::keyboardGrabber != 0) + QWidgetPrivate::keyboardGrabber->grabKeyboard(); + + QWidget *fw = QApplicationPrivate::active_window ? QApplicationPrivate::active_window->focusWidget() + : q_func()->focusWidget(); + if (fw) { + if(fw->window()->isModal()) // restore pointer capture for modal window + fw->effectiveWinId()->SetPointerCapture(true); + + 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 (QWidget *fw = QApplication::focusWidget()) { + QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); + q_func()->sendEvent(fw, &e); + } + + // Enable pointer capture for previous popup + if (aw) { + aw->effectiveWinId()->SetPointerCapture(true); + } + } +} + +QWidget * QApplication::topLevelAt(QPoint const& point) +{ + QWidget *found = 0; + int lowestZ = INT_MAX; + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = 0; i < list.count(); ++i) { + QWidget *widget = list.at(i); + if (widget->isVisible() && !(widget->windowType() == Qt::Desktop)) { + Q_ASSERT(widget->testAttribute(Qt::WA_WState_Created)); + if (widget->geometry().adjusted(0,0,1,1).contains(point)) { + // At this point we know there is a Qt widget under the point. + // Now we need to make sure it is the top most in the z-order. + RDrawableWindow *const window = widget->effectiveWinId()->DrawableWindow(); + int z = window->OrdinalPosition(); + if (z < lowestZ) { + lowestZ = z; + found = widget; + } + } + } + } + return found; +} + +void QApplication::alert(QWidget * /* widget */, int /* duration */) +{ + // TODO: Implement QApplication::alert(QWidget *widget, int duration) +} + +int QApplication::doubleClickInterval() +{ + TTimeIntervalMicroSeconds32 us; + TInt distance; + S60->wsSession().GetDoubleClickSettings(us, distance); + return (us.Int() / 1000); +} + +void QApplication::setDoubleClickInterval(int ms) +{ + TTimeIntervalMicroSeconds32 newUs( ms * 1000); + TTimeIntervalMicroSeconds32 us; + TInt distance; + S60->wsSession().GetDoubleClickSettings(us, distance); + if (us != newUs) + S60->wsSession().SetDoubleClick(newUs, distance); +} + +int QApplication::keyboardInputInterval() +{ + return QApplicationPrivate::keyboard_input_time; +} + +void QApplication::setKeyboardInputInterval(int ms) +{ + QApplicationPrivate::keyboard_input_time = ms; +} + +int QApplication::cursorFlashTime() +{ + return QApplicationPrivate::cursor_flash_time; +} + +void QApplication::setCursorFlashTime(int msecs) +{ + QApplicationPrivate::cursor_flash_time = msecs; +} + +void QApplication::beep() +{ + if (!qt_S60Beep) { + TInt frequency = 880; + TTimeIntervalMicroSeconds duration(500000); + TRAP_IGNORE(qt_S60Beep=QS60Beep::NewL(frequency, duration)); + } + if (qt_S60Beep) + qt_S60Beep->Play(); +} + +static inline bool callSymbianEventFilters(const QSymbianEvent *event) +{ + long unused; + return qApp->filterEvent(const_cast<QSymbianEvent *>(event), &unused); +} + +/*! + \warning This function is only available on Symbian. + \since 4.6 + + This function processes an individual Symbian event + \a event. It returns 1 if the event was handled, 0 if + the \a event was not handled, and -1 if the event was + not handled because the event is not known to Qt. + */ + +int QApplication::symbianProcessEvent(const QSymbianEvent *event) +{ + Q_D(QApplication); + + QScopedLoopLevelCounter counter(d->threadData); + + if (d->eventDispatcher->filterEvent(const_cast<QSymbianEvent *>(event))) + return 1; + + QWidget *w = qApp ? qApp->focusWidget() : 0; + if (w) { + QInputContext *ic = w->inputContext(); + if (ic && ic->symbianFilterEvent(w, event)) + return 1; + } + + if (symbianEventFilter(event)) + return 1; + + switch (event->type()) { + case QSymbianEvent::WindowServerEvent: + return d->symbianProcessWsEvent(event); + case QSymbianEvent::CommandEvent: + return d->symbianHandleCommand(event); + case QSymbianEvent::ResourceChangeEvent: + return d->symbianResourceChange(event); + default: + return -1; + } +} + +int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent) +{ + // Qt event handling. Handle some events regardless of if the handle is in our + // widget map or not. + const TWsEvent *event = symbianEvent->windowServerEvent(); + CCoeControl* control = reinterpret_cast<CCoeControl*>(event->Handle()); + const bool controlInMap = QWidgetPrivate::mapper && QWidgetPrivate::mapper->contains(control); + switch (event->Type()) { + case EEventPointerEnter: + if (controlInMap) { + callSymbianEventFilters(symbianEvent); + return 1; // Qt::Enter will be generated in HandlePointerL + } + break; + case EEventPointerExit: + if (controlInMap) { + if (callSymbianEventFilters(symbianEvent)) + return 1; + if (S60) { + // mouseEvent outside our window, send leave event to last focused widget + QMouseEvent mEvent(QEvent::Leave, S60->lastPointerEventPos, S60->lastCursorPos, + Qt::NoButton, QApplicationPrivate::mouse_buttons, Qt::NoModifier); + if (S60->lastPointerEventTarget) + qt_sendSpontaneousEvent(S60->lastPointerEventTarget,&mEvent); + S60->lastPointerEventTarget = 0; + } + return 1; + } + break; + case EEventScreenDeviceChanged: // fallthrough +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + case EEventDisplayChanged: +#endif + if (callSymbianEventFilters(symbianEvent)) + return 1; + if (S60) + S60->updateScreenSize(); + if (qt_desktopWidget) { + QSize oldSize = qt_desktopWidget->size(); + qt_desktopWidget->data->crect.setWidth(S60->screenWidthInPixels); + qt_desktopWidget->data->crect.setHeight(S60->screenHeightInPixels); + QResizeEvent e(qt_desktopWidget->size(), oldSize); + QApplication::sendEvent(qt_desktopWidget, &e); + } + return 0; // Propagate to CONE + case EEventWindowVisibilityChanged: + if (controlInMap) { + if (callSymbianEventFilters(symbianEvent)) + return 1; + const TWsVisibilityChangedEvent *visChangedEvent = event->VisibilityChanged(); + if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::ENotVisible) + S60->controlVisibilityChanged(control, false); + else if (visChangedEvent->iFlags & TWsVisibilityChangedEvent::EPartiallyVisible) + S60->controlVisibilityChanged(control, true); + return 1; + } + break; + case EEventFocusGained: + if (callSymbianEventFilters(symbianEvent)) + return 1; +#ifndef QT_NO_CURSOR + //re-enable mouse interaction + if (S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_show_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); + } +#endif +#ifdef QT_SOFTKEYS_ENABLED + if (!CEikonEnv::Static()->EikAppUi()->IsDisplayingMenuOrDialog()) + QSoftKeyManager::updateSoftKeys(); +#endif + break; + case EEventFocusLost: + if (callSymbianEventFilters(symbianEvent)) + return 1; +#ifndef QT_NO_CURSOR + //disable mouse as may be moving to application that does not support it + if (S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_hide_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNone); + } +#endif + break; + case KGoomMemoryLowEvent: +#ifdef QT_DEBUG + qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryLowEvent"; +#endif + if (callSymbianEventFilters(symbianEvent)) + return 1; +#ifdef QT_GRAPHICSSYSTEM_RUNTIME + if(QApplicationPrivate::runtime_graphics_system) { + bool switchToSwRendering(false); + + foreach (QWidget *w, QApplication::topLevelWidgets()) { + if(w->d_func()->topData()->backingStore) { + switchToSwRendering = true; + break; + } + } + + if (switchToSwRendering) { + QRuntimeGraphicsSystem *gs = + static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system); + gs->setGraphicsSystem(QLatin1String("raster")); + } + } +#endif + break; + case KGoomMemoryGoodEvent: +#ifdef QT_DEBUG + qDebug() << "QApplicationPrivate::symbianProcessWsEvent - KGoomMemoryGoodEvent"; +#endif + if (callSymbianEventFilters(symbianEvent)) + return 1; +#ifdef QT_GRAPHICSSYSTEM_RUNTIME + if(QApplicationPrivate::runtime_graphics_system) { + QRuntimeGraphicsSystem *gs = + static_cast<QRuntimeGraphicsSystem*>(QApplicationPrivate::graphics_system); + gs->setGraphicsSystem(QLatin1String("openvg")); + } +#endif + break; +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES + case EEventUser: + { + // GOOM is looking for candidates to kill so indicate that we are + // capable of cleaning up by handling this event + TInt32 *data = reinterpret_cast<TInt32 *>(event->EventData()); + if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent) + return 1; + } + break; +#endif + +#ifdef Q_WS_S60 + case KEikInputLanguageChange: + qt_keymapper_private()->updateInputLanguage(); + break; +#endif + + default: + break; + } + + if (!controlInMap) + return -1; + + return 0; +} + +/*! + \warning This virtual function is only available on Symbian. + \since 4.6 + + If you create an application that inherits QApplication and reimplement + this function, you get direct access to events that the are received + from Symbian. 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, and does nothing with \a event. + */ +bool QApplication::symbianEventFilter(const QSymbianEvent *event) +{ + Q_UNUSED(event); + return false; +} + +/*! + \warning This function is only available on Symbian. + \since 4.6 + + Handles \a{command}s which are typically handled by + CAknAppUi::HandleCommandL(). Qts Ui integration into Symbian is + partially achieved by deriving from CAknAppUi. Currently, exit, + menu and softkey commands are handled. + + \sa s60EventFilter(), s60ProcessEvent() +*/ +int QApplicationPrivate::symbianHandleCommand(const QSymbianEvent *symbianEvent) +{ + Q_Q(QApplication); + int ret = 0; + + if (callSymbianEventFilters(symbianEvent)) + return 1; + + int command = symbianEvent->command(); + + switch (command) { +#ifdef Q_WS_S60 + case EAknSoftkeyExit: { + QCloseEvent ev; + QApplication::sendSpontaneousEvent(q, &ev); + if (ev.isAccepted()) { + q->quit(); + ret = 1; + } + break; + } +#endif + case EEikCmdExit: + q->quit(); + ret = 1; + break; + default: +#ifdef Q_WS_S60 + bool handled = QSoftKeyManager::handleCommand(command); + if (handled) + ret = 1; + else + ret = QMenuBarPrivate::symbianCommands(command); +#endif + break; + } + + return ret; +} + +/*! + \warning This function is only available on Symbian. + \since 4.6 + + Handles the resource change specified by \a type. + + Currently, KEikDynamicLayoutVariantSwitch and + KAknsMessageSkinChange are handled. + */ +int QApplicationPrivate::symbianResourceChange(const QSymbianEvent *symbianEvent) +{ + int ret = 0; + + int type = symbianEvent->resourceChangeType(); + + switch (type) { +#ifdef Q_WS_S60 + case KEikDynamicLayoutVariantSwitch: + { + if (callSymbianEventFilters(symbianEvent)) + return 1; + if (S60) + S60->updateScreenSize(); + +#ifndef QT_NO_STYLE_S60 + QS60Style *s60Style = 0; + +#ifndef QT_NO_STYLE_STYLESHEET + QStyleSheetStyle *proxy = qobject_cast<QStyleSheetStyle*>(QApplication::style()); + if (proxy) + s60Style = qobject_cast<QS60Style*>(proxy->baseStyle()); + else +#endif + s60Style = qobject_cast<QS60Style*>(QApplication::style()); + + if (s60Style) { + s60Style->d_func()->handleDynamicLayoutVariantSwitch(); + ret = 1; + } +#endif + } + break; + +#ifndef QT_NO_STYLE_S60 + case KAknsMessageSkinChange: + if (callSymbianEventFilters(symbianEvent)) + return 1; + if (QS60Style *s60Style = qobject_cast<QS60Style*>(QApplication::style())) { + s60Style->d_func()->handleSkinChange(); + ret = 1; + } + break; +#endif +#endif // Q_WS_S60 + default: + break; + } + + return ret; +} + +#ifndef QT_NO_WHEELEVENT +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} + +void QApplication::setWheelScrollLines(int n) +{ + QApplicationPrivate::wheel_scroll_lines = n; +} +#endif //QT_NO_WHEELEVENT + +bool QApplication::isEffectEnabled(Qt::UIEffect /* effect */) +{ + // TODO: Implement QApplication::isEffectEnabled(Qt::UIEffect effect) + return false; +} + +void QApplication::setEffectEnabled(Qt::UIEffect /* effect */, bool /* enable */) +{ + // TODO: Implement QApplication::setEffectEnabled(Qt::UIEffect effect, bool enable) +} + +TUint QApplicationPrivate::resolveS60ScanCode(TInt scanCode, TUint keysym) +{ + if (!scanCode) + return keysym; + + QApplicationPrivate *d = QApplicationPrivate::instance(); + + if (keysym) { + // If keysym is specified, cache it. + d->scanCodeCache.insert(scanCode, keysym); + return keysym; + } else { + // If not, retrieve the cached version. + return d->scanCodeCache[scanCode]; + } +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + if (HAL::Get(HALData::EPointer3DPressureSupported, pressureSupported) != KErrNone) + pressureSupported = 0; + if (HAL::Get(HALData::EPointer3DMaxPressure, maxTouchPressure) != KErrNone) + maxTouchPressure = KMaxTInt; +#else + pressureSupported = 0; + maxTouchPressure = KMaxTInt; +#endif +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + +#ifndef QT_NO_SESSIONMANAGER +QSessionManager::QSessionManager(QApplication * /* app */, QString & /* id */, QString& /* key */) +{ + +} + +QSessionManager::~QSessionManager() +{ + +} + +bool QSessionManager::allowsInteraction() +{ + return false; +} + +void QSessionManager::cancel() +{ + +} +#endif //QT_NO_SESSIONMANAGER + +#ifdef QT_KEYPAD_NAVIGATION +/* + * Show/Hide the mouse cursor depending on phone type and chosen mode + */ +void QApplicationPrivate::setNavigationMode(Qt::NavigationMode mode) +{ +#ifndef QT_NO_CURSOR + const bool wasCursorOn = (QApplicationPrivate::navigationMode == Qt::NavigationModeCursorAuto + && !S60->hasTouchscreen) + || QApplicationPrivate::navigationMode == Qt::NavigationModeCursorForceVisible; + const bool isCursorOn = (mode == Qt::NavigationModeCursorAuto + && !S60->hasTouchscreen) + || mode == Qt::NavigationModeCursorForceVisible; + + if (!wasCursorOn && isCursorOn) { + //Show the cursor, when changing from another mode to cursor mode + qt_symbian_set_cursor_visible(true); + } + else if (wasCursorOn && !isCursorOn) { + //Hide the cursor, when leaving cursor mode + qt_symbian_set_cursor_visible(false); + } +#endif + QApplicationPrivate::navigationMode = mode; +} +#endif + +#ifndef QT_NO_CURSOR +/***************************************************************************** + QApplication cursor stack + *****************************************************************************/ + +void QApplication::setOverrideCursor(const QCursor &cursor) +{ + qApp->d_func()->cursor_list.prepend(cursor); + qt_symbian_setGlobalCursor(cursor); +} + +void QApplication::restoreOverrideCursor() +{ + if (qApp->d_func()->cursor_list.isEmpty()) + return; + qApp->d_func()->cursor_list.removeFirst(); + + if (!qApp->d_func()->cursor_list.isEmpty()) { + qt_symbian_setGlobalCursor(qApp->d_func()->cursor_list.first()); + } + else { + //determine which widget has focus + QWidget *w = QApplication::widgetAt(QCursor::pos()); +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) { + qt_symbian_set_pointer_sprite(w ? w->cursor() : Qt::ArrowCursor); + } + else +#endif + { + //because of the internals of window server, we need to force the cursor + //to be set in all child windows too, otherwise when the cursor is over + //the child window it may show a widget cursor or arrow cursor instead, + //depending on construction order. + QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys()); + while (iter.hasNext()) { + CCoeControl *ctrl = iter.next(); + if(ctrl->OwnsWindow()) { + ctrl->DrawableWindow()->ClearPointerCursor(); + } + } + if (w) + qt_symbian_setWindowCursor(w->cursor(), w->effectiveWinId()); + else + qt_symbian_setWindowGroupCursor(Qt::ArrowCursor, S60->windowGroup()); + } + } +} + +#endif // QT_NO_CURSOR + +void QApplicationPrivate::_q_aboutToQuit() +{ + qt_beginFullScreenEffect(); + +#ifdef Q_SYMBIAN_TRANSITION_EFFECTS + // Send the shutdown tfx command + S60->wsSession().SendEffectCommand(ETfxCmdAppShutDown); +#endif +} + +QS60ThreadLocalData::QS60ThreadLocalData() +{ + CCoeEnv *env = CCoeEnv::Static(); + if (env) { + //if this is the UI thread, share objects owned by CONE + usingCONEinstances = true; + wsSession = env->WsSession(); + screenDevice = env->ScreenDevice(); + } + else { + usingCONEinstances = false; + qt_symbian_throwIfError(wsSession.Connect(qt_s60GetRFs())); + screenDevice = new CWsScreenDevice(wsSession); + screenDevice->Construct(); + } +} + +QS60ThreadLocalData::~QS60ThreadLocalData() +{ + if (!usingCONEinstances) { + delete screenDevice; + wsSession.Close(); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qclipboard_s60.cpp b/src/gui/platforms/s60/qclipboard_s60.cpp new file mode 100644 index 0000000000..0dafae0996 --- /dev/null +++ b/src/gui/platforms/s60/qclipboard_s60.cpp @@ -0,0 +1,331 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 "private/qcore_symbian_p.h" +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include "txtclipboard.h" +#endif +#include "txtetext.h" +#include <QtDebug> + +// Symbian's clipboard +#include <baclipb.h> +QT_BEGIN_NAMESPACE + +const TUid KQtCbDataStream = {0x2001B2DD}; +const TInt KPlainTextBegin = 0; + +class QClipboardData +{ +public: + QClipboardData(); + ~QClipboardData(); + + void setSource(QMimeData* s) + { + if (s == src) + return; + delete src; + src = s; + } + QMimeData* source() + { return src; } + bool connected() + { return connection; } + void clear(); + +private: + QMimeData* src; + bool connection; +}; + +QClipboardData::QClipboardData():src(0),connection(true) +{ + clear(); +} + +QClipboardData::~QClipboardData() +{ + connection = false; + delete src; +} + +void QClipboardData::clear() +{ + QMimeData* newSrc = new QMimeData; + delete src; + src = newSrc; +} + +static QClipboardData *internalCbData = 0; + +static void cleanupClipboardData() +{ + delete internalCbData; + internalCbData = 0; +} + +static QClipboardData *clipboardData() +{ + if (internalCbData == 0) { + internalCbData = new QClipboardData; + if (internalCbData) + { + if (!internalCbData->connected()) + { + delete internalCbData; + internalCbData = 0; + } + else + { + qAddPostRoutine(cleanupClipboardData); + } + } + } + return internalCbData; +} + +void writeToStreamLX(const QMimeData* aData, RWriteStream& aStream) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + QStringList headers = aData->formats(); + aStream << TCardinality(headers.count()); + for (QStringList::const_iterator iter= headers.constBegin();iter != headers.constEnd();iter++) + { + HBufC* stringData = TPtrC(reinterpret_cast<const TUint16*>((*iter).utf16())).AllocLC(); + QByteArray ba = aData->data((*iter)); + // mime type + aStream << TCardinality(stringData->Size()); + aStream << *(stringData); + // mime data + aStream << TCardinality(ba.size()); + aStream.WriteL(reinterpret_cast<const uchar*>(ba.constData()),ba.size()); + CleanupStack::PopAndDestroy(stringData); + } +} + +void writeToSymbianStoreLX(const QMimeData* aData, CClipboard* clipboard) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + if (aData->hasText()) { + CPlainText* text = CPlainText::NewL(); + CleanupStack::PushL(text); + + TPtrC textPtr(qt_QString2TPtrC(aData->text())); + text->InsertL(KPlainTextBegin, textPtr); + text->CopyToStoreL(clipboard->Store(), clipboard->StreamDictionary(), + KPlainTextBegin, textPtr.Length()); + CleanupStack::PopAndDestroy(text); + } +} + +void readSymbianStoreLX(QMimeData* aData, CClipboard* clipboard) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + CPlainText* text = CPlainText::NewL(); + CleanupStack::PushL(text); + TInt dataLength = text->PasteFromStoreL(clipboard->Store(), clipboard->StreamDictionary(), + KPlainTextBegin); + if (dataLength == 0) { + User::Leave(KErrNotFound); + } + HBufC* hBuf = HBufC::NewL(dataLength); + TPtr buf = hBuf->Des(); + text->Extract(buf, KPlainTextBegin, dataLength); + + QString string = qt_TDesC2QString(buf); + CleanupStack::PopAndDestroy(text); + + aData->setText(string); +} + +void readFromStreamLX(QMimeData* aData,RReadStream& aStream) +{ + // This function both leaves and throws exceptions. There must be no destructor + // dependencies between cleanup styles, and no cleanup stack dependencies on stacked objects. + TCardinality mimeTypeCount; + aStream >> mimeTypeCount; + for (int i = 0; i< mimeTypeCount;i++) + { + // mime type + TCardinality mimeTypeSize; + aStream >> mimeTypeSize; + HBufC* mimeTypeBuf = HBufC::NewLC(aStream,mimeTypeSize); + QString mimeType = QString(reinterpret_cast<const QChar *>(mimeTypeBuf->Des().Ptr()), + mimeTypeBuf->Length()); + CleanupStack::PopAndDestroy(mimeTypeBuf); + // mime data + TCardinality dataSize; + aStream >> dataSize; + QByteArray ba; + ba.reserve(dataSize); + aStream.ReadL(reinterpret_cast<uchar*>(ba.data_ptr()->data),dataSize); + ba.data_ptr()->size = dataSize; + aData->setData(mimeType,ba); + } +} + + +/***************************************************************************** + QClipboard member functions + *****************************************************************************/ + +void QClipboard::clear(Mode mode) +{ + setText(QString(), mode); +} +const QMimeData* QClipboard::mimeData(Mode mode) const +{ + if (mode != Clipboard) return 0; + QClipboardData *d = clipboardData(); + bool dataExists(false); + if (d) + { + TRAPD(err,{ + RFs fs = qt_s60GetRFs(); + CClipboard* cb = CClipboard::NewForReadingLC(fs); + Q_ASSERT(cb); + //stream for qt + RStoreReadStream stream; + TStreamId stid = (cb->StreamDictionary()).At(KQtCbDataStream); + if (stid != 0) { + stream.OpenLC(cb->Store(),stid); + QT_TRYCATCH_LEAVING(readFromStreamLX(d->source(),stream)); + CleanupStack::PopAndDestroy(&stream); + dataExists = true; + } + else { + //symbian clipboard + RStoreReadStream symbianStream; + TStreamId symbianStId = (cb->StreamDictionary()).At(KClipboardUidTypePlainText); + if (symbianStId != 0) { + symbianStream.OpenLC(cb->Store(), symbianStId); + QT_TRYCATCH_LEAVING(readSymbianStoreLX(d->source(), cb)); + CleanupStack::PopAndDestroy(&symbianStream); + dataExists = true; + } + } + CleanupStack::PopAndDestroy(cb); + }); + if (err != KErrNone){ + qDebug()<< "clipboard is empty/err: " << err; + } + + if (dataExists) { + return d->source(); + } + } + return 0; +} + + +void QClipboard::setMimeData(QMimeData* src, Mode mode) +{ + if (mode != Clipboard) return; + QClipboardData *d = clipboardData(); + if (d) + { + TRAPD(err,{ + RFs fs = qt_s60GetRFs(); + CClipboard* cb = CClipboard::NewForWritingLC(fs); + //stream for qt + RStoreWriteStream stream; + TStreamId stid = stream.CreateLC(cb->Store()); + QT_TRYCATCH_LEAVING(writeToStreamLX(src,stream)); + d->setSource(src); + stream.CommitL(); + (cb->StreamDictionary()).AssignL(KQtCbDataStream,stid); + cb->CommitL(); + + //stream for symbian + RStoreWriteStream symbianStream; + TStreamId symbianStId = symbianStream.CreateLC(cb->Store()); + QT_TRYCATCH_LEAVING(writeToSymbianStoreLX(src, cb)); + (cb->StreamDictionary()).AssignL(KClipboardUidTypePlainText, symbianStId); + cb->CommitL(); + CleanupStack::PopAndDestroy(3,cb); + }); + if (err != KErrNone){ + qDebug()<< "clipboard write err :" << err; + } + } + 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; +} + +bool QClipboard::event(QEvent * /* e */) +{ + return true; +} + +void QClipboard::connectNotify( const char * ) +{ +} + +void QClipboard::ownerDestroyed() +{ +} +QT_END_NAMESPACE +#endif // QT_NO_CLIPBOARD diff --git a/src/gui/platforms/s60/qcolormap_s60.cpp b/src/gui/platforms/s60/qcolormap_s60.cpp new file mode 100644 index 0000000000..2c634db8a5 --- /dev/null +++ b/src/gui/platforms/s60/qcolormap_s60.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolormap.h" +#include "qcolor.h" + +QT_BEGIN_NAMESPACE + +class QColormapPrivate +{ +public: + inline QColormapPrivate() + : ref(1) + { } + + QAtomicInt ref; +}; + +void QColormap::initialize() +{ +} + +void QColormap::cleanup() +{ +} + +QColormap QColormap::instance(int) +{ + return QColormap(); +} + +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/platforms/s60/qcursor_s60.cpp b/src/gui/platforms/s60/qcursor_s60.cpp new file mode 100644 index 0000000000..8dfe87ef81 --- /dev/null +++ b/src/gui/platforms/s60/qcursor_s60.cpp @@ -0,0 +1,533 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qcursor_p.h> +#include <private/qwidget_p.h> +#include <private/qapplication_p.h> +#include <coecntrl.h> +#include <qcursor.h> +#include <private/qt_s60_p.h> +#include <qbitmap.h> +#include <w32std.h> +#include <qapplication.h> +#include <qwidget.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_CURSOR +static QCursor cursorSprite; +static int cursorSpriteVisible; +#endif + +//pos and setpos are required whether cursors are configured or not. +QPoint QCursor::pos() +{ + return S60->lastCursorPos; +} + +void QCursor::setPos(int x, int y) +{ + //clip to screen size (window server allows a sprite hotspot to be outside the screen) + if (x < 0) + x=0; + else if (x >= S60->screenWidthInPixels) + x = S60->screenWidthInPixels - 1; + if (y < 0) + y = 0; + else if (y >= S60->screenHeightInPixels) + y = S60->screenHeightInPixels - 1; + +#ifndef QT_NO_CURSOR +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors && cursorSpriteVisible) + cursorSprite.d->scurs.SetPosition(TPoint(x,y)); + else +#endif + S60->wsSession().SetPointerCursorPosition(TPoint(x, y)); +#endif + S60->lastCursorPos = QPoint(x, y); + //send a fake mouse move event, so that enter/leave events go to the widget hierarchy + QWidget *w = QApplication::topLevelAt(S60->lastCursorPos); + if (w) { + CCoeControl* ctrl = w->effectiveWinId(); + TPoint epos(x, y); + TPoint cpos = epos - ctrl->PositionRelativeToScreen(); + TPointerEvent fakeEvent; + fakeEvent.iType = TPointerEvent::EMove; + fakeEvent.iModifiers = 0U; + fakeEvent.iPosition = cpos; + fakeEvent.iParentPosition = epos; + ctrl->HandlePointerEventL(fakeEvent); + } +} + +#ifndef QT_NO_CURSOR +/* + * Request cursor to be turned on or off. + * Reference counted, so 2 on + 1 off = on, for example + */ +void qt_symbian_set_cursor_visible(bool visible) { + if (visible) + cursorSpriteVisible++; + else + cursorSpriteVisible--; + Q_ASSERT(cursorSpriteVisible >=0); + + if (cursorSpriteVisible && !S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_show_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNormal); + } else if (!cursorSpriteVisible && S60->mouseInteractionEnabled) { +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_hide_pointer_sprite(); + else +#endif + S60->wsSession().SetPointerCursorMode(EPointerCursorNone); + } + S60->mouseInteractionEnabled = ((cursorSpriteVisible > 0) ? true : false); +} + +/* + * Check if the cursor is on or off + */ +bool qt_symbian_is_cursor_visible() { + return S60->mouseInteractionEnabled; +} + +QCursorData::QCursorData(Qt::CursorShape s) : + cshape(s), bm(0), bmm(0), hx(0), hy(0), pcurs() +{ + ref = 1; +} + +QCursorData::~QCursorData() +{ + for(int i=0;i<nativeSpriteMembers.Count();i++) { + delete nativeSpriteMembers[i]->iBitmap; + delete nativeSpriteMembers[i]->iMaskBitmap; + } + nativeSpriteMembers.ResetAndDestroy(); + pcurs.Close(); + delete bm; + delete bmm; +} + +/* Create a bitmap cursor, this is called by public constructors in the + * generic QCursor code. + */ +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->hx = hotX >= 0 ? hotX : bitmap.width() / 2; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + return d; +} + +/* + * returns an opaque native handle to a cursor. + * It happens to be the address of the native handle, as window server handles + * are not POD types. Note there is no QCursor(HANDLE) constructor on Symbian, + * Mac or QWS. + */ +Qt::HANDLE QCursor::handle() const +{ + if (d->pcurs.WsHandle()) + return reinterpret_cast<Qt::HANDLE> (&(d->pcurs)); + +#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS + // don't construct shape cursors, QApplication_s60 will use the system cursor instead + if (!(d->bm)) + return 0; +#endif + + d->pcurs = RWsPointerCursor(S60->wsSession()); + d->pcurs.Construct(0); + d->constructCursorSprite(d->pcurs); + d->pcurs.Activate(); + + return reinterpret_cast<Qt::HANDLE> (&(d->pcurs)); +} + +#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS +/* + * Loads a single cursor shape from resources and appends it to a native sprite. + * Animated cursors (e.g. the busy cursor) have multiple members. + */ +void QCursorData::loadShapeFromResource(RWsSpriteBase& target, QString resource, int hx, int hy, int interval) +{ + QPixmap pix; + CFbsBitmap* native; + QScopedPointer<TSpriteMember> member(new TSpriteMember); + member->iInterval = interval; + member->iInvertMask = false; + member->iMaskBitmap = 0; // all shapes are RGBA + member->iDrawMode = CGraphicsContext::EDrawModePEN; + member->iOffset = TPoint(-hx, -hy); + QString res(QLatin1String(":/trolltech/symbian/cursors/images/%1.png")); + pix.load(res.arg(resource)); + native = pix.toSymbianCFbsBitmap(); + member->iBitmap = native; + qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); + target.AppendMember(*(member.take())); +} + +//TODO: after 4.6, connect with style & skins? +/* + * Constructs the native cursor from resources compiled into QtGui + * This is needed only when the platform doesn't have system cursors. + * + * System cursors are higher performance, since they are constructed once + * and shared by all applications by specifying the shape number. + * Due to symbian platform security considerations, and the fact most + * existing phones have a broken RWsPointerCursor, system cursors are not + * being used. + */ +void QCursorData::constructShapeSprite(RWsSpriteBase& target) +{ + int i; + switch (cshape) { + default: + qWarning("QCursorData::constructShapeSprite unknown shape %d", cshape); + //fall through and give arrow cursor + case Qt::ArrowCursor: + loadShapeFromResource(target, QLatin1String("pointer"), 1, 1); + break; + case Qt::UpArrowCursor: + loadShapeFromResource(target, QLatin1String("uparrow"), 4, 0); + break; + case Qt::CrossCursor: + loadShapeFromResource(target, QLatin1String("cross"), 7, 7); + break; + case Qt::WaitCursor: + for (i = 1; i <= 12; i++) { + loadShapeFromResource(target, QString(QLatin1String("wait%1")).arg(i), 7, 7, 1000000); + } + break; + case Qt::IBeamCursor: + loadShapeFromResource(target, QLatin1String("ibeam"), 3, 10); + break; + case Qt::SizeVerCursor: + loadShapeFromResource(target, QLatin1String("sizever"), 4, 8); + break; + case Qt::SizeHorCursor: + loadShapeFromResource(target, QLatin1String("sizehor"), 8, 4); + break; + case Qt::SizeBDiagCursor: + loadShapeFromResource(target, QLatin1String("sizebdiag"), 8, 8); + break; + case Qt::SizeFDiagCursor: + loadShapeFromResource(target, QLatin1String("sizefdiag"), 8, 8); + break; + case Qt::SizeAllCursor: + loadShapeFromResource(target, QLatin1String("sizeall"), 7, 7); + break; + case Qt::BlankCursor: + loadShapeFromResource(target, QLatin1String("blank"), 0, 0); + break; + case Qt::SplitVCursor: + loadShapeFromResource(target, QLatin1String("splitv"), 7, 7); + break; + case Qt::SplitHCursor: + loadShapeFromResource(target, QLatin1String("splith"), 7, 7); + break; + case Qt::PointingHandCursor: + loadShapeFromResource(target, QLatin1String("handpoint"), 5, 0); + break; + case Qt::ForbiddenCursor: + loadShapeFromResource(target, QLatin1String("forbidden"), 7, 7); + break; + case Qt::WhatsThisCursor: + loadShapeFromResource(target, QLatin1String("whatsthis"), 1, 1); + break; + case Qt::BusyCursor: + loadShapeFromResource(target, QLatin1String("busy3"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy6"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy9"), 1, 1, 1000000); + loadShapeFromResource(target, QLatin1String("busy12"), 1, 1, 1000000); + break; + case Qt::OpenHandCursor: + loadShapeFromResource(target, QLatin1String("openhand"), 7, 7); + break; + case Qt::ClosedHandCursor: + loadShapeFromResource(target, QLatin1String("closehand"), 7, 7); + break; + } +} +#endif + +/* + * Common code between the sprite workaround and standard modes of operation. + * RWsSpriteBase is the base class for both RWsSprite and RWsPointerCursor. + * It is called from both handle() and qt_s60_show_pointer_sprite() + */ +void QCursorData::constructCursorSprite(RWsSpriteBase& target) +{ + int count = nativeSpriteMembers.Count(); + if (count) { + // already constructed + for (int i = 0; i < count; i++) + target.AppendMember(*(nativeSpriteMembers[i])); + + return; + } + if (pixmap.isNull() && !bm) { +#ifndef Q_SYMBIAN_HAS_SYSTEM_CURSORS + //shape cursor + constructShapeSprite(target); +#endif + return; + } + QScopedPointer<TSpriteMember> member(new TSpriteMember); + if (pixmap.isNull()) { + //construct mono cursor + member->iBitmap = bm->toSymbianCFbsBitmap(); + member->iMaskBitmap = bmm->toSymbianCFbsBitmap(); + } + else { + //construct normal cursor + member->iBitmap = pixmap.toSymbianCFbsBitmap(); + if (pixmap.hasAlphaChannel()) { + member->iMaskBitmap = 0; //use alpha blending + } + else if (pixmap.hasAlpha()) { + member->iMaskBitmap = pixmap.mask().toSymbianCFbsBitmap(); + } + else { + member->iMaskBitmap = 0; //opaque rectangle cursor (due to EDrawModePEN) + } + } + + member->iDrawMode = CGraphicsContext::EDrawModePEN; + member->iInvertMask = EFalse; + member->iInterval = 0; + member->iOffset = TPoint(-(hx), -(hy)); //Symbian hotspot coordinates are negative + qt_symbian_throwIfError(nativeSpriteMembers.Append(member.data())); + target.AppendMember(*(member.take())); +} + +/* + * shows the pointer sprite by constructing a native handle, and registering + * it with the window server. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_show_pointer_sprite() +{ + if (cursorSprite.d) { + if (cursorSprite.d->scurs.WsHandle()) + cursorSprite.d->scurs.Close(); + } else { + cursorSprite = QCursor(Qt::ArrowCursor); + } + + cursorSprite.d->scurs = RWsSprite(S60->wsSession()); + QPoint pos = QCursor::pos(); + cursorSprite.d->scurs.Construct(S60->windowGroup(), TPoint(pos.x(), pos.y()), ESpriteNoChildClip | ESpriteNoShadows); + + cursorSprite.d->constructCursorSprite(cursorSprite.d->scurs); + cursorSprite.d->scurs.Activate(); +} + +/* + * hides the pointer sprite by closing the native handle. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_hide_pointer_sprite() +{ + if (cursorSprite.d) { + cursorSprite.d->scurs.Close(); + } +} + +/* + * Changes the cursor sprite to the cursor specified. + * Only used when the sprite workaround is in use. + */ +void qt_symbian_set_pointer_sprite(const QCursor& cursor) +{ + if (S60->mouseInteractionEnabled) + qt_symbian_hide_pointer_sprite(); + cursorSprite = cursor; + if (S60->mouseInteractionEnabled) + qt_symbian_show_pointer_sprite(); +} + +/* + * When using sprites as a workaround on phones that have a broken + * RWsPointerCursor, this function is called in response to pointer events + * and when QCursor::setPos() is called. + * Performance is worse than a real pointer cursor, due to extra context + * switches vs. the window server moving the cursor by itself. + */ +void qt_symbian_move_cursor_sprite() +{ + if (S60->mouseInteractionEnabled) { + cursorSprite.d->scurs.SetPosition(TPoint(S60->lastCursorPos.x(), S60->lastCursorPos.y())); + } +} + +/* + * Translate from Qt::CursorShape to OS system pointer cursor list index. + * Currently we control the implementation of the system pointer cursor list, + * so this function is trivial. That may not always be the case. + */ +TInt qt_symbian_translate_cursor_shape(Qt::CursorShape shape) +{ + return (TInt) shape; +} + +/* + 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. +*/ +void qt_symbian_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 (!S60->curWin && w && w->internalWinId()) + return; + QWidget* cW = w && !w->internalWinId() ? w : QWidget::find(S60->curWin); + if (!cW || cW->window() != w->window() || !cW->isVisible() || !cW->underMouse() + || QApplication::overrideCursor()) + return; + +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) + qt_symbian_set_pointer_sprite(cW->cursor()); + else +#endif + qt_symbian_setWindowCursor(cW->cursor(), w->effectiveWinId()); +} + +/* + * Makes the specified cursor appear above a specific native window group + * Called from QSymbianControl and QApplication::restoreOverrideCursor + * + * Window server is needed for this, so there is no equivalent when using + * the sprite workaround. + */ +void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node) +{ + Qt::HANDLE handle = cursor.handle(); + if (handle) { + RWsPointerCursor *pcurs = reinterpret_cast<RWsPointerCursor *> (handle); + node.SetCustomPointerCursor(*pcurs); + } else +#ifdef Q_SYMBIAN_HAS_SYSTEM_CURSORS + { + TInt shape = qt_symbian_translate_cursor_shape(cursor.shape()); + node.SetPointerCursor(shape); + } +#else + qWarning("qt_s60_setWindowGroupCursor - null handle"); +#endif +} + +/* + * Makes the specified cursor appear above a specific native window + * Called from QSymbianControl and QApplication::restoreOverrideCursor + * + * Window server is needed for this, so there is no equivalent when using + * the sprite workaround. + */ +void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid) +{ + //find the window for this control + while (!wid->OwnsWindow()) { + wid = wid->Parent(); + if (!wid) + return; + } + RWindowTreeNode *node = wid->DrawableWindow(); + qt_symbian_setWindowGroupCursor(cursor, *node); +} + +/* + * Makes the specified cursor appear everywhere. + * Called from QApplication::setOverrideCursor + */ +void qt_symbian_setGlobalCursor(const QCursor &cursor) +{ +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + if (S60->brokenPointerCursors) { + qt_symbian_set_pointer_sprite(cursor); + } else +#endif + { + //because of the internals of window server, we need to force the cursor + //to be set in all child windows too, otherwise when the cursor is over + //the child window it may show a widget cursor or arrow cursor instead, + //depending on construction order. + QListIterator<WId> iter(QWidgetPrivate::mapper->uniqueKeys()); + while(iter.hasNext()) + { + CCoeControl *ctrl = iter.next(); + if(ctrl->OwnsWindow()) { + RWindowTreeNode *node = ctrl->DrawableWindow(); + qt_symbian_setWindowGroupCursor(cursor, *node); + } + } + } +} +QT_END_NAMESPACE +#endif // QT_NO_CURSOR diff --git a/src/gui/platforms/s60/qdesktopwidget_s60.cpp b/src/gui/platforms/s60/qdesktopwidget_s60.cpp new file mode 100644 index 0000000000..62a4d40eba --- /dev/null +++ b/src/gui/platforms/s60/qdesktopwidget_s60.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesktopwidget.h" +#include "qapplication_p.h" +#include "qwidget_p.h" +#include "qt_s60_p.h" +#include <w32std.h> +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) +#include <graphics/displaycontrol.h> +#endif + +QT_BEGIN_NAMESPACE + +extern int qt_symbian_create_desktop_on_screen; + +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(); + static void init(QDesktopWidget *that); + static void cleanup(); + static void init_sys(); + + static int screenCount; + static int primaryScreen; + + static QVector<QRect> *rects; + static QVector<QRect> *workrects; + static QVector<QWidget *> *screens; + + static int refcount; + +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + static MDisplayControl *displayControl; +#endif +}; + +int QDesktopWidgetPrivate::screenCount = 1; +int QDesktopWidgetPrivate::primaryScreen = 0; +QVector<QRect> *QDesktopWidgetPrivate::rects = 0; +QVector<QRect> *QDesktopWidgetPrivate::workrects = 0; +QVector<QWidget *> *QDesktopWidgetPrivate::screens = 0; +int QDesktopWidgetPrivate::refcount = 0; +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) +MDisplayControl *QDesktopWidgetPrivate::displayControl = 0; +#endif + +QDesktopWidgetPrivate::QDesktopWidgetPrivate() +{ + ++refcount; +} + +QDesktopWidgetPrivate::~QDesktopWidgetPrivate() +{ + if (!--refcount) + cleanup(); +} + +void QDesktopWidgetPrivate::init(QDesktopWidget *that) +{ + // Note that on S^3 devices the screen count retrieved via RWsSession + // will always be 2 but the width and height for screen number 1 will + // be 0 as long as TV-out is not connected. + // + // On the other hand a valid size for screen 1 will be reported even + // after the cable is disconnected. In order to overcome this, we use + // MDisplayControl::NumberOfResolutions() to check if the display is + // valid or not. + + screenCount = S60->screenCount(); +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + if (displayControl) { + if (displayControl->NumberOfResolutions() < 1) + screenCount = 1; + } +#endif + if (screenCount < 1) { + qWarning("No screen available"); + screenCount = 1; + } + + rects = new QVector<QRect>(); + workrects = new QVector<QRect>(); + screens = new QVector<QWidget *>(); + + rects->resize(screenCount); + workrects->resize(screenCount); + screens->resize(screenCount); + + for (int i = 0; i < screenCount; ++i) { + // All screens will have a position of (0, 0) as there is no true virtual desktop + // or pointer event support for multiple screens on Symbian. + QRect r(0, 0, + S60->screenWidthInPixelsForScreen[i], S60->screenHeightInPixelsForScreen[i]); + // Stop here if empty and ignore this screen. + if (r.isEmpty()) { + screenCount = i; + break; + } + (*rects)[i] = r; + QRect wr; + if (i == 0) + wr = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); + else + wr = rects->at(i); + (*workrects)[i].setRect(wr.x(), wr.y(), wr.width(), wr.height()); + (*screens)[i] = 0; + } + (*screens)[0] = that; +} + +void QDesktopWidgetPrivate::cleanup() +{ + delete rects; + rects = 0; + delete workrects; + workrects = 0; + if (screens) { + // First item is the QDesktopWidget so skip it. + for (int i = 1; i < screens->count(); ++i) + delete screens->at(i); + } + delete screens; + screens = 0; +} + +void QDesktopWidgetPrivate::init_sys() +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + if (S60->screenCount() > 1) { + CWsScreenDevice *dev = S60->screenDevice(1); + if (dev) { + displayControl = static_cast<MDisplayControl *>( + dev->GetInterface(MDisplayControl::ETypeId)); + if (displayControl) { + displayControl->EnableDisplayChangeEvents(ETrue); + } + } + } +#endif +} + + +QDesktopWidget::QDesktopWidget() + : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) +{ + setObjectName(QLatin1String("desktop")); + QDesktopWidgetPrivate::init_sys(); + QDesktopWidgetPrivate::init(this); +} + +QDesktopWidget::~QDesktopWidget() +{ +} + +bool QDesktopWidget::isVirtualDesktop() const +{ + return false; +} + +int QDesktopWidget::primaryScreen() const +{ + return QDesktopWidgetPrivate::primaryScreen; +} + +int QDesktopWidget::numScreens() const +{ + Q_D(const QDesktopWidget); + return QDesktopWidgetPrivate::screenCount; +} + +static inline QWidget *newSingleDesktopWidget(int screen) +{ + qt_symbian_create_desktop_on_screen = screen; + QWidget *w = new QSingleDesktopWidget; + qt_symbian_create_desktop_on_screen = -1; + return w; +} + +QWidget *QDesktopWidget::screen(int screen) +{ + Q_D(QDesktopWidget); + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + if (!d->screens->at(screen) + || d->screens->at(screen)->windowType() != Qt::Desktop) + (*d->screens)[screen] = newSingleDesktopWidget(screen); + return (*d->screens)[screen]; +} + +const QRect QDesktopWidget::availableGeometry(int screen) const +{ + Q_D(const QDesktopWidget); + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + + return d->workrects->at(screen); +} + +const QRect QDesktopWidget::screenGeometry(int screen) const +{ + Q_D(const QDesktopWidget); + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + + return d->rects->at(screen); +} + +int QDesktopWidget::screenNumber(const QWidget *widget) const +{ + Q_D(const QDesktopWidget); + return widget + ? S60->screenNumberForWidget(widget) + : d->primaryScreen; +} + +int QDesktopWidget::screenNumber(const QPoint &point) const +{ + Q_UNUSED(point); + Q_D(const QDesktopWidget); + return d->primaryScreen; +} + +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); + + 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); + } + + if (oldscreencount != d->screenCount) { + emit screenCountChanged(d->screenCount); + } +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qdnd_s60.cpp b/src/gui/platforms/s60/qdnd_s60.cpp new file mode 100644 index 0000000000..a9847a98f8 --- /dev/null +++ b/src/gui/platforms/s60/qdnd_s60.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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" +#include "qt_s60_p.h" + +#include <coecntrl.h> +// pointer cursor +#include <w32std.h> +#include <gdi.h> +#include <QCursor> + +QT_BEGIN_NAMESPACE +//### artistic impression of Symbians default DnD cursor ? + +static QPixmap *defaultPm = 0; +static const int default_pm_hotx = -50; +static const int default_pm_hoty = -50; +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", +}; +//### actions need to be redefined for S60 +// Shift/Ctrl handling, and final drop status +static Qt::DropAction global_accepted_action = Qt::MoveAction; +static Qt::DropActions possible_actions = Qt::IgnoreAction; + + +// static variables in place of a proper cross-process solution +static QDrag *drag_object; +static bool qt_symbian_dnd_dragging = false; + + +static Qt::KeyboardModifiers oldstate; + +void QDragManager::updatePixmap() +{ + 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; + } +#ifndef QT_NO_CURSOR + QCursor cursor(pm, pm_hot.x(), pm_hot.y()); + overrideCursor = cursor; +#endif +} + +void QDragManager::timerEvent(QTimerEvent *) { } + +void QDragManager::move(const QPoint&) { +} + +void QDragManager::updateCursor() +{ +#ifndef QT_NO_CURSOR + QCursor cursor = willDrop ? overrideCursor : Qt::ForbiddenCursor; + if (!restoreCursor) { + QApplication::setOverrideCursor(cursor); + restoreCursor = true; + } + else { + QApplication::changeOverrideCursor(cursor); + } +#endif +} + + +bool QDragManager::eventFilter(QObject *o, QEvent *e) +{ + if (beingCancelled) { + return false; + } + if (!o->isWidgetType()) + return false; + + switch(e->type()) { + 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()); + // map the Coords relative to the window. + if (!cw) + return true; + + while (cw && !cw->acceptDrops() && !cw->isWindow()) + cw = cw->parentWidget(); + + bool oldWillDrop = willDrop; + if (object->target() != cw) { + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + willDrop = false; + global_accepted_action = Qt::IgnoreAction; + if (oldWillDrop != willDrop) + updateCursor(); + 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; + if (oldWillDrop != willDrop) + updateCursor(); + } + } 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; + if (oldWillDrop != willDrop) { + updatePixmap(); + updateCursor(); + } + } + if (global_accepted_action != prevAction) + emitActionChanged(global_accepted_action); + } + return true; // Eat all mouse events + } + + case QEvent::MouseButtonRelease: + { + qApp->removeEventFilter(this); +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + willDrop = false; + restoreCursor = false; + } +#endif + 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) +{ + Q_ASSERT(!qt_symbian_dnd_dragging); + if (object == o || !o || !o->source()) + return Qt::IgnoreAction; + + if (object) { + cancel(); + qApp->removeEventFilter(this); + beingCancelled = false; + } + + object = drag_object = o; + + oldstate = Qt::NoModifier; // #### Should use state that caused the drag + willDrop = false; + updatePixmap(); + updateCursor(); + +#ifndef QT_NO_CURSOR + qt_symbian_set_cursor_visible(true); //force cursor on even for touch phone +#endif + + object->d_func()->target = 0; + + qApp->installEventFilter(this); + + global_accepted_action = defaultAction(dragPrivate()->possible_actions, Qt::NoModifier); + qt_symbian_dnd_dragging = true; + + eventLoop = new QEventLoop; + // block + (void) eventLoop->exec(QEventLoop::AllEvents); + delete eventLoop; + eventLoop = 0; + +#ifndef QT_NO_CURSOR + qt_symbian_set_cursor_visible(false); + + overrideCursor = QCursor(); //deref the cursor data + qt_symbian_dnd_dragging = false; +#endif + + return global_accepted_action; +} + + +void QDragManager::cancel(bool deleteSource) +{ + beingCancelled = true; + + if (object->target()) { + QDragLeaveEvent dle; + QApplication::sendEvent(object->target(), &dle); + } + + if (drag_object) { + if (deleteSource) + object->deleteLater(); + drag_object = object = 0; + } + +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif + + global_accepted_action = Qt::IgnoreAction; +} + + +void QDragManager::drop() +{ +#ifndef QT_NO_CURSOR + if (restoreCursor) { + QApplication::restoreOverrideCursor(); + restoreCursor = false; + } +#endif +} + +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(); +} + +QT_END_NAMESPACE +#endif // QT_NO_DRAGANDDROP diff --git a/src/gui/platforms/s60/qeventdispatcher_s60.cpp b/src/gui/platforms/s60/qeventdispatcher_s60.cpp new file mode 100644 index 0000000000..2d92c89c07 --- /dev/null +++ b/src/gui/platforms/s60/qeventdispatcher_s60.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qwidget.h> + +#include "qeventdispatcher_s60_p.h" + +QT_BEGIN_NAMESPACE + +QtEikonEnv::QtEikonEnv() + : m_lastIterationCount(0) + , m_savedStatusCode(KRequestPending) + , m_hasAlreadyRun(false) +{ +} + +QtEikonEnv::~QtEikonEnv() +{ +} + +void QtEikonEnv::RunL() +{ + QEventDispatcherS60 *dispatcher = qobject_cast<QEventDispatcherS60 *>(QAbstractEventDispatcher::instance()); + if (!dispatcher) { + CEikonEnv::RunL(); + return; + } + + if (m_lastIterationCount != dispatcher->iterationCount()) { + m_hasAlreadyRun = false; + m_lastIterationCount = dispatcher->iterationCount(); + } + + if (m_hasAlreadyRun) { + // Fool the active scheduler into believing we are still waiting for events. + // The window server thinks we are not, however. + m_savedStatusCode = iStatus.Int(); + iStatus = KRequestPending; + SetActive(); + dispatcher->queueDeferredActiveObjectsCompletion(); + } else { + m_hasAlreadyRun = true; + CEikonEnv::RunL(); + } +} + +void QtEikonEnv::DoCancel() +{ + complete(); + + CEikonEnv::DoCancel(); +} + +void QtEikonEnv::complete() +{ + if (m_hasAlreadyRun) { + if (m_savedStatusCode != KRequestPending) { + TRequestStatus *status = &iStatus; + QEventDispatcherSymbian::RequestComplete(status, m_savedStatusCode); + m_savedStatusCode = KRequestPending; + } + m_hasAlreadyRun = false; + } +} + +QEventDispatcherS60::QEventDispatcherS60(QObject *parent) + : QEventDispatcherSymbian(parent), + m_noInputEvents(false) +{ +} + +QEventDispatcherS60::~QEventDispatcherS60() +{ + for (int c = 0; c < m_deferredInputEvents.size(); ++c) { + delete m_deferredInputEvents[c].event; + } +} + +bool QEventDispatcherS60::processEvents ( QEventLoop::ProcessEventsFlags flags ) +{ + bool ret = false; + + QT_TRY { + bool oldNoInputEventsValue = m_noInputEvents; + if (flags & QEventLoop::ExcludeUserInputEvents) { + m_noInputEvents = true; + } else { + m_noInputEvents = false; + ret = sendDeferredInputEvents() || ret; + } + + ret = QEventDispatcherSymbian::processEvents(flags) || ret; + + m_noInputEvents = oldNoInputEventsValue; + } QT_CATCH (const std::exception& ex) { +#ifndef QT_NO_EXCEPTIONS + CActiveScheduler::Current()->Error(qt_symbian_exception2Error(ex)); +#endif + } + + return ret; +} + +bool QEventDispatcherS60::hasPendingEvents() +{ + return !m_deferredInputEvents.isEmpty() || QEventDispatcherSymbian::hasPendingEvents(); +} + +void QEventDispatcherS60::saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event) +{ + DeferredInputEvent inputEvent = {control, widget, event}; + m_deferredInputEvents.append(inputEvent); + connect(widget, SIGNAL(destroyed(QObject*)), SLOT(removeInputEventsForWidget(QObject*))); +} + +bool QEventDispatcherS60::sendDeferredInputEvents() +{ + bool eventsSent = false; + while (!m_deferredInputEvents.isEmpty()) { + DeferredInputEvent inputEvent = m_deferredInputEvents.takeFirst(); +#ifndef QT_NO_EXCEPTIONS + try { +#endif + inputEvent.control->sendInputEvent(inputEvent.widget, inputEvent.event); +#ifndef QT_NO_EXCEPTIONS + } catch (...) { + delete inputEvent.event; + throw; + } +#endif + delete inputEvent.event; + eventsSent = true; + } + + return eventsSent; +} + +void QEventDispatcherS60::removeInputEventsForWidget(QObject *object) +{ + for (int c = 0; c < m_deferredInputEvents.size(); ++c) { + if (m_deferredInputEvents[c].widget == object) { + delete m_deferredInputEvents[c].event; + m_deferredInputEvents.removeAt(c--); + } + } +} + +// reimpl +void QEventDispatcherS60::reactivateDeferredActiveObjects() +{ + if (S60->qtOwnsS60Environment) { + static_cast<QtEikonEnv *>(CCoeEnv::Static())->complete(); + } + + QEventDispatcherSymbian::reactivateDeferredActiveObjects(); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qeventdispatcher_s60_p.h b/src/gui/platforms/s60/qeventdispatcher_s60_p.h new file mode 100644 index 0000000000..7c5a8d03d4 --- /dev/null +++ b/src/gui/platforms/s60/qeventdispatcher_s60_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVENTDISPATCHER_S60_P_H +#define QEVENTDISPATCHER_S60_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_symbian_p.h> +#include "qt_s60_p.h" + +#include <eikenv.h> + +QT_BEGIN_NAMESPACE + +class QEventDispatcherS60; + +class QtEikonEnv : public CEikonEnv +{ +public: + QtEikonEnv(); + ~QtEikonEnv(); + + // from CActive. + void RunL(); + void DoCancel(); + + void complete(); + +private: + // Workaround for a BC break from S60 3.2 -> 5.0, where the CEikonEnv override was removed. + // To avoid linking to that when we build against 3.2, define an empty body here. + // Reserved_*() have been verified to be empty in the S60 code. + void Reserved_1() {} + void Reserved_2() {} + +private: + int m_lastIterationCount; + TInt m_savedStatusCode; + bool m_hasAlreadyRun; +}; + +class Q_GUI_EXPORT QEventDispatcherS60 : public QEventDispatcherSymbian +{ + Q_OBJECT + +public: + QEventDispatcherS60(QObject *parent = 0); + ~QEventDispatcherS60(); + + bool processEvents ( QEventLoop::ProcessEventsFlags flags ); + bool hasPendingEvents(); + + bool excludeUserInputEvents() { return m_noInputEvents; } + + void saveInputEvent(QSymbianControl *control, QWidget *widget, QInputEvent *event); + + void reactivateDeferredActiveObjects(); + +private: + bool sendDeferredInputEvents(); + +private Q_SLOTS: + void removeInputEventsForWidget(QObject *object); + +private: + bool m_noInputEvents; + + struct DeferredInputEvent + { + QSymbianControl *control; + QWidget *widget; + QInputEvent *event; + }; + QList<DeferredInputEvent> m_deferredInputEvents; +}; + +QT_END_NAMESPACE + +#endif // QEVENTDISPATCHER_S60_P_H diff --git a/src/gui/platforms/s60/qfont_s60.cpp b/src/gui/platforms/s60/qfont_s60.cpp new file mode 100644 index 0000000000..114191d765 --- /dev/null +++ b/src/gui/platforms/s60/qfont_s60.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfont.h" +#include "qfont_p.h" +#include <private/qt_s60_p.h> +#include <private/qpixmap_s60_p.h> +#include "qmutex.h" + +QT_BEGIN_NAMESPACE + +#ifdef QT_NO_FREETYPE +Q_GLOBAL_STATIC(QMutex, lastResortFamilyMutex); +#endif // QT_NO_FREETYPE + +extern QStringList qt_symbian_fontFamiliesOnFontServer(); // qfontdatabase_s60.cpp +Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, fontFamiliesOnFontServer, { + // We are only interested in the initial font families. No Application fonts. + // Therefore, we are allowed to cache the list. + x->append(qt_symbian_fontFamiliesOnFontServer()); +}); + +QString QFont::lastResortFont() const +{ + // Symbian's font Api does not distinguish between font and family. + // Therefore we try to get a "Family" first, then fall back to "Sans". + static QString font = lastResortFamily(); + if (font.isEmpty()) + font = QLatin1String("Sans"); + return font; +} + +QString QFont::lastResortFamily() const +{ +#ifdef QT_NO_FREETYPE + QMutexLocker locker(lastResortFamilyMutex()); + static QString family; + if (family.isEmpty()) { + + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + + CFont *font; + const TInt err = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec()); + Q_ASSERT(err == KErrNone); + const TFontSpec spec = font->FontSpecInTwips(); + family = QString((const QChar *)spec.iTypeface.iName.Ptr(), spec.iTypeface.iName.Length()); + S60->screenDevice()->ReleaseFont(font); + + lock.relock(); + } + return family; +#else // QT_NO_FREETYPE + // For the FreeType case we just hard code the face name, since otherwise on + // East Asian systems we may get a name for a stroke based (non-ttf) font. + + // TODO: Get the type face name in a proper way + + const bool isJapaneseOrChineseSystem = + User::Language() == ELangJapanese || User::Language() == ELangPrcChinese; + + static QString family; + if (family.isEmpty()) { + QStringList families = qt_symbian_fontFamiliesOnFontServer(); + const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"}; + for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) { + const QString preferredFamily = QLatin1String(preferredFamilies[i]); + if (families.contains(preferredFamily)) { + family = preferredFamily; + break; + } + } + } + + return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1()); +#endif // QT_NO_FREETYPE +} + +QString QFont::defaultFamily() const +{ +#ifdef QT_NO_FREETYPE + switch(d->request.styleHint) { + case QFont::SansSerif: { + static const char* const preferredSansSerif[] = {"Nokia Sans S60", "Series 60 Sans"}; + for (int i = 0; i < sizeof preferredSansSerif / sizeof preferredSansSerif[0]; ++i) { + const QString sansSerif = QLatin1String(preferredSansSerif[i]); + if (fontFamiliesOnFontServer()->contains(sansSerif)) + return sansSerif; + } + } + // No break. Intentional fall through. + default: + return lastResortFamily(); + } +#endif // QT_NO_FREETYPE + return lastResortFamily(); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qfontdatabase_s60.cpp b/src/gui/platforms/s60/qfontdatabase_s60.cpp new file mode 100644 index 0000000000..1db4a7d359 --- /dev/null +++ b/src/gui/platforms/s60/qfontdatabase_s60.cpp @@ -0,0 +1,1091 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qapplication_p.h> +#include "qdir.h" +#include "qfont_p.h" +#include "qfontengine_s60_p.h" +#include "qabstractfileengine.h" +#include "qdesktopservices.h" +#include "qtemporaryfile.h" +#include "qtextcodec.h" +#include <private/qpixmap_s60_p.h> +#include <private/qt_s60_p.h> +#include "qendian.h" +#include <private/qcore_symbian_p.h> +#ifdef QT_NO_FREETYPE +#include <openfont.h> +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file +#endif // SYMBIAN_ENABLE_SPLIT_HEADERS +#endif // QT_NO_FREETYPE + +QT_BEGIN_NAMESPACE + +QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp +{ + QStringList result; + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + const int numTypeFaces = S60->screenDevice()->NumTypefaces(); + for (int i = 0; i < numTypeFaces; i++) { + TTypefaceSupport typefaceSupport; + S60->screenDevice()->TypefaceSupport(typefaceSupport, i); + const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); + result.append(familyName); + } + lock.relock(); + return result; +} + +QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters, + QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort, + bool uniqueFileNames = true) +{ + QFileInfoList result; + + // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z: + QStringList driveStrings; + foreach (const QFileInfo &drive, QDir::drives()) + driveStrings.append(drive.absolutePath()); + driveStrings.sort(); + const QString zDriveString(QLatin1String("Z:/")); + driveStrings.removeAll(zDriveString); + driveStrings.prepend(zDriveString); + + QStringList uniqueFileNameList; + for (int i = driveStrings.count() - 1; i >= 0; --i) { + const QDir dirOnDrive(driveStrings.at(i) + path); + const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort); + if (uniqueFileNames) { + foreach(const QFileInfo &entry, entriesOnDrive) { + if (!uniqueFileNameList.contains(entry.fileName())) { + uniqueFileNameList.append(entry.fileName()); + result.append(entry); + } + } + } else { + result.append(entriesOnDrive); + } + } + return result; +} + +#ifdef QT_NO_FREETYPE +class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras +{ +public: + QSymbianFontDatabaseExtrasImplementation(); + ~QSymbianFontDatabaseExtrasImplementation(); + + const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const; + void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt); + static inline bool appFontLimitReached(); + TUid addFontFileToFontStore(const QFileInfo &fontFileInfo); + static void clear(); + + static inline QString tempAppFontFolder(); + static const QString appFontMarkerPrefix; + static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>' + + struct CFontFromFontStoreReleaser { + static inline void cleanup(CFont *font) + { + if (!font) + return; + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras); + dbExtras->m_store->ReleaseFont(font); + } + }; + + struct CFontFromScreenDeviceReleaser { + static inline void cleanup(CFont *font) + { + if (!font) + return; + S60->screenDevice()->ReleaseFont(font); + } + }; + +// m_heap, m_store, m_rasterizer and m_extras are used if Symbian +// does not provide the Font Table API + RHeap* m_heap; + CFontStore *m_store; + COpenFontRasterizer *m_rasterizer; + mutable QList<const QSymbianTypeFaceExtras *> m_extras; + + mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash; + mutable QSet<QString> m_applicationFontFamilies; +}; + +const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix = + QLatin1String("Q"); + +inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() +{ + return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\'); +} + +QString QSymbianFontDatabaseExtrasImplementation::appFontMarker() +{ + static QString result; + if (result.isEmpty()) { + quint16 id = 0; + if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + // We are allowed to load app fonts even from previous, crashed runs + // of this application, since we can access the font tables. + const quint32 uid = RProcess().Type().MostDerived().iUid; + id = static_cast<quint16>(uid + (uid >> 16)); + } else { + // If no font table Api is available, we must not even load a font + // from a previous (crashed) run of this application. Reason: we + // won't get the font tables, they are not in the CFontStore. + // So, we use the pid, for more uniqueness. + id = static_cast<quint16>(RProcess().Id().Id()); + } + result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0')); + Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4); + } + return result; +} + +static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName) +{ + const int idLength = 3; // Keep in sync with id length in appFontMarker(). + const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix; + if (fontName.length() < prefix.length() + idLength + || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix) + return false; + // Testing if the the id is base32 data + for (int i = fontName.length() - idLength; i < fontName.length(); ++i) { + const QChar &c = fontName.at(i); + if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9') + || c >= QLatin1Char('a') && c <= QLatin1Char('v'))) + return false; + } + return true; +} + +// If fontName is an application font of this app, prepend the app font marker +QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName) +{ + QFontDatabasePrivate *db = privateDb(); + Q_ASSERT(db); + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + return dbExtras->m_applicationFontFamilies.contains(fontName) ? + fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + : fontName; +} + +static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName) +{ + return markedFontName.left(markedFontName.length() + - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length()); +} + +QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation() +{ + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + QStringList filters; + filters.append(QLatin1String("*.ttf")); + filters.append(QLatin1String("*.ccc")); + filters.append(QLatin1String("*.ltt")); + const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters); + + const TInt heapMinLength = 0x1000; + const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength); + m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength); + QT_TRAP_THROWING( + m_store = CFontStore::NewL(m_heap); + m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); + CleanupStack::PushL(m_rasterizer); + m_store->InstallRasterizerL(m_rasterizer); + CleanupStack::Pop(m_rasterizer);); + + foreach (const QFileInfo &fontFileInfo, fontFiles) + addFontFileToFontStore(fontFileInfo); + } +} + +void QSymbianFontDatabaseExtrasImplementation::clear() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return; // initializeDb() has never been called + if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + qDeleteAll(dbExtras->m_extrasHash); + } else { + typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator; + for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) { + dbExtras->m_store->ReleaseFont((*p)->fontOwner()); + delete *p; + } + dbExtras->m_extras.clear(); + } + dbExtras->m_extrasHash.clear(); +} + +void qt_cleanup_symbianFontDatabase() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!db->applicationFonts.isEmpty()) { + QFontDatabase::removeAllApplicationFonts(); + // We remove the left over temporary font files of Qt application. + // Active fonts are undeletable since the font server holds a handle + // on them, so we do not need to worry to delete other running + // applications' fonts. + const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()); + const QStringList filter( + QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf")); + foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter)) + QFile(ttfFile.absoluteFilePath()).remove(); + db->applicationFonts.clear(); + } +} + +QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation() +{ + qt_cleanup_symbianFontDatabase(); + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + delete m_store; + m_heap->Close(); + } +} + +#ifndef FNTSTORE_H_INLINES_SUPPORT_FMM +/* + Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()' + that returns a private data member. The header will change between SDKs. But Qt has + to build on any SDK version and run on other versions of Symbian OS. + This function performs the needed pointer arithmetic to get the right COpenFont* +*/ +COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont) +{ + const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private + const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont); + return (valueIOpenFont & 1) ? + (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset + (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer +} +#endif // FNTSTORE_H_INLINES_SUPPORT_FMM + +const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface, + bool bold, bool italic) const +{ + const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface); + const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic)); + if (!m_extrasHash.contains(searchKey)) { + TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1); + if (bold) + searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); + if (italic) + searchSpec.iFontStyle.SetPosture(EPostureItalic); + + CFont* font = NULL; + if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { + const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec); + Q_ASSERT(err == KErrNone && font); + QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font); + QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font); + sFont.take(); + m_extrasHash.insert(searchKey, extras); + } else { + const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec); + Q_ASSERT(err == KErrNone && font); + const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font); + COpenFont *openFont = +#ifdef FNTSTORE_H_INLINES_SUPPORT_FMM + bitmapFont->OpenFont(); +#else // FNTSTORE_H_INLINES_SUPPORT_FMM + OpenFontFromBitmapFont(bitmapFont); +#endif // FNTSTORE_H_INLINES_SUPPORT_FMM + const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib(); + const QString foundKey = + QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length()); + if (!m_extrasHash.contains(foundKey)) { + QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font); + QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont); + sFont.take(); + m_extras.append(extras); + m_extrasHash.insert(searchKey, extras); + m_extrasHash.insert(foundKey, extras); + } else { + m_store->ReleaseFont(font); + m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey)); + } + } + } + return m_extrasHash.value(searchKey); +} + +void QSymbianFontDatabaseExtrasImplementation::removeAppFontData( + QFontDatabasePrivate::ApplicationFont *fnt) +{ + clear(); + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable() + && fnt->fontStoreFontFileUid.iUid != 0) + m_store->RemoveFile(fnt->fontStoreFontFileUid); + if (!fnt->families.isEmpty()) + m_applicationFontFamilies.remove(fnt->families.first()); + if (fnt->screenDeviceFontFileId != 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); +} + +bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() +{ + QFontDatabasePrivate *db = privateDb(); + if (!db) + return false; + const int maxAppFonts = 5; + int registeredAppFonts = 0; + foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts) + if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts) + return true; + return false; +} + +TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) +{ + Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()); + const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath()); + const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); + TUid fontUid = {0}; + TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr)); + return fontUid; +} + +#else // QT_NO_FREETYPE +class QFontEngineFTS60 : public QFontEngineFT +{ +public: + QFontEngineFTS60(const QFontDef &fd); +}; + +QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd) + : QFontEngineFT(fd) +{ + default_hint_style = HintFull; +} +#endif // QT_NO_FREETYPE + +/* + QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60 + and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the + Freetype based font rendering need them, they are here. +*/ +qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation) +{ + CWsScreenDevice* device = S60->screenDevice(); + return (orientation == Qt::Horizontal? + device->HorizontalPixelsToTwips(pixels) + :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint; +} + +qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation) +{ + CWsScreenDevice* device = S60->screenDevice(); + const int twips = points * KTwipsPerPoint; + return orientation == Qt::Horizontal? + device->HorizontalTwipsToPixels(twips) + :device->VerticalTwipsToPixels(twips); +} + +QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies) + : QFontEngineMulti(fallbackFamilies.size() + 1) + , m_script(script) + , m_fallbackFamilies(fallbackFamilies) +{ + engines[0] = first; + first->ref.ref(); + fontDef = engines[0]->fontDef; +} + +void QFontEngineMultiS60::loadEngine(int at) +{ + Q_ASSERT(at < engines.size()); + Q_ASSERT(engines.at(at) == 0); + + QFontDef request = fontDef; + request.styleStrategy |= QFont::NoFontMerging; + request.family = m_fallbackFamilies.at(at-1); + engines[at] = QFontDatabase::findFont(m_script, + /*fontprivate*/0, + request); + Q_ASSERT(engines[at]); +} + +#ifdef QT_NO_FREETYPE +static bool registerScreenDeviceFont(int screenDeviceFontIndex, + const QSymbianFontDatabaseExtrasImplementation *dbExtras) +{ + TTypefaceSupport typefaceSupport; + S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex); + + QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); + if (qt_symbian_fontNameHasAppFontMarker(familyName)) { + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + if (familyName.endsWith(marker)) { + familyName = qt_symbian_appFontNameWithoutMarker(familyName); + dbExtras->m_applicationFontFamilies.insert(familyName); + } else { + return false; // This was somebody else's application font. Skip it. + } + } + + CFont *font; // We have to get a font instance in order to know all the details + TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11); + if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone) + return false; + QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font); + if (font->TypeUid() != KCFbsFontUid) + return false; + TOpenFontFaceAttrib faceAttrib; + const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font); + cfbsFont->GetFaceAttrib(faceAttrib); + + QtFontStyle::Key styleKey; + styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal; + styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal; + + QtFontFamily *family = privateDb()->family(familyName, true); + family->fixedPitch = faceAttrib.IsMonoWidth(); + QtFontFoundry *foundry = family->foundry(QString(), true); + QtFontStyle *style = foundry->style(styleKey, true); + style->smoothScalable = typefaceSupport.iIsScalable; + style->pixelSize(0, true); + + const QSymbianTypeFaceExtras *typeFaceExtras = + dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic()); + const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData()); + const unsigned char* ulUnicodeRange = data + 42; + quint32 unicodeRange[4] = { + qFromBigEndian<quint32>(ulUnicodeRange), + qFromBigEndian<quint32>(ulUnicodeRange + 4), + qFromBigEndian<quint32>(ulUnicodeRange + 8), + qFromBigEndian<quint32>(ulUnicodeRange + 12) + }; + const unsigned char* ulCodePageRange = data + 78; + quint32 codePageRange[2] = { + qFromBigEndian<quint32>(ulCodePageRange), + qFromBigEndian<quint32>(ulCodePageRange + 4) + }; + const QList<QFontDatabase::WritingSystem> writingSystems = + qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); + foreach (const QFontDatabase::WritingSystem system, writingSystems) + family->writingSystems[system] = QtFontFamily::Supported; + return true; +} +#endif + +static void initializeDb() +{ + QFontDatabasePrivate *db = privateDb(); + if(!db || db->count) + return; + +#ifdef QT_NO_FREETYPE + if (!db->symbianExtras) + db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation; + + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + + const int numTypeFaces = S60->screenDevice()->NumTypefaces(); + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + for (int i = 0; i < numTypeFaces; i++) + registerScreenDeviceFont(i, dbExtras); + + // We have to clear/release all CFonts, here, in case one of the fonts is + // an application font of another running Qt app. Otherwise the other Qt app + // cannot remove it's application font, anymore -> "Zombie Font". + QSymbianFontDatabaseExtrasImplementation::clear(); + + lock.relock(); + +#else // QT_NO_FREETYPE + QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation)); + dir.setNameFilters(QStringList() << QLatin1String("*.ttf") + << QLatin1String("*.ttc") << QLatin1String("*.pfa") + << QLatin1String("*.pfb")); + for (int i = 0; i < int(dir.count()); ++i) { + const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); + db->addTTFile(file); + } +#endif // QT_NO_FREETYPE +} + +static inline void load(const QString &family = QString(), int script = -1) +{ + Q_UNUSED(family) + Q_UNUSED(script) + initializeDb(); +} + +struct OffsetTable { + quint32 sfntVersion; + quint16 numTables, searchRange, entrySelector, rangeShift; +}; + +struct TableRecord { + quint32 tag, checkSum, offset, length; +}; + +struct NameTableHead { + quint16 format, count, stringOffset; +}; + +struct NameRecord { + quint16 platformID, encodingID, languageID, nameID, length, offset; +}; + +static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount) +{ + quint32 result = 0; + const quint32 *ptr = reinterpret_cast<const quint32*>(data); + const quint32 *endPtr = + ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32); + while (ptr < endPtr) { + const quint32 unit32Value = *ptr++; + result += qFromBigEndian(unit32Value); + } + return result; +} + +static inline quint32 toDWordBoundary(quint32 value) +{ + return (value + 3) & ~3; +} + +static inline quint32 dWordPadding(quint32 value) +{ + return (4 - (value & 3)) & 3; +} + +static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker) +{ + const quint32 tableLength = static_cast<quint32>(table.size()); + + if (tableLength > 50000 // hard limit + || tableLength < sizeof(NameTableHead)) // corrupt name table + return false; + + const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData()); + const quint16 count = qFromBigEndian(head->count); + const quint16 stringOffset = qFromBigEndian(head->stringOffset); + if (count > 200 // hard limit + || stringOffset >= tableLength // corrupt name table + || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table + return false; + + QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader); + const QByteArray markerUtf16BE = encoder.fromUnicode(marker); + const QByteArray markerAscii = marker.toAscii(); + + QByteArray markedTable; + markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra + markedTable.append(table, stringOffset); + QByteArray markedStrings; + quint32 stringDataCount = stringOffset; + for (quint16 i = 0; i < count; ++i) { + const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i; + NameRecord *nameRecord = + reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset); + const quint16 nameID = qFromBigEndian(nameRecord->nameID); + const quint16 platformID = qFromBigEndian(nameRecord->platformID); + const quint16 encodingID = qFromBigEndian(nameRecord->encodingID); + const quint16 offset = qFromBigEndian(nameRecord->offset); + const quint16 length = qFromBigEndian(nameRecord->length); + stringDataCount += length; + if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string. + || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds + return false; + const bool needsMarker = + nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21; + const bool isUnicode = + platformID == 0 || platformID == 3 && encodingID == 1; + const QByteArray originalString = + QByteArray::fromRawData(table.constData() + stringOffset + offset, length); + QByteArray markedString; + if (needsMarker) { + const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1); + markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii); + } else { + markedString = originalString; + } + nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length())); + nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length())); + markedStrings.append(markedString); + } + markedTable.append(markedStrings); + table = markedTable; + return true; +} + +const quint32 ttfMaxFileSize = 3500000; + +static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker) +{ + const quint32 ttfChecksumNumber = 0xb1b0afba; + const quint32 alignment = 4; + const quint32 ttfLength = static_cast<quint32>(ttf.size()); + if (ttfLength > ttfMaxFileSize // hard limit + || ttfLength % alignment != 0 // ttf sizes are always factors of 4 + || ttfLength <= sizeof(OffsetTable) // ttf too short + || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid + return false; + + const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData()); + const quint16 numTables = qFromBigEndian(offsetTable->numTables); + const quint32 recordsLength = + toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord)); + if (numTables > 30 // hard limit + || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty. + return false; + + QByteArray markedTtf; + markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra + markedTtf.append(ttf.constData(), recordsLength); + + const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head' + int indexOfHeadTable = -1; + quint32 ttfDataSize = recordsLength; + typedef QPair<quint32, quint32> Range; + QList<Range> memoryRanges; + memoryRanges.reserve(numTables); + for (int i = 0; i < numTables; ++i) { + TableRecord *tableRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + const quint32 lengthAligned = toDWordBoundary(length); + ttfDataSize += lengthAligned; + if (offset < recordsLength // must not intersect ttf header/records + || offset % alignment != 0 // must be aligned + || offset > ttfLength - alignment // table out of bounds + || offset + lengthAligned > ttfLength // table out of bounds + || ttfDataSize > ttfLength) // tables would not fit into the ttf + return false; + + foreach (const Range &range, memoryRanges) + if (offset < range.first + range.second && offset + lengthAligned > range.first) + return false; // Overlaps with another table + memoryRanges.append(Range(offset, lengthAligned)); + + quint32 checkSum = qFromBigEndian(tableRecord->checkSum); + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) { + if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32)) + return false; // Invalid 'head' table + const quint32 *checkSumAdjustmentTag = + reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset); + const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag); + checkSum += checkSumAdjustment; + indexOfHeadTable = i; // For the ttf checksum re-calculation, later + } + if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length)) + return false; // Table checksum is invalid + + bool updateTableChecksum = false; + QByteArray table; + if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) { + table = QByteArray(ttf.constData() + offset, length); + if (!ttfMarkNameTable(table, marker)) + return false; // Name table was not markable. + updateTableChecksum = true; + } else { + table = QByteArray::fromRawData(ttf.constData() + offset, length); + } + + tableRecord->offset = qToBigEndian(markedTtf.size()); + tableRecord->length = qToBigEndian(table.size()); + markedTtf.append(table); + markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding + if (updateTableChecksum) { + TableRecord *tableRecord = // Need to recalculate, since markedTtf changed + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); + const quint32 offset = qFromBigEndian(tableRecord->offset); + const quint32 length = qFromBigEndian(tableRecord->length); + tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length)); + } + } + if (indexOfHeadTable == -1 // 'head' table is mandatory + || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian. + return false; + TableRecord *headRecord = + reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord)); + quint32 *checkSumAdjustmentTag = + reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset); + *checkSumAdjustmentTag = 0; + const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count()); + *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum); + ttf = markedTtf; + return true; +} + +static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName) +{ + bool result = false; + QString ttfFileName; + QFile tempFileGuard; + QFileInfo info(fileName); + if (!data.isEmpty()) { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + QSymbianFontDatabaseExtrasImplementation::appFontMarker() + + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open() || tempfile.write(data) == -1) + return false; + ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath()); + tempfile.setAutoRemove(false); + tempfile.close(); + tempFileGuard.setFileName(ttfFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return false; + } else if (info.isFile()) { + ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath()); + } else { + return false; + } + + CFontStore *store = 0; + RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000); + if (heap) { + QT_TRAP_THROWING( + CleanupClosePushL(*heap); + store = CFontStore::NewL(heap); + CleanupStack::PushL(store); + COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); + CleanupStack::PushL(rasterizer); + store->InstallRasterizerL(rasterizer); + CleanupStack::Pop(rasterizer); + TUid fontUid = {-1}; + TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName))); + if (fontUid.iUid != -1) + result = true; + CleanupStack::PopAndDestroy(2, heap); // heap, store + ); + } + + if (tempFileGuard.isOpen()) + tempFileGuard.remove(); + + return result; +} + +static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) +{ + if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() + || fnt->data.size() > ttfMaxFileSize // hard limit + || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf + || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit + return; + +// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower). +// Therefore, not using it for now, but eventually in a later version. +// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName)) +// return; + + QFontDatabasePrivate *db = privateDb(); + if (!db) + return; + + if (!db->count) + initializeDb(); + + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return; + + const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); + + // The QTemporaryFile object being used in the following section must be + // destructed before letting Symbian load the TTF file. Symbian would not + // load it otherwise, because QTemporaryFile will still keep some handle + // on it. The scope is used to reduce the life time of the QTemporaryFile. + // In order to prevent other processes from modifying the file between the + // moment where the QTemporaryFile is destructed and the file is loaded by + // Symbian, we have a QFile "tempFileGuard" outside the scope which opens + // the file in ReadOnly mode while the QTemporaryFile is still alive. + QFile tempFileGuard; + { + QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() + + marker + QLatin1String("XXXXXX.ttf")); + if (!tempfile.open()) + return; + const QString tempFileName = QFileInfo(tempfile).canonicalFilePath(); + if (fnt->data.isEmpty()) { + QFile sourceFile(fnt->fileName); + if (!sourceFile.open(QIODevice::ReadOnly)) + return; + fnt->data = sourceFile.readAll(); + } + if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1) + return; + tempfile.setAutoRemove(false); + tempfile.close(); // Tempfile still keeps a file handle, forbidding write access + fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore. + tempFileGuard.setFileName(tempFileName); + if (!tempFileGuard.open(QIODevice::ReadOnly)) + return; + fnt->temporaryFileName = tempFileName; + } + + const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName); + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer(); + const TInt err = + S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId); + tempFileGuard.close(); // Did its job + const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer(); + if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device? + int fontOnServerIndex = fontsOnServerAfter.count() - 1; + for (int i = 0; i < fontsOnServerBefore.count(); i++) { + if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) { + fontOnServerIndex = i; + break; + } + } + + // Must remove all font engines with their CFonts, first. + QFontCache::instance()->clear(); + db->free(); + QSymbianFontDatabaseExtrasImplementation::clear(); + + if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) + fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName)); + + const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex); + fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName)); + if (!qt_symbian_fontNameHasAppFontMarker(appFontName) + || !registerScreenDeviceFont(fontOnServerIndex, dbExtras)) + dbExtras->removeAppFontData(fnt); + } else { + if (fnt->screenDeviceFontFileId > 0) + S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open! + QFile::remove(fnt->temporaryFileName); + *fnt = QFontDatabasePrivate::ApplicationFont(); + } + lock.relock(); +} + +bool QFontDatabase::removeApplicationFont(int handle) +{ + QMutexLocker locker(fontDatabaseMutex()); + + QFontDatabasePrivate *db = privateDb(); + if (!db || handle < 0 || handle >= db->applicationFonts.count()) + return false; + QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + if (!dbExtras) + return false; + + QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle]; + if (fnt->families.isEmpty()) + return true; // Nothing to remove. Return peacefully. + + // Must remove all font engines with their CFonts, first + QFontCache::instance()->clear(); + db->free(); + dbExtras->removeAppFontData(fnt); + + db->invalidate(); // This will just emit 'fontDatabaseChanged()' + return true; +} + +bool QFontDatabase::removeAllApplicationFonts() +{ + QMutexLocker locker(fontDatabaseMutex()); + + const int applicationFontsCount = privateDb()->applicationFonts.count(); + for (int i = 0; i < applicationFontsCount; ++i) + if (!removeApplicationFont(i)) + return false; + return true; +} + +bool QFontDatabase::supportsThreadedFontRendering() +{ + return false; +} + +static +QFontDef cleanedFontDef(const QFontDef &req) +{ + QFontDef result = req; + if (result.pixelSize <= 0) { + result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize)); + result.pointSize = 0; + } + return result; +} + +QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req) +{ + const QFontCache::Key key(cleanedFontDef(req), script); + + if (!privateDb()->count) + initializeDb(); + + QFontEngine *fe = QFontCache::instance()->findEngine(key); + if (!fe) { + // Making sure that fe->fontDef.family will be an existing font. + initializeDb(); + QFontDatabasePrivate *db = privateDb(); + QtFontDesc desc; + QList<int> blacklistedFamilies; + match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies); + if (!desc.family) // falling back to application font + desc.family = db->family(QApplication::font().defaultFamily()); + Q_ASSERT(desc.family); + + // Making sure that desc.family supports the requested script + QtFontDesc mappedDesc; + bool supportsScript = false; + do { + match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); + if (mappedDesc.family == desc.family) { + supportsScript = true; + break; + } + blacklistedFamilies.append(mappedDesc.familyIndex); + } while (mappedDesc.family); + if (!supportsScript) { + blacklistedFamilies.clear(); + match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); + if (mappedDesc.family) + desc = mappedDesc; + } + + const QString fontFamily = desc.family->name; + QFontDef request = req; + request.family = fontFamily; +#ifdef QT_NO_FREETYPE + const QSymbianFontDatabaseExtrasImplementation *dbExtras = + static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); + const QSymbianTypeFaceExtras *typeFaceExtras = + dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal); + + // We need a valid pixelSize, e.g. for lineThickness() + if (request.pixelSize < 0) + request.pixelSize = request.pointSize * d->dpi / 72; + + fe = new QFontEngineS60(request, typeFaceExtras); +#else // QT_NO_FREETYPE + Q_UNUSED(d) + QFontEngine::FaceId faceId; + const QtFontFamily * const reqQtFontFamily = db->family(fontFamily); + faceId.filename = reqQtFontFamily->fontFilename; + faceId.index = reqQtFontFamily->fontFileIndex; + + QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request)); + if (fte->init(faceId, true, QFontEngineFT::Format_A8)) + fe = fte; + else + delete fte; +#endif // QT_NO_FREETYPE + + Q_ASSERT(fe); + if (script == QUnicodeTables::Common + && !(req.styleStrategy & QFont::NoFontMerging) + && !fe->symbol) { + + QStringList commonFonts; + for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) { + if (scriptForWritingSystem[ws] != script) + continue; + for (int i = 0; i < db->count; ++i) { + if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported) + commonFonts.append(db->families[i]->name); + } + } + + // Hack: Prioritize .ccc fonts + const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60")); + if (commonFonts.removeAll(niceEastAsianFont) > 0) + commonFonts.prepend(niceEastAsianFont); + + fe = new QFontEngineMultiS60(fe, script, commonFonts); + } + } + fe->ref.ref(); + QFontCache::instance()->insertEngine(key, fe); + return fe; +} + +void QFontDatabase::load(const QFontPrivate *d, int script) +{ + QFontEngine *fe = 0; + QFontDef req = d->request; + + if (!d->engineData) { + const QFontCache::Key key(cleanedFontDef(req), script); + getEngineData(d, key); + } + + // the cached engineData could have already loaded the engine we want + if (d->engineData->engines[script]) + fe = d->engineData->engines[script]; + + if (!fe) { + if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { + fe = new QTestFontEngine(req.pixelSize); + fe->fontDef = req; + } else { + fe = findFont(script, d, req); + } + d->engineData->engines[script] = fe; + } +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qfontengine_s60.cpp b/src/gui/platforms/s60/qfontengine_s60.cpp new file mode 100644 index 0000000000..e9b54e350f --- /dev/null +++ b/src/gui/platforms/s60/qfontengine_s60.cpp @@ -0,0 +1,569 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_s60_p.h" +#include "qtextengine_p.h" +#include "qendian.h" +#include "qglobal.h" +#include <private/qapplication_p.h> +#include "qimage.h" +#include <private/qt_s60_p.h> +#include <private/qpixmap_s60_p.h> + +#include <e32base.h> +#include <e32std.h> +#include <eikenv.h> +#include <gdi.h> +#if defined(Q_SYMBIAN_HAS_GLYPHOUTLINE_API) +#include <graphics/gdi/gdiplatapi.h> +#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API + +// Replication of TGetFontTableParam & friends. +// There is unfortunately no compile time flag like SYMBIAN_FONT_TABLE_API +// that would help us to only replicate these things for Symbian versions +// that do not yet have the font table Api. Symbian's public SDK does +// generally not define any usable macros. +class QSymbianTGetFontTableParam +{ +public: + TUint32 iTag; + TAny *iContent; + TInt iLength; +}; +const TUid QSymbianKFontGetFontTable = {0x102872C1}; +const TUid QSymbianKFontReleaseFontTable = {0x2002AC24}; + +QT_BEGIN_NAMESPACE + +QSymbianTypeFaceExtras::QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont) + : m_cFont(cFont) + , m_symbolCMap(false) + , m_openFont(openFont) +{ + if (!symbianFontTableApiAvailable()) { + TAny *trueTypeExtension = NULL; + m_openFont->ExtendedInterface(KUidOpenFontTrueTypeExtension, trueTypeExtension); + m_trueTypeExtension = static_cast<MOpenFontTrueTypeExtension*>(trueTypeExtension); + Q_ASSERT(m_trueTypeExtension); + } +} + +QSymbianTypeFaceExtras::~QSymbianTypeFaceExtras() +{ + if (symbianFontTableApiAvailable()) + S60->screenDevice()->ReleaseFont(m_cFont); +} + +QByteArray QSymbianTypeFaceExtras::getSfntTable(uint tag) const +{ + if (symbianFontTableApiAvailable()) { + QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 }; + if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { + const char* const fontTableContent = + static_cast<const char *>(fontTableParams.iContent); + const QByteArray fontTable(fontTableContent, fontTableParams.iLength); + m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); + return fontTable; + } + return QByteArray(); + } else { + Q_ASSERT(m_trueTypeExtension->HasTrueTypeTable(tag)); + TInt error = KErrNone; + TInt tableByteLength = 0; + TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); + Q_CHECK_PTR(table); + const QByteArray result(static_cast<const char*>(table), tableByteLength); + m_trueTypeExtension->ReleaseTrueTypeTable(table); + return result; + } +} + +bool QSymbianTypeFaceExtras::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + bool result = true; + if (symbianFontTableApiAvailable()) { + QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 }; + if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { + if (*length > 0 && *length < fontTableParams.iLength) { + result = false; // Caller did not allocate enough memory + } else { + *length = fontTableParams.iLength; + if (buffer) + memcpy(buffer, fontTableParams.iContent, fontTableParams.iLength); + } + m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); + } else { + result = false; + } + } else { + if (!m_trueTypeExtension->HasTrueTypeTable(tag)) + return false; + + TInt error = KErrNone; + TInt tableByteLength; + TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); + Q_CHECK_PTR(table); + + if (error != KErrNone) { + return false; + } else if (*length > 0 && *length < tableByteLength) { + result = false; // Caller did not allocate enough memory + } else { + *length = tableByteLength; + if (buffer) + memcpy(buffer, table, tableByteLength); + } + + m_trueTypeExtension->ReleaseTrueTypeTable(table); + } + return result; +} + +const uchar *QSymbianTypeFaceExtras::cmap() const +{ + if (m_cmapTable.isNull()) { + const QByteArray cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); + int size = 0; + const uchar *cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *> + (cmapTable.constData()), cmapTable.size(), &m_symbolCMap, &size); + m_cmapTable = QByteArray(reinterpret_cast<const char *>(cmap), size); + } + return reinterpret_cast<const uchar *>(m_cmapTable.constData()); +} + +bool QSymbianTypeFaceExtras::isSymbolCMap() const +{ + return m_symbolCMap; +} + +CFont *QSymbianTypeFaceExtras::fontOwner() const +{ + return m_cFont; +} + +QFixed QSymbianTypeFaceExtras::unitsPerEm() const +{ + if (m_unitsPerEm.value() != 0) + return m_unitsPerEm; + const QByteArray head = getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')); + const int unitsPerEmOffset = 18; + if (head.size() > unitsPerEmOffset + sizeof(quint16)) { + const uchar* tableData = reinterpret_cast<const uchar*>(head.constData()); + const uchar* unitsPerEm = tableData + unitsPerEmOffset; + m_unitsPerEm = qFromBigEndian<quint16>(unitsPerEm); + } else { + // Bitmap font? Corrupt font? + // We return -1 and let the QFontEngineS60 return the pixel size. + m_unitsPerEm = -1; + } + return m_unitsPerEm; +} + +bool QSymbianTypeFaceExtras::symbianFontTableApiAvailable() +{ + enum FontTableApiAvailability { + Unknown, + Available, + Unavailable + }; + static FontTableApiAvailability availability = + QSysInfo::symbianVersion() < QSysInfo::SV_SF_3 ? + Unavailable : Unknown; + if (availability == Unknown) { + // Actually, we should ask CFeatureDiscovery::IsFeatureSupportedL() + // with FfFontTable here. But since at the time of writing, the + // FfFontTable flag check either gave false positives or false + // negatives. Here comes an implicit check via CFont::ExtendedFunction. + QSymbianTGetFontTableParam fontTableParams = { + MAKE_TAG('O', 'S', '/', '2'), 0, 0 }; + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + CFont *font; + const TInt getFontErr = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec()); + Q_ASSERT(getFontErr == KErrNone); + if (font->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { + font->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); + availability = Available; + } else { + availability = Unavailable; + } + S60->screenDevice()->ReleaseFont(font); + lock.relock(); + } + return availability == Available; +} + +// duplicated from qfontengine_xyz.cpp +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; +} + +extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp + +CFont *QFontEngineS60::fontWithSize(qreal size) const +{ + CFont *result = 0; + const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family); + TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size)); + fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap); + fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic); + fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal); + const TInt errorCode = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(result, fontSpec); + Q_ASSERT(result && (errorCode == 0)); + return result; +} + +void QFontEngineS60::setFontScale(qreal scale) +{ + if (qFuzzyCompare(scale, qreal(1))) { + if (!m_originalFont) + m_originalFont = fontWithSize(m_originalFontSizeInPixels); + m_activeFont = m_originalFont; + } else { + const qreal scaledFontSizeInPixels = m_originalFontSizeInPixels * scale; + if (!m_scaledFont || + (TInt(scaledFontSizeInPixels) != TInt(m_scaledFontSizeInPixels))) { + releaseFont(m_scaledFont); + m_scaledFontSizeInPixels = scaledFontSizeInPixels; + m_scaledFont = fontWithSize(m_scaledFontSizeInPixels); + } + m_activeFont = m_scaledFont; + } +} + +void QFontEngineS60::releaseFont(CFont *&font) +{ + if (font) { + S60->screenDevice()->ReleaseFont(font); + font = 0; + } +} + +QFontEngineS60::QFontEngineS60(const QFontDef &request, const QSymbianTypeFaceExtras *extras) + : m_extras(extras) + , m_originalFont(0) + , m_originalFontSizeInPixels((request.pixelSize >= 0)? + request.pixelSize:pointsToPixels(request.pointSize)) + , m_scaledFont(0) + , m_scaledFontSizeInPixels(0) + , m_activeFont(0) +{ + QFontEngine::fontDef = request; + setFontScale(1.0); + cache_cost = sizeof(QFontEngineS60); +} + +QFontEngineS60::~QFontEngineS60() +{ + releaseFont(m_originalFont); + releaseFont(m_scaledFont); +} + +QFixed QFontEngineS60::emSquareSize() const +{ + const QFixed unitsPerEm = m_extras->unitsPerEm(); + return unitsPerEm.toInt() == -1 ? + QFixed::fromReal(m_originalFontSizeInPixels) : unitsPerEm; +} + +bool QFontEngineS60::stringToCMap(const QChar *characters, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + HB_Glyph *g = glyphs->glyphs; + const unsigned char* cmap = m_extras->cmap(); + const bool isRtl = (flags & QTextEngine::RightToLeft); + for (int i = 0; i < len; ++i) { + const unsigned int uc = getChar(characters, i, len); + *g++ = QFontEngine::getTrueTypeGlyphIndex(cmap, + (isRtl && !m_extras->isSymbolCMap()) ? QChar::mirroredChar(uc) : uc); + } + + glyphs->numGlyphs = g - glyphs->glyphs; + *nglyphs = glyphs->numGlyphs; + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + + recalcAdvances(glyphs, flags); + return true; +} + +void QFontEngineS60::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + Q_UNUSED(flags); + TOpenFontCharMetrics metrics; + const TUint8 *glyphBitmapBytes; + TSize glyphBitmapSize; + for (int i = 0; i < glyphs->numGlyphs; i++) { + getCharacterData(glyphs->glyphs[i], metrics, glyphBitmapBytes, glyphBitmapSize); + glyphs->advances_x[i] = metrics.HorizAdvance(); + glyphs->advances_y[i] = 0; + } +} + +#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API +static bool parseGlyphPathData(const char *dataStr, const char *dataEnd, QPainterPath &path, + qreal fontPixelSize, const QPointF &offset, bool hinted); +#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API + +void QFontEngineS60::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, + int nglyphs, QPainterPath *path, + QTextItem::RenderFlags flags) +{ +#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API + Q_UNUSED(flags) + RGlyphOutlineIterator iterator; + const TInt error = iterator.Open(*m_activeFont, glyphs, nglyphs); + if (KErrNone != error) + return; + const qreal fontSizeInPixels = qreal(m_activeFont->HeightInPixels()); + int count = 0; + do { + const TUint8* outlineUint8 = iterator.Outline(); + const char* const outlineChar = reinterpret_cast<const char*>(outlineUint8); + const char* const outlineEnd = outlineChar + iterator.OutlineLength(); + parseGlyphPathData(outlineChar, outlineEnd, *path, fontSizeInPixels, + positions[count++].toPointF(), false); + } while(KErrNone == iterator.Next() && count <= nglyphs); + iterator.Close(); +#else // Q_SYMBIAN_HAS_GLYPHOUTLINE_API + QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags); +#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API +} + +QImage QFontEngineS60::alphaMapForGlyph(glyph_t glyph) +{ + // Note: On some Symbian versions (apparently <= Symbian^1), this + // function will return gray values 0x00, 0x10 ... 0xe0, 0xf0 due + // to a bug. The glyphs are nowhere perfectly opaque. + // This has been fixed for Symbian^3. + + TOpenFontCharMetrics metrics; + const TUint8 *glyphBitmapBytes; + TSize glyphBitmapSize; + getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize); + QImage result(glyphBitmapBytes, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight, glyphBitmapSize.iWidth, QImage::Format_Indexed8); + result.setColorTable(grayPalette()); + return result; +} + +glyph_metrics_t QFontEngineS60::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 - lastRightBearing(glyphs), ascent()+descent()+1, w, 0); +} + +glyph_metrics_t QFontEngineS60::boundingBox_const(glyph_t glyph) const +{ + TOpenFontCharMetrics metrics; + const TUint8 *glyphBitmapBytes; + TSize glyphBitmapSize; + getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize); + const glyph_metrics_t result( + metrics.HorizBearingX(), + -metrics.HorizBearingY(), + metrics.Width(), + metrics.Height(), + metrics.HorizAdvance(), + 0 + ); + return result; +} + +glyph_metrics_t QFontEngineS60::boundingBox(glyph_t glyph) +{ + return boundingBox_const(glyph); +} + +QFixed QFontEngineS60::ascent() const +{ + // Workaround for QTBUG-8013 + // Stroke based fonts may return an incorrect FontMaxAscent of 0. + const QFixed ascent = m_originalFont->FontMaxAscent(); + return (ascent > 0) ? ascent : QFixed::fromReal(m_originalFontSizeInPixels) - descent(); +} + +QFixed QFontEngineS60::descent() const +{ + return m_originalFont->FontMaxDescent(); +} + +QFixed QFontEngineS60::leading() const +{ + return 0; +} + +qreal QFontEngineS60::maxCharWidth() const +{ + return m_originalFont->MaxCharWidthInPixels(); +} + +const char *QFontEngineS60::name() const +{ + return "QFontEngineS60"; +} + +bool QFontEngineS60::canRender(const QChar *string, int len) +{ + const unsigned char *cmap = m_extras->cmap(); + for (int i = 0; i < len; ++i) { + const unsigned int uc = getChar(string, i, len); + if (QFontEngine::getTrueTypeGlyphIndex(cmap, uc) == 0) + return false; + } + return true; +} + +QByteArray QFontEngineS60::getSfntTable(uint tag) const +{ + return m_extras->getSfntTable(tag); +} + +bool QFontEngineS60::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + return m_extras->getSfntTableData(tag, buffer, length); +} + +QFontEngine::Type QFontEngineS60::type() const +{ + return QFontEngine::S60FontEngine; +} + +void QFontEngineS60::getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const +{ + // Setting the most significant bit tells GetCharacterData + // that 'code' is a Glyph ID, rather than a UTF-16 value + const TUint specialCode = (TUint)glyph | 0x80000000; + + const CFont::TCharacterDataAvailability availability = + m_activeFont->GetCharacterData(specialCode, metrics, bitmap, bitmapSize); + const glyph_t fallbackGlyph = '?'; + if (availability != CFont::EAllCharacterData) { + const CFont::TCharacterDataAvailability fallbackAvailability = + m_activeFont->GetCharacterData(fallbackGlyph, metrics, bitmap, bitmapSize); + Q_ASSERT(fallbackAvailability == CFont::EAllCharacterData); + } +} + +#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API +static inline void skipSpacesAndComma(const char* &str, const char* const strEnd) +{ + while (str <= strEnd && (*str == ' ' || *str == ',')) + ++str; +} + +static bool parseGlyphPathData(const char *svgPath, const char *svgPathEnd, QPainterPath &path, + qreal fontPixelSize, const QPointF &offset, bool hinted) +{ + Q_UNUSED(hinted) + QPointF p1, p2, firstSubPathPoint; + qreal *elementValues[] = + {&p1.rx(), &p1.ry(), &p2.rx(), &p2.ry()}; + const int unitsPerEm = 2048; // See: http://en.wikipedia.org/wiki/Em_%28typography%29 + const qreal resizeFactor = fontPixelSize / unitsPerEm; + + while (svgPath < svgPathEnd) { + skipSpacesAndComma(svgPath, svgPathEnd); + const char pathElem = *svgPath++; + skipSpacesAndComma(svgPath, svgPathEnd); + + if (pathElem != 'Z') { + char *endStr = 0; + int elementValuesCount = 0; + for (int i = 0; i < 4; ++i) { // 4 = size of elementValues[] + qreal coordinateValue = strtod(svgPath, &endStr); + if (svgPath == endStr) + break; + if (i % 2) // Flip vertically + coordinateValue = -coordinateValue; + *elementValues[i] = coordinateValue * resizeFactor; + elementValuesCount++; + svgPath = endStr; + skipSpacesAndComma(svgPath, svgPathEnd); + } + p1 += offset; + if (elementValuesCount == 2) + p2 = firstSubPathPoint; + else + p2 += offset; + } + + switch (pathElem) { + case 'M': + firstSubPathPoint = p1; + path.moveTo(p1); + break; + case 'Z': + path.closeSubpath(); + break; + case 'L': + path.lineTo(p1); + break; + case 'Q': + path.quadTo(p1, p2); + break; + default: + return false; + } + } + return true; +} +#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qfontengine_s60_p.h b/src/gui/platforms/s60/qfontengine_s60_p.h new file mode 100644 index 0000000000..c0eeef5264 --- /dev/null +++ b/src/gui/platforms/s60/qfontengine_s60_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINE_S60_P_H +#define QFONTENGINE_S60_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 "qconfig.h" +#include <private/qfontengine_p.h> +#include "qsize.h" +#include <openfont.h> + +// The glyph outline code is intentionally disabled. It will be reactivated as +// soon as the glyph outline API is backported from Symbian(^4) to Symbian(^3). +#if 0 +#define Q_SYMBIAN_HAS_GLYPHOUTLINE_API +#endif + +class CFont; + +QT_BEGIN_NAMESPACE + +// ..gives us access to truetype tables +class QSymbianTypeFaceExtras +{ +public: + QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont = 0); + ~QSymbianTypeFaceExtras(); + + QByteArray getSfntTable(uint tag) const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + const uchar *cmap() const; + CFont *fontOwner() const; + bool isSymbolCMap() const; + QFixed unitsPerEm() const; + static bool symbianFontTableApiAvailable(); + +private: + CFont* m_cFont; + mutable bool m_symbolCMap; + mutable QByteArray m_cmapTable; + mutable QFixed m_unitsPerEm; + + // m_openFont and m_openFont are used if Symbian does not provide + // the Font Table API + COpenFont *m_openFont; + mutable MOpenFontTrueTypeExtension *m_trueTypeExtension; +}; + +class QFontEngineS60 : public QFontEngine +{ +public: + QFontEngineS60(const QFontDef &fontDef, const QSymbianTypeFaceExtras *extras); + ~QFontEngineS60(); + + QFixed emSquareSize() const; + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags); + + QImage alphaMapForGlyph(glyph_t glyph); + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + glyph_metrics_t boundingBox_const(glyph_t glyph) const; // Const correctnes quirk. + glyph_metrics_t boundingBox(glyph_t glyph); + + QFixed ascent() const; + QFixed descent() const; + QFixed leading() const; + qreal maxCharWidth() const; + qreal minLeftBearing() const { return 0; } + qreal minRightBearing() const { return 0; } + + QByteArray getSfntTable(uint tag) const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + + static qreal pixelsToPoints(qreal pixels, Qt::Orientation orientation = Qt::Horizontal); + static qreal pointsToPixels(qreal points, Qt::Orientation orientation = Qt::Horizontal); + + const char *name() const; + + bool canRender(const QChar *string, int len); + + Type type() const; + + void getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const; + void setFontScale(qreal scale); + +private: + friend class QFontPrivate; + friend class QSymbianVGFontGlyphCache; + + QFixed glyphAdvance(HB_Glyph glyph) const; + CFont *fontWithSize(qreal size) const; + static void releaseFont(CFont *&font); + + const QSymbianTypeFaceExtras *m_extras; + CFont* m_originalFont; + const qreal m_originalFontSizeInPixels; + CFont* m_scaledFont; + qreal m_scaledFontSizeInPixels; + CFont* m_activeFont; +}; + +class QFontEngineMultiS60 : public QFontEngineMulti +{ +public: + QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies); + void loadEngine(int at); + + int m_script; + QStringList m_fallbackFamilies; +}; + +QT_END_NAMESPACE + +#endif // QFONTENGINE_S60_P_H diff --git a/src/gui/platforms/s60/qkeymapper_s60.cpp b/src/gui/platforms/s60/qkeymapper_s60.cpp new file mode 100644 index 0000000000..08cfae0d2d --- /dev/null +++ b/src/gui/platforms/s60/qkeymapper_s60.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qkeymapper_p.h" +#include <private/qcore_symbian_p.h> +#include <e32keys.h> +#include <e32cmn.h> +#include <centralrepository.h> +#include <biditext.h> + +QT_BEGIN_NAMESPACE + +QKeyMapperPrivate::QKeyMapperPrivate() +{ +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent * /* e */) +{ + QList<int> result; + return result; +} + +void QKeyMapperPrivate::clearMappings() +{ + // stub +} + +QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */) +{ + if (keySym >= Qt::Key_Escape) { + switch (keySym) { + case Qt::Key_Tab: + return QString(QChar('\t')); + case Qt::Key_Return: // fall through + case Qt::Key_Enter: + return QString(QChar('\r')); + default: + return QString(); + } + } + + // Symbian doesn't actually use modifiers, but gives us the character code directly. + + return QString(QChar(keySym)); +} + +#include <e32keys.h> +struct KeyMapping{ + TKeyCode s60KeyCode; + TStdScanCode s60ScanCode; + Qt::Key qtKey; +}; + +using namespace Qt; + +static const KeyMapping keyMapping[] = { + {EKeyBackspace, EStdKeyBackspace, Key_Backspace}, + {EKeyTab, EStdKeyTab, Key_Tab}, + {EKeyEnter, EStdKeyEnter, Key_Enter}, + {EKeyEscape, EStdKeyEscape, Key_Escape}, + {EKeySpace, EStdKeySpace, Key_Space}, + {EKeyDelete, EStdKeyDelete, Key_Delete}, + {EKeyPrintScreen, EStdKeyPrintScreen, Key_SysReq}, + {EKeyPause, EStdKeyPause, Key_Pause}, + {EKeyHome, EStdKeyHome, Key_Home}, + {EKeyEnd, EStdKeyEnd, Key_End}, + {EKeyPageUp, EStdKeyPageUp, Key_PageUp}, + {EKeyPageDown, EStdKeyPageDown, Key_PageDown}, + {EKeyInsert, EStdKeyInsert, Key_Insert}, + {EKeyLeftArrow, EStdKeyLeftArrow, Key_Left}, + {EKeyRightArrow, EStdKeyRightArrow, Key_Right}, + {EKeyUpArrow, EStdKeyUpArrow, Key_Up}, + {EKeyDownArrow, EStdKeyDownArrow, Key_Down}, + {EKeyLeftShift, EStdKeyLeftShift, Key_Shift}, + {EKeyRightShift, EStdKeyRightShift, Key_Shift}, + {EKeyLeftAlt, EStdKeyLeftAlt, Key_Alt}, + {EKeyRightAlt, EStdKeyRightAlt, Key_AltGr}, + {EKeyLeftCtrl, EStdKeyLeftCtrl, Key_Control}, + {EKeyRightCtrl, EStdKeyRightCtrl, Key_Control}, + {EKeyLeftFunc, EStdKeyLeftFunc, Key_Super_L}, + {EKeyRightFunc, EStdKeyRightFunc, Key_Super_R}, + {EKeyCapsLock, EStdKeyCapsLock, Key_CapsLock}, + {EKeyNumLock, EStdKeyNumLock, Key_NumLock}, + {EKeyScrollLock, EStdKeyScrollLock, Key_ScrollLock}, + {EKeyF1, EStdKeyF1, Key_F1}, + {EKeyF2, EStdKeyF2, Key_F2}, + {EKeyF3, EStdKeyF3, Key_F3}, + {EKeyF4, EStdKeyF4, Key_F4}, + {EKeyF5, EStdKeyF5, Key_F5}, + {EKeyF6, EStdKeyF6, Key_F6}, + {EKeyF7, EStdKeyF7, Key_F7}, + {EKeyF8, EStdKeyF8, Key_F8}, + {EKeyF9, EStdKeyF9, Key_F9}, + {EKeyF10, EStdKeyF10, Key_F10}, + {EKeyF11, EStdKeyF11, Key_F11}, + {EKeyF12, EStdKeyF12, Key_F12}, + {EKeyF13, EStdKeyF13, Key_F13}, + {EKeyF14, EStdKeyF14, Key_F14}, + {EKeyF15, EStdKeyF15, Key_F15}, + {EKeyF16, EStdKeyF16, Key_F16}, + {EKeyF17, EStdKeyF17, Key_F17}, + {EKeyF18, EStdKeyF18, Key_F18}, + {EKeyF19, EStdKeyF19, Key_F19}, + {EKeyF20, EStdKeyF20, Key_F20}, + {EKeyF21, EStdKeyF21, Key_F21}, + {EKeyF22, EStdKeyF22, Key_F22}, + {EKeyF23, EStdKeyF23, Key_F23}, + {EKeyF24, EStdKeyF24, Key_F24}, + {EKeyOff, EStdKeyOff, Key_PowerOff}, +// {EKeyMenu, EStdKeyMenu, Key_Menu}, // Menu is EKeyApplication0 + {EKeyHelp, EStdKeyHelp, Key_Help}, + {EKeyDial, EStdKeyDial, Key_Call}, + {EKeyIncVolume, EStdKeyIncVolume, Key_VolumeUp}, + {EKeyDecVolume, EStdKeyDecVolume, Key_VolumeDown}, + {EKeyDevice0, EStdKeyDevice0, Key_Context1}, // Found by manual testing. + {EKeyDevice1, EStdKeyDevice1, Key_Context2}, // Found by manual testing. + {EKeyDevice3, EStdKeyDevice3, Key_Select}, + {EKeyDevice7, EStdKeyDevice7, Key_Camera}, + {EKeyApplication0, EStdKeyApplication0, Key_Menu}, // Found by manual testing. + {EKeyApplication1, EStdKeyApplication1, Key_Launch1}, // Found by manual testing. + {EKeyApplication2, EStdKeyApplication2, Key_MediaPlay}, // Found by manual testing. + {EKeyApplication3, EStdKeyApplication3, Key_MediaStop}, // Found by manual testing. + {EKeyApplication4, EStdKeyApplication4, Key_MediaNext}, // Found by manual testing. + {EKeyApplication5, EStdKeyApplication5, Key_MediaPrevious}, // Found by manual testing. + {EKeyApplication6, EStdKeyApplication6, Key_Launch6}, + {EKeyApplication7, EStdKeyApplication7, Key_Launch7}, + {EKeyApplication8, EStdKeyApplication8, Key_Launch8}, + {EKeyApplication9, EStdKeyApplication9, Key_Launch9}, + {EKeyApplicationA, EStdKeyApplicationA, Key_LaunchA}, + {EKeyApplicationB, EStdKeyApplicationB, Key_LaunchB}, + {EKeyApplicationC, EStdKeyApplicationC, Key_LaunchC}, + {EKeyApplicationD, EStdKeyApplicationD, Key_LaunchD}, + {EKeyApplicationE, EStdKeyApplicationE, Key_LaunchE}, + {EKeyApplicationF, EStdKeyApplicationF, Key_LaunchF}, + {EKeyApplication19, EStdKeyApplication19, Key_CameraFocus}, + {EKeyYes, EStdKeyYes, Key_Yes}, + {EKeyNo, EStdKeyNo, Key_No}, + {TKeyCode(0), TStdScanCode(0), Qt::Key(0)} +}; + +int QKeyMapperPrivate::mapS60KeyToQt(TUint s60key) +{ + int res = Qt::Key_unknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60KeyCode == s60key) { + res = keyMapping[i].qtKey; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapS60ScanCodesToQt(TUint s60scanCode) +{ + int res = Qt::Key_unknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].s60ScanCode == s60scanCode) { + res = keyMapping[i].qtKey; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapQtToS60Key(int qtKey) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].qtKey == qtKey) { + res = keyMapping[i].s60KeyCode; + break; + } + } + return res; +} + +int QKeyMapperPrivate::mapQtToS60ScanCodes(int qtKey) +{ + int res = KErrUnknown; + for (int i = 0; keyMapping[i].s60KeyCode != 0; i++) { + if (keyMapping[i].qtKey == qtKey) { + res = keyMapping[i].s60ScanCode; + break; + } + } + return res; +} + +void QKeyMapperPrivate::updateInputLanguage() +{ +#ifdef Q_WS_S60 + TInt err; + CRepository *repo; + const TUid KCRUidAknFep = TUid::Uid(0x101F876D); + const TUint32 KAknFepInputTxtLang = 0x00000005; + TRAP(err, repo = CRepository::NewL(KCRUidAknFep)); + if (err != KErrNone) + return; + + TInt symbianLang; + err = repo->Get(KAknFepInputTxtLang, symbianLang); + delete repo; + if (err != KErrNone) + return; + + QString qtLang = QString::fromAscii(qt_symbianLocaleName(symbianLang)); + keyboardInputLocale = QLocale(qtLang); + keyboardInputDirection = (TBidiText::ScriptDirectionality(TLanguage(symbianLang)) == TBidiText::ERightToLeft) + ? Qt::RightToLeft : Qt::LeftToRight; +#else + keyboardInputLocale = QLocale(); + keyboardInputDirection = Qt::LeftToRight; +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qpaintengine_s60.cpp b/src/gui/platforms/s60/qpaintengine_s60.cpp new file mode 100644 index 0000000000..ca303be03d --- /dev/null +++ b/src/gui/platforms/s60/qpaintengine_s60.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <private/qpaintengine_s60_p.h> +#include <private/qpixmap_s60_p.h> +#include <private/qt_s60_p.h> +#include <private/qvolatileimage_p.h> + +QT_BEGIN_NAMESPACE + +class QS60PaintEnginePrivate : public QRasterPaintEnginePrivate +{ +public: + QS60PaintEnginePrivate() {} +}; + +QS60PaintEngine::QS60PaintEngine(QPaintDevice *device, QS60PixmapData *data) + : QRasterPaintEngine(*(new QS60PaintEnginePrivate), device), pixmapData(data) +{ +} + +bool QS60PaintEngine::begin(QPaintDevice *device) +{ + Q_D(QS60PaintEngine); + + if (pixmapData->classId() == QPixmapData::RasterClass) { + pixmapData->beginDataAccess(); + bool ret = QRasterPaintEngine::begin(device); + // Make sure QPaintEngine::paintDevice() returns the proper device. + // QRasterPaintEngine changes pdev to QImage in case of RasterClass QPixmapData + // which is incorrect in Symbian. + d->pdev = device; + return ret; + } + + return QRasterPaintEngine::begin(device); +} + +bool QS60PaintEngine::end() +{ + if (pixmapData->classId() == QPixmapData::RasterClass) { + bool ret = QRasterPaintEngine::end(); + pixmapData->endDataAccess(); + return ret; + } + return QRasterPaintEngine::end(); +} + +void QS60PaintEngine::drawPixmap(const QPointF &p, const QPixmap &pm) +{ + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawPixmap(p, pm); + srcData->endDataAccess(); + } else { + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(p, img->imageRef()); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(p, pm); + } + } +} + +void QS60PaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) +{ + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawPixmap(r, pm, sr); + srcData->endDataAccess(); + } else { + void *nativeData = pm.pixmapData()->toNativeType(QPixmapData::VolatileImage); + if (nativeData) { + QVolatileImage *img = static_cast<QVolatileImage *>(nativeData); + img->beginDataAccess(); + QRasterPaintEngine::drawImage(r, img->imageRef(), sr); + img->endDataAccess(true); + } else { + QRasterPaintEngine::drawPixmap(r, pm, sr); + } + } +} + +void QS60PaintEngine::drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr) +{ + if (pm.pixmapData()->classId() == QPixmapData::RasterClass) { + QS60PixmapData *srcData = static_cast<QS60PixmapData *>(pm.pixmapData()); + srcData->beginDataAccess(); + QRasterPaintEngine::drawTiledPixmap(r, pm, sr); + srcData->endDataAccess(); + } else { + QRasterPaintEngine::drawTiledPixmap(r, pm, sr); + } +} + +void QS60PaintEngine::prepare(QImage *image) +{ + QRasterBuffer *buffer = d_func()->rasterBuffer.data(); + if (buffer) + buffer->prepare(image); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qpaintengine_s60_p.h b/src/gui/platforms/s60/qpaintengine_s60_p.h new file mode 100644 index 0000000000..a62bdac97c --- /dev/null +++ b/src/gui/platforms/s60/qpaintengine_s60_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPAINTENGINE_S60_P_H +#define QPAINTENGINE_S60_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/qpaintengine_raster_p.h" + +QT_BEGIN_NAMESPACE + +class QS60PaintEnginePrivate; +class QS60PixmapData; + +class QS60PaintEngine : public QRasterPaintEngine +{ + Q_DECLARE_PRIVATE(QS60PaintEngine) + +public: + QS60PaintEngine(QPaintDevice *device, QS60PixmapData* data); + bool begin(QPaintDevice *device); + bool end(); + + void drawPixmap(const QPointF &p, const QPixmap &pm); + void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); + void drawTiledPixmap(const QRectF &r, const QPixmap &pm, const QPointF &sr); + + void prepare(QImage* image); + +private: + QS60PixmapData *pixmapData; +}; + +QT_END_NAMESPACE + +#endif // QPAINTENGINE_S60_P_H diff --git a/src/gui/platforms/s60/qpixmap_s60.cpp b/src/gui/platforms/s60/qpixmap_s60.cpp new file mode 100644 index 0000000000..c8aa003ffa --- /dev/null +++ b/src/gui/platforms/s60/qpixmap_s60.cpp @@ -0,0 +1,1040 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <exception> +#include <w32std.h> +#include <fbs.h> + +#include <private/qapplication_p.h> +#include <private/qgraphicssystem_p.h> +#include <private/qt_s60_p.h> +#include <private/qpaintengine_s60_p.h> +#include <private/qfont_p.h> + +#include "qpixmap.h" +#include "qpixmap_raster_p.h" +#include <qwidget.h> +#include "qpixmap_s60_p.h" +#include "qnativeimage_p.h" +#include "qbitmap.h" +#include "qimage.h" +#include "qimage_p.h" + +#include <fbs.h> + +QT_BEGIN_NAMESPACE + +const uchar qt_pixmap_bit_mask[] = { 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80 }; + +static bool cleanup_function_registered = false; +static QS60PixmapData *firstPixmap = 0; + +// static +void QS60PixmapData::qt_symbian_register_pixmap(QS60PixmapData *pd) +{ + if (!cleanup_function_registered) { + qAddPostRoutine(qt_symbian_release_pixmaps); + cleanup_function_registered = true; + } + + pd->next = firstPixmap; + pd->prev = 0; + if (firstPixmap) + firstPixmap->prev = pd; + firstPixmap = pd; +} + +// static +void QS60PixmapData::qt_symbian_unregister_pixmap(QS60PixmapData *pd) +{ + if (pd->next) + pd->next->prev = pd->prev; + if (pd->prev) + pd->prev->next = pd->next; + else + firstPixmap = pd->next; +} + +// static +void QS60PixmapData::qt_symbian_release_pixmaps() +{ + // Scan all QS60PixmapData objects in the system and destroy them. + QS60PixmapData *pd = firstPixmap; + while (pd != 0) { + pd->release(); + pd = pd->next; + } +} + +/* + \class QSymbianFbsClient + \since 4.6 + \internal + + Symbian Font And Bitmap server client that is + used to lock the global bitmap heap. Only used in + S60 v3.1 and S60 v3.2. +*/ +_LIT(KFBSERVLargeBitmapAccessName,"FbsLargeBitmapAccess"); +class QSymbianFbsClient +{ +public: + + QSymbianFbsClient() : heapLocked(false) + { + heapLock.OpenGlobal(KFBSERVLargeBitmapAccessName); + } + + ~QSymbianFbsClient() + { + heapLock.Close(); + } + + bool lockHeap() + { + bool wasLocked = heapLocked; + + if (heapLock.Handle() && !heapLocked) { + heapLock.Wait(); + heapLocked = true; + } + + return wasLocked; + } + + bool unlockHeap() + { + bool wasLocked = heapLocked; + + if (heapLock.Handle() && heapLocked) { + heapLock.Signal(); + heapLocked = false; + } + + return wasLocked; + } + + +private: + + RMutex heapLock; + bool heapLocked; +}; + +Q_GLOBAL_STATIC(QSymbianFbsClient, qt_symbianFbsClient); + + + +// QSymbianFbsHeapLock + +QSymbianFbsHeapLock::QSymbianFbsHeapLock(LockAction a) +: action(a), wasLocked(false) +{ + QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); + if (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3) + wasLocked = qt_symbianFbsClient()->unlockHeap(); +} + +QSymbianFbsHeapLock::~QSymbianFbsHeapLock() +{ + // Do nothing +} + +void QSymbianFbsHeapLock::relock() +{ + QSysInfo::SymbianVersion symbianVersion = QSysInfo::symbianVersion(); + if (wasLocked && (symbianVersion == QSysInfo::SV_9_2 || symbianVersion == QSysInfo::SV_9_3)) + qt_symbianFbsClient()->lockHeap(); +} + +/* + \class QSymbianBitmapDataAccess + \since 4.6 + \internal + + Data access class that is used to locks/unlocks pixel data + when drawing or modifying CFbsBitmap pixel data. +*/ +class QSymbianBitmapDataAccess +{ +public: + + static int heapRefCount; + QSysInfo::SymbianVersion symbianVersion; + + explicit QSymbianBitmapDataAccess() + { + symbianVersion = QSysInfo::symbianVersion(); + }; + + ~QSymbianBitmapDataAccess() {}; + + inline void beginDataAccess(CFbsBitmap *bitmap) + { + if (symbianVersion == QSysInfo::SV_9_2) { + if (heapRefCount == 0) + qt_symbianFbsClient()->lockHeap(); + } else { + bitmap->LockHeap(ETrue); + } + + heapRefCount++; + } + + inline void endDataAccess(CFbsBitmap *bitmap) + { + heapRefCount--; + + if (symbianVersion == QSysInfo::SV_9_2) { + if (heapRefCount == 0) + qt_symbianFbsClient()->unlockHeap(); + } else { + bitmap->UnlockHeap(ETrue); + } + } +}; + +int QSymbianBitmapDataAccess::heapRefCount = 0; + + +#define UPDATE_BUFFER() \ + { \ + beginDataAccess(); \ + endDataAccess(); \ +} + + +static CFbsBitmap* createSymbianCFbsBitmap(const TSize& size, TDisplayMode mode) +{ + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + + CFbsBitmap* bitmap = 0; + QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap); + + if (bitmap->Create(size, mode) != KErrNone) { + delete bitmap; + bitmap = 0; + } + + lock.relock(); + + return bitmap; +} + +static CFbsBitmap* uncompress(CFbsBitmap* bitmap) +{ + if(bitmap->IsCompressedInRAM()) { + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + + CFbsBitmap *uncompressed = 0; + QT_TRAP_THROWING(uncompressed = new (ELeave) CFbsBitmap); + + if (uncompressed->Create(bitmap->SizeInPixels(), bitmap->DisplayMode()) != KErrNone) { + delete bitmap; + bitmap = 0; + lock.relock(); + + return bitmap; + } + + lock.relock(); + + CFbsBitmapDevice* bitmapDevice = 0; + CFbsBitGc *bitmapGc = 0; + QT_TRAP_THROWING(bitmapDevice = CFbsBitmapDevice::NewL(uncompressed)); + QT_TRAP_THROWING(bitmapGc = CFbsBitGc::NewL()); + bitmapGc->Activate(bitmapDevice); + + bitmapGc->BitBlt(TPoint(), bitmap); + + delete bitmapGc; + delete bitmapDevice; + + return uncompressed; + } else { + return bitmap; + } +} + +QPixmap QPixmap::grabWindow(WId winId, int x, int y, int w, int h) +{ + CWsScreenDevice* screenDevice = S60->screenDevice(); + TSize screenSize = screenDevice->SizeInPixels(); + + TSize srcSize; + // Find out if this is one of our windows. + QSymbianControl *sControl; + sControl = winId->MopGetObject(sControl); + if (sControl && sControl->widget()->windowType() == Qt::Desktop) { + // Grabbing desktop widget + srcSize = screenSize; + } else { + TPoint relativePos = winId->PositionRelativeToScreen(); + x += relativePos.iX; + y += relativePos.iY; + srcSize = winId->Size(); + } + + TRect srcRect(TPoint(x, y), srcSize); + // Clip to the screen + srcRect.Intersection(TRect(screenSize)); + + if (w > 0 && h > 0) { + TRect subRect(TPoint(x, y), TSize(w, h)); + // Clip to the subRect + srcRect.Intersection(subRect); + } + + if (srcRect.IsEmpty()) + return QPixmap(); + + CFbsBitmap* temporary = createSymbianCFbsBitmap(srcRect.Size(), screenDevice->DisplayMode()); + + QPixmap pix; + + if (temporary && screenDevice->CopyScreenToBitmap(temporary, srcRect) == KErrNone) { + pix = QPixmap::fromSymbianCFbsBitmap(temporary); + } + + delete temporary; + return pix; +} + +/*! + \fn CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const + \since 4.6 + + Creates a \c CFbsBitmap that is equivalent to the QPixmap. Internally this + function will try to duplicate the handle instead of copying the data, + however in scenarios where this is not possible the data will be copied. + If the creation fails or the pixmap is null, then this function returns 0. + + It is the caller's responsibility to release the \c CFbsBitmap data + after use either by deleting the bitmap or calling \c Reset(). + + \warning On S60 3.1 and S60 3.2, semi-transparent pixmaps are always copied + and not duplicated. + \warning This function is only available on Symbian OS. + + \sa fromSymbianCFbsBitmap() +*/ +CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const +{ + QPixmapData *data = pixmapData(); + if (!data || data->isNull()) + return 0; + + return reinterpret_cast<CFbsBitmap*>(data->toNativeType(QPixmapData::FbsBitmap)); +} + +/*! + \fn QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) + \since 4.6 + + Creates a QPixmap from a \c CFbsBitmap \a bitmap. Internally this function + will try to duplicate the bitmap handle instead of copying the data, however + in scenarios where this is not possible the data will be copied. + To be sure that QPixmap does not modify your original instance, you should + make a copy of your \c CFbsBitmap before calling this function. + If the CFbsBitmap is not valid this function will return a null QPixmap. + For performance reasons it is recommended to use a \a bitmap with a display + mode of EColor16MAP or EColor16MU whenever possible. + + \warning This function is only available on Symbian OS. + + \sa toSymbianCFbsBitmap(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} +*/ +QPixmap QPixmap::fromSymbianCFbsBitmap(CFbsBitmap *bitmap) +{ + if (!bitmap) + return QPixmap(); + + QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType)); + data->fromNativeType(reinterpret_cast<void*>(bitmap), QPixmapData::FbsBitmap); + QPixmap pixmap(data.take()); + return pixmap; +} + +QS60PixmapData::QS60PixmapData(PixelType type) : QRasterPixmapData(type), + symbianBitmapDataAccess(new QSymbianBitmapDataAccess), + cfbsBitmap(0), + pengine(0), + bytes(0), + formatLocked(false), + next(0), + prev(0) +{ + qt_symbian_register_pixmap(this); +} + +QS60PixmapData::~QS60PixmapData() +{ + release(); + delete symbianBitmapDataAccess; + qt_symbian_unregister_pixmap(this); +} + +void QS60PixmapData::resize(int width, int height) +{ + if (width <= 0 || height <= 0) { + w = width; + h = height; + is_null = true; + + release(); + return; + } else if (!cfbsBitmap) { + TDisplayMode mode; + if (pixelType() == BitmapType) + mode = EGray2; + else + mode = EColor16MU; + + CFbsBitmap* bitmap = createSymbianCFbsBitmap(TSize(width, height), mode); + fromSymbianBitmap(bitmap); + } else { + + TSize newSize(width, height); + + if(cfbsBitmap->SizeInPixels() != newSize) { + cfbsBitmap->Resize(TSize(width, height)); + if(pengine) { + delete pengine; + pengine = 0; + } + } + + UPDATE_BUFFER(); + } +} + +void QS60PixmapData::release() +{ + if (cfbsBitmap) { + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + delete cfbsBitmap; + lock.relock(); + } + + delete pengine; + image = QImage(); + cfbsBitmap = 0; + pengine = 0; + bytes = 0; +} + +/*! + * Takes ownership of bitmap. Used by window surface + */ +void QS60PixmapData::fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat) +{ + Q_ASSERT(bitmap); + + release(); + + cfbsBitmap = bitmap; + formatLocked = lockFormat; + + setSerialNumber(cfbsBitmap->Handle()); + + UPDATE_BUFFER(); + + // Create default palette if needed + if (cfbsBitmap->DisplayMode() == EGray2) { + image.setColorCount(2); + image.setColor(0, QColor(Qt::color0).rgba()); + image.setColor(1, QColor(Qt::color1).rgba()); + + //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid + //So invert mono bitmaps so that masks work correctly. + image.invertPixels(); + } else if (cfbsBitmap->DisplayMode() == EGray256) { + for (int i=0; i < 256; ++i) + image.setColor(i, qRgb(i, i, i)); + } else if (cfbsBitmap->DisplayMode() == EColor256) { + const TColor256Util *palette = TColor256Util::Default(); + for (int i=0; i < 256; ++i) + image.setColor(i, (QRgb)(palette->Color256(i).Value())); + } +} + +QImage QS60PixmapData::toImage(const QRect &r) const +{ + QS60PixmapData *that = const_cast<QS60PixmapData*>(this); + that->beginDataAccess(); + QImage copy = that->image.copy(r); + that->endDataAccess(); + + return copy; +} + +void QS60PixmapData::fromImage(const QImage &img, Qt::ImageConversionFlags flags) +{ + release(); + + QImage sourceImage; + + if (pixelType() == BitmapType) { + sourceImage = img.convertToFormat(QImage::Format_MonoLSB); + } else { + if (img.depth() == 1) { + sourceImage = img.hasAlphaChannel() + ? img.convertToFormat(QImage::Format_ARGB32_Premultiplied) + : img.convertToFormat(QImage::Format_RGB32); + } else { + + QImage::Format opaqueFormat = QNativeImage::systemFormat(); + QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; + + if (!img.hasAlphaChannel() + || ((flags & Qt::NoOpaqueDetection) == 0 + && !const_cast<QImage &>(img).data_ptr()->checkForAlphaPixels())) { + sourceImage = img.convertToFormat(opaqueFormat); + } else { + sourceImage = img.convertToFormat(alphaFormat); + } + } + } + + + QImage::Format destFormat = sourceImage.format(); + TDisplayMode mode; + switch (destFormat) { + case QImage::Format_MonoLSB: + mode = EGray2; + break; + case QImage::Format_RGB32: + mode = EColor16MU; + break; + case QImage::Format_ARGB32_Premultiplied: + if (S60->supportsPremultipliedAlpha) { + mode = Q_SYMBIAN_ECOLOR16MAP; + break; + } else { + destFormat = QImage::Format_ARGB32; + } + // Fall through intended + case QImage::Format_ARGB32: + mode = EColor16MA; + break; + case QImage::Format_Invalid: + return; + default: + qWarning("Image format not supported: %d", image.format()); + return; + } + + cfbsBitmap = createSymbianCFbsBitmap(TSize(sourceImage.width(), sourceImage.height()), mode); + if (!cfbsBitmap) { + qWarning("Could not create CFbsBitmap"); + release(); + return; + } + + setSerialNumber(cfbsBitmap->Handle()); + + const uchar *sptr = const_cast<const QImage &>(sourceImage).bits(); + symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); + uchar *dptr = (uchar*)cfbsBitmap->DataAddress(); + Mem::Copy(dptr, sptr, sourceImage.byteCount()); + symbianBitmapDataAccess->endDataAccess(cfbsBitmap); + + UPDATE_BUFFER(); + + if (destFormat == QImage::Format_MonoLSB) { + image.setColorCount(2); + image.setColor(0, QColor(Qt::color0).rgba()); + image.setColor(1, QColor(Qt::color1).rgba()); + } else { + image.setColorTable(sourceImage.colorTable()); + } +} + +void QS60PixmapData::copy(const QPixmapData *data, const QRect &rect) +{ + const QS60PixmapData *s60Data = static_cast<const QS60PixmapData*>(data); + fromImage(s60Data->toImage(rect), Qt::AutoColor | Qt::OrderedAlphaDither); +} + +bool QS60PixmapData::scroll(int dx, int dy, const QRect &rect) +{ + beginDataAccess(); + bool res = QRasterPixmapData::scroll(dx, dy, rect); + endDataAccess(); + return res; +} + +int QS60PixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const +{ + if (!cfbsBitmap) + return 0; + + switch (metric) { + case QPaintDevice::PdmWidth: + return cfbsBitmap->SizeInPixels().iWidth; + case QPaintDevice::PdmHeight: + return cfbsBitmap->SizeInPixels().iHeight; + case QPaintDevice::PdmWidthMM: + return qRound(cfbsBitmap->SizeInPixels().iWidth * 25.4 / qt_defaultDpiX()); + case QPaintDevice::PdmHeightMM: + return qRound(cfbsBitmap->SizeInPixels().iHeight * 25.4 / qt_defaultDpiY()); + case QPaintDevice::PdmNumColors: + return TDisplayModeUtils::NumDisplayModeColors(cfbsBitmap->DisplayMode()); + case QPaintDevice::PdmDpiX: + case QPaintDevice::PdmPhysicalDpiX: + return qt_defaultDpiX(); + case QPaintDevice::PdmDpiY: + case QPaintDevice::PdmPhysicalDpiY: + return qt_defaultDpiY(); + case QPaintDevice::PdmDepth: + return TDisplayModeUtils::NumDisplayModeBitsPerPixel(cfbsBitmap->DisplayMode()); + default: + qWarning("QPixmap::metric: Invalid metric command"); + } + return 0; + +} + +void QS60PixmapData::fill(const QColor &color) +{ + if (color.alpha() != 255) { + QImage im(width(), height(), QImage::Format_ARGB32_Premultiplied); + im.fill(PREMUL(color.rgba())); + release(); + fromImage(im, Qt::AutoColor | Qt::OrderedAlphaDither); + } else { + beginDataAccess(); + QRasterPixmapData::fill(color); + endDataAccess(); + } +} + +void QS60PixmapData::setMask(const QBitmap &mask) +{ + if (mask.size().isEmpty()) { + if (image.depth() != 1) { + QImage newImage = image.convertToFormat(QImage::Format_RGB32); + release(); + fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither); + } + } else if (image.depth() == 1) { + beginDataAccess(); + QRasterPixmapData::setMask(mask); + endDataAccess(); + } else { + const int w = image.width(); + const int h = image.height(); + + const QImage imageMask = mask.toImage().convertToFormat(QImage::Format_MonoLSB); + QImage newImage = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + for (int y = 0; y < h; ++y) { + const uchar *mscan = imageMask.scanLine(y); + QRgb *tscan = (QRgb *)newImage.scanLine(y); + for (int x = 0; x < w; ++x) { + if (!(mscan[x>>3] & qt_pixmap_bit_mask[x&7])) + tscan[x] = 0; + } + } + release(); + fromImage(newImage, Qt::AutoColor | Qt::OrderedAlphaDither); + } +} + +void QS60PixmapData::setAlphaChannel(const QPixmap &alphaChannel) +{ + QImage img(toImage()); + img.setAlphaChannel(alphaChannel.toImage()); + release(); + fromImage(img, Qt::OrderedDither | Qt::OrderedAlphaDither); +} + +QImage QS60PixmapData::toImage() const +{ + return toImage(QRect()); +} + +QPaintEngine* QS60PixmapData::paintEngine() const +{ + if (!pengine) { + QS60PixmapData *that = const_cast<QS60PixmapData*>(this); + that->pengine = new QS60PaintEngine(&that->image, that); + } + return pengine; +} + +void QS60PixmapData::beginDataAccess() +{ + if(!cfbsBitmap) + return; + + symbianBitmapDataAccess->beginDataAccess(cfbsBitmap); + + uchar* newBytes = (uchar*)cfbsBitmap->DataAddress(); + + TSize size = cfbsBitmap->SizeInPixels(); + + if (newBytes == bytes && image.width() == size.iWidth && image.height() == size.iHeight) + return; + + bytes = newBytes; + TDisplayMode mode = cfbsBitmap->DisplayMode(); + QImage::Format format = qt_TDisplayMode2Format(mode); + // On S60 3.1, premultiplied alpha pixels are stored in a bitmap with 16MA type. + // S60 window surface needs backing store pixmap for transparent window in ARGB32 format. + // In that case formatLocked is true. + if (!formatLocked && format == QImage::Format_ARGB32) + format = QImage::Format_ARGB32_Premultiplied; // pixel data is actually in premultiplied format + + QVector<QRgb> savedColorTable; + if (!image.isNull()) + savedColorTable = image.colorTable(); + + image = QImage(bytes, size.iWidth, size.iHeight, format); + + // Restore the palette or create a default + if (!savedColorTable.isEmpty()) { + image.setColorTable(savedColorTable); + } + + w = size.iWidth; + h = size.iHeight; + d = image.depth(); + is_null = (w <= 0 || h <= 0); + + if (pengine) { + QS60PaintEngine *engine = static_cast<QS60PaintEngine *>(pengine); + engine->prepare(&image); + } +} + +void QS60PixmapData::endDataAccess(bool readOnly) const +{ + Q_UNUSED(readOnly); + + if(!cfbsBitmap) + return; + + symbianBitmapDataAccess->endDataAccess(cfbsBitmap); +} + +/*! + \since 4.6 + + Returns a QPixmap that wraps given \a sgImage graphics resource. + The data should be valid even when original RSgImage handle has been + closed. + + \warning This function is only available on Symbian OS. + + \sa toSymbianRSgImage(), {QPixmap#Pixmap Conversion}{Pixmap Conversion} +*/ + +QPixmap QPixmap::fromSymbianRSgImage(RSgImage *sgImage) +{ + // It is expected that RSgImage will + // CURRENTLY be used in conjuction with + // OpenVG graphics system + // + // Surely things might change in future + + if (!sgImage) + return QPixmap(); + + QScopedPointer<QPixmapData> data(QPixmapData::create(0,0, QPixmapData::PixmapType)); + data->fromNativeType(reinterpret_cast<void*>(sgImage), QPixmapData::SgImage); + QPixmap pixmap(data.take()); + return pixmap; +} + +/*! +\since 4.6 + +Returns a \c RSgImage that is equivalent to the QPixmap by copying the data. + +It is the caller's responsibility to close/delete the \c RSgImage after use. + +\warning This function is only available on Symbian OS. + +\sa fromSymbianRSgImage() +*/ + +RSgImage *QPixmap::toSymbianRSgImage() const +{ + // It is expected that RSgImage will + // CURRENTLY be used in conjuction with + // OpenVG graphics system + // + // Surely things might change in future + + if (isNull()) + return 0; + + RSgImage *sgImage = reinterpret_cast<RSgImage*>(pixmapData()->toNativeType(QPixmapData::SgImage)); + + return sgImage; +} + +void* QS60PixmapData::toNativeType(NativeType type) +{ + if (type == QPixmapData::SgImage) { + return 0; + } else if (type == QPixmapData::FbsBitmap) { + + if (isNull() || !cfbsBitmap) + return 0; + + bool convertToArgb32 = false; + bool needsCopy = false; + + if (!(S60->supportsPremultipliedAlpha)) { + // Convert argb32_premultiplied to argb32 since Symbian 9.2 does + // not support premultipied format. + + if (image.format() == QImage::Format_ARGB32_Premultiplied) { + needsCopy = true; + convertToArgb32 = true; + } + } + + CFbsBitmap *bitmap = 0; + + TDisplayMode displayMode = cfbsBitmap->DisplayMode(); + + if(displayMode == EGray2) { + //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid + //So invert mono bitmaps so that masks work correctly. + beginDataAccess(); + image.invertPixels(); + endDataAccess(); + needsCopy = true; + } + + if (needsCopy) { + QImage source; + + if (convertToArgb32) { + beginDataAccess(); + source = image.convertToFormat(QImage::Format_ARGB32); + endDataAccess(); + displayMode = EColor16MA; + } else { + source = image; + } + + CFbsBitmap *newBitmap = createSymbianCFbsBitmap(TSize(source.width(), source.height()), displayMode); + const uchar *sptr = source.bits(); + symbianBitmapDataAccess->beginDataAccess(newBitmap); + + uchar *dptr = (uchar*)newBitmap->DataAddress(); + Mem::Copy(dptr, sptr, source.byteCount()); + + symbianBitmapDataAccess->endDataAccess(newBitmap); + + bitmap = newBitmap; + } else { + + QT_TRAP_THROWING(bitmap = new (ELeave) CFbsBitmap); + + TInt err = bitmap->Duplicate(cfbsBitmap->Handle()); + if (err != KErrNone) { + qWarning("Could not duplicate CFbsBitmap"); + delete bitmap; + bitmap = 0; + } + } + + if(displayMode == EGray2) { + // restore pixels + beginDataAccess(); + image.invertPixels(); + endDataAccess(); + } + + return reinterpret_cast<void*>(bitmap); + + } + + return 0; +} + +void QS60PixmapData::fromNativeType(void* pixmap, NativeType nativeType) +{ + if (nativeType == QPixmapData::SgImage) { + return; + } else if (nativeType == QPixmapData::FbsBitmap && pixmap) { + + CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap); + + bool deleteSourceBitmap = false; + bool needsCopy = false; + +#ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE + + // Rasterize extended bitmaps + + TUid extendedBitmapType = bitmap->ExtendedBitmapType(); + if (extendedBitmapType != KNullUid) { + CFbsBitmap *rasterBitmap = createSymbianCFbsBitmap(bitmap->SizeInPixels(), EColor16MA); + + CFbsBitmapDevice *rasterBitmapDev = 0; + QT_TRAP_THROWING(rasterBitmapDev = CFbsBitmapDevice::NewL(rasterBitmap)); + + CFbsBitGc *rasterBitmapGc = 0; + TInt err = rasterBitmapDev->CreateContext(rasterBitmapGc); + if (err != KErrNone) { + delete rasterBitmap; + delete rasterBitmapDev; + rasterBitmapDev = 0; + return; + } + + rasterBitmapGc->BitBlt(TPoint( 0, 0), bitmap); + + bitmap = rasterBitmap; + + delete rasterBitmapDev; + delete rasterBitmapGc; + + rasterBitmapDev = 0; + rasterBitmapGc = 0; + + deleteSourceBitmap = true; + } +#endif + + + deleteSourceBitmap = bitmap->IsCompressedInRAM(); + CFbsBitmap *sourceBitmap = uncompress(bitmap); + + TDisplayMode displayMode = sourceBitmap->DisplayMode(); + QImage::Format format = qt_TDisplayMode2Format(displayMode); + + QImage::Format opaqueFormat = QNativeImage::systemFormat(); + QImage::Format alphaFormat = QImage::Format_ARGB32_Premultiplied; + + if (format != opaqueFormat && format != alphaFormat && format != QImage::Format_MonoLSB) + needsCopy = true; + + + type = (format != QImage::Format_MonoLSB) + ? QPixmapData::PixmapType + : QPixmapData::BitmapType; + + if (needsCopy) { + + TSize size = sourceBitmap->SizeInPixels(); + int bytesPerLine = sourceBitmap->ScanLineLength(size.iWidth, displayMode); + + QSymbianBitmapDataAccess da; + da.beginDataAccess(sourceBitmap); + uchar *bytes = (uchar*)sourceBitmap->DataAddress(); + QImage img = QImage(bytes, size.iWidth, size.iHeight, bytesPerLine, format); + img = img.copy(); + da.endDataAccess(sourceBitmap); + + if(displayMode == EGray2) { + //Symbian thinks set pixels are white/transparent, Qt thinks they are foreground/solid + //So invert mono bitmaps so that masks work correctly. + img.invertPixels(); + } else if(displayMode == EColor16M) { + img = img.rgbSwapped(); // EColor16M is BGR + } + + fromImage(img, Qt::AutoColor); + + if(deleteSourceBitmap) + delete sourceBitmap; + } else { + CFbsBitmap* duplicate = 0; + QT_TRAP_THROWING(duplicate = new (ELeave) CFbsBitmap); + + TInt err = duplicate->Duplicate(sourceBitmap->Handle()); + if (err != KErrNone) { + qWarning("Could not duplicate CFbsBitmap"); + + if(deleteSourceBitmap) + delete sourceBitmap; + + delete duplicate; + return; + } + + fromSymbianBitmap(duplicate); + + if(deleteSourceBitmap) + delete sourceBitmap; + } + } +} + +void QS60PixmapData::convertToDisplayMode(int mode) +{ + const TDisplayMode displayMode = static_cast<TDisplayMode>(mode); + if (!cfbsBitmap || cfbsBitmap->DisplayMode() == displayMode) + return; + if (image.depth() != TDisplayModeUtils::NumDisplayModeBitsPerPixel(displayMode)) { + qWarning("Cannot convert display mode due to depth mismatch"); + return; + } + + const TSize size = cfbsBitmap->SizeInPixels(); + QScopedPointer<CFbsBitmap> newBitmap(createSymbianCFbsBitmap(size, displayMode)); + + const uchar *sptr = const_cast<const QImage &>(image).bits(); + symbianBitmapDataAccess->beginDataAccess(newBitmap.data()); + uchar *dptr = (uchar*)newBitmap->DataAddress(); + Mem::Copy(dptr, sptr, image.byteCount()); + symbianBitmapDataAccess->endDataAccess(newBitmap.data()); + + QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); + delete cfbsBitmap; + lock.relock(); + cfbsBitmap = newBitmap.take(); + setSerialNumber(cfbsBitmap->Handle()); + UPDATE_BUFFER(); +} + +QPixmapData *QS60PixmapData::createCompatiblePixmapData() const +{ + return new QS60PixmapData(pixelType()); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qpixmap_s60_p.h b/src/gui/platforms/s60/qpixmap_s60_p.h new file mode 100644 index 0000000000..c440bbc33a --- /dev/null +++ b/src/gui/platforms/s60/qpixmap_s60_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPIXMAPDATA_S60_P_H +#define QPIXMAPDATA_S60_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/qpixmap_raster_p.h> + +QT_BEGIN_NAMESPACE + +class CFbsBitmap; +class CFbsBitmapDevice; +class CFbsBitGc; + +class QSymbianBitmapDataAccess; + +class QSymbianFbsHeapLock +{ +public: + + enum LockAction { + Unlock + }; + + explicit QSymbianFbsHeapLock(LockAction a); + ~QSymbianFbsHeapLock(); + void relock(); + +private: + + LockAction action; + bool wasLocked; +}; + +class QS60PixmapData : public QRasterPixmapData +{ +public: + QS60PixmapData(PixelType type); + ~QS60PixmapData(); + + QPixmapData *createCompatiblePixmapData() const; + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, const QRect &rect); + + int metric(QPaintDevice::PaintDeviceMetric metric) const; + void fill(const QColor &color); + void setMask(const QBitmap &mask); + void setAlphaChannel(const QPixmap &alphaChannel); + QImage toImage() const; + QPaintEngine* paintEngine() const; + + void beginDataAccess(); + void endDataAccess(bool readOnly=false) const; + + void* toNativeType(NativeType type); + void fromNativeType(void* pixmap, NativeType type); + + void convertToDisplayMode(int mode); + +private: + void release(); + void fromSymbianBitmap(CFbsBitmap* bitmap, bool lockFormat=false); + QImage toImage(const QRect &r) const; + + QSymbianBitmapDataAccess *symbianBitmapDataAccess; + + CFbsBitmap *cfbsBitmap; + QPaintEngine *pengine; + uchar* bytes; + + bool formatLocked; + + QS60PixmapData *next; + QS60PixmapData *prev; + + static void qt_symbian_register_pixmap(QS60PixmapData *pd); + static void qt_symbian_unregister_pixmap(QS60PixmapData *pd); + static void qt_symbian_release_pixmaps(); + + friend class QPixmap; + friend class QS60WindowSurface; + friend class QS60PaintEngine; + friend class QS60Data; +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_S60_P_H + diff --git a/src/gui/platforms/s60/qregion_s60.cpp b/src/gui/platforms/s60/qregion_s60.cpp new file mode 100644 index 0000000000..eafff1b965 --- /dev/null +++ b/src/gui/platforms/s60/qregion_s60.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbitmap.h" +#include "qbuffer.h" +#include "qimage.h" +#include "qpolygon.h" +#include "qregion.h" + +QT_BEGIN_NAMESPACE + +QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), 0 }; + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qsoftkeymanager_s60.cpp b/src/gui/platforms/s60/qsoftkeymanager_s60.cpp new file mode 100644 index 0000000000..79ed91af5b --- /dev/null +++ b/src/gui/platforms/s60/qsoftkeymanager_s60.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qapplication.h" +#include "qevent.h" +#include "qbitmap.h" +#include "qstyle.h" +#include "qmenubar.h" +#include "private/qt_s60_p.h" +#include "private/qmenu_p.h" +#include "private/qaction_p.h" +#include "private/qsoftkeymanager_p.h" +#include "private/qsoftkeymanager_s60_p.h" +#include "private/qobject_p.h" +#include <eiksoftkeyimage.h> +#include <eikcmbut.h> + +#ifndef QT_NO_SOFTKEYMANAGER +QT_BEGIN_NAMESPACE + +const int S60_COMMAND_START = 6000; +const int LSK_POSITION = 0; +const int MSK_POSITION = 3; +const int RSK_POSITION = 2; + +QSoftKeyManagerPrivateS60::QSoftKeyManagerPrivateS60() : cbaHasImage(4) // 4 since MSK position index is 3 +{ + cachedCbaIconSize[0] = QSize(0,0); + cachedCbaIconSize[1] = QSize(0,0); + cachedCbaIconSize[2] = QSize(0,0); + cachedCbaIconSize[3] = QSize(0,0); +} + +bool QSoftKeyManagerPrivateS60::skipCbaUpdate() +{ + // Lets not update softkeys if + // 1. We don't have application panes, i.e. cba + // 2. Our CBA is not active, i.e. S60 native dialog or menu with custom CBA is shown + // 2.1. Except if thre is no current CBA at all and WindowSoftkeysRespondHint is set + + // Note: Cannot use IsDisplayingMenuOrDialog since CBA update can be triggered before + // menu/dialog CBA is actually displayed i.e. it is being costructed. + CEikButtonGroupContainer *appUiCba = S60->buttonGroupContainer(); + if (!appUiCba) + return true; + // CEikButtonGroupContainer::Current returns 0 if CBA is not visible at all + CEikButtonGroupContainer *currentCba = CEikButtonGroupContainer::Current(); + // Check if softkey need to be update even they are not visible + bool cbaRespondsWhenInvisible = false; + QWidget *window = QApplication::activeWindow(); + if (window && (window->windowFlags() & Qt::WindowSoftkeysRespondHint)) + cbaRespondsWhenInvisible = true; + + if (QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes) + || (appUiCba != currentCba && !cbaRespondsWhenInvisible)) { + return true; + } + return false; +} + +void QSoftKeyManagerPrivateS60::ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba) +{ + RDrawableWindow *cbaWindow = cba.DrawableWindow(); + Q_ASSERT_X(cbaWindow, Q_FUNC_INFO, "Native CBA does not have window!"); + // Make sure CBA is visible, i.e. CBA window is on top + cbaWindow->SetOrdinalPosition(0); + // Qt shares same CBA instance between top-level widgets, + // make sure we are not faded by underlying window. + cbaWindow->SetFaded(EFalse, RWindowTreeNode::EFadeIncludeChildren); + // Modal dialogs capture pointer events, but shared cba instance + // shall stay responsive. Raise pointer capture priority to keep + // softkeys responsive in modal dialogs + cbaWindow->SetPointerCapturePriority(1); +} + +void QSoftKeyManagerPrivateS60::clearSoftkeys(CEikButtonGroupContainer &cba) +{ +#ifdef SYMBIAN_VERSION_SYMBIAN3 + QT_TRAP_THROWING( + //EAknSoftkeyEmpty is used, because using -1 adds softkeys without actions on Symbian3 + cba.SetCommandL(0, EAknSoftkeyEmpty, KNullDesC); + cba.SetCommandL(2, EAknSoftkeyEmpty, KNullDesC); + ); +#else + QT_TRAP_THROWING( + //Using -1 instead of EAknSoftkeyEmpty to avoid flickering. + cba.SetCommandL(0, -1, KNullDesC); + // TODO: Should we clear also middle SK? + cba.SetCommandL(2, -1, KNullDesC); + ); +#endif + realSoftKeyActions.clear(); +} + +QString QSoftKeyManagerPrivateS60::softkeyText(QAction &softkeyAction) +{ + // In S60 softkeys and menu items do not support key accelerators (i.e. + // CTRL+X). Therefore, removing the accelerator characters from both softkey + // and menu item texts. + const int underlineShortCut = QApplication::style()->styleHint(QStyle::SH_UnderlineShortcut); + QString iconText = softkeyAction.iconText(); + return underlineShortCut ? softkeyAction.text() : iconText; +} + +QAction *QSoftKeyManagerPrivateS60::highestPrioritySoftkey(QAction::SoftKeyRole role) +{ + QAction *ret = NULL; + // Priority look up is two level + // 1. First widget with softkeys always has highest priority + for (int level = 0; !ret; level++) { + // 2. Highest priority action within widget + QList<QAction*> actions = requestedSoftKeyActions.values(level); + if (actions.isEmpty()) + break; + qSort(actions.begin(), actions.end(), QSoftKeyManagerPrivateS60::actionPriorityMoreThan); + foreach (QAction *action, actions) { + if (action->softKeyRole() == role) { + ret = action; + break; + } + } + } + return ret; +} + +bool QSoftKeyManagerPrivateS60::actionPriorityMoreThan(const QAction *firstItem, const QAction *secondItem) +{ + return firstItem->priority() > secondItem->priority(); +} + +void QSoftKeyManagerPrivateS60::setNativeSoftkey(CEikButtonGroupContainer &cba, + TInt position, TInt command, const TDesC &text) +{ + // Calling SetCommandL causes CBA redraw + QT_TRAP_THROWING(cba.SetCommandL(position, command, text)); +} + +QPoint QSoftKeyManagerPrivateS60::softkeyIconPosition(int position, QSize sourceSize, QSize targetSize) +{ + QPoint iconPosition(0,0); + switch( AknLayoutUtils::CbaLocation() ) + { + case AknLayoutUtils::EAknCbaLocationBottom: + // RSK must be moved to right, LSK in on correct position by default + if (position == RSK_POSITION) + iconPosition.setX(targetSize.width() - sourceSize.width()); + break; + case AknLayoutUtils::EAknCbaLocationRight: + case AknLayoutUtils::EAknCbaLocationLeft: + // Already in correct position + default: + break; + } + + // Align horizontally to center + iconPosition.setY((targetSize.height() - sourceSize.height()) >> 1); + return iconPosition; +} + +QPixmap QSoftKeyManagerPrivateS60::prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize) +{ + QPixmap target(targetSize); + target.fill(Qt::transparent); + QPainter p; + p.begin(&target); + p.drawPixmap(softkeyIconPosition(position, src.size(), targetSize), src); + p.end(); + return target; +} + +bool QSoftKeyManagerPrivateS60::isOrientationLandscape() +{ + // Hard to believe that there is no public API in S60 to + // get current orientation. This workaround works with currently supported resolutions + return S60->screenHeightInPixels < S60->screenWidthInPixels; +} + +QSize QSoftKeyManagerPrivateS60::cbaIconSize(CEikButtonGroupContainer *cba, int position) +{ + + int index = position; + index += isOrientationLandscape() ? 0 : 1; + if(cachedCbaIconSize[index].isNull()) { + // Only way I figured out to get CBA icon size without RnD SDK, was + // to set some dummy icon to CBA first and then ask CBA button CCoeControl::Size() + // The returned value is cached to avoid unnecessary icon setting every time. + const bool left = (position == LSK_POSITION); + if(position == LSK_POSITION || position == RSK_POSITION) { + CEikImage* tmpImage = NULL; + QT_TRAP_THROWING(tmpImage = new (ELeave) CEikImage); + EikSoftkeyImage::SetImage(cba, *tmpImage, left); // Takes myimage ownership + int command = S60_COMMAND_START + position; + setNativeSoftkey(*cba, position, command, KNullDesC()); + cachedCbaIconSize[index] = qt_TSize2QSize(cba->ControlOrNull(command)->Size()); + EikSoftkeyImage::SetLabel(cba, left); + + if(cachedCbaIconSize[index] == QSize(138,72)) { + // Hack for S60 5.0 (5800) landscape orientation, which return wrong icon size + cachedCbaIconSize[index] = QSize(60,60); + } + } + } + + return cachedCbaIconSize[index]; +} + +bool QSoftKeyManagerPrivateS60::setSoftkeyImage(CEikButtonGroupContainer *cba, + QAction &action, int position) +{ + bool ret = false; + + const bool left = (position == LSK_POSITION); + if(position == LSK_POSITION || position == RSK_POSITION) { + QIcon icon = action.icon(); + if (!icon.isNull()) { + // Get size of CBA icon area based on button position and orientation + QSize requiredIconSize = cbaIconSize(cba, position); + // Get pixmap out of icon based on preferred size, the aspect ratio is kept + QPixmap pmWihtAspectRatio = icon.pixmap(requiredIconSize); + // Native softkeys require that pixmap size is exactly the same as requiredIconSize + // prepareSoftkeyPixmap creates a new pixmap with requiredIconSize and blits the 'pmWihtAspectRatio' + // to correct location of it + QPixmap softkeyPixmap = prepareSoftkeyPixmap(pmWihtAspectRatio, position, requiredIconSize); + + QPixmap softkeyAlpha = softkeyPixmap.alphaChannel(); + // Alpha channel in 5.1 and older devices need to be inverted + // TODO: Switch to use toSymbianCFbsBitmap with invert when available + if(QSysInfo::s60Version() <= QSysInfo::SV_S60_5_1) { + QImage alphaImage = softkeyAlpha.toImage(); + alphaImage.invertPixels(); + softkeyAlpha = QPixmap::fromImage(alphaImage); + } + + CFbsBitmap* nBitmap = softkeyPixmap.toSymbianCFbsBitmap(); + CFbsBitmap* nMask = softkeyAlpha.toSymbianCFbsBitmap(); + + CEikImage* myimage = new (ELeave) CEikImage; + myimage->SetPicture( nBitmap, nMask ); // nBitmap and nMask ownership transferred + + EikSoftkeyImage::SetImage(cba, *myimage, left); // Takes myimage ownership + cbaHasImage[position] = true; + ret = true; + } else { + // Restore softkey to text based + if (cbaHasImage[position]) { + EikSoftkeyImage::SetLabel(cba, left); + cbaHasImage[position] = false; + } + } + } + return ret; +} + +bool QSoftKeyManagerPrivateS60::setSoftkey(CEikButtonGroupContainer &cba, + QAction::SoftKeyRole role, int position) +{ + QAction *action = highestPrioritySoftkey(role); + if (action) { + setSoftkeyImage(&cba, *action, position); + QString text = softkeyText(*action); + TPtrC nativeText = qt_QString2TPtrC(text); + int command = S60_COMMAND_START + position; +#ifdef SYMBIAN_VERSION_SYMBIAN3 + if (softKeyCommandActions.contains(action)) + command = softKeyCommandActions.value(action); +#endif + setNativeSoftkey(cba, position, command, nativeText); + const bool dimmed = !action->isEnabled() && !QSoftKeyManager::isForceEnabledInSofkeys(action); + cba.DimCommand(command, dimmed); + realSoftKeyActions.insert(command, action); + return true; + } + return false; +} + +bool QSoftKeyManagerPrivateS60::setLeftSoftkey(CEikButtonGroupContainer &cba) +{ + return setSoftkey(cba, QAction::PositiveSoftKey, LSK_POSITION); +} + +bool QSoftKeyManagerPrivateS60::setMiddleSoftkey(CEikButtonGroupContainer &cba) +{ + // Note: In order to get MSK working, application has to have EAknEnableMSK flag set + // Currently it is not possible very easily) + // For more information see: http://wiki.forum.nokia.com/index.php/Middle_softkey_usage + return setSoftkey(cba, QAction::SelectSoftKey, MSK_POSITION); +} + +bool QSoftKeyManagerPrivateS60::setRightSoftkey(CEikButtonGroupContainer &cba) +{ + if (!setSoftkey(cba, QAction::NegativeSoftKey, RSK_POSITION)) { + const Qt::WindowType windowType = initialSoftKeySource + ? initialSoftKeySource->window()->windowType() : Qt::Window; + if (windowType != Qt::Dialog && windowType != Qt::Popup) { + QString text(QSoftKeyManager::tr("Exit")); + TPtrC nativeText = qt_QString2TPtrC(text); + if (cbaHasImage[RSK_POSITION]) { + EikSoftkeyImage::SetLabel(&cba, false); + cbaHasImage[RSK_POSITION] = false; + } + setNativeSoftkey(cba, RSK_POSITION, EAknSoftkeyExit, nativeText); + cba.DimCommand(EAknSoftkeyExit, false); + return true; + } + } + return false; +} + +void QSoftKeyManagerPrivateS60::setSoftkeys(CEikButtonGroupContainer &cba) +{ + int requestedSoftkeyCount = requestedSoftKeyActions.count(); + const int maxSoftkeyCount = 2; // TODO: differs based on orientation ans S60 versions (some have MSK) + if (requestedSoftkeyCount > maxSoftkeyCount) { + // We have more softkeys than available slots + // Put highest priority negative action to RSK and Options menu with rest of softkey actions to LSK + // TODO: Build menu + setLeftSoftkey(cba); + if(AknLayoutUtils::MSKEnabled()) + setMiddleSoftkey(cba); + setRightSoftkey(cba); + } else { + // We have less softkeys than available slots + // Put softkeys to request slots based on role + setLeftSoftkey(cba); + if(AknLayoutUtils::MSKEnabled()) + setMiddleSoftkey(cba); + setRightSoftkey(cba); + } +} + +void QSoftKeyManagerPrivateS60::updateSoftKeys_sys() +{ + if (skipCbaUpdate()) + return; + + CEikButtonGroupContainer *nativeContainer = S60->buttonGroupContainer(); + Q_ASSERT_X(nativeContainer, Q_FUNC_INFO, "Native CBA does not exist!"); + ensureCbaVisibilityAndResponsiviness(*nativeContainer); + clearSoftkeys(*nativeContainer); + setSoftkeys(*nativeContainer); + + nativeContainer->DrawDeferred(); // 3.1 needs an extra invitation +} + +static void resetMenuBeingConstructed(TAny* /*aAny*/) +{ + S60->menuBeingConstructed = false; +} + +void QSoftKeyManagerPrivateS60::tryDisplayMenuBarL() +{ + CleanupStack::PushL(TCleanupItem(resetMenuBeingConstructed, NULL)); + S60->menuBeingConstructed = true; + S60->menuBar()->TryDisplayMenuBarL(); + CleanupStack::PopAndDestroy(); // Reset menuBeingConstructed to false in all cases +} + +bool QSoftKeyManagerPrivateS60::handleCommand(int command) +{ + QAction *action = realSoftKeyActions.value(command); + if (action) { + bool property = QActionPrivate::get(action)->menuActionSoftkeys; + if (property) { + QT_TRAP_THROWING(tryDisplayMenuBarL()); + } else if (action->menu()) { + // TODO: This is hack, in order to use exising QMenuBar implementation for Symbian + // menubar needs to have widget to which it is associated. Since we want to associate + // menubar to action (which is inherited from QObject), we create and associate QWidget + // to action and pass that for QMenuBar. This associates the menubar to action, and we + // can have own menubar for each action. + QWidget *actionContainer = action->property("_q_action_widget").value<QWidget*>(); + if(!actionContainer) { + actionContainer = new QWidget(action->parentWidget()); + QMenuBar *menuBar = new QMenuBar(actionContainer); + foreach(QAction *menuAction, action->menu()->actions()) { + QMenu *menu = menuAction->menu(); + if(menu) + menuBar->addMenu(menu); + else + menuBar->addAction(menuAction); + } + QVariant v; + v.setValue(actionContainer); + action->setProperty("_q_action_widget", v); + } + qt_symbian_next_menu_from_action(actionContainer); + QT_TRAP_THROWING(tryDisplayMenuBarL()); + } + + Q_ASSERT(action->softKeyRole() != QAction::NoSoftKey); + QWidget *actionParent = action->parentWidget(); + Q_ASSERT_X(actionParent, Q_FUNC_INFO, "No parent set for softkey action!"); + if (actionParent->isEnabled()) { + action->activate(QAction::Trigger); + return true; + } + } + return false; +} + +QT_END_NAMESPACE +#endif //QT_NO_SOFTKEYMANAGER diff --git a/src/gui/platforms/s60/qsoftkeymanager_s60_p.h b/src/gui/platforms/s60/qsoftkeymanager_s60_p.h new file mode 100644 index 0000000000..9cb3787cb8 --- /dev/null +++ b/src/gui/platforms/s60/qsoftkeymanager_s60_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSOFTKEYMANAGER_S60_P_H +#define QSOFTKEYMANAGER_S60_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 "qbitarray.h" +#include "private/qobject_p.h" +#include "private/qsoftkeymanager_common_p.h" + +QT_BEGIN_HEADER + +#ifndef QT_NO_SOFTKEYMANAGER + +QT_BEGIN_NAMESPACE + +class CEikButtonGroupContainer; +class QAction; + +class QSoftKeyManagerPrivateS60 : public QSoftKeyManagerPrivate +{ + Q_DECLARE_PUBLIC(QSoftKeyManager) + +public: + QSoftKeyManagerPrivateS60(); + +public: + void updateSoftKeys_sys(); + bool handleCommand(int command); + +private: + void tryDisplayMenuBarL(); + bool skipCbaUpdate(); + void ensureCbaVisibilityAndResponsiviness(CEikButtonGroupContainer &cba); + void clearSoftkeys(CEikButtonGroupContainer &cba); + QString softkeyText(QAction &softkeyAction); + QAction *highestPrioritySoftkey(QAction::SoftKeyRole role); + static bool actionPriorityMoreThan(const QAction* item1, const QAction* item2); + void setNativeSoftkey(CEikButtonGroupContainer &cba, TInt position, TInt command, const TDesC& text); + QPoint softkeyIconPosition(int position, QSize sourceSize, QSize targetSize); + QPixmap prepareSoftkeyPixmap(QPixmap src, int position, QSize targetSize); + bool isOrientationLandscape(); + QSize cbaIconSize(CEikButtonGroupContainer *cba, int position); + bool setSoftkeyImage(CEikButtonGroupContainer *cba, QAction &action, int position); + bool setSoftkey(CEikButtonGroupContainer &cba, QAction::SoftKeyRole role, int position); + bool setLeftSoftkey(CEikButtonGroupContainer &cba); + bool setMiddleSoftkey(CEikButtonGroupContainer &cba); + bool setRightSoftkey(CEikButtonGroupContainer &cba); + void setSoftkeys(CEikButtonGroupContainer &cba); + +private: + QHash<int, QAction*> realSoftKeyActions; + QSize cachedCbaIconSize[4]; + QBitArray cbaHasImage; +}; + + +QT_END_NAMESPACE + +#endif //QT_NO_SOFTKEYMANAGER + +QT_END_HEADER + +#endif // QSOFTKEYMANAGER_S60_P_H diff --git a/src/gui/platforms/s60/qsound_s60.cpp b/src/gui/platforms/s60/qsound_s60.cpp new file mode 100644 index 0000000000..acc5c2a56f --- /dev/null +++ b/src/gui/platforms/s60/qsound_s60.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#ifndef QT_NO_SOUND + +#include "qdir.h" +#include "qapplication.h" +#include "qsound.h" +#include "qsound_p.h" +#include "qfileinfo.h" +#include <private/qcore_symbian_p.h> + +#include <e32std.h> +#include <mdaaudiosampleplayer.h> + +QT_BEGIN_NAMESPACE + +class QAuServerS60; + +class QAuBucketS60 : public QAuBucket, public MMdaAudioPlayerCallback +{ +public: + QAuBucketS60(QAuServerS60 *server, QSound *sound); + ~QAuBucketS60(); + + void play(); + void stop(); + + inline QSound *sound() const { return m_sound; } + +public: // from MMdaAudioPlayerCallback + void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration); + void MapcPlayComplete(TInt aError); + +private: + QSound *m_sound; + QAuServerS60 *m_server; + bool m_prepared; + bool m_playCalled; + CMdaAudioPlayerUtility *m_playUtility; +}; + + +class QAuServerS60 : public QAuServer +{ +public: + QAuServerS60(QObject *parent); + + void init(QSound *s) + { + QAuBucketS60 *bucket = new QAuBucketS60(this, s); + setBucket(s, bucket); + } + + void play(QSound *s) + { + bucket(s)->play(); + } + + void stop(QSound *s) + { + bucket(s)->stop(); + } + + bool okay() { return true; } + + void play(const QString& filename); + +protected: + void playCompleted(QAuBucketS60 *bucket, int error); + +protected: + QAuBucketS60 *bucket(QSound *s) + { + return (QAuBucketS60 *)QAuServer::bucket( s ); + } + + friend class QAuBucketS60; + + // static QSound::play(filename) cannot be stopped, meaning that playCompleted + // will get always called and QSound gets removed form this list. + QList<QSound *> staticPlayingSounds; +}; + +QAuServerS60::QAuServerS60(QObject *parent) : + QAuServer(parent) +{ + setObjectName(QLatin1String("QAuServerS60")); +} + +void QAuServerS60::play(const QString& filename) +{ + QSound *s = new QSound(filename); + staticPlayingSounds.append(s); + play(s); +} + +void QAuServerS60::playCompleted(QAuBucketS60 *bucket, int error) +{ + QSound *sound = bucket->sound(); + if (!error) { + // We need to handle repeats by ourselves, since with Symbian API we don't + // know how many loops have been played when user asks it + if (decLoop(sound)) { + play(sound); + } else { + if (staticPlayingSounds.removeAll(sound)) + delete sound; + } + } else { + // We don't have a way to inform about errors -> just decrement loops + // in order that QSound::isFinished will return true; + while (decLoop(sound) > 0) {} + if (staticPlayingSounds.removeAll(sound)) + delete sound; + } +} + +QAuServer *qt_new_audio_server() +{ + return new QAuServerS60(qApp); +} + +QAuBucketS60::QAuBucketS60(QAuServerS60 *server, QSound *sound) + : m_sound(sound), m_server(server), m_prepared(false), m_playCalled(false) +{ + QString filepath = QFileInfo(m_sound->fileName()).absoluteFilePath(); + filepath = QDir::toNativeSeparators(filepath); + TPtrC filepathPtr(qt_QString2TPtrC(filepath)); + TRAPD(err, m_playUtility = CMdaAudioPlayerUtility::NewL(*this); + m_playUtility->OpenFileL(filepathPtr)); + if (err) { + m_server->playCompleted(this, err); + } +} + +void QAuBucketS60::play() +{ + if (m_prepared) { + // OpenFileL call is completed we can start playing immediately + m_playUtility->Play(); + } else { + m_playCalled = true; + } + +} + +void QAuBucketS60::stop() +{ + m_playCalled = false; + m_playUtility->Stop(); +} + +void QAuBucketS60::MapcPlayComplete(TInt aError) +{ + m_server->playCompleted(this, aError); +} + +void QAuBucketS60::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& /*aDuration*/) +{ + if (aError) { + m_server->playCompleted(this, aError); + } else { + m_prepared = true; + if (m_playCalled){ + play(); + } + } +} + +QAuBucketS60::~QAuBucketS60() +{ + if (m_playUtility){ + m_playUtility->Stop(); + m_playUtility->Close(); + } + + delete m_playUtility; +} + + +#endif // QT_NO_SOUND + +QT_END_NAMESPACE diff --git a/src/gui/platforms/s60/qt_s60_p.h b/src/gui/platforms/s60/qt_s60_p.h new file mode 100644 index 0000000000..8aba53a168 --- /dev/null +++ b/src/gui/platforms/s60/qt_s60_p.h @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_S60_P_H +#define QT_S60_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 "private/qcore_symbian_p.h" +#include "qhash.h" +#include "qpoint.h" +#include "QtGui/qfont.h" +#include "QtGui/qimage.h" +#include "QtGui/qevent.h" +#include "qpointer.h" +#include "qapplication.h" +#include "qelapsedtimer.h" +#include "QtCore/qthreadstorage.h" +#include "qwidget_p.h" +#include <w32std.h> +#include <coecntrl.h> +#include <eikenv.h> +#include <eikappui.h> + +#ifdef Q_WS_S60 +#include <AknUtils.h> // AknLayoutUtils +#include <avkon.hrh> // EEikStatusPaneUidTitle +#include <akntitle.h> // CAknTitlePane +#include <akncontext.h> // CAknContextPane +#include <eikspane.h> // CEikStatusPane +#include <AknPopupFader.h> // MAknFadedComponent and TAknPopupFader +#include <gfxtranseffect/gfxtranseffect.h> // BeginFullScreen +#ifdef QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H +#include <akntranseffect.h> // BeginFullScreen +#endif +#endif + +QT_BEGIN_NAMESPACE + +// Application internal HandleResourceChangeL events, +// system events seems to start with 0x10 +const TInt KInternalStatusPaneChange = 0x50000000; + +// For BeginFullScreen(). +const TUint KQtAppExitFlag = 0x400; + +static const int qt_symbian_max_screens = 4; + +//this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2 +#define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13) + +class Q_AUTOTEST_EXPORT QS60ThreadLocalData +{ +public: + QS60ThreadLocalData(); + ~QS60ThreadLocalData(); + bool usingCONEinstances; + RWsSession wsSession; + CWsScreenDevice *screenDevice; +}; + +class QS60Data +{ +public: + QS60Data(); + QThreadStorage<QS60ThreadLocalData *> tls; + TUid uid; + int screenDepth; + QPoint lastCursorPos; + QPoint lastPointerEventPos; + QPointer<QWidget> lastPointerEventTarget; + QPointer<QWidget> mousePressTarget; + int screenWidthInPixels; + int screenHeightInPixels; + int screenWidthInTwips; + int screenHeightInTwips; + int defaultDpiX; + int defaultDpiY; + WId curWin; + enum PressedKeys { + Select = 0x1, + Right = 0x2, + Down = 0x4, + Left = 0x8, + Up = 0x10, + LeftUp = 0x20, + RightUp = 0x40, + RightDown = 0x80, + LeftDown = 0x100 + }; + int virtualMousePressedKeys; // of the above type, but avoids casting problems + int virtualMouseAccelDX; + int virtualMouseAccelDY; + QElapsedTimer virtualMouseAccelTimeout; + int virtualMouseMaxAccel; +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + int brokenPointerCursors : 1; +#endif + int hasTouchscreen : 1; + int mouseInteractionEnabled : 1; + int virtualMouseRequired : 1; + int qtOwnsS60Environment : 1; + int supportsPremultipliedAlpha : 1; + int avkonComponentsSupportTransparency : 1; + int menuBeingConstructed : 1; + int orientationSet : 1; + int partial_keyboard : 1; + QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type + QPointer<QWidget> splitViewLastWidget; + + static CEikButtonGroupContainer *cba; + + enum ScanCodeState { + Unpressed, + KeyDown, + KeyDownAndKey + }; + QHash<TInt, ScanCodeState> scanCodeStates; + + static inline void updateScreenSize(); + inline RWsSession& wsSession(); + static inline int screenCount(); + static inline RWindowGroup& windowGroup(); + static inline RWindowGroup& windowGroup(const QWidget *widget); + static inline RWindowGroup& windowGroup(int screenNumber); + inline CWsScreenDevice* screenDevice(); + inline CWsScreenDevice* screenDevice(const QWidget *widget); + inline CWsScreenDevice* screenDevice(int screenNumber); + static inline int screenNumberForWidget(const QWidget *widget); + static inline CCoeAppUi* appUi(); + static inline CEikMenuBar* menuBar(); +#ifdef Q_WS_S60 + static inline CEikStatusPane* statusPane(); + static inline CCoeControl* statusPaneSubPane(TInt aPaneId); + static inline CAknTitlePane* titlePane(); + static inline CAknContextPane* contextPane(); + static inline CEikButtonGroupContainer* buttonGroupContainer(); + static inline void setButtonGroupContainer(CEikButtonGroupContainer* newCba); + static void setStatusPaneAndButtonGroupVisibility(bool statusPaneVisible, bool buttonGroupVisible); + static bool setRecursiveDecorationsVisibility(QWidget *window, Qt::WindowStates newState); +#endif + static void controlVisibilityChanged(CCoeControl *control, bool visible); + +#ifdef Q_OS_SYMBIAN + TTrapHandler *s60InstalledTrapHandler; +#endif + + int screenWidthInPixelsForScreen[qt_symbian_max_screens]; + int screenHeightInPixelsForScreen[qt_symbian_max_screens]; + int screenWidthInTwipsForScreen[qt_symbian_max_screens]; + int screenHeightInTwipsForScreen[qt_symbian_max_screens]; + + int nativeScreenWidthInPixels; + int nativeScreenHeightInPixels; + + int beginFullScreenCalled : 1; + int endFullScreenCalled : 1; +}; + +Q_AUTOTEST_EXPORT QS60Data* qGlobalS60Data(); +#define S60 qGlobalS60Data() + +class QAbstractLongTapObserver +{ +public: + virtual void HandleLongTapEventL( const TPoint& aPenEventLocation, + const TPoint& aPenEventScreenLocation ) = 0; +}; +class QLongTapTimer; + + +class QSymbianControl : public CCoeControl, public QAbstractLongTapObserver +#ifdef Q_WS_S60 +, public MAknFadedComponent, public MEikStatusPaneObserver +#endif +{ +public: + DECLARE_TYPE_ID(0x51740000) // Fun fact: the two first values are "Qt" in ASCII. + +public: + QSymbianControl(QWidget *w); + void ConstructL(bool isWindowOwning = false, bool desktop = false); + ~QSymbianControl(); + void HandleResourceChange(int resourceType); + void HandlePointerEventL(const TPointerEvent& aPointerEvent); + TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType); +#if !defined(QT_NO_IM) && defined(Q_WS_S60) + TCoeInputCapabilities InputCapabilities() const; +#endif + TTypeUid::Ptr MopSupplyObject(TTypeUid id); + + inline QWidget* widget() const { return qwidget; } + void setWidget(QWidget *w); + void sendInputEvent(QWidget *widget, QInputEvent *inputEvent); + void setIgnoreFocusChanged(bool enabled) { m_ignoreFocusChanged = enabled; } + void CancelLongTapTimer(); + + void setFocusSafely(bool focus); + + bool isControlActive(); + + void ensureFixNativeOrientation(); + QPoint translatePointForFixedNativeOrientation(const TPoint &pointerEventPos) const; + TRect translateRectForFixedNativeOrientation(const TRect &controlRect) const; + +#ifdef Q_WS_S60 + void FadeBehindPopup(bool fade){ popupFader.FadeBehindPopup( this, this, fade); } + void HandleStatusPaneSizeChange(); + +protected: // from MAknFadedComponent + TInt CountFadedComponents() {return 1;} + CCoeControl* FadedComponent(TInt /*aIndex*/) {return this;} +#else + // #warning No fallback implementation for QSymbianControl::FadeBehindPopup + void FadeBehindPopup(bool /*fade*/){ } +#endif + +protected: + void Draw(const TRect& aRect) const; + void SizeChanged(); + void PositionChanged(); + void FocusChanged(TDrawNow aDrawNow); + +protected: + void qwidgetResize_helper(const QSize &newSize); + +private: + void HandlePointerEvent(const TPointerEvent& aPointerEvent); + TKeyResponse OfferKeyEvent(const TKeyEvent& aKeyEvent,TEventCode aType); + TKeyResponse sendSymbianKeyEvent(const TKeyEvent &keyEvent, QEvent::Type type); + TKeyResponse sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent); + TKeyResponse handleVirtualMouse(const TKeyEvent& keyEvent,TEventCode type); + bool sendMouseEvent(QWidget *widget, QMouseEvent *mEvent); + void sendMouseEvent( + QWidget *receiver, + QEvent::Type type, + const QPoint &globalPos, + Qt::MouseButton button, + Qt::KeyboardModifiers modifiers); + void processTouchEvent(int pointerNumber, TPointerEvent::TType type, QPointF screenPos, qreal pressure); + void HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ); +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event); +#endif + bool isSplitViewWidget(QWidget *widget); + +public: + void handleClientAreaChange(); + +private: + static QSymbianControl *lastFocusedControl; + +private: + QWidget *qwidget; + QLongTapTimer* m_longTapDetector; + QElapsedTimer m_doubleClickTimer; + bool m_ignoreFocusChanged : 1; + bool m_symbianPopupIsOpen : 1; + +#ifdef Q_WS_S60 + // Fader object used to fade everything except this menu and the CBA. + TAknPopupFader popupFader; +#endif + + bool m_inExternalScreenOverride : 1; + bool m_lastStatusPaneVisibility : 1; +}; + +inline QS60Data::QS60Data() +: uid(TUid::Null()), + screenDepth(0), + screenWidthInPixels(0), + screenHeightInPixels(0), + screenWidthInTwips(0), + screenHeightInTwips(0), + defaultDpiX(0), + defaultDpiY(0), + curWin(0), + virtualMousePressedKeys(0), + virtualMouseAccelDX(0), + virtualMouseAccelDY(0), + virtualMouseMaxAccel(0), +#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS + brokenPointerCursors(0), +#endif + hasTouchscreen(0), + mouseInteractionEnabled(0), + virtualMouseRequired(0), + qtOwnsS60Environment(0), + supportsPremultipliedAlpha(0), + avkonComponentsSupportTransparency(0), + menuBeingConstructed(0), + orientationSet(0), + partial_keyboard(0), + s60ApplicationFactory(0) +#ifdef Q_OS_SYMBIAN + ,s60InstalledTrapHandler(0) +#endif + ,beginFullScreenCalled(0), + endFullScreenCalled(0) +{ +} + +inline void QS60Data::updateScreenSize() +{ + CWsScreenDevice *dev = S60->screenDevice(); + int screenModeCount = dev->NumScreenModes(); + int mode = dev->CurrentScreenMode(); + TPixelsTwipsAndRotation params; + dev->GetScreenModeSizeAndRotation(mode, params); + S60->screenWidthInPixels = params.iPixelSize.iWidth; + S60->screenHeightInPixels = params.iPixelSize.iHeight; + S60->screenWidthInTwips = params.iTwipsSize.iWidth; + S60->screenHeightInTwips = params.iTwipsSize.iHeight; + + S60->virtualMouseMaxAccel = qMax(S60->screenHeightInPixels, S60->screenWidthInPixels) / 10; + + TReal inches = S60->screenHeightInTwips / (TReal)KTwipsPerInch; + S60->defaultDpiY = S60->screenHeightInPixels / inches; + inches = S60->screenWidthInTwips / (TReal)KTwipsPerInch; + S60->defaultDpiX = S60->screenWidthInPixels / inches; + + int screens = S60->screenCount(); + for (int i = 0; i < screens; ++i) { + CWsScreenDevice *dev = S60->screenDevice(i); + mode = dev->CurrentScreenMode(); + dev->GetScreenModeSizeAndRotation(mode, params); + S60->screenWidthInPixelsForScreen[i] = params.iPixelSize.iWidth; + S60->screenHeightInPixelsForScreen[i] = params.iPixelSize.iHeight; + S60->screenWidthInTwipsForScreen[i] = params.iTwipsSize.iWidth; + S60->screenHeightInTwipsForScreen[i] = params.iTwipsSize.iHeight; + } + + // Look for a screen mode with rotation 0 + // in order to decide what the native orientation is. + for (mode = 0; mode < screenModeCount; ++mode) { + TPixelsAndRotation sizeAndRotation; + dev->GetScreenModeSizeAndRotation(mode, sizeAndRotation); + if (sizeAndRotation.iRotation == CFbsBitGc::EGraphicsOrientationNormal) { + S60->nativeScreenWidthInPixels = sizeAndRotation.iPixelSize.iWidth; + S60->nativeScreenHeightInPixels = sizeAndRotation.iPixelSize.iHeight; + break; + } + } +} + +inline RWsSession& QS60Data::wsSession() +{ + if(!tls.hasLocalData()) { + tls.setLocalData(new QS60ThreadLocalData); + } + return tls.localData()->wsSession; +} + +inline int QS60Data::screenCount() +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + CCoeEnv *env = CCoeEnv::Static(); + if (env) { + return qMin(env->WsSession().NumberOfScreens(), qt_symbian_max_screens); + } +#endif + return 1; +} + +inline RWindowGroup& QS60Data::windowGroup() +{ + return CCoeEnv::Static()->RootWin(); +} + +inline RWindowGroup& QS60Data::windowGroup(const QWidget *widget) +{ + return windowGroup(screenNumberForWidget(widget)); +} + +inline RWindowGroup& QS60Data::windowGroup(int screenNumber) +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + RWindowGroup *wg = CCoeEnv::Static()->RootWin(screenNumber); + return wg ? *wg : windowGroup(); +#else + Q_UNUSED(screenNumber); + return windowGroup(); +#endif +} + +inline CWsScreenDevice* QS60Data::screenDevice() +{ + if(!tls.hasLocalData()) { + tls.setLocalData(new QS60ThreadLocalData); + } + return tls.localData()->screenDevice; +} + +inline CWsScreenDevice* QS60Data::screenDevice(const QWidget *widget) +{ + return screenDevice(screenNumberForWidget(widget)); +} + +inline CWsScreenDevice* QS60Data::screenDevice(int screenNumber) +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + CCoeEnv *env = CCoeEnv::Static(); + if (env) { + CWsScreenDevice *dev = env->ScreenDevice(screenNumber); + return dev ? dev : screenDevice(); + } else { + return screenDevice(); + } +#else + return screenDevice(); +#endif +} + +inline int QS60Data::screenNumberForWidget(const QWidget *widget) +{ + if (!widget) + return 0; + const QWidget *w = widget; + while (w->parentWidget()) + w = w->parentWidget(); + return qt_widget_private(const_cast<QWidget *>(w))->symbianScreenNumber; +} + +inline CCoeAppUi* QS60Data::appUi() +{ + return CCoeEnv::Static()-> AppUi(); +} + +inline CEikMenuBar* QS60Data::menuBar() +{ + return CEikonEnv::Static()->AppUiFactory()->MenuBar(); +} + +#ifdef Q_WS_S60 +inline CEikStatusPane* QS60Data::statusPane() +{ + return CEikonEnv::Static()->AppUiFactory()->StatusPane(); +} + +// Returns the application's status pane control, if not present returns NULL. +inline CCoeControl* QS60Data::statusPaneSubPane( TInt aPaneId ) +{ + const TUid paneUid = { aPaneId }; + CEikStatusPane* statusPane = S60->statusPane(); + if (statusPane && statusPane->PaneCapabilities(paneUid).IsPresent()) { + CCoeControl* control = NULL; + // ControlL shouldn't leave because the pane is present + TRAPD(err, control = statusPane->ControlL(paneUid)); + return err != KErrNone ? NULL : control; + } + return NULL; +} + +// Returns the application's title pane, if not present returns NULL. +inline CAknTitlePane* QS60Data::titlePane() +{ + return static_cast<CAknTitlePane*>(S60->statusPaneSubPane(EEikStatusPaneUidTitle)); +} + +// Returns the application's title pane, if not present returns NULL. +inline CAknContextPane* QS60Data::contextPane() +{ + return static_cast<CAknContextPane*>(S60->statusPaneSubPane(EEikStatusPaneUidContext)); +} + +inline CEikButtonGroupContainer* QS60Data::buttonGroupContainer() +{ + return QS60Data::cba; +} + +inline void QS60Data::setButtonGroupContainer(CEikButtonGroupContainer *newCba) +{ + QS60Data::cba = newCba; +} +#endif // Q_WS_S60 + +static inline QFont qt_TFontSpec2QFontL(const TFontSpec &fontSpec) +{ + return QFont( + qt_TDesC2QString(fontSpec.iTypeface.iName), + fontSpec.iHeight / KTwipsPerPoint, + fontSpec.iFontStyle.StrokeWeight() == EStrokeWeightNormal ? QFont::Normal : QFont::Bold, + fontSpec.iFontStyle.Posture() == EPostureItalic + ); +} + +static inline QImage::Format qt_TDisplayMode2Format(TDisplayMode mode) +{ + QImage::Format format; + switch(mode) { + case EGray2: + format = QImage::Format_MonoLSB; + break; + case EColor256: + case EGray256: + format = QImage::Format_Indexed8; + break; + case EColor4K: + format = QImage::Format_RGB444; + break; + case EColor64K: + format = QImage::Format_RGB16; + break; + case EColor16M: + format = QImage::Format_RGB888; + break; + case EColor16MU: + format = QImage::Format_RGB32; + break; + case EColor16MA: + format = QImage::Format_ARGB32; + break; + case Q_SYMBIAN_ECOLOR16MAP: + format = QImage::Format_ARGB32_Premultiplied; + break; + default: + format = QImage::Format_Invalid; + break; + } + return format; +} + +#ifndef QT_NO_CURSOR +void qt_symbian_setWindowCursor(const QCursor &cursor, const CCoeControl* wid); +void qt_symbian_setWindowGroupCursor(const QCursor &cursor, RWindowTreeNode &node); +void qt_symbian_setGlobalCursor(const QCursor &cursor); +void qt_symbian_set_cursor_visible(bool visible); +bool qt_symbian_is_cursor_visible(); +#endif + +static inline bool qt_beginFullScreenEffect() +{ +#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) + // Only for post-S^3. On earlier versions the system transition effects + // may not be able to capture the non-Avkon content, leading to confusing + // looking effects, so just skip the whole thing. + if (S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) + return false; + S60->beginFullScreenCalled = true; + // For Avkon apps the app-exit effect is triggered from CAknAppUi::PrepareToExit(). + // That is good for Avkon apps, but in case of Qt the RWindows are destroyed earlier. + // Therefore we call BeginFullScreen() ourselves. + GfxTransEffect::BeginFullScreen(AknTransEffect::EApplicationExit, + TRect(0, 0, 0, 0), + AknTransEffect::EParameterType, + AknTransEffect::GfxTransParam(S60->uid, + AknTransEffect::TParameter::EAvkonCheck | KQtAppExitFlag)); + return true; +#else + return false; +#endif +} + +static inline void qt_abortFullScreenEffect() +{ +#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) + if (!S60->beginFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) + return; + GfxTransEffect::AbortFullScreen(); + S60->beginFullScreenCalled = S60->endFullScreenCalled = false; +#endif +} + +static inline void qt_endFullScreenEffect() +{ +#if defined(Q_WS_S60) && defined(QT_SYMBIAN_HAVE_AKNTRANSEFFECT_H) + if (S60->endFullScreenCalled || QSysInfo::s60Version() <= QSysInfo::SV_S60_5_2) + return; + S60->endFullScreenCalled = true; + GfxTransEffect::EndFullScreen(); +#endif +} + +QT_END_NAMESPACE + +#endif // QT_S60_P_H diff --git a/src/gui/platforms/s60/qwidget_s60.cpp b/src/gui/platforms/s60/qwidget_s60.cpp new file mode 100644 index 0000000000..e28a75a6ab --- /dev/null +++ b/src/gui/platforms/s60/qwidget_s60.cpp @@ -0,0 +1,1450 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwidget_p.h" +#include "qdesktopwidget.h" +#include "qapplication.h" +#include "qapplication_p.h" +#include "private/qbackingstore_p.h" +#include "qevent.h" +#include "qt_s60_p.h" + +#include "qbitmap.h" +#include "private/qwindowsurface_s60_p.h" + +#include <qinputcontext.h> + +#ifdef Q_WS_S60 +#include <aknappui.h> +#include <eikbtgpc.h> +#endif + +// This is necessary in order to be able to perform delayed invocation on slots +// which take arguments of type WId. One example is +// QWidgetPrivate::_q_delayedDestroy, which is used to delay destruction of +// CCoeControl objects until after the CONE event handler has finished running. +Q_DECLARE_METATYPE(WId) + +// Workaround for the fact that S60 SDKs 3.x do not contain the akntoolbar.h +// header, even though the documentation says that it should be there, and indeed +// it is present in the library. +class CAknToolbar : public CAknControl, + public MCoeControlObserver, + public MCoeControlBackground, + public MEikCommandObserver, + public MAknFadedComponent +{ +public: + IMPORT_C void SetToolbarVisibility(const TBool visible); +}; + +QT_BEGIN_NAMESPACE + +extern bool qt_nograb(); + +QWidget *QWidgetPrivate::mouseGrabber = 0; +QWidget *QWidgetPrivate::keyboardGrabber = 0; +CEikButtonGroupContainer *QS60Data::cba = 0; + +int qt_symbian_create_desktop_on_screen = -1; + +static bool isEqual(const QList<QAction*>& a, const QList<QAction*>& b) +{ + if ( a.count() != b.count()) + return false; + int index=0; + while (index<a.count()) { + if (a.at(index)->softKeyRole() != b.at(index)->softKeyRole()) + return false; + if (a.at(index)->text().compare(b.at(index)->text())!=0) + return false; + index++; + } + return true; +} + +void QWidgetPrivate::setWSGeometry(bool dontShow, const QRect &) +{ + // Note: based on x11 implementation + + static const int XCOORD_MAX = 16383; + static const int WRECT_MAX = 16383; + + 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. + Symbian coordinate system for this widget (relative to wrect). + Qt coordinate system for parent + Symbian 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 Symbian geometry of my widget. (starts out in parent's Qt coord sys, and ends up in parent's Symbian 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) + data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height())); + 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) + data.winid->DrawableWindow()->SetVisible(EFalse); + 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(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 (!parent->internalWinId()) + xrect.translate(parent->mapTo(q->nativeParentWidget(), QPoint(0, 0))); + + data.winid->SetExtent(TPoint(xrect.x(), xrect.y()), TSize(xrect.width(), xrect.height())); + } + + if (mapWindow and !dontShow) { + q->setAttribute(Qt::WA_Mapped); + if (q->internalWinId()) + q->internalWinId()->DrawableWindow()->SetVisible(ETrue); + } + + if (jump && data.winid) { + RWindow *const window = static_cast<RWindow *>(data.winid->DrawableWindow()); + window->Invalidate(TRect(0, 0, wrect.width(), wrect.height())); + } +} + +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; + + QPoint oldPos(q->pos()); + QSize oldSize(q->size()); + QRect oldGeom(data.crect); + + // Lose maximized status if deliberate resize + if (w != oldSize.width() || h != oldSize.height()) + data.window_state &= ~Qt::WindowMaximized; + + 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); + else { + uint s = data.window_state; + s &= ~(Qt::WindowMaximized | Qt::WindowFullScreen); + data.window_state = s; + } + + bool isResize = w != oldSize.width() || h != oldSize.height(); + if (!isMove && !isResize) + return; + + if (q->isWindow()) { + 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); + data.window_state &= ~Qt::WindowFullScreen; + } else if (q->isVisible() && q->testAttribute(Qt::WA_OutsideWSRange)) { + q->setAttribute(Qt::WA_OutsideWSRange, false); + + // put the window in its place and show it + q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h))); + data.crect.setRect(x, y, w, h); + show_sys(); + } else { + QRect r = QRect(x, y, w, h); + data.crect = r; + q->internalWinId()->SetRect(TRect(TPoint(x, y), TSize(w, h))); + topData()->normalGeometry = data.crect; + } + QSymbianControl *window = static_cast<QSymbianControl *>(q->internalWinId()); + window->ensureFixNativeOrientation(); + } else { + data.crect.setRect(x, y, w, h); + + QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); + const bool inTopLevelResize = tlwExtra ? tlwExtra->inTopLevelResize : false; + + 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 && maybeBackingStore()) + 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(); + } + + if (q->isVisible()) { + if (isMove && q->pos() != oldPos) { + QMoveEvent e(q->pos(), oldPos); + QApplication::sendEvent(q, &e); + } + if (isResize) { + bool slowResize = qgetenv("QT_SLOW_TOPLEVEL_RESIZE").toInt(); + const bool setTopLevelResize = !slowResize && q->isWindow() && extra && extra->topextra + && !extra->topextra->inTopLevelResize; + if (setTopLevelResize) + extra->topextra->inTopLevelResize = true; + QResizeEvent e(q->size(), oldSize); + QApplication::sendEvent(q, &e); + if (!q->testAttribute(Qt::WA_StaticContents) && q->internalWinId()) + q->internalWinId()->DrawDeferred(); + 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::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(); + + 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); + + if (popup) + flags |= Qt::WindowStaysOnTopHint; // a popup stays on top + + TRect clientRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); + int sw = clientRect.Width(); + int sh = clientRect.Height(); + + if (desktop) { + symbianScreenNumber = qMax(qt_symbian_create_desktop_on_screen, 0); + TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); + data.crect.setRect(0, 0, screenSize.iWidth, screenSize.iHeight); + q->setAttribute(Qt::WA_DontShowOnScreen); + } else if (topLevel && !q->testAttribute(Qt::WA_Resized)){ + int width = sw; + int height = sh; + if (symbianScreenNumber > 0) { + TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); + width = screenSize.iWidth; + height = screenSize.iHeight; + } + if (extra) { + width = qMax(qMin(width, extra->maxw), extra->minw); + height = qMax(qMin(height, extra->maxh), extra->minh); + } + data.crect.setSize(QSize(width, height)); + } + + CCoeControl *const destroyw = destroyOldWindow ? data.winid : 0; + + createExtra(); + if (window) { + setWinId(window); + TRect tr = window->Rect(); + data.crect.setRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height()); + + } else if (topLevel) { + if (!q->testAttribute(Qt::WA_Moved) && !q->testAttribute(Qt::WA_DontShowOnScreen)) + data.crect.moveTopLeft(QPoint(clientRect.iTl.iX, clientRect.iTl.iY)); + + QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); + Q_CHECK_PTR(control); + + QT_TRAP_THROWING(control->ConstructL(true, desktop)); + control->SetMopParent(static_cast<CEikAppUi*>(S60->appUi())); + + // Symbian windows are always created in an inactive state + // We perform this assignment for the case where the window is being re-created + // as a result of a call to setParent_sys, on either this widget or one of its + // ancestors. + extra->activated = 0; + + if (!desktop) { + TInt stackingFlags; + if ((q->windowType() & Qt::Popup) == Qt::Popup) { + stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus; + } else { + stackingFlags = ECoeStackFlagStandard; + } + control->MakeVisible(false); + QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags)); + // Avoid keyboard focus to a hidden window. + control->setFocusSafely(false); + + RDrawableWindow *const drawableWindow = control->DrawableWindow(); + // Request mouse move events. + drawableWindow->PointerFilter(EPointerFilterEnterExit + | EPointerFilterMove | EPointerFilterDrag, 0); + drawableWindow->EnableVisibilityChangeEvents(); + + } + + q->setAttribute(Qt::WA_WState_Created); + + int x, y, w, h; + data.crect.getRect(&x, &y, &w, &h); + control->SetRect(TRect(TPoint(x, y), TSize(w, h))); + + // We wait until the control is fully constructed before calling setWinId, because + // this generates a WinIdChanged event. + setWinId(control.take()); + + if (!desktop) + s60UpdateIsOpaque(); // must be called after setWinId() + + } else if (q->testAttribute(Qt::WA_NativeWindow) || paintOnScreen()) { // create native child widget + + QScopedPointer<QSymbianControl> control( new QSymbianControl(q) ); + Q_CHECK_PTR(control); + + QT_TRAP_THROWING(control->ConstructL(!parentWidget)); + + // Symbian windows are always created in an inactive state + // We perform this assignment for the case where the window is being re-created + // as a result of a call to setParent_sys, on either this widget or one of its + // ancestors. + extra->activated = 0; + + TInt stackingFlags; + if ((q->windowType() & Qt::Popup) == Qt::Popup) { + stackingFlags = ECoeStackFlagRefusesAllKeys | ECoeStackFlagRefusesFocus; + } else { + stackingFlags = ECoeStackFlagStandard; + } + control->MakeVisible(false); + QT_TRAP_THROWING(control->ControlEnv()->AppUi()->AddToStackL(control.data(), ECoeStackPriorityDefault, stackingFlags)); + // Avoid keyboard focus to a hidden window. + control->setFocusSafely(false); + + q->setAttribute(Qt::WA_WState_Created); + int x, y, w, h; + data.crect.getRect(&x, &y, &w, &h); + control->SetRect(TRect(TPoint(x, y), TSize(w, h))); + + RDrawableWindow *const drawableWindow = control->DrawableWindow(); + // Request mouse move events. + drawableWindow->PointerFilter(EPointerFilterEnterExit + | EPointerFilterMove | EPointerFilterDrag, 0); + drawableWindow->EnableVisibilityChangeEvents(); + + if (q->isVisible() && q->testAttribute(Qt::WA_Mapped)) { + activateSymbianWindow(control.data()); + control->MakeVisible(true); + } + + // We wait until the control is fully constructed before calling setWinId, because + // this generates a WinIdChanged event. + setWinId(control.take()); + } + + if (destroyw) { + destroyw->ControlEnv()->AppUi()->RemoveFromStack(destroyw); + + // Delay deletion of the control in case this function is called in the + // context of a CONE event handler such as + // CCoeControl::ProcessPointerEventL + QMetaObject::invokeMethod(q, "_q_delayedDestroy", + Qt::QueuedConnection, Q_ARG(WId, destroyw)); + } + + if (q->testAttribute(Qt::WA_AcceptTouchEvents)) + registerTouchWindow(); +} + + +void QWidgetPrivate::show_sys() +{ + Q_Q(QWidget); + + if (q->testAttribute(Qt::WA_OutsideWSRange)) + return; + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + + q->setAttribute(Qt::WA_Mapped); + + if (q->testAttribute(Qt::WA_DontShowOnScreen)) { + invalidateBuffer(q->rect()); + return; + } + + if (q->internalWinId()) { + if (!extra->activated) + activateSymbianWindow(); + + QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId()); + const bool isFullscreen = q->windowState() & Qt::WindowFullScreen; + const TBool cbaRequested = q->windowFlags() & Qt::WindowSoftkeysVisibleHint; + +#ifdef Q_WS_S60 + // Lazily initialize the S60 screen furniture when the first window is shown. + if (q->isWindow() && !QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes) + && !S60->buttonGroupContainer() && !S60->statusPane()) { + + if (!q->testAttribute(Qt::WA_DontShowOnScreen)) { + + // Create the status pane and CBA here + CEikAppUi *ui = static_cast<CEikAppUi *>(S60->appUi()); + MEikAppUiFactory *factory = CEikonEnv::Static()->AppUiFactory(); + + QT_TRAP_THROWING( + factory->CreateResourceIndependentFurnitureL(ui); + + TRect boundingRect = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); + + CEikButtonGroupContainer *cba = CEikButtonGroupContainer::NewL(CEikButtonGroupContainer::ECba, + CEikButtonGroupContainer::EHorizontal,ui,R_AVKON_SOFTKEYS_EMPTY_WITH_IDS); + if (isFullscreen && !cbaRequested) + cba->MakeVisible(false); + + CEikButtonGroupContainer *oldCba = factory->SwapButtonGroup(cba); + Q_ASSERT(!oldCba); + S60->setButtonGroupContainer(cba); + + // If the creation of the first widget is delayed, for example by doing it + // inside the event loop, S60 somehow "forgets" to set the visibility of the + // toolbar (the three middle softkeys) when you flip the phone over, so we + // need to do it ourselves to avoid a "hole" in the application, even though + // Qt itself does not use the toolbar directly.. + CAknAppUi *appui = dynamic_cast<CAknAppUi *>(CEikonEnv::Static()->AppUi()); + if (appui) { + CAknToolbar *toolbar = appui->PopupToolbar(); + if (toolbar && !toolbar->IsVisible()) + toolbar->SetToolbarVisibility(ETrue); + } + + CEikMenuBar *menuBar = new(ELeave) CEikMenuBar; + menuBar->ConstructL(ui, 0, R_AVKON_MENUPANE_EMPTY); + menuBar->SetMenuType(CEikMenuBar::EMenuOptions); + S60->appUi()->AddToStackL(menuBar,ECoeStackPriorityMenu,ECoeStackFlagRefusesFocus); + + CEikMenuBar *oldMenu = factory->SwapMenuBar(menuBar); + Q_ASSERT(!oldMenu); + ) + + if (S60->statusPane()) { + // Use QDesktopWidget as the status pane observer to proxy for the AppUi. + // Can't use AppUi directly because it privately inherits from MEikStatusPaneObserver. + QSymbianControl *desktopControl = static_cast<QSymbianControl *>(QApplication::desktop()->winId()); + S60->statusPane()->SetObserver(desktopControl); + if (isFullscreen) { + const bool cbaVisible = S60->buttonGroupContainer() && S60->buttonGroupContainer()->IsVisible(); + S60->setStatusPaneAndButtonGroupVisibility(false, cbaVisible); + } + } + } + } +#endif + + // Fill client area if maximized OR + // Put window below status pane unless the window has an explicit position. + if (!isFullscreen) { + if (q->windowState() & Qt::WindowMaximized) { + TRect r = static_cast<CEikAppUi*>(S60->appUi())->ClientRect(); + id->SetExtent(r.iTl, r.Size()); + } else if (!q->testAttribute(Qt::WA_Moved) && q->windowType() != Qt::Dialog) { + id->SetPosition(static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl); + } + } + + id->MakeVisible(true); + + if(q->isWindow()&&!q->testAttribute(Qt::WA_ShowWithoutActivating)) + id->setFocusSafely(true); + } + + invalidateBuffer(q->rect()); +} + +void QWidgetPrivate::activateSymbianWindow(WId wid) +{ + Q_Q(QWidget); + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + Q_ASSERT(q->testAttribute(Qt::WA_Mapped)); + Q_ASSERT(!extra->activated); + + if(!wid) + wid = q->internalWinId(); + + Q_ASSERT(wid); + + QT_TRAP_THROWING(wid->ActivateL()); + extra->activated = 1; +} + +void QWidgetPrivate::hide_sys() +{ + Q_Q(QWidget); + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + deactivateWidgetCleanup(); + QSymbianControl *id = static_cast<QSymbianControl *>(q->internalWinId()); + + if (id) { + //Incorrect optimization - for popup windows, Qt's focus is moved before + //hide_sys is called, resulting in the popup window keeping its elevated + //position in the CONE control stack. + //This can result in keyboard focus being in an invisible widget in some + //conditions - e.g. QTBUG-4733 + //if(id->IsFocused()) // Avoid unnecessary calls to FocusChanged() + id->setFocusSafely(false); + id->MakeVisible(false); + if (QWidgetBackingStore *bs = maybeBackingStore()) + bs->releaseBuffer(); + } else { + invalidateBuffer(q->rect()); + } + + q->setAttribute(Qt::WA_Mapped, false); +} + +void QWidgetPrivate::setFocus_sys() +{ + Q_Q(QWidget); + if (q->testAttribute(Qt::WA_WState_Created) && q->window()->windowType() != Qt::Popup) + if (!q->effectiveWinId()->IsFocused()) // Avoid unnecessry calls to FocusChanged() + static_cast<QSymbianControl *>(q->effectiveWinId())->setFocusSafely(true); +} + +void QWidgetPrivate::raise_sys() +{ + Q_Q(QWidget); + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + if (q->internalWinId()) { + q->internalWinId()->DrawableWindow()->SetOrdinalPosition(0); + + // If toplevel widget, raise app to foreground + if (q->isWindow()) + S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), 0); + } +} + +void QWidgetPrivate::lower_sys() +{ + Q_Q(QWidget); + + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + if (q->internalWinId()) { + // If toplevel widget, lower app to background + if (q->isWindow()) + S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), -1); + else + q->internalWinId()->DrawableWindow()->SetOrdinalPosition(-1); + } + + if (!q->isWindow()) + invalidateBuffer(q->rect()); +} + +void QWidgetPrivate::setModal_sys() +{ + +} + +void QWidgetPrivate::stackUnder_sys(QWidget* w) +{ + Q_Q(QWidget); + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + + if (q->internalWinId() && w->internalWinId()) { + RDrawableWindow *const thisWindow = q->internalWinId()->DrawableWindow(); + RDrawableWindow *const otherWindow = w->internalWinId()->DrawableWindow(); + thisWindow->SetOrdinalPosition(otherWindow->OrdinalPosition() + 1); + } + + if (!q->isWindow() || !w->internalWinId()) + invalidateBuffer(q->rect()); +} + +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->testAttribute(Qt::WA_WState_Created)) + continue; + if (!w->isWindow()) { + w->d_func()->invalidateBuffer(w->rect()); + WId parent = q->effectiveWinId(); + WId child = w->effectiveWinId(); + if (parent != child) { + // Child widget is native. Because Symbian windows cannot be + // re-parented, we must re-create the window. + const WId window = 0; + const bool initializeWindow = false; + const bool destroyOldWindow = true; + w->d_func()->create_sys(window, initializeWindow, destroyOldWindow); + } + // ### TODO: We probably also need to update the component array here + w->d_func()->reparentChildren(); + } else { + bool showIt = w->isVisible(); + QPoint old_pos = w->pos(); + w->setParent(q, w->windowFlags()); + w->move(old_pos); + if (showIt) + w->show(); + } + } + } +} + +void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) +{ + Q_Q(QWidget); + + if (parent && parent->windowType() == Qt::Desktop) { + symbianScreenNumber = qt_widget_private(parent)->symbianScreenNumber; + parent = 0; + } + + bool wasCreated = q->testAttribute(Qt::WA_WState_Created); + + if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) + q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + + if (q->testAttribute(Qt::WA_DropSiteRegistered)) + q->setAttribute(Qt::WA_DropSiteRegistered, false); + + QSymbianControl *old_winid = static_cast<QSymbianControl *>(wasCreated ? data.winid : 0); + if ((q->windowType() == Qt::Desktop)) + old_winid = 0; + + // old_winid may not have received a 'not visible' visibility + // changed event before being destroyed; make sure that it is + // removed from the backing store's list of visible windows. + if (old_winid) + S60->controlVisibilityChanged(old_winid, false); + + setWinId(0); + + // 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) { + old_winid->MakeVisible(false); + if (old_winid->IsFocused()) // Avoid unnecessary calls to FocusChanged() + old_winid->setFocusSafely(false); + old_winid->SetParent(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 (old_winid) { + CBase::Delete(old_winid); + } + + if (q->testAttribute(Qt::WA_AcceptDrops) + || (!q->isWindow() && q->parentWidget() && q->parentWidget()->testAttribute(Qt::WA_DropSiteRegistered))) + q->setAttribute(Qt::WA_DropSiteRegistered, true); + + invalidateBuffer(q->rect()); +} + +void QWidgetPrivate::setConstraints_sys() +{ + +} + + +void QWidgetPrivate::s60UpdateIsOpaque() +{ + Q_Q(QWidget); + + if (!q->testAttribute(Qt::WA_WState_Created)) + return; + + const bool writeAlpha = extraData()->nativePaintMode == QWExtra::BlitWriteAlpha; + if (!q->testAttribute(Qt::WA_TranslucentBackground) && !writeAlpha) + return; + const bool requireAlphaChannel = !isOpaque || writeAlpha; + + createTLExtra(); + + RWindow *const window = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow()); + +#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE + if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { + window->SetSurfaceTransparency(!isOpaque); + extra->topextra->nativeWindowTransparencyEnabled = !isOpaque; + return; + } +#endif + if (requireAlphaChannel) { + const TDisplayMode displayMode = static_cast<TDisplayMode>(window->SetRequiredDisplayMode(EColor16MA)); + if (window->SetTransparencyAlphaChannel() == KErrNone) { + window->SetBackgroundColor(TRgb(255, 255, 255, 0)); + extra->topextra->nativeWindowTransparencyEnabled = 1; + if (extra->topextra->backingStore.data() && ( + QApplicationPrivate::graphics_system_name == QLatin1String("openvg") + || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"))) { + // Semi-transparent EGL surfaces aren't supported. We need to + // recreate backing store to get translucent surface (raster surface). + extra->topextra->backingStore.create(q); + extra->topextra->backingStore.registerWidget(q); + // FixNativeOrientation() will not work without an EGL surface. + q->setAttribute(Qt::WA_SymbianNoSystemRotation, false); + } + } + } else if (extra->topextra->nativeWindowTransparencyEnabled) { + window->SetTransparentRegion(TRegionFix<1>()); + extra->topextra->nativeWindowTransparencyEnabled = 0; + } +} + +void QWidgetPrivate::setWindowIcon_sys(bool forceReset) +{ +#ifdef Q_WS_S60 + Q_Q(QWidget); + + if (!q->testAttribute(Qt::WA_WState_Created) || !q->isWindow() ) + return; + + QTLWExtra* topData = this->topData(); + if (topData->iconPixmap && !forceReset) + // already been set + return; + + TRect cPaneRect; + TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::EContextPane, cPaneRect ); + CAknContextPane* contextPane = S60->contextPane(); + if (found && contextPane) { // We have context pane with valid metrics + QIcon icon = q->windowIcon(); + if (!icon.isNull()) { + // Valid icon -> set it as an context pane picture + QSize size = icon.actualSize(QSize(cPaneRect.Size().iWidth, cPaneRect.Size().iHeight)); + QPixmap pm = icon.pixmap(size); + QBitmap mask = pm.mask(); + if (mask.isNull()) { + mask = QBitmap(pm.size()); + mask.fill(Qt::color1); + } + + CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap(); + CFbsBitmap* nMask = mask.toSymbianCFbsBitmap(); + contextPane->SetPicture(nBitmap,nMask); + } else { + // Icon set to null -> set context pane picture to default + QT_TRAP_THROWING(contextPane->SetPictureToDefaultL()); + } + } else { + // Context pane does not exist, try setting small icon to title pane + TRect titlePaneRect; + TBool found = AknLayoutUtils::LayoutMetricsRect( AknLayoutUtils::ETitlePane, titlePaneRect ); + CAknTitlePane* titlePane = S60->titlePane(); + if (found && titlePane) { // We have title pane with valid metrics + // The API to get title_pane graphics size is not public -> assume square space based + // on titlebar font height. CAknBitmap would be optimum, wihtout setting the size, since + // then title pane would automatically scale the bitmap. Unfortunately it is not public API + // Also this function is leaving, although it is not named as such. + const CFont * font; + QT_TRAP_THROWING(font = AknLayoutUtils::FontFromId(EAknLogicalFontTitleFont)); + TSize iconSize(font->HeightInPixels(), font->HeightInPixels()); + + QIcon icon = q->windowIcon(); + if (!icon.isNull()) { + // Valid icon -> set it as an title pane small picture + QSize size = icon.actualSize(QSize(iconSize.iWidth, iconSize.iHeight)); + QPixmap pm = icon.pixmap(size); + QBitmap mask = pm.mask(); + if (mask.isNull()) { + mask = QBitmap(pm.size()); + mask.fill(Qt::color1); + } + + CFbsBitmap* nBitmap = pm.toSymbianCFbsBitmap(); + CFbsBitmap* nMask = mask.toSymbianCFbsBitmap(); + titlePane->SetSmallPicture( nBitmap, nMask, ETrue ); + } else { + // Icon set to null -> set context pane picture to default + titlePane->SetSmallPicture( NULL, NULL, EFalse ); + } + } + } + +#else + Q_UNUSED(forceReset) +#endif +} + +void QWidgetPrivate::setWindowTitle_sys(const QString &caption) +{ +#ifdef Q_WS_S60 + Q_Q(QWidget); + if (q->isWindow()) { + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + CAknTitlePane* titlePane = S60->titlePane(); + if (titlePane) { + if (caption.isEmpty()) { + QT_TRAP_THROWING(titlePane->SetTextToDefaultL()); + } else { + QT_TRAP_THROWING(titlePane->SetTextL(qt_QString2TPtrC(caption))); + } + } + } +#else + Q_UNUSED(caption) +#endif +} + +void QWidgetPrivate::setWindowIconText_sys(const QString & /*iconText */) +{ + +} + +void QWidgetPrivate::scroll_sys(int dx, int dy) +{ + Q_Q(QWidget); + + scrollChildren(dx, dy); + if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) { + scrollRect(q->rect(), dx, dy); + } else { + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + RDrawableWindow *const window = q->internalWinId()->DrawableWindow(); + window->Scroll(TPoint(dx, dy)); + } +} + +void QWidgetPrivate::scroll_sys(int dx, int dy, const QRect &r) +{ + Q_Q(QWidget); + + if (!paintOnScreen() || !q->internalWinId() || !q->internalWinId()->OwnsWindow()) { + scrollRect(r, dx, dy); + } else { + Q_ASSERT(q->testAttribute(Qt::WA_WState_Created)); + RDrawableWindow *const window = q->internalWinId()->DrawableWindow(); + window->Scroll(TPoint(dx, dy), qt_QRect2TRect(r)); + } +} + +/*! + For this function to work in the emulator, you must add: + TRANSPARENCY + To a line in the wsini.ini file. +*/ +void QWidgetPrivate::setWindowOpacity_sys(qreal) +{ + // ### TODO: Implement uniform window transparency +} + +void QWidgetPrivate::updateFrameStrut() +{ + +} + +void QWidgetPrivate::updateSystemBackground() +{ + +} + +void QWidgetPrivate::registerDropSite(bool /* on */) +{ + +} + +void QWidgetPrivate::createTLSysExtra() +{ + extra->topextra->inExpose = 0; + extra->topextra->nativeWindowTransparencyEnabled = 0; +} + +void QWidgetPrivate::deleteTLSysExtra() +{ + extra->topextra->backingStore.destroy(); +} + +void QWidgetPrivate::createSysExtra() +{ + extra->activated = 0; + extra->nativePaintMode = QWExtra::Default; + extra->receiveNativePaintEvents = 0; +} + +void QWidgetPrivate::deleteSysExtra() +{ + // this should only be non-zero if destroy() has not run due to constructor fail + if (data.winid) { + data.winid->ControlEnv()->AppUi()->RemoveFromStack(data.winid); + delete data.winid; + data.winid = 0; + } +} + +QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() +{ + return new QS60WindowSurface(q_func()); +} + +void QWidgetPrivate::setMask_sys(const QRegion& /* region */) +{ + +} + +void QWidgetPrivate::registerTouchWindow() +{ +#ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER + Q_Q(QWidget); + if (q->testAttribute(Qt::WA_WState_Created) && q->windowType() != Qt::Desktop) { + RWindow *rwindow = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow()); + QSymbianControl *window = static_cast<QSymbianControl *>(q->effectiveWinId()); + //Enabling advanced pointer events for controls that already have active windows causes a panic. + if (!window->isControlActive()) + rwindow->EnableAdvancedPointers(); + } +#endif +} + +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 { + CWsScreenDevice *scr = S60->screenDevice(this); + switch(m) { + case PdmDpiX: + case PdmPhysicalDpiX: + if (d->extra && d->extra->customDpiX) { + val = d->extra->customDpiX; + } else { + const QWidgetPrivate *p = d; + while (p->parent) { + p = static_cast<const QWidget *>(p->parent)->d_func(); + if (p->extra && p->extra->customDpiX) { + val = p->extra->customDpiX; + break; + } + } + if (p == d || !(p->extra && p->extra->customDpiX)) + val = S60->defaultDpiX; + } + break; + case PdmDpiY: + case PdmPhysicalDpiY: + if (d->extra && d->extra->customDpiY) { + val = d->extra->customDpiY; + } else { + const QWidgetPrivate *p = d; + while (p->parent) { + p = static_cast<const QWidget *>(p->parent)->d_func(); + if (p->extra && p->extra->customDpiY) { + val = p->extra->customDpiY; + break; + } + } + if (p == d || !(p->extra && p->extra->customDpiY)) + val = S60->defaultDpiY; + } + break; + case PdmWidthMM: + { + TInt twips = scr->HorizontalPixelsToTwips(data->crect.width()); + val = (int)(twips * (25.4/KTwipsPerInch)); + break; + } + case PdmHeightMM: + { + TInt twips = scr->VerticalPixelsToTwips(data->crect.height()); + val = (int)(twips * (25.4/KTwipsPerInch)); + break; + } + case PdmNumColors: + val = TDisplayModeUtils::NumDisplayModeColors(scr->DisplayMode()); + break; + case PdmDepth: + val = TDisplayModeUtils::NumDisplayModeBitsPerPixel(scr->DisplayMode()); + break; + default: + val = 0; + qWarning("QWidget::metric: Invalid metric command"); + } + } + return val; +} + +QPaintEngine *QWidget::paintEngine() const +{ + return 0; +} + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + + QPoint p = pos + data->crect.topLeft(); + return (isWindow() || !parentWidget()) ? p : parentWidget()->mapToGlobal(p); + + } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel + QPoint tp = geometry().topLeft(); + return pos + tp; + } + + // Native window case + const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen(); + const QPoint globalPos = QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY) + pos; + return globalPos; +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + if (!testAttribute(Qt::WA_WState_Created) || !internalWinId()) { + QPoint p = (isWindow() || !parentWidget()) ? pos : parentWidget()->mapFromGlobal(pos); + return p - data->crect.topLeft(); + } else if ((d->data.window_flags & Qt::Window) && internalWinId()) { //toplevel + QPoint tp = geometry().topLeft(); + return pos - tp; + } + + // Native window case + const TPoint widgetScreenOffset = internalWinId()->PositionRelativeToScreen(); + const QPoint widgetPos = pos - QPoint(widgetScreenOffset.iX, widgetScreenOffset.iY); + return widgetPos; +} + +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 QWidget::setWindowState(Qt::WindowStates newstate) +{ + Q_D(QWidget); + + Qt::WindowStates oldstate = windowState(); + + const TBool isFullscreen = newstate & Qt::WindowFullScreen; +#ifdef Q_WS_S60 + const TBool cbaRequested = windowFlags() & Qt::WindowSoftkeysVisibleHint; + const TBool cbaVisible = CEikButtonGroupContainer::Current() ? true : false; + const TBool softkeyVisibilityChange = isFullscreen && (cbaRequested != cbaVisible); + + if (oldstate == newstate && !softkeyVisibilityChange) + return; +#endif // Q_WS_S60 + + if (isWindow()) { + createWinId(); + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + + const bool wasResized = testAttribute(Qt::WA_Resized); + const bool wasMoved = testAttribute(Qt::WA_Moved); + + QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId()); + if (window && newstate & Qt::WindowMinimized) { + window->setFocusSafely(false); + window->MakeVisible(false); + } else if (window && oldstate & Qt::WindowMinimized) { + window->setFocusSafely(true); + window->MakeVisible(true); + } + +#ifdef Q_WS_S60 + // The window decoration visibility has to be changed before doing actual window state + // change since in that order the availableGeometry will return directly the right size and + // we will avoid unnecessary redraws + bool decorationsVisible = S60->setRecursiveDecorationsVisibility(this, newstate); +#endif // Q_WS_S60 + + // Ensure the initial size is valid, since we store it as normalGeometry below. + if (!wasResized && !isVisible()) + adjustSize(); + + QTLWExtra *top = d->topData(); + QRect normalGeometry = (top->normalGeometry.width() < 0) ? geometry() : top->normalGeometry; + + const bool cbaVisibilityHint = windowFlags() & Qt::WindowSoftkeysVisibleHint; + if (newstate & Qt::WindowFullScreen && !cbaVisibilityHint) { + setAttribute(Qt::WA_OutsideWSRange, false); + if (d->symbianScreenNumber > 0) { + int w = S60->screenWidthInPixelsForScreen[d->symbianScreenNumber]; + int h = S60->screenHeightInPixelsForScreen[d->symbianScreenNumber]; + if (w <= 0 || h <= 0) + window->SetExtentToWholeScreen(); + else + window->SetExtent(TPoint(0, 0), TSize(w, h)); + } else { + window->SetExtentToWholeScreen(); + } + } else if (newstate & Qt::WindowMaximized || ((newstate & Qt::WindowFullScreen) && cbaVisibilityHint)) { + setAttribute(Qt::WA_OutsideWSRange, false); + TRect maxExtent = qt_QRect2TRect(qApp->desktop()->availableGeometry(this)); + window->SetExtent(maxExtent.iTl, maxExtent.Size()); + } else { +#ifdef Q_WS_S60 + // With delayed creation of S60 app panes, the normalGeometry calculated above is not + // accurate because it did not consider the status pane. This means that when returning + // normal mode after showing the status pane, the geometry would overlap so we should + // move it if it never had an explicit position. + if (!wasMoved && S60->statusPane() && decorationsVisible) { + TPoint tl = static_cast<CEikAppUi*>(S60->appUi())->ClientRect().iTl; + normalGeometry.setTopLeft(QPoint(tl.iX, tl.iY)); + } +#endif + setGeometry(normalGeometry); + } + + //restore normal geometry + top->normalGeometry = normalGeometry; + + // FixMe QTBUG-8977 + // In some platforms, WA_Resized and WA_Moved are also not set when application window state is + // anything else than normal. In Symbian we can restore them only for normal window state since + // restoring for other modes, will make fluidlauncher to be launched in wrong size (200x100) + if (effectiveState(newstate) == Qt::WindowNoState) { + setAttribute(Qt::WA_Resized, wasResized); + setAttribute(Qt::WA_Moved, wasMoved); + } + } + + data->window_state = newstate; + + if (newstate & Qt::WindowActive) + activateWindow(); + + if (isWindow()) { + // Now that the new state is set, fix the display memory layout, if needed. + QSymbianControl *window = static_cast<QSymbianControl *>(effectiveWinId()); + window->ensureFixNativeOrientation(); + } + + QWindowStateChangeEvent e(oldstate); + QApplication::sendEvent(this, &e); +} + + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(geometry()); + d->deactivateWidgetCleanup(); + QSymbianControl *id = static_cast<QSymbianControl *>(internalWinId()); + if (testAttribute(Qt::WA_WState_Created)) { + +#ifndef QT_NO_IM + if (d->ic) { + delete d->ic; + } else { + QInputContext *ic = QApplicationPrivate::inputContext; + if (ic) { + ic->widgetDestroyed(this); + } + } +#endif + + if (QWidgetPrivate::mouseGrabber == this) + releaseMouse(); + if (QWidgetPrivate::keyboardGrabber == this) + releaseKeyboard(); + 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 (destroyWindow && !(windowType() == Qt::Desktop) && id) { + if (id->IsFocused()) // Avoid unnecessry calls to FocusChanged() + id->setFocusSafely(false); + id->ControlEnv()->AppUi()->RemoveFromStack(id); + } + } + + QT_TRY { + d->setWinId(0); + } QT_CATCH (const std::bad_alloc &) { + // swallow - destructors must not throw + } + + if (destroyWindow) { + delete id; + // At this point the backing store should already be destroyed + // so we flush the command buffer to ensure that the freeing of + // those resources and deleting the window can happen "atomically" + if (qApp) + S60->wsSession().Flush(); + } +} + +QWidget *QWidget::mouseGrabber() +{ + return QWidgetPrivate::mouseGrabber; +} + +QWidget *QWidget::keyboardGrabber() +{ + return QWidgetPrivate::keyboardGrabber; +} + +void QWidget::grabKeyboard() +{ + if (!qt_nograb()) { + if (QWidgetPrivate::keyboardGrabber && QWidgetPrivate::keyboardGrabber != this) + QWidgetPrivate::keyboardGrabber->releaseKeyboard(); + + // ### TODO: Native keyboard grab + + QWidgetPrivate::keyboardGrabber = this; + } +} + +void QWidget::releaseKeyboard() +{ + if (!qt_nograb() && QWidgetPrivate::keyboardGrabber == this) { + // ### TODO: Native keyboard release + QWidgetPrivate::keyboardGrabber = 0; + } +} + +void QWidget::grabMouse() +{ + if (isVisible() && !qt_nograb()) { + if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) + QWidgetPrivate::mouseGrabber->releaseMouse(); + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + WId id = effectiveWinId(); + id->SetPointerCapture(true); + QWidgetPrivate::mouseGrabber = this; + +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(cursor()); +#endif + } +} + +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ + if (isVisible() && !qt_nograb()) { + if (QWidgetPrivate::mouseGrabber && QWidgetPrivate::mouseGrabber != this) + QWidgetPrivate::mouseGrabber->releaseMouse(); + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + WId id = effectiveWinId(); + id->SetPointerCapture(true); + QWidgetPrivate::mouseGrabber = this; + + QApplication::setOverrideCursor(cursor); + } +} +#endif + +void QWidget::releaseMouse() +{ + if (!qt_nograb() && QWidgetPrivate::mouseGrabber == this) { + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + if(!window()->isModal()) { + WId id = effectiveWinId(); + id->SetPointerCapture(false); + } + QWidgetPrivate::mouseGrabber = 0; +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + } +} + +void QWidget::activateWindow() +{ + Q_D(QWidget); + + QWidget *tlw = window(); + if (tlw->isVisible()) { + window()->createWinId(); + QSymbianControl *id = static_cast<QSymbianControl *>(tlw->internalWinId()); + if (!id->IsFocused()) + id->setFocusSafely(true); + } +} + +#ifndef QT_NO_CURSOR + +void QWidgetPrivate::setCursor_sys(const QCursor &cursor) +{ + Q_UNUSED(cursor); + Q_Q(QWidget); + qt_symbian_set_cursor(q, false); +} + +void QWidgetPrivate::unsetCursor_sys() +{ + Q_Q(QWidget); + qt_symbian_set_cursor(q, false); +} +#endif + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qapplication_win.cpp b/src/gui/platforms/win/qapplication_win.cpp new file mode 100644 index 0000000000..72a05afbd0 --- /dev/null +++ b/src/gui/platforms/win/qapplication_win.cpp @@ -0,0 +1,4243 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef Q_WS_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_WS_WINCE_WM +#include <windowsm.h> +#include <tpcshell.h> +#ifdef QT_WINCE_GESTURES +#ifndef QT_NO_GESTURES +#include <gesture.h> +#endif +#endif +#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 "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" +#include "qscrollbar.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> +#include <private/qsystemlibrary_p.h> +#include "qevent_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 + +#if !defined(WINABLEAPI) +# if defined(Q_WS_WINCE) +# include <bldver.h> +# endif +# if !defined(Q_WS_WINCE) +# include <winable.h> +# endif +#endif + +#ifndef QT_NO_GESTURES +# ifndef GID_ZOOM +# define GID_ZOOM 3 +# define GID_TWOFINGERTAP 6 +# define GID_PRESSANDTAP 7 +# define GID_ROLLOVER GID_PRESSANDTAP +# endif +#endif + +#ifndef WM_TOUCH +# define WM_TOUCH 0x0240 +#endif + +#ifndef TOUCHEVENTF_MOVE +# define TOUCHEVENTF_MOVE 0x0001 +# define TOUCHEVENTF_DOWN 0x0002 +# define TOUCHEVENTF_UP 0x0004 +# define TOUCHEVENTF_INRANGE 0x0008 +# define TOUCHEVENTF_PRIMARY 0x0010 +# define TOUCHEVENTF_NOCOALESCE 0x0020 +# define TOUCHEVENTF_PEN 0x0040 +# define TOUCHEVENTF_PALM 0x0080 + +# define TOUCHINPUTMASKF_TIMEFROMSYSTEM 0x0001 +# define TOUCHINPUTMASKF_EXTRAINFO 0x0002 +# define TOUCHINPUTMASKF_CONTACTAREA 0x0004 + +typedef struct tagTOUCHINPUT +{ + LONG x; + LONG y; + HANDLE hSource; + DWORD dwID; + DWORD dwFlags; + DWORD dwMask; + DWORD dwTime; + ULONG_PTR dwExtraInfo; + DWORD cxContact; + DWORD cyContact; +} TOUCHINPUT, *PTOUCHINPUT; + +#endif + +#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 + +#ifndef IMR_RECONVERTSTRING +#define IMR_RECONVERTSTRING 4 +#endif + +#ifndef IMR_CONFIRMRECONVERTSTRING +#define IMR_CONFIRMRECONVERTSTRING 0x0005 +#endif +QT_BEGIN_NAMESPACE + +#ifdef Q_WS_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 + +#ifndef QT_NO_GESTURES +typedef DWORD (API *AygRecognizeGesture)(SHRGINFO*); +static AygRecognizeGesture ptrRecognizeGesture = 0; +static bool aygResolved = false; +static void resolveAygLibs() +{ + if (!aygResolved) { + aygResolved = true; + QSystemLibrary ayglib(QLatin1String("aygshell")); + ptrRecognizeGesture = (AygRecognizeGesture) ayglib.resolve("SHRecognizeGesture"); + } +} +#endif // QT_NO_GESTURES + +#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 + +Q_GUI_EXPORT qreal qt_fontsmoothing_gamma; +Q_GUI_EXPORT 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; + +#ifndef QT_NO_TABLETEVENT +static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab); +static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor); +static void initWinTabFunctions(); // resolve the WINTAB api functions +#endif // QT_NO_TABLETEVENT + + +#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<quint64, 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); + +// 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 +#endif +#ifndef GET_KEYSTATE_WPARAM +#define GET_KEYSTATE_WPARAM(wParam) (LOWORD(wParam)) +#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) +#define XBUTTON1 0x0001 +#define XBUTTON2 0x0002 +#endif +#ifndef MK_XBUTTON1 +#define MK_XBUTTON1 0x0020 +#define MK_XBUTTON2 0x0040 +#endif + +// support for multi-media-keys +#ifndef WM_APPCOMMAND +#define WM_APPCOMMAND 0x0319 +#endif + +#ifndef FAPPCOMMAND_MOUSE +#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 +#endif // FAPPCOMMAND_MOUSE + +// 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 + +#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 + *****************************************************************************/ + +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 + +extern "C" LRESULT QT_WIN_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); +#ifndef QT_NO_GESTURES + bool translateGestureEvent(const MSG &msg, const GESTUREINFO &gi); +#endif + 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_WS_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 (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_WS_WINCE + NONCLIENTMETRICS ncm; + ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0); + + QFont menuFont = qt_LOGFONTtoQFont(ncm.lfMenuFont, true); + QFont messageFont = qt_LOGFONTtoQFont(ncm.lfMessageFont, true); + QFont statusFont = qt_LOGFONTtoQFont(ncm.lfStatusFont, true); + QFont titleFont = qt_LOGFONTtoQFont(ncm.lfCaptionFont, true); + + LOGFONT lfIconTitleFont; + SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0); + QFont iconTitleFont = qt_LOGFONTtoQFont(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"); + smallerFont.setBold(true); + QApplication::setFont(smallerFont, "QAbstractButton"); + } +#endif// Q_WS_WINCE +} + +static void qt_win_read_cleartype_settings() +{ + UINT result = 0; +#ifdef Q_OS_WINCE + if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &result, 0)) + qt_cleartype_enabled = result; +#else + if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) + qt_cleartype_enabled = (result == FE_FONTSMOOTHINGCLEARTYPE); +#endif + + int winSmooth; + if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) { + qt_fontsmoothing_gamma = winSmooth / qreal(1000.0); + } else { + qt_fontsmoothing_gamma = 1.0; + } + + // Safeguard ourselves against corrupt registry values... + if (qt_fontsmoothing_gamma > 5 || qt_fontsmoothing_gamma < 1) + qt_fontsmoothing_gamma = qreal(1.4); +} + +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 = false; + if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP + && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) + SystemParametersInfo(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)); + 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"); +} + +static void qt_set_windows_updateScrollBar(QWidget *widget) +{ + QList<QObject*> children = widget->children(); + for (int i = 0; i < children.size(); ++i) { + QObject *o = children.at(i); + if(!o->isWidgetType()) + continue; + if (QWidget *w = static_cast<QWidget *>(o)) + qt_set_windows_updateScrollBar(w); + } +#ifndef QT_NO_SCROLLBAR + if (qobject_cast<QScrollBar*>(widget)) + widget->updateGeometry(); +#endif +} + + +/***************************************************************************** + qt_init() - initializes Qt for Windows + *****************************************************************************/ + +typedef BOOL (WINAPI *PtrSetProcessDPIAware) (VOID); +static PtrSetProcessDPIAware ptrSetProcessDPIAware = 0; +PtrUpdateLayeredWindow ptrUpdateLayeredWindow = 0; +PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect = 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 + argv[j++] = argv[i]; + } + if(j < priv->argc) { + priv->argv[j] = 0; + priv->argc = j; + } + +#ifndef Q_WS_WINCE + // No message boxes but important ones + SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); +#endif + +#ifndef Q_WS_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_WS_WINCE) + GdiSetBatchLimit(1); +#endif + + // initialize key mapper + QKeyMapper::changeKeyboard(); + + QColormap::initialize(); + QFont::initialize(); +#ifndef QT_NO_CURSOR + QCursorData::initialize(); +#endif + qApp->setObjectName(priv->appName()); + + // default font +#ifndef Q_WS_WINCE + HGDIOBJ stockFont = GetStockObject(DEFAULT_GUI_FONT); +#else + HGDIOBJ stockFont = GetStockObject(SYSTEM_FONT); +#endif + + LOGFONT lf; + GetObject(stockFont, sizeof(lf), &lf); + QFont systemFont = qt_LOGFONTtoQFont(lf, true); + +#ifndef Q_WS_WINCE + if (systemFont.family() == QLatin1String("MS Shell Dlg")) { + systemFont.setFamily(QLatin1String("MS Shell Dlg 2")); + } +#endif + + QApplicationPrivate::setSystemFont(systemFont); + + // QFont::locale_init(); ### Uncomment when it does something on Windows + + if (QApplication::desktopSettingsAware()) + qt_set_windows_resources(); + +#ifndef QT_NO_TABLETEVENT + initWinTabFunctions(); +#endif // QT_NO_TABLETEVENT + 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) QSystemLibrary::resolve(QLatin1String("user32"), + "UpdateLayeredWindowIndirect"); + ptrUpdateLayeredWindow = + (PtrUpdateLayeredWindow) QSystemLibrary::resolve(QLatin1String("user32"), + "UpdateLayeredWindow"); + + if (ptrUpdateLayeredWindow && !ptrUpdateLayeredWindowIndirect) + ptrUpdateLayeredWindowIndirect = qt_updateLayeredWindowIndirect; + + // Notify Vista and Windows 7 that we support highter DPI settings + ptrSetProcessDPIAware = (PtrSetProcessDPIAware) + QSystemLibrary::resolve(QLatin1String("user32"), "SetProcessDPIAware"); + if (ptrSetProcessDPIAware) + ptrSetProcessDPIAware(); +#endif + +#ifndef QT_NO_GESTURES + priv->GetGestureInfo = 0; + priv->GetGestureExtraArgs = 0; + priv->CloseGestureInfoHandle = 0; + priv->SetGestureConfig = 0; + priv->GetGestureConfig = 0; + priv->BeginPanningFeedback = 0; + priv->UpdatePanningFeedback = 0; + priv->EndPanningFeedback = 0; + +#if defined(Q_WS_WINCE_WM) && defined(QT_WINCE_GESTURES) + priv->GetGestureInfo = (PtrGetGestureInfo) &TKGetGestureInfo; + priv->GetGestureExtraArgs = (PtrGetGestureExtraArgs) &TKGetGestureExtraArguments; +#elif !defined(Q_WS_WINCE) + #if !defined(QT_NO_NATIVE_GESTURES) + priv->GetGestureInfo = + (PtrGetGestureInfo)QSystemLibrary::resolve(QLatin1String("user32"), + "GetGestureInfo"); + priv->GetGestureExtraArgs = + (PtrGetGestureExtraArgs)QSystemLibrary::resolve(QLatin1String("user32"), + "GetGestureExtraArgs"); + priv->CloseGestureInfoHandle = + (PtrCloseGestureInfoHandle)QSystemLibrary::resolve(QLatin1String("user32"), + "CloseGestureInfoHandle"); + priv->SetGestureConfig = + (PtrSetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"), + "SetGestureConfig"); + priv->GetGestureConfig = + (PtrGetGestureConfig)QSystemLibrary::resolve(QLatin1String("user32"), + "GetGestureConfig"); + #endif // QT_NO_NATIVE_GESTURES + QSystemLibrary libTheme(QLatin1String("uxtheme")); + priv->BeginPanningFeedback = + (PtrBeginPanningFeedback)libTheme.resolve("BeginPanningFeedback"); + priv->UpdatePanningFeedback = + (PtrUpdatePanningFeedback)libTheme.resolve("UpdatePanningFeedback"); + priv->EndPanningFeedback = + (PtrEndPanningFeedback)libTheme.resolve("EndPanningFeedback"); +#endif +#endif // QT_NO_GESTURES +} + +/***************************************************************************** + 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_WS_WINCE + // Deinitialize OLE/COM + OleUninitialize(); +#endif +} + + +/***************************************************************************** + Platform specific global and internal functions + *****************************************************************************/ + +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) + +// +// If 0 is passed as the widget pointer, register a window class +// for QWidget as default. This is used in QGLTemporaryContext +// during GL initialization, where we don't want to use temporary +// QWidgets or QGLWidgets, neither do we want to have separate code +// to register window classes. +// +const QString qt_reg_winclass(QWidget *w) // register window class +{ + Qt::WindowFlags flags = w ? w->windowFlags() : (Qt::WindowFlags)0; + Qt::WindowFlags type = flags & Qt::WindowType_Mask; + + uint style; + bool icon; + QString cname; + if (w && qt_widget_private(w)->isGLWidget) { + cname = QLatin1String("QGLWidget"); + style = CS_DBLCLKS; +#ifndef Q_WS_WINCE + style |= CS_OWNDC; +#endif + icon = true; + } else if (w && (flags & Qt::MSWindowsOwnDC)) { + cname = QLatin1String("QWidgetOwnDC"); + style = CS_DBLCLKS; +#ifndef Q_WS_WINCE + style |= CS_OWNDC; +#endif + icon = true; + } else if (w && (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 |= CS_DROPSHADOW; + } + cname = QLatin1String("QToolTip"); + } else { + cname = QLatin1String("QTool"); + } +#ifndef Q_WS_WINCE + style |= CS_SAVEBITS; +#endif + icon = false; + } else if (w && (type == Qt::Popup)) { + cname = QLatin1String("QPopup"); + style = CS_DBLCLKS; +#ifndef Q_WS_WINCE + style |= CS_SAVEBITS; +#endif + if ((QSysInfo::WindowsVersion >= QSysInfo::WV_XP + && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)) + style |= CS_DROPSHADOW; + icon = false; + } else { + cname = QLatin1String("QWidget"); + style = CS_DBLCLKS; + icon = true; + } + +#ifndef Q_WS_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[MAX_PATH]; + GetModuleFileName(0, uniqueAppID, MAX_PATH); + cname = QString::number(RegisterWindowMessage( + (const wchar_t *) QString::fromWCharArray(uniqueAppID).toLower().replace(QLatin1Char('\\'), + QLatin1Char('_')).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) { + WNDCLASS wcinfo; + classExists = GetClassInfo((HINSTANCE)qWinAppInst(), (wchar_t*)cname.utf16(), &wcinfo); + classExists = classExists && wcinfo.lpfnWndProc != QtWndProc; + } + + if (classExists) + cname += QString::number((quintptr)QtWndProc); + + if (winclassNames()->contains(cname)) // already registered in our list + return cname; + +#ifndef Q_WS_WINCE + WNDCLASSEX wc; + wc.cbSize = sizeof(WNDCLASSEX); +#else + WNDCLASS wc; +#endif + wc.style = style; + wc.lpfnWndProc = (WNDPROC)QtWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = qWinAppInst(); + if (icon) { + wc.hIcon = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE); +#ifndef Q_WS_WINCE + if (wc.hIcon) { + int sw = GetSystemMetrics(SM_CXSMICON); + int sh = GetSystemMetrics(SM_CYSMICON); + wc.hIconSm = (HICON)LoadImage(qWinAppInst(), L"IDI_ICON1", IMAGE_ICON, sw, sh, 0); + } else { + wc.hIcon = (HICON)LoadImage(0, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + wc.hIconSm = 0; + } +#endif + } else { + wc.hIcon = 0; +#ifndef Q_WS_WINCE + wc.hIconSm = 0; +#endif + } + wc.hCursor = 0; +#ifndef Q_WS_WINCE + HBRUSH brush = 0; + if (w && !qt_widget_private(w)->isGLWidget) + brush = (HBRUSH)GetSysColorBrush(COLOR_WINDOW); + wc.hbrBackground = brush; +#else + wc.hbrBackground = 0; +#endif + wc.lpszMenuName = 0; + wc.lpszClassName = (wchar_t*)cname.utf16(); + +#ifndef Q_WS_WINCE + ATOM atom = RegisterClassEx(&wc); +#else + ATOM atom = RegisterClass(&wc); +#endif + +#ifndef QT_NO_DEBUG + if (!atom) + qErrnoWarning("QApplication::regClass: Registering window class failed."); +#else + Q_UNUSED(atom); +#endif + + winclassNames()->insert(cname, 1); + return cname; +} + +Q_GUI_EXPORT const QString qt_getRegisteredWndClass() +{ + return qt_reg_winclass(0); +} + +static void unregWinClasses() +{ + WinClassNameHash *hash = winclassNames(); + QHash<QString, int>::ConstIterator it = hash->constBegin(); + while (it != hash->constEnd()) { + UnregisterClass((wchar_t*)it.key().utf16(), 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 +} + +static 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) +{ +#ifdef Q_OS_WINCE + Q_UNUSED(widget); + Q_UNUSED(duration); +#else + bool stopFlash = duration < 0; + + if (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; + + FlashWindowEx(&info); + } +#endif +} + +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(); + +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_WS_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 + && message != WM_MOUSEHWHEEL) + +#ifndef Q_WS_WINCE + || (message >= WM_NCMOUSEMOVE && message <= WM_NCMBUTTONDBLCLK) +#endif + ; +} + +extern "C" LRESULT QT_WIN_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_WS_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_WS_WINCE + || message == WM_NCMBUTTONDOWN || message == WM_NCLBUTTONDOWN + || message == WM_NCRBUTTONDOWN)) { +#else + )) { +#endif + ::SendMessage(imeParentWnd, WM_IME_ENDCOMPOSITION, 0, 0); + } + + switch (message) { +#ifndef Q_WS_WINCE +#ifndef QT_NO_SESSIONMANAGER + 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 = QApplicationPrivate::instance(); + 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 + QApplication::quit(); + } + + RETURN(0); + } +#endif + case WM_DISPLAYCHANGE: + if (QApplication::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_WS_WINCE + // CE SIP hide/show + if (qt_desktopWidget && 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 (QApplication::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, QApplication::topLevelWidgets()) { + if (!w->isVisible()) + continue; + ((QETWidget *) w)->forceUpdate(); + } + } + + break; + case WM_SYSCOLORCHANGE: + if (QApplication::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: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_XBUTTONDBLCLK: + 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 (QApplication::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_WS_WINCE) && !defined(QT_NO_CONTEXTMENU) + if (message == WM_LBUTTONDOWN && widget != QApplication::activePopupWidget()) { + QWidget* alienWidget = widget; + if ((alienWidget != QApplication::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); + if (alienWidget) + pos = alienWidget->mapFromGlobal(globalPos); + } + if (alienWidget) { + 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(); +#ifndef QT_NO_GESTURES + if (ptrRecognizeGesture && (ptrRecognizeGesture(&shrg) == GN_CONTEXTMENU)) { + if (QApplication::activePopupWidget()) + QApplication::activePopupWidget()->close(); + QContextMenuEvent e(QContextMenuEvent::Mouse, pos, globalPos); + result = qt_sendSpontaneousEvent(alienWidget, &e); + } +#endif // QT_NO_GESTURES + } + } + } +#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 PeekMessage + // 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 (PeekMessage(&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 { + switch (message) { + case WM_TOUCH: + result = QApplicationPrivate::instance()->translateTouchEvent(msg); + break; + case WM_KEYDOWN: // keyboard event + case WM_SYSKEYDOWN: + qt_keymapper_private()->updateKeyMap(msg); + // fall-through intended + case WM_KEYUP: + case WM_SYSKEYUP: +#if Q_OS_WINCE_WM + case WM_HOTKEY: + if(HIWORD(msg.lParam) == VK_TBACK) { + const bool hotKeyDown = !(LOWORD(msg.lParam) & MOD_KEYUP); + msg.lParam = 0x69 << 16; + msg.wParam = VK_BACK; + if (hotKeyDown) { + msg.message = WM_KEYDOWN; + qt_keymapper_private()->updateKeyMap(msg); + } else { + msg.message = WM_KEYUP; + } + } + // fall-through intended +#endif + case WM_IME_CHAR: + case WM_IME_KEYDOWN: + case WM_CHAR: { + MSG msg1; + bool anyMsg = PeekMessage(&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 (QApplication::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: + case WM_MOUSEHWHEEL: + 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_TREBLE_DOWN: + key = Qt::Key_TrebleDown; + break; + case APPCOMMAND_TREBLE_UP: + key = Qt::Key_TrebleUp; + break; + case APPCOMMAND_HELP: + key = Qt::Key_Help; + break; + case APPCOMMAND_FIND: + key = Qt::Key_Search; + break; + default: + break; + } + if (key) { + bool res = false; + QWidget *g = QWidget::keyboardGrabber(); + if (g) + widget = (QETWidget*)g; + else if (QApplication::focusWidget()) + widget = (QETWidget*)QApplication::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_WS_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->minimumHeight() == widget->maximumHeight()) { + if (pos.y() < -(fs.top() - fs.left())) + return HTCAPTION; + if (pos.y() >= widget->height()) + return HTBORDER; + } + if (widget->minimumWidth() == widget->maximumWidth() && (pos.x() < 0 || pos.x() >= widget->width())) + return HTBORDER; + } + } + + result = false; + break; +#endif + + case WM_SYSCOMMAND: { +#ifndef Q_WS_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 + DefWindowProc(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); + const QString title = widget->windowIconText(); + if (!title.isEmpty()) + widget->setWindowTitle_helper(title); + } + 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); + const QString title = widget->windowTitle(); + if (!title.isEmpty()) + widget->setWindowTitle_helper(title); + } + result = false; + break; + default: + result = false; + break; + } + + if (window_state_change) { + QWindowStateChangeEvent e(oldstate); + qt_sendSpontaneousEvent(widget, &e); + } +#endif // #ifndef Q_OS_WINCE + + break; + } + + case WM_SETTINGCHANGE: + if ( QApplication::type() == QApplication::Tty ) + break; + + if (!msg.wParam) { +#ifdef Q_WS_WINCE + // On Windows CE, lParam parameter is a constant, not a char pointer. + if (msg.lParam == INI_INTL) { +#else + QString area = QString::fromWCharArray((wchar_t*)msg.lParam); + if (area == QLatin1String("intl")) { +#endif + QLocalePrivate::updateSystemPrivate(); + if (!widget->testAttribute(Qt::WA_SetLocale)) + widget->dptr()->setLocale_helper(QLocale(), true); + QEvent e(QEvent::LocaleChange); + QApplication::sendEvent(qApp, &e); + } + } + else if (msg.wParam == SPI_SETICONTITLELOGFONT) { + if (QApplication::desktopSettingsAware()) { + widget = (QETWidget*)QWidget::find(hwnd); + if (widget && !widget->parentWidget()) { + qt_set_windows_font_resources(); + } + } + } + else if (msg.wParam == SPI_SETNONCLIENTMETRICS) { + widget = (QETWidget*)QWidget::find(hwnd); + if (widget && !widget->parentWidget()) { + qt_set_windows_updateScrollBar(widget); + QEvent e(QEvent::LayoutRequest); + QApplication::sendEvent(widget, &e); + } + } + + break; + + case WM_PAINT: // paint event + case WM_ERASEBKGND: // erase window background + result = widget->translatePaintEvent(msg); + break; + +#ifndef Q_WS_WINCE + case WM_ENTERSIZEMOVE: + autoCaptureWnd = hwnd; + break; + case WM_EXITSIZEMOVE: + autoCaptureWnd = 0; + 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 ( QApplication::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. +#ifdef Q_WS_WINCE + { +#ifdef Q_WS_WINCE_WM + // On Windows mobile we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages. + // Thus we have to unset the minimized state explicitly. We must do this for all + // top-level widgets, because we get the HWND of a random widget here. + foreach (QWidget* tlw, QApplication::topLevelWidgets()) { + if (tlw->isMinimized()) + tlw->setWindowState(tlw->windowState() & ~Qt::WindowMinimized); + } +#else + // On Windows CE we do not receive WM_SYSCOMMAND / SC_MINIMIZE messages. + // Thus we have to unset the minimized state explicitly. + if (widget->windowState() & Qt::WindowMinimized) + widget->setWindowState(widget->windowState() & ~Qt::WindowMinimized); +#endif // Q_WS_WINCE_WM + +#else + if (!(widget->windowState() & Qt::WindowMinimized)) { +#endif + // Ignore the activate message send by WindowsXP to a minimized window +#ifdef Q_WS_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_WS_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_WS_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; + +#ifndef QT_NO_CONTEXTMENU + 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 (QApplication::activePopupWidget()) + fw = (QApplication::activePopupWidget()->focusWidget() + ? QApplication::activePopupWidget()->focusWidget() + : QApplication::activePopupWidget()); + else if (QApplication::focusWidget()) + fw = QApplication::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 +#endif + + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: { + QWidget *fw = QApplication::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; + } + case WM_IME_REQUEST: { + QWidget *fw = QApplication::focusWidget(); + QWinInputContext *im = fw ? qobject_cast<QWinInputContext *>(fw->inputContext()) : 0; + if (fw && im) { + if(wParam == IMR_RECONVERTSTRING) { + int ret = im->reconvertString((RECONVERTSTRING *)lParam); + if (ret == -1) { + result = false; + } else { + return ret; + } + } else if (wParam == IMR_CONFIRMRECONVERTSTRING) { + RETURN(TRUE); + } else { + // in all other cases, call DefWindowProc() + result = false; + } + } + break; + } +#ifndef Q_WS_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 (QApplication::startingUp() || QApplication::closingDown() || lParam != (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)QSystemLibrary::resolve(QLatin1String("oleacc"), "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); + memcpy((void *)lParam, text.utf16(), (text.size() + 1) * sizeof(ushort)); + 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: + + #ifndef QT_NO_TABLETEVENT + if (ptrWTPacketsGet && ptrWTInfo) { + const bool enteredProximity = LOWORD(lParam) != 0; + PACKET proximityBuffer[1]; // we are only interested in the first packet in this case + const int totalPacks = ptrWTPacketsGet(qt_tablet_context, 1, proximityBuffer); + if (totalPacks > 0) { + const UINT currentCursor = proximityBuffer[0].pkCursor; + + UINT csr_physid; + ptrWTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &csr_physid); + UINT csr_type; + ptrWTInfo(WTI_CURSORS + currentCursor, CSR_TYPE, &csr_type); + const UINT deviceIdMask = 0xFF6; // device type mask && device color mask + quint64 uniqueId = (csr_type & deviceIdMask); + uniqueId = (uniqueId << 32) | csr_physid; + + // initialising and updating the cursor should be done in response to + // WT_CSRCHANGE. We do it in WT_PROXIMITY because some wintab never send + // the event WT_CSRCHANGE even if asked with CXO_CSRMESSAGES + const QTabletCursorInfo *const globalCursorInfo = tCursorInfo(); + if (!globalCursorInfo->contains(uniqueId)) + tabletInit(uniqueId, csr_type, qt_tablet_context); + + currentTabletPointer = globalCursorInfo->value(uniqueId); + tabletUpdateCursor(currentTabletPointer, currentCursor); + } + qt_tabletChokeMouse = false; + + 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_WS_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*)QApplication::focusWidget(); + HWND focus = ::GetFocus(); + //if there is a current widget and the new widget belongs to the same toplevel window + //or if the current widget was embedded into non-qt window (i.e. we won't get WM_ACTIVATEAPP) + //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 + const bool embedded = widget && ((QETWidget*)widget->window())->topData()->embedded; + if (widget && (embedded || ::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 || QApplication::closingDown() + || QApplication::type() == QApplication::Tty) + break; + + if (widget->testAttribute(Qt::WA_WState_Polished)) + QApplication::style()->unpolish(widget); + + if (widget->testAttribute(Qt::WA_WState_Polished)) + QApplication::style()->polish(widget); + widget->repolishStyle(*QApplication::style()); + if (widget->isVisible()) + widget->update(); + break; + +#ifndef Q_WS_WINCE + case WM_INPUTLANGCHANGE: { + wchar_t info[7]; + if (!GetLocaleInfo(MAKELCID(lParam, SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, info, 6)) { + inputcharset = CP_ACP; + } else { + inputcharset = QString::fromWCharArray(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) + widget->showMinimized(); + else +#ifndef QT_NO_MENUBAR + QMenuBar::wceCommands(LOWORD(wParam)); +#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; +#ifndef QT_NO_GESTURES +#if !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES) + case WM_GESTURE: { + GESTUREINFO gi; + memset(&gi, 0, sizeof(GESTUREINFO)); + gi.cbSize = sizeof(GESTUREINFO); + + QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); + BOOL bResult = false; + if (qAppPriv->GetGestureInfo) + bResult = qAppPriv->GetGestureInfo((HANDLE)msg.lParam, &gi); + if (bResult) { + if (gi.dwID == GID_BEGIN) { + // find the alien widget for the gesture position. + // This might not be accurate as the position is the center + // point of two fingers for multi-finger gestures. + QPoint pt(gi.ptsLocation.x, gi.ptsLocation.y); + QWidget *w = widget->childAt(widget->mapFromGlobal(pt)); + qAppPriv->gestureWidget = w ? w : widget; + } + if (qAppPriv->gestureWidget) + static_cast<QETWidget*>(qAppPriv->gestureWidget)->translateGestureEvent(msg, gi); + if (qAppPriv->CloseGestureInfoHandle) + qAppPriv->CloseGestureInfoHandle((HANDLE)msg.lParam); + if (gi.dwID == GID_END) + qAppPriv->gestureWidget = 0; + } else { + DWORD dwErr = GetLastError(); + if (dwErr > 0) + qWarning() << "translateGestureEvent: error = " << dwErr; + } + result = true; + break; + } +#endif // !defined(Q_WS_WINCE) || defined(QT_WINCE_GESTURES) +#endif // QT_NO_GESTURES +#ifndef QT_NO_CURSOR + case WM_SETCURSOR: { + QCursor *ovr = QApplication::overrideCursor(); + if (ovr) { + SetCursor(ovr->handle()); + RETURN(TRUE); + } + result = false; + break; + } +#endif + 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_WS_WINCE + if (type != WM_NCHITTEST) { +#endif + if ((type >= WM_MOUSEFIRST && type <= WM_MOUSELAST) || + type == WM_MOUSEWHEEL || type == WM_MOUSEHWHEEL || + type == WM_MOUSELEAVE || + (type >= WM_KEYFIRST && type <= WM_KEYLAST) +#ifndef Q_WS_WINCE + || type == WM_NCMOUSEMOVE +#endif + ) { + if (type == WM_MOUSEMOVE +#ifndef Q_WS_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_WS_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 = QApplication::focusWidget()) { + QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason); + QApplication::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() + : QApplication::focusWidget(); + if (fw) { + 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 = 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_WS_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} }; + while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) + ; + if (msg.message == WM_MOUSEMOVE) + PostMessage(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 +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 (PeekMessage(&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 (PeekMessage(&keyMsg, 0, WM_KEYFIRST, WM_KEYLAST, + PM_NOREMOVE)) { + if (keyMsg.time < mouseMsg.time) { + if ((keyMsg.lParam & 0xC0000000) == 0x40000000) { + PeekMessage(&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; + // Extract the x,y coordinates from the lParam as we do in the WndProc + msgPtr->pt.x = GET_X_LPARAM(mouseMsg.lParam); + msgPtr->pt.y = GET_Y_LPARAM(mouseMsg.lParam); + ClientToScreen(msg.hwnd, &(msgPtr->pt)); + // Remove the mouse move message + PeekMessage(&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; + } + } +#ifndef Q_OS_WINCE + static bool trackMouseEventLookup = false; + typedef BOOL (WINAPI *PtrTrackMouseEvent)(LPTRACKMOUSEEVENT); + static PtrTrackMouseEvent ptrTrackMouseEvent = 0; +#endif + 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 + || type == QEvent::TabletMove) { + + 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 = QApplication::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) { + if (!trackMouseEventLookup) { + trackMouseEventLookup = true; + ptrTrackMouseEvent = (PtrTrackMouseEvent)QSystemLibrary::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 = QApplication::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: + case QEvent::TabletRelease: + + releaseAfter = true; + break; + default: + break; // nothing for mouse move + } + + if (target->isEnabled()) { + if (popupButtonFocus) { + target = popupButtonFocus; + } else if (popupChild) { + 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; + } + +#ifndef Q_OS_WINCE + if (type == QEvent::MouseButtonPress + && QApplication::activePopupWidget() != activePopupWidget + && ptrTrackMouseEvent + && curWin) { + // Since curWin is already the window we clicked on, + // we have to setup the mouse tracking here. + 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 + if (type == QEvent::MouseButtonPress + && QApplication::activePopupWidget() != activePopupWidget + && replayPopupMouseEvent) { + // the popup disappeared. 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); + PostMessage(hwndTarget, msg.message, msg.wParam, lParam); + } + } else if (type == QEvent::MouseButtonRelease && button == Qt::RightButton + && QApplication::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 || msg.message == WM_MOUSEHWHEEL) + delta = (short) HIWORD (msg.wParam); + else + delta = (int) msg.wParam; + + Qt::Orientation orient = (msg.message == WM_MOUSEHWHEEL || 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; + + // according to the MSDN documentation on WM_MOUSEHWHEEL: + // a positive value indicates that the wheel was rotated to the right; + // a negative value indicates that the wheel was rotated to the left. + // Qt defines this value as the exact opposite, so we have to flip the value! + if (msg.message == WM_MOUSEHWHEEL) + delta = -delta; + + 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 = QApplication::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 != QApplication::focusWidget() && (w = QApplication::focusWidget())) { + QWidget* popup = QApplication::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) +/* -------------------------------------------------------------------------- */ +// Initialize the "static" information of a cursor device (pen, airbrush, etc). +// The QTabletDeviceData is initialized with the data that do not change in time +// (number of button, type of device, etc) but do not initialize the variable data +// (e.g.: pen or eraser) +#ifndef QT_NO_TABLETEVENT + +static void tabletInit(const quint64 uniqueId, const UINT csr_type, HCTX hTab) +{ + Q_ASSERT(ptrWTInfo); + Q_ASSERT(ptrWTGet); + + Q_ASSERT(!tCursorInfo()->contains(uniqueId)); + + /* browse WinTab's many info items to discover pressure handling. */ + AXIS np; + LOGCONTEXT lc; + + /* get the current context for its device variable. */ + ptrWTGet(hTab, &lc); + + /* get the size of the pressure axis. */ + QTabletDeviceData tdd; + tdd.llId = uniqueId; + + 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); + + LOGCONTEXT lcMine; + + /* get default region */ + ptrWTInfo(WTI_DEFCONTEXT, 0, &lcMine); + + tdd.minX = 0; + tdd.maxX = int(lcMine.lcInExtX) - int(lcMine.lcInOrgX); + + tdd.minY = 0; + tdd.maxY = int(lcMine.lcInExtY) - int(lcMine.lcInOrgY); + + tdd.minZ = 0; + tdd.maxZ = int(lcMine.lcInExtZ) - int(lcMine.lcInOrgZ); + + const uint cursorTypeBitMask = 0x0F06; // bitmask to find the specific cursor type (see Wacom FAQ) + if (((csr_type & 0x0006) == 0x0002) && ((csr_type & cursorTypeBitMask) != 0x0902)) { + tdd.currentDevice = QTabletEvent::Stylus; + } else { + switch (csr_type & cursorTypeBitMask) { + 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; + } + } + tCursorInfo()->insert(uniqueId, tdd); +} +#endif // QT_NO_TABLETEVENT + +// Update the "dynamic" information of a cursor device (pen, airbrush, etc). +// The dynamic information is the information of QTabletDeviceData that can change +// in time (eraser or pen if a device is turned around). +#ifndef QT_NO_TABLETEVENT + +static void tabletUpdateCursor(QTabletDeviceData &tdd, const UINT currentCursor) +{ + switch (currentCursor % 3) { // %3 for dual track + case 0: + tdd.currentPointerType = QTabletEvent::Cursor; + break; + case 1: + tdd.currentPointerType = QTabletEvent::Pen; + break; + case 2: + tdd.currentPointerType = QTabletEvent::Eraser; + break; + default: + tdd.currentPointerType = QTabletEvent::UnknownPointer; + } +} +#endif // QT_NO_TABLETEVENT + +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())); + + if (t == QEvent::TabletPress) + { + qt_button_down = QApplication::widgetAt(globalPos); + } + + // make sure the tablet event get's sent to the proper widget... + QWidget *w = 0; + + if (qt_button_down) + w = qt_button_down; // Pass it to the thing that's grabbed it. + else + w = QApplication::widgetAt(globalPos); + + if (!w) + w = this; + + if (t == QEvent::TabletRelease) + { + if (qt_win_ignoreNextMouseReleaseEvent) { + qt_win_ignoreNextMouseReleaseEvent = false; + if (qt_button_down && qt_button_down->internalWinId() == autoCaptureWnd) { + releaseAutoCapture(); + qt_button_down = 0; + } + } + + } + + 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; + + +#ifndef QT_NO_TABLETEVENT + +static void initWinTabFunctions() +{ +#if defined(Q_OS_WINCE) + return; +#else + if (!qt_is_gui_used) + return; + + QSystemLibrary library(QLatin1String("wintab32")); + ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); + ptrWTGet = (PtrWTGet)library.resolve("WTGetW"); + ptrWTEnable = (PtrWTEnable)library.resolve("WTEnable"); + ptrWTOverlap = (PtrWTEnable)library.resolve("WTOverlap"); + ptrWTPacketsGet = (PtrWTPacketsGet)library.resolve("WTPacketsGet"); +#endif // Q_OS_WINCE +} +#endif // QT_NO_TABLETEVENT + + +// +// 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); + + if (d_func()->isGLWidget) { + if (d_func()->usesDoubleBufferedGLContext) + InvalidateRect(internalWinId(), 0, false); + } else { + 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_WS_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_WS_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) { + bool window_state_changed = false; + Qt::WindowStates oldstate = Qt::WindowStates(dataPtr()->window_state); + if (isMinimized()) { +#ifndef Q_WS_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); + // Capture SIZE_MAXIMIZED and SIZE_RESTORED without preceding WM_SYSCOMMAND + // (Aero Snap on Win7) + } else if (msg.wParam == SIZE_MAXIMIZED && !isMaximized()) { + data->window_state |= Qt::WindowMaximized; + window_state_changed = true; + } else if (msg.wParam == SIZE_RESTORED && isMaximized()) { + data->window_state &= ~(Qt::WindowMaximized); + window_state_changed = true; + } + if (window_state_changed) { + QWindowStateChangeEvent e(oldstate); + 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_WS_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_WS_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); +} + +#ifndef QT_NO_GESTURES +bool QETWidget::translateGestureEvent(const MSG &, const GESTUREINFO &gi) +{ + const QPoint widgetPos = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); + QWidget *alienWidget = !internalWinId() ? this : childAt(widgetPos); + if (alienWidget && alienWidget->internalWinId()) + alienWidget = 0; + QWidget *widget = alienWidget ? alienWidget : this; + + QNativeGestureEvent event; + event.sequenceId = gi.dwSequenceID; + event.position = QPoint(gi.ptsLocation.x, gi.ptsLocation.y); + event.argument = gi.ullArguments; + + switch (gi.dwID) { + case GID_BEGIN: + event.gestureType = QNativeGestureEvent::GestureBegin; + break; + case GID_END: + event.gestureType = QNativeGestureEvent::GestureEnd; + break; + case GID_ZOOM: + event.gestureType = QNativeGestureEvent::Zoom; + break; + case GID_PAN: + event.gestureType = QNativeGestureEvent::Pan; + break; + case GID_ROTATE: + event.gestureType = QNativeGestureEvent::Rotate; + break; + case GID_TWOFINGERTAP: + case GID_ROLLOVER: + default: + break; + } + if (event.gestureType != QNativeGestureEvent::None) + qt_sendSpontaneousEvent(widget, &event); + return true; +} +#endif // QT_NO_GESTURES + +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_WS_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; + SystemParametersInfo(SPI_SETWHEELSCROLLLINES, (uint)n, 0, 0); +#else + QApplicationPrivate::wheel_scroll_lines = n; +#endif +} + +int QApplication::wheelScrollLines() +{ +#ifdef SPI_GETWHEELSCROLLLINES + uint i = 3; + SystemParametersInfo(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()) { + // 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: + api = SPI_GETMENUFADE; + break; + case Qt::UI_AnimateCombo: + api = SPI_GETCOMBOBOXANIMATION; + break; + case Qt::UI_AnimateTooltip: + api = SPI_GETTOOLTIPANIMATION; + break; + case Qt::UI_FadeTooltip: + api = SPI_GETTOOLTIPFADE; + break; + default: + api = SPI_GETUIEFFECTS; + break; + } + SystemParametersInfo(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 + + +bool QApplicationPrivate::HasTouchSupport = false; +PtrRegisterTouchWindow QApplicationPrivate::RegisterTouchWindow = 0; +PtrGetTouchInputInfo QApplicationPrivate::GetTouchInputInfo = 0; +PtrCloseTouchInputHandle QApplicationPrivate::CloseTouchInputHandle = 0; + +void QApplicationPrivate::initializeMultitouch_sys() +{ + if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) { + static const int QT_SM_DIGITIZER = 94; + int value = GetSystemMetrics(QT_SM_DIGITIZER); + static const int QT_NID_INTEGRATED_TOUCH = 0x01; + static const int QT_NID_EXTERNAL_TOUCH = 0x02; + static const int QT_NID_MULTI_INPUT = 0x40; + QApplicationPrivate::HasTouchSupport = + value & (QT_NID_INTEGRATED_TOUCH | QT_NID_EXTERNAL_TOUCH | QT_NID_MULTI_INPUT); + } + + QSystemLibrary library(QLatin1String("user32")); + // MinGW (g++ 3.4.5) accepts only C casts. + RegisterTouchWindow = (PtrRegisterTouchWindow)(library.resolve("RegisterTouchWindow")); + GetTouchInputInfo = (PtrGetTouchInputInfo)(library.resolve("GetTouchInputInfo")); + CloseTouchInputHandle = (PtrCloseTouchInputHandle)(library.resolve("CloseTouchInputHandle")); + + touchInputIDToTouchPointID.clear(); +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ + touchInputIDToTouchPointID.clear(); +} + +bool QApplicationPrivate::translateTouchEvent(const MSG &msg) +{ + QWidget *widgetForHwnd = QWidget::find(msg.hwnd); + if (!widgetForHwnd) + return false; + + QRect screenGeometry = QApplication::desktop()->screenGeometry(widgetForHwnd); + + QList<QTouchEvent::TouchPoint> touchPoints; + + QVector<TOUCHINPUT> winTouchInputs(msg.wParam); + memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count()); + Qt::TouchPointStates allStates = 0; + QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); + for (int i = 0; i < winTouchInputs.count(); ++i) { + const TOUCHINPUT &touchInput = winTouchInputs.at(i); + + int touchPointID = touchInputIDToTouchPointID.value(touchInput.dwID, -1); + if (touchPointID == -1) { + touchPointID = touchInputIDToTouchPointID.count(); + touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID); + } + + QTouchEvent::TouchPoint touchPoint(touchPointID); + + // update state + QPointF screenPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.)); + QRectF screenRect; + if (touchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) + screenRect.setSize(QSizeF(qreal(touchInput.cxContact) / qreal(100.), + qreal(touchInput.cyContact) / qreal(100.))); + screenRect.moveCenter(screenPos); + + Qt::TouchPointStates state; + if (touchInput.dwFlags & TOUCHEVENTF_DOWN) { + state = Qt::TouchPointPressed; + } else if (touchInput.dwFlags & TOUCHEVENTF_UP) { + state = Qt::TouchPointReleased; + } else { + state = (screenPos == touchPoint.screenPos() + ? Qt::TouchPointStationary + : Qt::TouchPointMoved); + } + if (touchInput.dwFlags & TOUCHEVENTF_PRIMARY) + state |= Qt::TouchPointPrimary; + touchPoint.setState(state); + touchPoint.setScreenRect(screenRect); + touchPoint.setNormalizedPos(QPointF(screenPos.x() / screenGeometry.width(), + screenPos.y() / screenGeometry.height())); + + allStates |= state; + + touchPoints.append(touchPoint); + } + QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam); + + if ((allStates & Qt::TouchPointStateMask) == Qt::TouchPointReleased) { + // all touch points released, forget the ids we've seen, they may not be reused + touchInputIDToTouchPointID.clear(); + } + + translateRawTouchEvent(widgetForHwnd, QTouchEvent::TouchScreen, touchPoints); + return true; +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qclipboard_win.cpp b/src/gui/platforms/win/qclipboard_win.cpp new file mode 100644 index 0000000000..ea41165b9c --- /dev/null +++ b/src/gui/platforms/win/qclipboard_win.cpp @@ -0,0 +1,398 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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" +#include <private/qwidget_p.h> +#include <private/qsystemlibrary_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 + +typedef BOOL (WINAPI *PtrIsHungAppWindow)(HWND); + +static PtrIsHungAppWindow ptrIsHungAppWindow = 0; + +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")); + // We don't need this internal widget to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) + QWidgetPrivate::allWidgets->remove(clipBoardViewer); + } + + ~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::fromWCharArray((wchar_t *)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) { + if (ptrIsHungAppWindow == 0) { + QSystemLibrary library(QLatin1String("User32")); + ptrIsHungAppWindow = (PtrIsHungAppWindow)library.resolve("IsHungAppWindow"); + } + if (ptrIsHungAppWindow && ptrIsHungAppWindow(d->nextClipboardViewer)) { + qWarning("%s: Cowardly refusing to send clipboard message to hung application...", Q_FUNC_INFO); + } else { + SendMessage(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/platforms/win/qcolormap_win.cpp b/src/gui/platforms/win/qcolormap_win.cpp new file mode 100644 index 0000000000..1773f717c0 --- /dev/null +++ b/src/gui/platforms/win/qcolormap_win.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcolor.h" +#include "qcolormap.h" +#include "qvector.h" +#include "qt_windows.h" + +#if defined(Q_WS_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) +{ + Q_ASSERT_X(screenMap, "QColormap", + "A QApplication object needs to be constructed before QColormap is used."); + 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/platforms/win/qcursor_win.cpp b/src/gui/platforms/win/qcursor_win.cpp new file mode 100644 index 0000000000..8a9362ebfc --- /dev/null +++ b/src/gui/platforms/win/qcursor_win.cpp @@ -0,0 +1,492 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qcursor_p.h> +#include <qbitmap.h> +#include <qcursor.h> + +#ifndef QT_NO_CURSOR + +#include <qimage.h> +#include <qt_windows.h> +#include <private/qapplication_p.h> + +QT_BEGIN_NAMESPACE + +/***************************************************************************** + 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_WS_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_WS_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.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + invm = mbits.colorCount() > 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 + }; + + wchar_t *sh = 0; + switch (cshape) { // map to windows cursor + case Qt::ArrowCursor: + sh = IDC_ARROW; + break; + case Qt::UpArrowCursor: + sh = IDC_UPARROW; + break; + case Qt::CrossCursor: + sh = IDC_CROSS; + break; + case Qt::WaitCursor: + sh = IDC_WAIT; + break; + case Qt::IBeamCursor: + sh = IDC_IBEAM; + break; + case Qt::SizeVerCursor: + sh = IDC_SIZENS; + break; + case Qt::SizeHorCursor: + sh = IDC_SIZEWE; + break; + case Qt::SizeBDiagCursor: + sh = IDC_SIZENESW; + break; + case Qt::SizeFDiagCursor: + sh = IDC_SIZENWSE; + break; + case Qt::SizeAllCursor: + sh = IDC_SIZEALL; + break; + case Qt::ForbiddenCursor: + sh = IDC_NO; + break; + case Qt::WhatsThisCursor: + sh = IDC_HELP; + break; + case Qt::BusyCursor: + sh = IDC_APPSTARTING; + break; + case Qt::PointingHandCursor: + sh = IDC_HAND; + break; + 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.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); + } + int n = qMax(1, bbits.width() / 8); + int h = bbits.height(); +#if !defined(Q_WS_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_WS_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; + } + case Qt::DragCopyCursor: + case Qt::DragMoveCursor: + case Qt::DragLinkCursor: { + QPixmap pixmap = QApplicationPrivate::instance()->getPixmapCursor(cshape); + hcurs = create32BitCursor(pixmap, hx, hy); + } + default: + qWarning("QCursor::update: Invalid cursor shape %d", cshape); + return; + } +#ifdef Q_WS_WINCE + hcurs = LoadCursor(0, sh); +#else + hcurs = (HCURSOR)LoadImage(0, sh, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); +#endif +} + +QT_END_NAMESPACE +#endif // QT_NO_CURSOR diff --git a/src/gui/platforms/win/qdesktopwidget_win.cpp b/src/gui/platforms/win/qdesktopwidget_win.cpp new file mode 100644 index 0000000000..d57b355ef4 --- /dev/null +++ b/src/gui/platforms/win/qdesktopwidget_win.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesktopwidget.h" +#include "qt_windows.h" +#include "qapplication_p.h" +#include <private/qsystemlibrary_p.h> +#include <qvector.h> +#include <limits.h> +#ifdef Q_WS_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 (QT_WIN_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_WS_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 QT_WIN_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>(); + screenCount = 0; + +#ifndef Q_OS_WINCE + QSystemLibrary user32Lib(QLatin1String("user32")); + enumDisplayMonitors = (EnumFunc)user32Lib.resolve("EnumDisplayMonitors"); + getMonitorInfo = (InfoFunc)user32Lib.resolve("GetMonitorInfoW"); + + 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 + QSystemLibrary coreLib(QLatin1String("coredll")); + // 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_WS_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_WS_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_WS_WINCE_WM + for(int i=0; i < d->workrects->size(); ++i) + qt_get_sip_info((*d->workrects)[i]); +#endif + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + + return d->workrects->at(screen); +} + +const QRect QDesktopWidget::screenGeometry(int screen) const +{ + const QDesktopWidgetPrivate *d = d_func(); + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + + return d->rects->at(screen); +} + +int QDesktopWidget::screenNumber(const QWidget *widget) const +{ + Q_D(const QDesktopWidget); + 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; +} + +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->at(i)); + if (thisDistance < shortestDistance) { + shortestDistance = thisDistance; + closestScreen = i; + } + } + + return closestScreen; +} + +void QDesktopWidget::resizeEvent(QResizeEvent *) +{ + Q_D(QDesktopWidget); + const QVector<QRect> oldrects(*d->rects); + const QVector<QRect> oldworkrects(*d->workrects); + int oldscreencount = d->screenCount; + + QDesktopWidgetPrivate::cleanup(); + QDesktopWidgetPrivate::init(this); +#ifdef Q_WS_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) { + const QRect oldrect = oldrects[i]; + const QRect newrect = d->rects->at(i); + if (oldrect != newrect) + emit resized(i); + } + + for (int j = 0; j < qMin(oldscreencount, d->screenCount); ++j) { + const QRect oldrect = oldworkrects[j]; + const QRect newrect = d->workrects->at(j); + if (oldrect != newrect) + emit workAreaResized(j); + } + + if (oldscreencount != d->screenCount) { + emit screenCountChanged(d->screenCount); + } +} + +#ifdef Q_CC_MSVC +# pragma warning(pop) +#endif + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qdnd_win.cpp b/src/gui/platforms/win/qdnd_win.cpp new file mode 100644 index 0000000000..176e3cef7f --- /dev/null +++ b/src/gui/platforms/win/qdnd_win.cpp @@ -0,0 +1,1027 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 + wchar_t buf[256] = {0}; + GetClipboardFormatName(pformatetc->cfFormat, buf, 255); + qDebug("CF = %d : %s", pformatetc->cfFormat, QString::fromWCharArray(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) + // 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 +//--------------------------------------------------------------------- +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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 ((GetAsyncKeyState(VK_LBUTTON) == 0) + && (GetAsyncKeyState(VK_MBUTTON) == 0) + && (GetAsyncKeyState(VK_RBUTTON) == 0)) { + // grfKeyState is broken on CE & some Windows XP versions, + // therefore we need to check the state manually + return ResultFromScode(DRAGDROP_S_DROP); + } else { +#if !defined(Q_OS_WINCE) + 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 + QApplication::processEvents(); + return NOERROR; + } +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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 +//--------------------------------------------------------------------- + +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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; + } + } + +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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; +} + +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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) + +QT_ENSURE_STACK_ALIGNED_FOR_SSE 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/platforms/win/qfont_win.cpp b/src/gui/platforms/win/qfont_win.cpp new file mode 100644 index 0000000000..3ef761bfa5 --- /dev/null +++ b/src/gui/platforms/win/qfont_win.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp + +// ### maybe move to qapplication_win +QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/) +{ + QString family = QString::fromWCharArray(lf.lfFaceName); + QFont qf(family); + qf.setItalic(lf.lfItalic); + if (lf.lfWeight != FW_DONTCARE) + qf.setWeight(weightFromInteger(lf.lfWeight)); + 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: + case QFont::Monospace: + return QString::fromLatin1("Courier New"); + case QFont::Decorative: + return QString::fromLatin1("Bookman Old Style"); + case QFont::Cursive: + return QString::fromLatin1("Comic Sans MS"); + case QFont::Fantasy: + return QString::fromLatin1("Impact"); + 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/platforms/win/qfontdatabase_win.cpp b/src/gui/platforms/win/qfontdatabase_win.cpp new file mode 100644 index 0000000000..05b7509bf6 --- /dev/null +++ b/src/gui/platforms/win/qfontdatabase_win.cpp @@ -0,0 +1,1348 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt_windows.h" +#include <qmath.h> +#include <private/qapplication_p.h> +#include "qfont_p.h" +#include "qfontengine_p.h" +#include "qpaintdevice.h" +#include <private/qsystemlibrary_p.h> +#include "qabstractfileengine.h" +#include "qendian.h" + +#if !defined(QT_NO_DIRECTWRITE) +# include "qsettings.h" +# include "qfontenginedirectwrite_p.h" +#endif + +#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 ); + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length()) * sizeof(wchar_t)); + lf.lfCharSet = DEFAULT_CHARSET; + HFONT hfont = CreateFontIndirect(&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; +} + +extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp + +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::fromWCharArray(f->elfScript); +// qDebug("script=%s", escript.latin1()); + + 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; + + // 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; + styleKey.weight = weightFromInteger(weight); + + 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_WS_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 = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); + + for (int i = 0; i < systems.count(); ++i) { + QFontDatabase::WritingSystem writingSystem = systems.at(i); + + // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains + // the symbol for Baht, and Windows thus reports that it supports the Thai script. + // Since it's the default UI font on this platform, most widgets will be unable to + // display Thai text by default. As a temporary work around, we special case Segoe UI + // and remove the Thai script from its list of supported writing systems. + if (writingSystem != QFontDatabase::Thai || familyName != QLatin1String("Segoe UI")) + family->writingSystems[writingSystem] = 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 = QString::fromWCharArray(f->elfLogFont.lfFaceName); + QString script = QString::fromWCharArray(f->elfScript); + + FONTSIGNATURE signature = textmetric->ntmFontSig; + + // 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); + + LOGFONT lf; + lf.lfCharSet = DEFAULT_CHARSET; + if (fam.isNull()) { + lf.lfFaceName[0] = 0; + } else { + memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + } + lf.lfPitchAndFamily = 0; + + EnumFontFamiliesEx(dummy, &lf, + (FONTENUMPROC)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); + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE, familyName.size())); + lf.lfCharSet = DEFAULT_CHARSET; + HFONT hfont = CreateFontIndirect(&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", qPrintable(family->name), 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, HDC fontHdc, int dpi) +{ + fe->fontDef = request; // most settings are equal + + HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fontHdc) ? fontHdc : shared_dc(); + SelectObject(dc, fe->hfont); + wchar_t n[64]; + GetTextFace(dc, 64, n); + fe->fontDef.family = QString::fromWCharArray(n); + fe->fontDef.fixedPitch = !(fe->tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (fe->fontDef.pointSize < 0) { + fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; + } else if (fe->fontDef.pixelSize == -1) { + fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); + } +} + +#if !defined(QT_NO_DIRECTWRITE) +static void initFontInfo(QFontEngineDirectWrite *fe, const QFontDef &request, + int dpi, IDWriteFont *font) +{ + fe->fontDef = request; + + IDWriteFontFamily *fontFamily = NULL; + HRESULT hr = font->GetFontFamily(&fontFamily); + + IDWriteLocalizedStrings *familyNames = NULL; + if (SUCCEEDED(hr)) + hr = fontFamily->GetFamilyNames(&familyNames); + + UINT32 index = 0; + BOOL exists = false; + + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + + if (SUCCEEDED(hr)) { + int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH); + + if (defaultLocaleSuccess) + hr = familyNames->FindLocaleName(localeName, &index, &exists); + + if (SUCCEEDED(hr) && !exists) + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + } + + if (!exists) + index = 0; + + UINT32 length = 0; + if (SUCCEEDED(hr)) + hr = familyNames->GetStringLength(index, &length); + + wchar_t *name = new (std::nothrow) wchar_t[length+1]; + if (name == NULL) + hr = E_OUTOFMEMORY; + + // Get the family name. + if (SUCCEEDED(hr)) + hr = familyNames->GetString(index, name, length + 1); + + if (SUCCEEDED(hr)) + fe->fontDef.family = QString::fromWCharArray(name); + + delete[] name; + if (familyNames != NULL) + familyNames->Release(); + + if (FAILED(hr)) + qErrnoWarning(hr, "initFontInfo: Failed to get family name"); + + if (fe->fontDef.pointSize < 0) + fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; + else if (fe->fontDef.pixelSize == -1) + fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); +} +#endif + +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; + +#if !defined(QT_NO_DIRECTWRITE) +static QString fontNameSubstitute(const QString &familyName) +{ + QLatin1String key("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\" + "FontSubstitutes"); + return QSettings(key, QSettings::NativeFormat).value(familyName, familyName).toString(); +} +#endif + +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 QFontDef &request, + HDC fontHdc, int dpi, bool rawMode, + const QtFontDesc *desc, + const QStringList &family_list) +{ + LOGFONT lf; + memset(&lf, 0, sizeof(LOGFONT)); + + bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc; + + HDC hdc = shared_dc(); + QString font_name = desc != 0 ? desc->family->name : request.family; + + if (useDevice) { + hdc = fontHdc; + font_name = request.family; + } + + bool stockFont = false; + bool preferClearTypeAA = false; + + HFONT hfont = 0; + + +#if !defined(QT_NO_DIRECTWRITE) + bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting) + || (request.hintingPreference == QFont::PreferVerticalHinting); + IDWriteFont *directWriteFont = 0; +#else + bool useDirectWrite = false; +#endif + + if (rawMode) { // will choose a stock font + int f, deffnt = SYSTEM_FONT; + QString fam = desc != 0 ? desc->family->name.toLower() : request.family.toLower(); + if (fam == QLatin1String("default")) + f = deffnt; + else if (fam == QLatin1String("system")) + f = SYSTEM_FONT; +#ifndef Q_WS_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 = -qRound(request.pixelSize); + lf.lfWidth = 0; + lf.lfEscapement = 0; + lf.lfOrientation = 0; + if (desc == 0 || desc->style->key.weight == 50) + lf.lfWeight = FW_DONTCARE; + else + lf.lfWeight = (desc->style->key.weight*900)/99; + lf.lfItalic = (desc != 0 && 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_WS_WINCE + } else if (request.styleStrategy & QFont::PreferDevice) { + strat = OUT_DEVICE_PRECIS; + } else if (request.styleStrategy & QFont::PreferOutline) { + strat = OUT_OUTLINE_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_WS_WINCE + else if (request.styleStrategy & QFont::PreferQuality) + qual = PROOF_QUALITY; +#endif + + if (request.styleStrategy & QFont::PreferAntialias) { + if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) { + qual = CLEARTYPE_QUALITY; + preferClearTypeAA = true; + } 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"); + + memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed"); + + stockFont = (hfont == 0); + bool ttf = false; + int avWidth = 0; + BOOL res; + HGDIOBJ oldObj = SelectObject(hdc, hfont); + + TEXTMETRIC tm; + res = GetTextMetrics(hdc, &tm); + avWidth = tm.tmAveCharWidth; + ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; + SelectObject(hdc, oldObj); + + if (!ttf || !useDirectWrite) { + useDirectWrite = false; + + if (hfont && (!ttf || request.stretch != 100)) { + DeleteObject(hfont); + if (!res) + qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); + lf.lfWidth = avWidth * request.stretch/100; + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); + } + +#ifndef Q_WS_WINCE + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } +#else + if (hfont == 0) { + hfont = (HFONT)GetStockObject(SYSTEM_FONT); + stockFont = true; + } +#endif + + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + // Default to false for DirectWrite (and re-enable once/if everything + // turns out okay) + useDirectWrite = false; + + QFontDatabasePrivate *db = privateDb(); + if (db->directWriteFactory == 0) { + HRESULT hr = DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&db->directWriteFactory) + ); + if (FAILED(hr)) { + qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed"); + } else { + hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop); + if (FAILED(hr)) + qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed"); + } + } + + if (db->directWriteGdiInterop != 0) { + QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName)); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), + sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); + + HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT( + &lf, + &directWriteFont); + if (FAILED(hr)) { +#ifndef QT_NO_DEBUG + qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed " + "for %ls (0x%lx)", + lf.lfFaceName, hr); +#endif + } else { + DeleteObject(hfont); + useDirectWrite = true; + } + } + } +#endif + + } + + QFontEngine *fe = 0; + if (!useDirectWrite) { + QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); + if (preferClearTypeAA) + few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; + + // 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; + } + } + + initFontInfo(few, request, fontHdc, dpi); + fe = few; + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + QFontDatabasePrivate *db = privateDb(); + + IDWriteFontFace *directWriteFontFace = NULL; + HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace); + if (SUCCEEDED(hr)) { + QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(db->directWriteFactory, + directWriteFontFace, + request.pixelSize); + + initFontInfo(fedw, request, dpi, directWriteFont); + + fe = fedw; + } else { + qErrnoWarning(hr, "QFontEngine::loadEngine: CreateFontFace failed"); + } + } + + if (directWriteFont != 0) + directWriteFont->Release(); +#endif + + if(script == QUnicodeTables::Common + && !(request.styleStrategy & QFont::NoFontMerging) + && desc != 0 + && !(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(fe, list); + mfe->fontDef = fe->fontDef; + fe = mfe; + } + return fe; +} + +QFontEngine *qt_load_font_engine_win(const QFontDef &request) +{ + // From qfont.cpp + extern int qt_defaultDpi(); + + QFontCache::Key key(request, QUnicodeTables::Common); + QFontEngine *fe = QFontCache::instance()->findEngine(key); + if (fe != 0) + return fe; + else + return loadEngine(QUnicodeTables::Common, request, 0, qt_defaultDpi(), false, 0, + QStringList()); +} + +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); + + 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, req, d->hdc, d->dpi, d->rawMode, &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 = floor((100.0 * req.pointSize * d->dpi) / 72. + 0.5) / 100; + if (req.pixelSize < 1) + req.pixelSize = 1; + 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 + if (req.pointSize < 0) + 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); + } else { + memset(&signature, 0, sizeof(signature)); + } + appFont->signatures << signature; + } +} + +static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) +{ + if(!fnt->data.isEmpty()) { +#ifndef Q_OS_WINCE + PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QSystemLibrary::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 + wchar_t lpBuffer[MAX_PATH]; + GetTempPath(MAX_PATH, lpBuffer); + QString s = QString::fromWCharArray(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 // QT_NO_TEMPORARYFILE + 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 // Q_OS_WINCE + + 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 + PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"), + "AddFontResourceExW"); + if (!ptrAddFontResourceExW + || ptrAddFontResourceExW((wchar_t*)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0) + return; +#endif // Q_OS_WINCE + + 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)QSystemLibrary::resolve(QLatin1String("gdi32"), + "RemoveFontMemResourceEx"); + if (!ptrRemoveFontMemResourceEx + || !ptrRemoveFontMemResourceEx(font.handle)) + return false; +#endif // Q_OS_WINCE + } else { +#ifdef Q_OS_WINCE + if (!RemoveFontResource((LPCWSTR)font.fileName.utf16())) + return false; +#else + PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"), + "RemoveFontResourceExW"); + if (!ptrRemoveFontResourceExW + || !ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0)) + return false; +#endif // Q_OS_WINCE + } + + 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/platforms/win/qfontengine_win.cpp b/src/gui/platforms/win/qfontengine_win.cpp new file mode 100644 index 0000000000..54d7ec2980 --- /dev/null +++ b/src/gui/platforms/win/qfontengine_win.cpp @@ -0,0 +1,1339 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if _WIN32_WINNT < 0x0500 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include "qfontengine_p.h" +#include "qtextengine_p.h" +#include <qglobal.h> +#include "qt_windows.h" +#include <private/qapplication_p.h> + +#include <private/qsystemlibrary_p.h> +#include <qpaintdevice.h> +#include <qpainter.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 "qpaintengine.h" +#include "qvarlengtharray.h" +#include <private/qpaintengine_raster_p.h> +#include <private/qnativeimage_p.h> + +#if defined(Q_WS_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)) \ + ) + +// 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 + +#ifndef Q_WS_WINCE +typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); +static PtrGetCharWidthI ptrGetCharWidthI = 0; +static bool resolvedGetCharWidthI = false; + +static void resolveGetCharWidthI() +{ + if (resolvedGetCharWidthI) + return; + resolvedGetCharWidthI = true; + ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI"); +} +#endif // !defined(Q_WS_WINCE) + +// 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; +} + +// general font engine + +QFixed QFontEngineWin::lineThickness() const +{ + if(lineWidth > 0) + return lineWidth; + + return QFontEngine::lineThickness(); +} + +static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) +{ + int size; + size = GetOutlineTextMetrics(hdc, 0, 0); + OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size); + GetOutlineTextMetrics(hdc, size, otm); + return otm; +} + +void QFontEngineWin::getCMap() +{ + ttf = (bool)(tm.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) { + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); + unitsPerEm = otm->otmEMSquare; + x_height = (int)otm->otmsXHeight; + loadKerningPairs(designToDevice); + _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); + lineWidth = otm->otmsUnderscoreSize; + fsType = otm->otmfsType; + free(otm); + } else { + unitsPerEm = tm.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_WS_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 + wchar_t first = tm.tmFirstChar; + wchar_t last = tm.tmLastChar; + + for (; i < numChars; ++i, ++glyph_pos) { + uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); + if ( +#ifdef Q_WS_WINCE + tm.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_WS_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 + wchar_t first = tm.tmFirstChar; + wchar_t last = tm.tmLastChar; + + for (; i < numChars; ++i, ++glyph_pos) { + uint uc = getChar(str, i, numChars); + if ( +#ifdef Q_WS_WINCE + tm.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 = GetTextMetrics(hdc, &tm); + fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (!res) { + qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); + ZeroMemory(&tm, sizeof(TEXTMETRIC)); + } + + cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; + getCMap(); + + widthCache = 0; + widthCacheSize = 0; + designAdvances = 0; + designAdvancesSize = 0; + +#ifndef Q_WS_WINCE + if (!resolvedGetCharWidthI) + resolveGetCharWidthI(); +#endif +} + +QFontEngineWin::~QFontEngineWin() +{ + if (designAdvances) + free(designAdvances); + + if (widthCache) + free(widthCache); + + // make sure we aren't by accident still selected + SelectObject(shared_dc(), (HFONT)GetStockObject(SYSTEM_FONT)); + + if (!stockFont) { + if (!DeleteObject(hfont)) + qErrnoWarning("QFontEngineWin: failed to delete non-stock font..."); + } +} + +HGDIOBJ QFontEngineWin::selectDesignFont() const +{ + LOGFONT f = logfont; + f.lfHeight = unitsPerEm; + HFONT designFont = CreateFontIndirect(&f); + return SelectObject(shared_dc(), designFont); +} + +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; + + recalcAdvances(glyphs, flags); + return true; +} + +inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) +{ +#if defined(Q_WS_WINCE) + GetCharWidth32(hdc, glyph, glyph, &width); +#else + if (ptrGetCharWidthI) + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); +#endif +} + +void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + HGDIOBJ oldFont = 0; + HDC hdc = shared_dc(); + if (ttf && (flags & QTextEngine::DesignMetrics)) { + 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 = q_check_ptr((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(); + + int width = 0; + calculateTTFGlyphWidth(hdc, glyph, width); + designAdvances[glyph] = QFixed(width) / designToDevice; + } + glyphs->advances_x[i] = designAdvances[glyph]; + glyphs->advances_y[i] = 0; + } + if(oldFont) + DeleteObject(SelectObject(hdc, oldFont)); + } else { + 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 = q_check_ptr((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}; + GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size); + width = size.cx; + } else { + calculateTTFGlyphWidth(hdc, glyph, width); + } + 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.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); +} + +#ifndef Q_WS_WINCE +bool QFontEngineWin::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const +{ + Q_ASSERT(metrics != 0); + + HDC hdc = shared_dc(); + + GLYPHMETRICS gm; + 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); + } + + uint format = GGO_METRICS; + if (ttf) + format |= GGO_GLYPH_INDEX; + res = GetGlyphOutline(hdc, glyph, format, &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) { + *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, + (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY); + return true; + } else { + return false; + } +} +#endif + +glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t) +{ +#ifndef Q_WS_WINCE + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + + glyph_metrics_t glyphMetrics; + bool success = getOutlineMetrics(glyph, t, &glyphMetrics); + + if (!ttf && !success) { + // Bitmap fonts + wchar_t ch = glyph; + ABCFLOAT abc; + GetCharABCWidthsFloat(hdc, ch, ch, &abc); + int width = qRound(abc.abcfB); + + return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); + } + + return glyphMetrics; +#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.tmMaxCharWidth; + advance = width; + } + + SelectObject(hdc, oldFont); + return glyph_metrics_t(0, -tm.tmAscent, width, tm.tmHeight, advance, 0).transformed(t); +#endif +} + +QFixed QFontEngineWin::ascent() const +{ + return tm.tmAscent; +} + +QFixed QFontEngineWin::descent() const +{ + // ### we substract 1 to even out the historical +1 in QFontMetrics's + // ### height=asc+desc+1 equation. Fix in Qt5. + return tm.tmDescent - 1; +} + +QFixed QFontEngineWin::leading() const +{ + return tm.tmExternalLeading; +} + + +QFixed QFontEngineWin::xHeight() const +{ + if(x_height >= 0) + return x_height; + return QFontEngine::xHeight(); +} + +QFixed QFontEngineWin::averageCharWidth() const +{ + return tm.tmAveCharWidth; +} + +qreal QFontEngineWin::maxCharWidth() const +{ + return tm.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); + +#ifndef Q_CC_MINGW +void QFontEngineWin::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) +{ + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + +#ifndef Q_WS_WINCE + if (ttf) +#endif + + { + ABC abcWidths; + GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); + if (leftBearing) + *leftBearing = abcWidths.abcA; + if (rightBearing) + *rightBearing = abcWidths.abcC; + } + +#ifndef Q_WS_WINCE + else { + QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); + } +#endif +} +#endif // Q_CC_MINGW + +qreal QFontEngineWin::minLeftBearing() const +{ + if (lbearing == SHRT_MIN) + minRightBearing(); // calculates both + + return lbearing; +} + +qreal QFontEngineWin::minRightBearing() const +{ +#ifdef Q_WS_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 = tm.tmLastChar - tm.tmFirstChar; + if (n <= max_font_count) { + abc = new ABC[n+1]; + GetCharABCWidths(hdc, tm.tmFirstChar, tm.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; + } + 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 = tm.tmLastChar - tm.tmFirstChar; + if (n <= max_font_count) { + abc = new ABC[n+1]; + GetCharABCWidths(hdc, tm.tmFirstChar, tm.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 { + ABCFLOAT *abc = 0; + int n = tm.tmLastChar - tm.tmFirstChar+1; + if (n <= max_font_count) { + abc = new ABCFLOAT[n]; + GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.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; + } + 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 { + while(len--) { + if (tm.tmFirstChar > string->unicode() || tm.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_WS_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_WS_WINCE) + bufferSize = GetGlyphOutline(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_WS_WINCE) + ret = GetGlyphOutline(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 = CreateFontIndirect(&lf); + 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_WS_WINCE) + if(tm.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.tmItalic && !(macStyle & 2)) + synthesized_flags = SynthesizedItalic; + if (fontDef.stretch != 100 && ttf) + synthesized_flags |= SynthesizedStretch; + if (tm.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 = CreateFontIndirect(&lf); + HDC hdc = shared_dc(); + HGDIOBJ oldfont = SelectObject(hdc, hf); + OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); + Properties p; + p.emSquare = unitsPerEm; + p.italicAngle = otm->otmItalicAngle; + p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1(); + p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1(); + p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(p.postscriptName); + 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 = CreateFontIndirect(&lf); + 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 + +extern bool qt_cleartype_enabled; + +QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, + const QTransform &t, QImage::Format mask_format) +{ + Q_UNUSED(mask_format) + 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_WS_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; + + if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == 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 + 4, + ih + 2 * margin + 4, + QNativeImage::systemFormat(), !qt_cleartype_enabled); + + /*If cleartype is enabled we use the standard system format even on Windows CE + and not the special textbuffer format we have to use if cleartype is disabled*/ + + 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); + ExtTextOut(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0); + } else +#endif + { + ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0); + } + + SelectObject(hdc, old_font); + return ni; +} + + +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 = CreateFontIndirect(&lf); + } + QImage::Format mask_format = QNativeImage::systemFormat(); +#ifndef Q_OS_WINCE + mask_format = QImage::Format_RGB32; +#endif + + QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); + 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 don't 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->image.format() == QImage::Format_RGB16) { + const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y); + for (int x=0; x<mask->width(); ++x) + dest[x] = 255 - qGray(src[x]); + } 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 + if (QNativeImage::systemFormat() == QImage::Format_RGB16) + 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, QFixed, 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, QImage::Format_RGB32); + 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; +} + +// From qfontdatabase_win.cpp +extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); +QFontEngine *QFontEngineWin::cloneWithSize(qreal pixelSize) const +{ + QFontDef request = fontDef; + QString actualFontName = request.family; + if (!uniqueFamilyName.isEmpty()) + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + + QFontEngine *fontEngine = qt_load_font_engine_win(request); + if (fontEngine != NULL) + fontEngine->fontDef.family = actualFontName; + + return fontEngine; +} + +// -------------------------------------- Multi font engine + +QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks) + : QFontEngineMulti(fallbacks.size()+1), + fallbacks(fallbacks) +{ + engines[0] = first; + first->ref.ref(); + fontDef = engines[0]->fontDef; + cache_cost = first->cache_cost; +} + +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; + memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + HFONT hfont = CreateFontIndirect(&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; + + // TODO: increase cost in QFontCache for the font engine loaded here +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qfontengine_win_p.h b/src/gui/platforms/win/qfontengine_win_p.h new file mode 100644 index 0000000000..114149d61f --- /dev/null +++ b/src/gui/platforms/win/qfontengine_win_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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() 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, QFixed subPixelPosition, int margin, const QTransform &xform); + + virtual QFontEngine *cloneWithSize(qreal pixelSize) const; + +#ifndef Q_CC_MINGW + virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); +#endif + + int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const; + void getCMap(); + +#ifndef Q_WS_WINCE + bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const; +#endif + + QString _name; + QString uniqueFamilyName; + HFONT hfont; + LOGFONT logfont; + uint stockFont : 1; + uint ttf : 1; + uint hasOutline : 1; + TEXTMETRIC 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, + QImage::Format mask_format); + +}; + +class QFontEngineMultiWin : public QFontEngineMulti +{ +public: + QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks); + void loadEngine(int at); + + QStringList fallbacks; +}; + +QT_END_NAMESPACE + +#endif // QFONTENGINE_WIN_P_H diff --git a/src/gui/platforms/win/qguifunctions_wince.cpp b/src/gui/platforms/win/qguifunctions_wince.cpp new file mode 100644 index 0000000000..bb4ed11589 --- /dev/null +++ b/src/gui/platforms/win/qguifunctions_wince.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "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 SHDB_HIDE +#define SHDB_HIDE 0x0002 +#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 +#ifndef SPI_GETPLATFORMTYPE +#define SPI_GETPLATFORMTYPE 257 +#endif + +typedef BOOL (*AygInitDialog)(AygSHINITDLGINFO*); +typedef BOOL (*AygFullScreen)(HWND, DWORD); +typedef BOOL (*AygSHSipInfo)(UINT, UINT, PVOID, UINT); +typedef BOOL (*AygSHDoneButton)(HWND, DWORD); + +static AygInitDialog ptrAygInitDialog = 0; +static AygFullScreen ptrAygFullScreen = 0; +static AygSHSipInfo ptrAygSHSipInfo = 0; +static AygSHDoneButton ptrAygSHDoneButton = 0; +static bool aygResolved = false; + +static void resolveAygLibs() +{ + if (!aygResolved) { + aygResolved = true; + QLibrary ayglib(QLatin1String("aygshell")); + ptrAygInitDialog = (AygInitDialog) ayglib.resolve("SHInitDialog"); + ptrAygFullScreen = (AygFullScreen) ayglib.resolve("SHFullScreen"); + ptrAygSHSipInfo = (AygSHSipInfo) ayglib.resolve("SHSipInfo"); + ptrAygSHDoneButton = (AygSHDoneButton) ayglib.resolve("SHDoneButton"); + } +} + +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; +} + +HINSTANCE qt_wince_ShellExecute(HWND hwnd, LPCWSTR, LPCWSTR file, LPCWSTR params, LPCWSTR 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; +} + +// Internal Qt ----------------------------------------------------- +bool qt_wince_is_platform(const QString &platformString) { + wchar_t tszPlatform[64]; + if (SystemParametersInfo(SPI_GETPLATFORMTYPE, sizeof(tszPlatform) / sizeof(wchar_t), tszPlatform, 0)) + if (0 == _tcsicmp(reinterpret_cast<const wchar_t *> (platformString.utf16()), tszPlatform)) + return true; + return false; +} + +int qt_wince_get_build() +{ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (GetVersionEx(&osvi)) { + return osvi.dwBuildNumber; + } + return 0; +} + +int qt_wince_get_version() +{ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (GetVersionEx(&osvi)) { + return (osvi.dwMajorVersion * 10 + osvi.dwMinorVersion); + } + return 0; +} + +bool qt_wince_is_windows_mobile_65() +{ + const DWORD dwFirstWM65BuildNumber = 21139; + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (!GetVersionEx(&osvi)) + return false; + return osvi.dwMajorVersion > 5 + || (osvi.dwMajorVersion == 5 && (osvi.dwMinorVersion > 2 || + (osvi.dwMinorVersion == 2 && osvi.dwBuildNumber >= dwFirstWM65BuildNumber))); +} + +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; + if (!(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint))) + shidi.dwFlags |= SHIDIF_CANCELBUTTON; + 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_unmaximize(QWidget *widget) +{ + if (ptrAygSHDoneButton && qt_wince_is_mobile() + && !(widget->windowFlags() & (Qt::WindowCancelButtonHint | Qt::WindowOkButtonHint))) + { + // Hide the [X] button, we've added in qt_wince_maximize. + ptrAygSHDoneButton(widget->winId(), SHDB_HIDE); + } +} + +void qt_wince_minimize(HWND hwnd) +{ +#ifdef Q_OS_WINCE_WM + ShowWindow(hwnd, SW_HIDE); +#else + if (!IsWindowVisible(hwnd)) { + // Hack for an initial showMinimized. + // Without it, our widget doesn't appear in the task bar. + ShowWindow(hwnd, SW_SHOW); + } + ShowWindow(hwnd, SW_MINIMIZE); +#endif +} + +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/platforms/win/qguifunctions_wince.h b/src/gui/platforms/win/qguifunctions_wince.h new file mode 100644 index 0000000000..2e14de0693 --- /dev/null +++ b/src/gui/platforms/win/qguifunctions_wince.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef 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 + +// 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, LPCWSTR operation, LPCWSTR file, LPCWSTR params, LPCWSTR 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) + +#endif // Q_OS_WINCE +#endif // QGUIFUNCTIONS_WCE_H diff --git a/src/gui/platforms/win/qkeymapper_win.cpp b/src/gui/platforms/win/qkeymapper_win.cpp new file mode 100644 index 0000000000..92fa582617 --- /dev/null +++ b/src/gui/platforms/win/qkeymapper_win.cpp @@ -0,0 +1,1207 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qkeymapper_p.h" + +#include <qt_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 QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); + +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_* +}; + +/** + Remap return or action key to select key for windows mobile. +*/ +inline int winceKeyBend(int keyCode) +{ +#if defined(Q_OS_WINCE_WM) && defined(QT_KEYPAD_NAVIGATION) + // remap return or action key to select key for windows mobile. + // will be changed to a table remapping function in the next version (4.6/7). + if (keyCode == VK_RETURN && QApplication::keypadNavigationEnabled()) + return Qt::Key_Select; + else + return KeyTbl[keyCode]; +#else + return KeyTbl[keyCode]; +#endif +} + +#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 = 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 = winceKeyBend(vk); + + if (isDeadkey) + *isDeadkey = (res == -1); + + return code == Qt::Key_unknown ? 0 : code; +} + +Q_GUI_EXPORT int qt_translateKeyCode(int vk) +{ + int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : 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 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() +{ + deleteLayouts(); +} + +void QKeyMapperPrivate::deleteLayouts() +{ + for (int i = 0; i < 255; ++i) { + if (keyLayout[i]) { + delete keyLayout[i]; + keyLayout[i] = 0; + } + } +} + +void QKeyMapperPrivate::clearMappings() +{ + deleteLayouts(); + + /* MAKELCID()'s first argument is a WORD, and GetKeyboardLayout() + * returns a DWORD. */ + + LCID newLCID = MAKELCID((quintptr)GetKeyboardLayout(0), SORT_DEFAULT); +// keyboardInputLocale = qt_localeFromLCID(newLCID); + + bool bidi = false; + wchar_t LCIDFontSig[16]; + if (GetLocaleInfo(newLCID, LOCALE_FONTSIGNATURE, LCIDFontSig, sizeof(LCIDFontSig) / sizeof(wchar_t)) + && (LCIDFontSig[7] & (wchar_t)0x0800)) + 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 = winceKeyBend(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) & 0x80000000) { + // 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 defined(Q_OS_WINCE) + 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); +#endif // Q_OS_WINCE + + 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) & 0x80000000; + + // A multi-character key not found by our look-ahead + if (msgType == WM_CHAR) { + QString s; + QChar ch = QChar((ushort)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 = QChar((ushort)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 keycodes (see bugs 127424, QTBUG-3630) + if (msg.wParam == 0 || 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 = winceKeyBend(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 (PeekMessage(&wm_char, 0, charType, charType, PM_REMOVE)) { + // Found a ?_CHAR + uch = QChar((ushort)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_t 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 = MapVirtualKey(msg.wParam, 2); + // If the high bit of the return value is set, it's a deadkey + if (!(map & 0x80000000)) + uch = QChar((ushort)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; + } +#else + Q_UNUSED(grab); +#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/platforms/win/qmime_win.cpp b/src/gui/platforms/win/qmime_win.cpp new file mode 100644 index 0000000000..feb8b78eca --- /dev/null +++ b/src/gui/platforms/win/qmime_win.cpp @@ -0,0 +1,1556 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 draganddrop + + 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) +{ + int f = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (mime.utf16())); + 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()"); + wchar_t buf[256] = {0}; + GetClipboardFormatName(fmtetc.cfFormat, buf, 255); + qDebug("CF = %d : %s", fmtetc.cfFormat, QString::fromWCharArray(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() * sizeof(ushort); + 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::fromWCharArray((const wchar_t *)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()) { + size += sizeof(ushort) * (fn.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; + + d->fWide = true; + wchar_t* f = (wchar_t*)files; + for (int i=0; i<fileNames.size(); i++) { + int l = fileNames.at(i).length(); + memcpy(f, fileNames.at(i).utf16(), l * sizeof(ushort)); + f += l; + *f++ = 0; + } + *f = 0; + + return setData(result, pmedium); + } else if (getCf(formatetc) == CF_INETURL_W) { + QList<QUrl> urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) { + QString url = urls.at(0).toString(); + result = QByteArray((const char *)url.utf16(), url.length() * sizeof(ushort)); + } + result.append('\0'); + result.append('\0'); + return setData(result, pmedium); + } else if (getCf(formatetc) == CF_INETURL) { + QList<QUrl> urls = mimeData->urls(); + QByteArray result; + if (!urls.isEmpty()) + 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 wchar_t* filesw = (const wchar_t *)(data.data() + hdrop->pFiles); + int i = 0; + while (filesw[i]) { + QString fileurl = QString::fromWCharArray(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::fromWCharArray((const wchar_t *)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() +{ + CF_PNG = RegisterClipboardFormat(L"PNG"); +} + +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 (img.format() > QImage::Format_ARGB32) + img = img.convertToFormat(QImage::Format_RGB32); + 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() * sizeof(ushort); + 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 Mozilla) + val = QString::fromWCharArray((const wchar_t *)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=\""; + +static bool isCustomMimeType(const QString &mimeType) +{ + return mimeType.startsWith(QLatin1String(x_qt_windows_mime), Qt::CaseInsensitive); +} + +static QString customMimeType(const QString &mimeType) +{ + int len = sizeof(x_qt_windows_mime) - 1; + 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); + int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); + 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); + int cf = RegisterClipboardFormat(reinterpret_cast<const wchar_t *> (clipFormat.utf16())); + 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()) + return format; + + wchar_t buffer[256]; + int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); + + if (len) { + QString clipFormat = QString::fromWCharArray(buffer, 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/platforms/win/qole_win.cpp b/src/gui/platforms/win/qole_win.cpp new file mode 100644 index 0000000000..24e2d5b292 --- /dev/null +++ b/src/gui/platforms/win/qole_win.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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/platforms/win/qpaintdevice_win.cpp b/src/gui/platforms/win/qpaintdevice_win.cpp new file mode 100644 index 0000000000..3dbe97492a --- /dev/null +++ b/src/gui/platforms/win/qpaintdevice_win.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 + +HDC QPaintDevice::getDC() const +{ + return 0; +} + +void QPaintDevice::releaseDC(HDC) const +{ +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qpixmap_win.cpp b/src/gui/platforms/win/qpixmap_win.cpp new file mode 100644 index 0000000000..9c14ac7726 --- /dev/null +++ b/src/gui/platforms/win/qpixmap_win.cpp @@ -0,0 +1,477 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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_WS_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_WS_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_WS_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; +} + +HBITMAP QPixmap::toWinHBITMAP(HBitmapFormat format) const +{ + if (isNull()) + return 0; + + HBITMAP bitmap = 0; + if (data->classId() == QPixmapData::RasterClass) { + QRasterPixmapData* d = static_cast<QRasterPixmapData*>(data.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; +} + +QPixmap QPixmap::fromWinHBITMAP(HBITMAP bitmap, HBitmapFormat format) +{ + // Verify size + BITMAP bitmap_info; + memset(&bitmap_info, 0, sizeof(BITMAP)); + + int res = GetObject(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); +} + +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 QPixmap::toWinHICON() const +{ + QBitmap maskBitmap = mask(); + if (maskBitmap.isNull()) { + maskBitmap= QBitmap(size()); + maskBitmap.fill(Qt::color1); + } + + ICONINFO ii; + ii.fIcon = true; + ii.hbmMask = qt_createIconMask(maskBitmap); + ii.hbmColor = toWinHBITMAP(QPixmap::Alpha); + ii.xHotspot = 0; + ii.yHotspot = 0; + + HICON hIcon = CreateIconIndirect(&ii); + + DeleteObject(ii.hbmColor); + DeleteObject(ii.hbmMask); + + return hIcon; +} + +#ifdef Q_WS_WIN +#ifndef Q_WS_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"); + } + qFree(data); + + return image; +} + +QPixmap QPixmap::fromWinHICON(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("QPixmap::fromWinHICON(), 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_WS_WINCE +QPixmap QPixmap::fromWinHICON(HICON icon) +{ + HDC screenDevice = GetDC(0); + HDC hdc = CreateCompatibleDC(screenDevice); + ReleaseDC(0, screenDevice); + + ICONINFO iconinfo; + bool result = GetIconInfo(icon, &iconinfo); + if (!result) + qWarning("QPixmap::fromWinHICON(), failed to GetIconInfo()"); + + int w = 0; + int h = 0; + if (!iconinfo.xHotspot || !iconinfo.yHotspot) { + // We could not retrieve the icon size via GetIconInfo, + // so we try again using the icon bitmap. + BITMAP bm; + int result = GetObject(iconinfo.hbmColor, sizeof(BITMAP), &bm); + if (!result) result = GetObject(iconinfo.hbmMask, sizeof(BITMAP), &bm); + if (!result) { + qWarning("QPixmap::fromWinHICON(), failed to retrieve icon size"); + return QPixmap(); + } + w = bm.bmWidth; + h = bm.bmHeight; + } else { + // x and y Hotspot describes the icon center + w = iconinfo.xHotspot * 2; + h = iconinfo.yHotspot * 2; + } + const DWORD dwImageSize = w * h * 4; + + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFO); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = dwImageSize; + + uchar* bits; + + HBITMAP winBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**) &bits, 0, 0); + if (winBitmap ) + memset(bits, 0xff, dwImageSize); + if (!winBitmap) { + qWarning("QPixmap::fromWinHICON(), failed to CreateDIBSection()"); + return QPixmap(); + } + + HGDIOBJ oldhdc = (HBITMAP)SelectObject(hdc, winBitmap); + if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_NORMAL)) + qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()"); + + uint mask = 0xff000000; + // Create image and copy data into image. + QImage image(w, h, QImage::Format_ARGB32); + + 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 *) (bits + y * bytes_per_line); + for (int x=0; x < w; ++x) { + dest[x] = src[x]; + } + } + } + if (!DrawIconEx( hdc, 0, 0, icon, w, h, 0, 0, DI_MASK)) + qWarning("QPixmap::fromWinHICON(), failed to DrawIcon()"); + 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 *) (bits + y * bytes_per_line); + for (int x=0; x < w; ++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_WS_WINCE +#endif //ifdef Q_WS_WIN + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qprintengine_win.cpp b/src/gui/platforms/win/qprintengine_win.cpp new file mode 100644 index 0000000000..07d66f5bd0 --- /dev/null +++ b/src/gui/platforms/win/qprintengine_win.cpp @@ -0,0 +1,1776 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 ®ion); + +// #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; + + d->devMode->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; + } + + 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) { + 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; + 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 + || d->txop >= QTransform::TxProject + || 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) { + wchar_t n[64]; + GetTextFace(d->hdc, 64, n); + fallBack = QString::fromWCharArray(n) + != QString::fromWCharArray(fe->logfont.lfFaceName); + } + } + + + if (fallBack) { + QPaintEngine::drawTextItem(p, textItem); + return ; + } + + // We only want to convert the glyphs to text if the entire string is compatible with ASCII + // and if we actually have access to the chars. + bool convertToText = ti.chars != 0; + for (int i=0; i < ti.num_chars; ++i) { + if (ti.chars[i].unicode() >= 0x80) { + convertToText = false; + break; + } + + if (ti.logClusters[i] != i) { + convertToText = 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, convertToText, 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 = QTransform::fromScale(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); + LOGBRUSH brush; + brush.lbStyle = BS_SOLID; + brush.lbColor = RGB(color.red(), color.green(), color.blue()); + DWORD capStyle = PS_ENDCAP_SQUARE; + DWORD joinStyle = PS_JOIN_BEVEL; + if (pen.capStyle() == Qt::FlatCap) + capStyle = PS_ENDCAP_FLAT; + else if (pen.capStyle() == Qt::RoundCap) + capStyle = PS_ENDCAP_ROUND; + + if (pen.joinStyle() == Qt::MiterJoin) + joinStyle = PS_JOIN_MITER; + else if (pen.joinStyle() == Qt::RoundJoin) + joinStyle = PS_JOIN_ROUND; + + HPEN pen = ExtCreatePen(((penWidth == 0) ? PS_COSMETIC : PS_GEOMETRIC) + | PS_SOLID | capStyle | joinStyle, + (penWidth == 0) ? 1 : penWidth, &brush, 0, 0); + + 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")); + wchar_t buffer[256]; + GetProfileString(L"windows", L"device", + reinterpret_cast<const wchar_t *>(noPrinters.utf16()), + buffer, 256); + QString output = QString::fromWCharArray(buffer); + if (output.isEmpty() || output == noPrinters) // no printers + return; + + QStringList info = output.split(QLatin1Char(',')); + int infoSize = info.size(); + if (infoSize > 0) { + if (name.isEmpty()) + name = info.at(0); + if (program.isEmpty() && infoSize > 1) + program = info.at(1); + if (port.isEmpty() && infoSize > 2) + 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 = OpenPrinter((LPWSTR)name.utf16(), (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; + GetPrinter(hPrinter, 2, NULL, 0, &infoSize); + hMem = GlobalAlloc(GHND, infoSize); + pInfo = (PRINTER_INFO_2*) GlobalLock(hMem); + ok = GetPrinter(hPrinter, 2, (LPBYTE)pInfo, infoSize, &numBytes); + + if (!ok) { + qErrnoWarning("QWin32PrintEngine::initialize: GetPrinter failed"); + GlobalUnlock(pInfo); + GlobalFree(hMem); + ClosePrinter(hPrinter); + pInfo = 0; + hMem = 0; + hPrinter = 0; + return; + } + + devMode = pInfo->pDevMode; + hdc = CreateDC(reinterpret_cast<const wchar_t *>(program.utf16()), + reinterpret_cast<const wchar_t *>(name.utf16()), 0, devMode); + + Q_ASSERT(hPrinter); + Q_ASSERT(pInfo); + + if (devMode) { + num_copies = devMode->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. + QList<QVariant> list; + + DWORD 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; + + LONG *enumRes = (LONG*)malloc(numRes * 2 * sizeof(LONG)); + DWORD errRes = DeviceCapabilities(reinterpret_cast<const wchar_t *>(name.utf16()), + reinterpret_cast<const wchar_t *>(port.utf16()), + DC_ENUMRESOLUTIONS, (LPWSTR)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; + d->devMode->dmCollate = value.toBool() ? DMCOLLATE_TRUE : DMCOLLATE_FALSE; + d->doReinit(); + } + break; + + case PPK_ColorMode: + { + if (!d->devMode) + break; + d->devMode->dmColor = (value.toInt() == QPrinter::Color) ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME; + 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_CopyCount: // fallthrough + case PPK_NumberOfCopies: + if (!d->devMode) + break; + d->num_copies = value.toInt(); + d->devMode->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 = d->devMode->dmOrientation; + d->devMode->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; + d->devMode->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())); + + d->devMode->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; + d->devMode->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 = d->devMode->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->devMode->dmPaperSize = i + 1; + break; + } + } + } + free(forms); + } + 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).toReal()*25.4/72.0) * 100; + top = (margins.at(1).toReal()*25.4/72.0) * 100; + right = (margins.at(2).toReal()*25.4/72.0) * 100; + bottom = (margins.at(3).toReal()*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 { + value = (d->devMode->dmColor == DMCOLOR_COLOR) ? QPrinter::Color : QPrinter::GrayScale; + } + } + break; + + case PPK_DocumentName: + value = d->docName; + break; + + case PPK_FullPage: + value = d->fullPage; + break; + + case PPK_CopyCount: + value = d->num_copies; + break; + + case PPK_SupportsMultipleCopies: + value = true; + break; + + case PPK_NumberOfCopies: + value = 1; + break; + + case PPK_Orientation: + { + if (!d->devMode) { + value = QPrinter::Portrait; + } else { + value = (d->devMode->dmOrientation == 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->devPhysicalPageRect : d->devPageRect); + } + break; + + case PPK_PaperSize: + if (d->has_custom_paper_size) { + value = QPrinter::Custom; + } else { + if (!d->devMode) { + value = QPrinter::A4; + } else { + value = mapDevmodePaperSize(d->devMode->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 { + value = mapDevmodePaperSource(d->devMode->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 { + value = d->devMode->dmPaperSize; + } + break; + + case PPK_PaperSources: + { + int available = DeviceCapabilities((const wchar_t *)d->name.utf16(), + (const wchar_t *)d->port.utf16(), DC_BINS, 0, d->devMode); + + if (available <= 0) + break; + + wchar_t *data = new wchar_t[available]; + int count = DeviceCapabilities((const wchar_t *)d->name.utf16(), + (const wchar_t *)d->port.utf16(), DC_BINS, data, d->devMode); + + QList<QVariant> out; + for (int i=0; i<count; ++i) { + QPrinter::PaperSource src = mapDevmodePaperSource(data[i]); + if (src != -1) + out << (int) src; + } + value = out; + + delete [] 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() +{ + 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(wchar_t); + 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::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset).latin1(), +// QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset).latin1(), +// QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset).latin1()); + + return hGlobal; +} + +void QWin32PrintEnginePrivate::readDevnames(HGLOBAL globalDevnames) +{ + if (globalDevnames) { + DEVNAMES *dn = (DEVNAMES*) GlobalLock(globalDevnames); + name = QString::fromWCharArray((wchar_t*)(dn) + dn->wDeviceOffset); + port = QString::fromWCharArray((wchar_t*)(dn) + dn->wOutputOffset); + program = QString::fromWCharArray((wchar_t*)(dn) + dn->wDriverOffset); + GlobalUnlock(globalDevnames); + } +} + +void QWin32PrintEnginePrivate::readDevmode(HGLOBAL globalDevmode) +{ + if (globalDevmode) { + 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 = devMode->dmCopies; + if (!OpenPrinter((wchar_t*)name.utf16(), &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) +{ + 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; + + if (winfe) { + hfont = winfe->hfont; + ttf = winfe->ttf; + } 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; + + 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. + 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); +#endif + + if (fast) { + // fast path + QVarLengthArray<wchar_t> g(glyphs.numGlyphs); + for (int i = 0; i < glyphs.numGlyphs; ++i) + g[i] = glyphs.glyphs[i]; + ExtTextOut(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 = QTransform::fromTranslate(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 = _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]; + ExtTextOut(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]; + + ExtTextOut(hdc, qRound(positions[i].x), + qRound(positions[i].y), options, 0, + convertToText ? convertedGlyphs + i : &g, 1, 0); + ++i; + } + } + } + +#if !defined(Q_OS_WINCE) + 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); +#endif + + SelectObject(hdc, old_font); +} + + +void QWin32PrintEnginePrivate::updateCustomPaperSize() +{ + uint paperSize = devMode->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; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_PRINTER diff --git a/src/gui/platforms/win/qprintengine_win_p.h b/src/gui/platforms/win/qprintengine_win_p.h new file mode 100644 index 0000000000..b4d0670e7b --- /dev/null +++ b/src/gui/platforms/win/qprintengine_win_p.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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; +}; + +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 bool resetDC() { + hdc = ResetDC(hdc, devMode); + 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; + DEVMODE *devMode; + PRINTER_INFO_2 *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/platforms/win/qprinterinfo_win.cpp b/src/gui/platforms/win/qprinterinfo_win.cpp new file mode 100644 index 0000000000..2c4014d8dc --- /dev/null +++ b/src/gui/platforms/win/qprinterinfo_win.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qprinterinfo.h" +#include "qprinterinfo_p.h" + +#include <qstringlist.h> + +#include <qt_windows.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_PRINTER + +extern QPrinter::PaperSize mapDevmodePaperSize(int s); + +QList<QPrinterInfo> QPrinterInfo::availablePrinters() +{ + QList<QPrinterInfo> printers; + + DWORD needed = 0; + DWORD returned = 0; + if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, 0, 0, &needed, &returned)) { + LPBYTE buffer = new BYTE[needed]; + if (EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, buffer, needed, &needed, &returned)) { + PPRINTER_INFO_4 infoList = reinterpret_cast<PPRINTER_INFO_4>(buffer); + QPrinterInfo defPrn = defaultPrinter(); + for (uint i = 0; i < returned; ++i) { + QString printerName(QString::fromWCharArray(infoList[i].pPrinterName)); + + QPrinterInfo printerInfo(printerName); + if (printerInfo.printerName() == defPrn.printerName()) + printerInfo.d_ptr->isDefault = true; + printers.append(printerInfo); + } + } + delete [] buffer; + } + + return printers; +} + +QPrinterInfo QPrinterInfo::defaultPrinter() +{ + QString noPrinters(QLatin1String("qt_no_printers")); + wchar_t buffer[256]; + GetProfileString(L"windows", L"device", (wchar_t*)noPrinters.utf16(), buffer, 256); + QString output = QString::fromWCharArray(buffer); + if (output != noPrinters) { + // Filter out the name of the printer, which should be everything before a comma. + QString printerName = output.split(QLatin1Char(',')).value(0); + QPrinterInfo printerInfo(printerName); + printerInfo.d_ptr->isDefault = true; + return printerInfo; + } + + return QPrinterInfo(); +} + +QList<QPrinter::PaperSize> QPrinterInfo::supportedPaperSizes() const +{ + const Q_D(QPrinterInfo); + + QList<QPrinter::PaperSize> paperSizes; + if (isNull()) + return paperSizes; + + DWORD size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), + NULL, DC_PAPERS, NULL, NULL); + if ((int)size != -1) { + wchar_t *papers = new wchar_t[size]; + size = DeviceCapabilities(reinterpret_cast<const wchar_t*>(d->name.utf16()), + NULL, DC_PAPERS, papers, NULL); + for (int c = 0; c < (int)size; ++c) + paperSizes.append(mapDevmodePaperSize(papers[c])); + delete [] papers; + } + + return paperSizes; +} + +#endif // QT_NO_PRINTER + +QT_END_NAMESPACE diff --git a/src/gui/platforms/win/qrawfont_win.cpp b/src/gui/platforms/win/qrawfont_win.cpp new file mode 100644 index 0000000000..d8acf57431 --- /dev/null +++ b/src/gui/platforms/win/qrawfont_win.cpp @@ -0,0 +1,707 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrawfont_p.h" +#include <private/qsystemlibrary_p.h> + +#if !defined(QT_NO_DIRECTWRITE) +# include "qfontenginedirectwrite_p.h" +# include <dwrite.h> +#endif + +#if !defined(QT_NO_RAWFONT) + +QT_BEGIN_NAMESPACE + +namespace { + + template<typename T> + struct BigEndian + { + quint8 data[sizeof(T)]; + + operator T() const + { + T littleEndian = 0; + for (int i=0; i<sizeof(T); ++i) { + littleEndian |= data[i] << ((sizeof(T) - i - 1) * 8); + } + + return littleEndian; + } + + BigEndian<T> &operator=(const T &t) + { + for (int i=0; i<sizeof(T); ++i) { + data[i] = ((t >> (sizeof(T) - i - 1) * 8) & 0xff); + } + + return *this; + } + }; + +# pragma pack(1) + + // Common structure for all formats of the "name" table + struct NameTable + { + BigEndian<quint16> format; + BigEndian<quint16> count; + BigEndian<quint16> stringOffset; + }; + + struct NameRecord + { + BigEndian<quint16> platformID; + BigEndian<quint16> encodingID; + BigEndian<quint16> languageID; + BigEndian<quint16> nameID; + BigEndian<quint16> length; + BigEndian<quint16> offset; + }; + + struct OffsetSubTable + { + BigEndian<quint32> scalerType; + BigEndian<quint16> numTables; + BigEndian<quint16> searchRange; + BigEndian<quint16> entrySelector; + BigEndian<quint16> rangeShift; + }; + + struct TableDirectory + { + BigEndian<quint32> identifier; + BigEndian<quint32> checkSum; + BigEndian<quint32> offset; + BigEndian<quint32> length; + }; + + struct OS2Table + { + BigEndian<quint16> version; + BigEndian<qint16> avgCharWidth; + BigEndian<quint16> weightClass; + BigEndian<quint16> widthClass; + BigEndian<quint16> type; + BigEndian<qint16> subscriptXSize; + BigEndian<qint16> subscriptYSize; + BigEndian<qint16> subscriptXOffset; + BigEndian<qint16> subscriptYOffset; + BigEndian<qint16> superscriptXSize; + BigEndian<qint16> superscriptYSize; + BigEndian<qint16> superscriptXOffset; + BigEndian<qint16> superscriptYOffset; + BigEndian<qint16> strikeOutSize; + BigEndian<qint16> strikeOutPosition; + BigEndian<qint16> familyClass; + quint8 panose[10]; + BigEndian<quint32> unicodeRanges[4]; + quint8 vendorID[4]; + BigEndian<quint16> selection; + BigEndian<quint16> firstCharIndex; + BigEndian<quint16> lastCharIndex; + BigEndian<qint16> typoAscender; + BigEndian<qint16> typoDescender; + BigEndian<qint16> typoLineGap; + BigEndian<quint16> winAscent; + BigEndian<quint16> winDescent; + BigEndian<quint32> codepageRanges[2]; + BigEndian<qint16> height; + BigEndian<qint16> capHeight; + BigEndian<quint16> defaultChar; + BigEndian<quint16> breakChar; + BigEndian<quint16> maxContext; + }; + +# pragma pack() + + class EmbeddedFont + { + public: + EmbeddedFont(const QByteArray &fontData); + + QString changeFamilyName(const QString &newFamilyName); + QByteArray data() const { return m_fontData; } + TableDirectory *tableDirectoryEntry(const QByteArray &tagName); + QString familyName(TableDirectory *nameTableDirectory = 0); + + private: + QByteArray m_fontData; + }; + + EmbeddedFont::EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) + { + } + + TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) + { + Q_ASSERT(tagName.size() == 4); + + const BigEndian<quint32> *tagIdPtr = + reinterpret_cast<const BigEndian<quint32> *>(tagName.constData()); + quint32 tagId = *tagIdPtr; + + OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); + TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); + + TableDirectory *nameTableDirectoryEntry = 0; + for (int i=0; i<offsetSubTable->numTables; ++i, ++tableDirectory) { + if (tableDirectory->identifier == tagId) { + nameTableDirectoryEntry = tableDirectory; + break; + } + } + + return nameTableDirectoryEntry; + } + + QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) + { + QString name; + + if (nameTableDirectoryEntry == 0) + nameTableDirectoryEntry = tableDirectoryEntry("name"); + + if (nameTableDirectoryEntry != 0) { + NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + + nameTableDirectoryEntry->offset); + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i=0; i<nameTable->count; ++i, ++nameRecord) { + if (nameRecord->nameID == 1 + && nameRecord->platformID == 3 // Windows + && nameRecord->languageID == 0x0409) { // US English + const void *ptr = reinterpret_cast<const quint8 *>(nameTable) + + nameTable->stringOffset + + nameRecord->offset; + + const BigEndian<quint16> *s = reinterpret_cast<const BigEndian<quint16> *>(ptr); + for (int j=0; j<nameRecord->length / sizeof(quint16); ++j) + name += QChar(s[j]); + + break; + } + } + } + + return name; + } + + QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) + { + TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); + if (nameTableDirectoryEntry == 0) + return QString(); + + QString oldFamilyName = familyName(nameTableDirectoryEntry); + + // Reserve size for name table header, five required name records and string + const int requiredRecordCount = 5; + quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; + + int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; + int newFamilyNameSize = newFamilyName.size() * sizeof(quint16); + + const QString regularString = QString::fromLatin1("Regular"); + int regularStringSize = regularString.size() * sizeof(quint16); + + // Align table size of table to 32 bits (pad with 0) + int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; + + QByteArray newNameTable(fullSize, char(0)); + + { + NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); + nameTable->count = requiredRecordCount; + nameTable->stringOffset = sizeOfHeader; + + NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); + for (int i=0; i<requiredRecordCount; ++i, nameRecord++) { + nameRecord->nameID = nameIds[i]; + nameRecord->encodingID = 1; + nameRecord->languageID = 0x0409; + nameRecord->platformID = 3; + nameRecord->length = newFamilyNameSize; + + // Special case for sub-family + if (nameIds[i] == 4) { + nameRecord->offset = newFamilyNameSize; + nameRecord->length = regularStringSize; + } + } + + // nameRecord now points to string data + BigEndian<quint16> *stringStorage = reinterpret_cast<BigEndian<quint16> *>(nameRecord); + const quint16 *sourceString = newFamilyName.utf16(); + for (int i=0; i<newFamilyName.size(); ++i) + stringStorage[i] = sourceString[i]; + stringStorage += newFamilyName.size(); + + sourceString = regularString.utf16(); + for (int i=0; i<regularString.size(); ++i) + stringStorage[i] = sourceString[i]; + } + + quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); + quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); + + quint32 checkSum = 0; + while (p < tableEnd) + checkSum += *(p++); + + nameTableDirectoryEntry->checkSum = checkSum; + nameTableDirectoryEntry->offset = m_fontData.size(); + nameTableDirectoryEntry->length = fullSize; + + m_fontData.append(newNameTable); + + return oldFamilyName; + } + +#if !defined(QT_NO_DIRECTWRITE) + + class DirectWriteFontFileStream: public IDWriteFontFileStream + { + public: + DirectWriteFontFileStream(const QByteArray &fontData) + : m_fontData(fontData) + , m_referenceCount(0) + { + } + + ~DirectWriteFontFileStream() + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, + UINT64 fragmentSize, OUT void **fragmentContext); + void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); + HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); + HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); + + private: + QByteArray m_fontData; + ULONG m_referenceCount; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( + const void **fragmentStart, + UINT64 fileOffset, + UINT64 fragmentSize, + OUT void **fragmentContext) + { + *fragmentContext = NULL; + if (fragmentSize + fileOffset <= m_fontData.size()) { + *fragmentStart = m_fontData.data() + fileOffset; + return S_OK; + } else { + *fragmentStart = NULL; + return E_FAIL; + } + } + + void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) + { + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) + { + *fileSize = m_fontData.size(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) + { + *lastWriteTime = 0; + return E_NOTIMPL; + } + + class DirectWriteFontFileLoader: public IDWriteFontFileLoader + { + public: + DirectWriteFontFileLoader() : m_referenceCount(0) {} + + ~DirectWriteFontFileLoader() + { + } + + inline void addKey(const void *key, const QByteArray &fontData) + { + Q_ASSERT(!m_fontDatas.contains(key)); + m_fontDatas.insert(key, fontData); + } + + inline void removeKey(const void *key) + { + m_fontDatas.remove(key); + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + OUT IDWriteFontFileStream **fontFileStream); + + private: + ULONG m_referenceCount; + QHash<const void *, QByteArray> m_fontDatas; + }; + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, + void **object) + { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + *object = this; + AddRef(); + return S_OK; + } else { + *object = NULL; + return E_NOINTERFACE; + } + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() + { + return InterlockedIncrement(&m_referenceCount); + } + + ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() + { + ULONG newCount = InterlockedDecrement(&m_referenceCount); + if (newCount == 0) + delete this; + return newCount; + } + + HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( + void const *fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream **fontFileStream) + { + Q_UNUSED(fontFileReferenceKeySize); + + if (fontFileReferenceKeySize != sizeof(const void *)) { + qWarning("DirectWriteFontFileLoader::CreateStreamFromKey: Wrong key size"); + return E_FAIL; + } + + const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); + *fontFileStream = NULL; + if (!m_fontDatas.contains(key)) + return E_FAIL; + + QByteArray fontData = m_fontDatas.value(key); + DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); + stream->AddRef(); + *fontFileStream = stream; + + return S_OK; + } + + class CustomFontFileLoader + { + public: + CustomFontFileLoader() : m_directWriteFactory(0), m_directWriteFontFileLoader(0) + { + HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&m_directWriteFactory)); + if (FAILED(hres)) { + qErrnoWarning(hres, "CustomFontFileLoader::CustomFontFileLoader: " + "DWriteCreateFactory failed."); + } else { + m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); + m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); + } + } + + ~CustomFontFileLoader() + { + if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) + m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); + + if (m_directWriteFactory != 0) + m_directWriteFactory->Release(); + } + + void addKey(const void *key, const QByteArray &fontData) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->addKey(key, fontData); + } + + void removeKey(const void *key) + { + if (m_directWriteFontFileLoader != 0) + m_directWriteFontFileLoader->removeKey(key); + } + + IDWriteFontFileLoader *loader() const + { + return m_directWriteFontFileLoader; + } + + private: + IDWriteFactory *m_directWriteFactory; + DirectWriteFontFileLoader *m_directWriteFontFileLoader; + }; + +#endif + +} // Anonymous namespace + + +// From qfontdatabase_win.cpp +extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); +// From qfontdatabase.cpp +extern QFont::Weight weightFromInteger(int weight); + +void QRawFontPrivate::platformCleanUp() +{ + if (fontHandle != NULL) { + if (ptrRemoveFontMemResourceEx == NULL) { + void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); + ptrRemoveFontMemResourceEx = + reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func); + } + + if (ptrRemoveFontMemResourceEx == NULL) { + qWarning("QRawFont::platformCleanUp: Can't find RemoveFontMemResourceEx in gdi32"); + fontHandle = NULL; + } else { + ptrRemoveFontMemResourceEx(fontHandle); + fontHandle = NULL; + } + } +} + +void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData, + int pixelSize, + QFont::HintingPreference hintingPreference) +{ + QByteArray fontData(_fontData); + EmbeddedFont font(fontData); + +#if !defined(QT_NO_DIRECTWRITE) + if (hintingPreference == QFont::PreferDefaultHinting + || hintingPreference == QFont::PreferFullHinting) +#endif + { + GUID guid; + CoCreateGuid(&guid); + + QString uniqueFamilyName = QString::fromLatin1("f") + + QString::number(guid.Data1, 36) + QLatin1Char('-') + + QString::number(guid.Data2, 36) + QLatin1Char('-') + + QString::number(guid.Data3, 36) + QLatin1Char('-') + + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); + + QString actualFontName = font.changeFamilyName(uniqueFamilyName); + if (actualFontName.isEmpty()) { + qWarning("QRawFont::platformLoadFromData: Can't change family name of font"); + return; + } + + if (ptrAddFontMemResourceEx == NULL || ptrRemoveFontMemResourceEx == NULL) { + void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); + ptrRemoveFontMemResourceEx = + reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func); + + func = QSystemLibrary::resolve(QLatin1String("gdi32"), "AddFontMemResourceEx"); + ptrAddFontMemResourceEx = + reinterpret_cast<QRawFontPrivate::PtrAddFontMemResourceEx>(func); + } + + Q_ASSERT(fontHandle == NULL); + if (ptrAddFontMemResourceEx != NULL && ptrRemoveFontMemResourceEx != NULL) { + DWORD count = 0; + fontData = font.data(); + fontHandle = ptrAddFontMemResourceEx(fontData.data(), fontData.size(), 0, &count); + + if (count == 0 && fontHandle != NULL) { + ptrRemoveFontMemResourceEx(fontHandle); + fontHandle = NULL; + } + } + + if (fontHandle == NULL) { + qWarning("QRawFont::platformLoadFromData: AddFontMemResourceEx failed"); + } else { + QFontDef request; + request.family = uniqueFamilyName; + request.pixelSize = pixelSize; + request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; + request.hintingPreference = hintingPreference; + + fontEngine = qt_load_font_engine_win(request); + if (request.family != fontEngine->fontDef.family) { + qWarning("QRawFont::platformLoadFromData: Failed to load font. " + "Got fallback instead: %s", qPrintable(fontEngine->fontDef.family)); + if (fontEngine->cache_count == 0 && fontEngine->ref == 0) + delete fontEngine; + fontEngine = 0; + } else { + Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0); + + // Override the generated font name + static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName; + fontEngine->fontDef.family = actualFontName; + fontEngine->ref.ref(); + } + } + } +#if !defined(QT_NO_DIRECTWRITE) + else { + CustomFontFileLoader fontFileLoader; + fontFileLoader.addKey(this, fontData); + + IDWriteFactory *factory = NULL; + HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&factory)); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: DWriteCreateFactory failed"); + return; + } + + IDWriteFontFile *fontFile = NULL; + void *key = this; + + hres = factory->CreateCustomFontFileReference(&key, sizeof(void *), + fontFileLoader.loader(), &fontFile); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: " + "CreateCustomFontFileReference failed"); + factory->Release(); + return; + } + + BOOL isSupportedFontType; + DWRITE_FONT_FILE_TYPE fontFileType; + DWRITE_FONT_FACE_TYPE fontFaceType; + UINT32 numberOfFaces; + fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); + if (!isSupportedFontType) { + fontFile->Release(); + factory->Release(); + return; + } + + IDWriteFontFace *directWriteFontFace = NULL; + hres = factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, + &directWriteFontFace); + if (FAILED(hres)) { + qErrnoWarning(hres, "QRawFont::platformLoadFromData: CreateFontFace failed"); + fontFile->Release(); + factory->Release(); + return; + } + + fontFile->Release(); + + fontEngine = new QFontEngineDirectWrite(factory, directWriteFontFace, pixelSize); + + // Get font family from font data + fontEngine->fontDef.family = font.familyName(); + fontEngine->ref.ref(); + + directWriteFontFace->Release(); + factory->Release(); + } +#endif + + // Get style and weight info + if (fontEngine != 0) { + TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); + if (os2TableEntry != 0) { + const OS2Table *os2Table = + reinterpret_cast<const OS2Table *>(fontData.constData() + + os2TableEntry->offset); + + bool italic = os2Table->selection & 1; + bool oblique = os2Table->selection & 128; + + if (italic) + fontEngine->fontDef.style = QFont::StyleItalic; + else if (oblique) + fontEngine->fontDef.style = QFont::StyleOblique; + else + fontEngine->fontDef.style = QFont::StyleNormal; + + fontEngine->fontDef.weight = weightFromInteger(os2Table->weightClass); + } + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_RAWFONT diff --git a/src/gui/platforms/win/qregion_win.cpp b/src/gui/platforms/win/qregion_win.cpp new file mode 100644 index 0000000000..3466b62cbd --- /dev/null +++ b/src/gui/platforms/win/qregion_win.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qatomic.h" +#include "qbitmap.h" +#include "qbuffer.h" +#include "qimage.h" +#include "qpolygon.h" +#include "qregion.h" +#include "qt_windows.h" +#include "qpainterpath.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 = 0; + 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; +} + +QRegion qt_region_from_HRGN(HRGN rgn) +{ + int numBytes = GetRegionData(rgn, 0, 0); + if (numBytes == 0) + return QRegion(); + + char *buf = new char[numBytes]; + if (buf == 0) + return QRegion(); + + RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); + if (GetRegionData(rgn, numBytes, rd) == 0) { + delete [] buf; + return QRegion(); + } + + QRegion region; + RECT *r = reinterpret_cast<RECT*>(rd->Buffer); + for (uint i = 0; i < rd->rdh.nCount; ++i) { + QRect rect; + rect.setCoords(r->left, r->top, r->right - 1, r->bottom - 1); + ++r; + region |= rect; + } + + delete [] buf; + + return region; +} + +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; + } + DeleteObject(rgn); + } +} + +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/platforms/win/qsound_win.cpp b/src/gui/platforms/win/qsound_win.cpp new file mode 100644 index 0000000000..c11482d608 --- /dev/null +++ b/src/gui/platforms/win/qsound_win.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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) +{ + mutex = CreateMutex(0, 0, 0); + event = CreateEvent(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; + + PlaySound((wchar_t*)filename.utf16(), 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) { + PlaySound((wchar_t*)filename.utf16(), 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/platforms/win/qwidget_win.cpp b/src/gui/platforms/win/qwidget_win.cpp new file mode 100644 index 0000000000..a02c5ba008 --- /dev/null +++ b/src/gui/platforms/win/qwidget_win.cpp @@ -0,0 +1,2139 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 "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" + +#include "qscrollbar.h" +#include "qabstractscrollarea.h" +#include <private/qabstractscrollarea_p.h> + +#include <qdebug.h> + +#include <private/qapplication_p.h> +#include <private/qwininputcontext_p.h> +#include <private/qpaintengine_raster_p.h> +#include <private/qsystemlibrary_p.h> + +#if defined(Q_WS_WINCE) +#include "qguifunctions_wince.h" +QT_USE_NAMESPACE +extern void qt_wince_maximize(QWidget *widget); //defined in qguifunctions_wince.cpp +extern void qt_wince_unmaximize(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; +#ifndef QT_NO_TABLETEVENT +static void init_wintab_functions(); +static void qt_tablet_init(); +static void qt_tablet_cleanup(); +#endif // QT_NO_TABLETEVENT +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; + +#ifndef QT_NO_TABLETEVENT +static void init_wintab_functions() +{ +#if defined(Q_OS_WINCE) + return; +#else + if (!qt_is_gui_used) + return; + QSystemLibrary library(QLatin1String("wintab32")); + ptrWTOpen = (PtrWTOpen)library.resolve("WTOpenW"); + ptrWTInfo = (PtrWTInfo)library.resolve("WTInfoW"); + 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")); + // We don't need this internal widget to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) + QWidgetPrivate::allWidgets->remove(qt_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; +} +#endif // QT_NO_TABLETEVENT + +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 QT_WIN_CALLBACK QtWndProc(HWND, UINT, WPARAM, LPARAM); + +#define XCOORD_MAX 16383 +#define WRECT_MAX 16383 + +/***************************************************************************** + QWidget member functions + *****************************************************************************/ + +#ifndef Q_WS_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 = 0; + + 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 + data.crect.setRect(GetSystemMetrics(76 /* SM_XVIRTUALSCREEN */), GetSystemMetrics(77 /* SM_YVIRTUALSCREEN */), + GetSystemMetrics(78 /* SM_CXVIRTUALSCREEN */), GetSystemMetrics(79 /* SM_CYVIRTUALSCREEN */)); + } + + parentw = q->parentWidget() ? q->parentWidget()->effectiveWinId() : 0; + + QString title; + int style = WS_CHILD; + int exsty = 0; + + if (window) { + style = GetWindowLong(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)) { + style |= WS_POPUP; + if (!(flags & Qt::MSWindowsFixedSizeDialogHint)) + style |= WS_THICKFRAME; + else + style |= 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) { + title = q->isWindow() ? qAppName() : q->objectName(); + } + + // 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 = SetWindowLong(window, GWL_STYLE, style); + if (!res) + qErrnoWarning("QWidget::create: Failed to set window style"); +#ifdef _WIN64 + res = SetWindowLongPtr( window, GWLP_WNDPROC, (LONG_PTR)QtWndProc ); +#else + res = SetWindowLong( 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 (extra) { + int dx = rect.right - rect.left; + int dy = rect.bottom - rect.top; + w = qMin(w, extra->maxw + dx); + h = qMin(h, extra->maxh + dy); + w = qMax(w, extra->minw + dx); + h = qMax(h, extra->minh + dy); + } + } + if (!wasMoved) { + x = sw/2 - w/2; + y = sh/2 - h/2; + } + } + + id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), + reinterpret_cast<const wchar_t *>(title.utf16()), style, + x, y, w, h, + parentw, NULL, appinst, NULL); + 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 + id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), + reinterpret_cast<const wchar_t *>(title.utf16()), 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(); + } + } + + if (topLevel) { + 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); + } + } + + q->setAttribute(Qt::WA_WState_Created); // accept move/resize events + hd = 0; // no display context + + if (q->testAttribute(Qt::WA_AcceptTouchEvents)) + registerTouchWindow(); + + 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); + } + +#ifndef QT_NO_TABLETEVENT + if (q != qt_tablet_widget && QWidgetPrivate::mapper) + qt_tablet_init(); +#endif // QT_NO_TABLETEVENT + + 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_WS_WINCE + + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(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_WS_WINCE + if (destroyWindow && (windowType() == Qt::Desktop) && !GetDesktopWindow()) { + DestroyWindow(internalWinId()); + } + +#endif + QT_TRY { + d->setWinId(0); + } QT_CATCH (const std::bad_alloc &) { + // swallow - destructors must not throw + } + } +} + +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(effectiveRectFor(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); + +#ifdef Q_WS_WINCE + // Show borderless toplevel windows in tasklist & NavBar + if (!parent) { + QString txt = q->windowTitle().isEmpty()?qAppName():q->windowTitle(); + SetWindowText(q->internalWinId(), (wchar_t*)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 +#ifndef QT_NO_GRAPHICSVIEW + && extra->proxyWidget +#endif //QT_NO_GRAPHICSVIEW + )) { + 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 +#ifndef QT_NO_GRAPHICSVIEW + && extra->proxyWidget +#endif //QT_NO_GRAPHICSVIEW + )) { + 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() {} + +#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)); + SetWindowText(q->internalWinId(), (wchar_t*)caption.utf16()); +} + +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; + + result = pm.toWinHICON(); + + if (cache) { + delete *cache; + *cache = new QPixmap(pm);; + } + } + 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) { + SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall); + SendMessage(q->internalWinId(), WM_SETICON, 1 /* ICON_BIG */, (LPARAM)x->winIconBig); + } else { + SendMessage(q->internalWinId(), WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)x->winIconSmall); + SendMessage(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_WS_WINCE +LRESULT QT_WIN_CALLBACK qJournalRecordProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + return CallNextHookEx(journalRec, nCode, wParam, lParam); +} +#endif //Q_WS_WINCE + +/* Works only as long as pointer is inside the application's window, + which is good enough for QDockWidget. + + Doesn't call SetWindowsHookEx() - 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_WS_WINCE +void QWidget::grabMouse() +{ + if (!qt_nograb()) { + if (mouseGrb) + mouseGrb->releaseMouse(); + journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0); + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + SetCapture(effectiveWinId()); + mouseGrb = this; +#ifndef QT_NO_CURSOR + mouseGrbCur = new QCursor(mouseGrb->cursor()); +#endif + } +} + +#ifndef QT_NO_CURSOR +void QWidget::grabMouse(const QCursor &cursor) +{ + if (!qt_nograb()) { + if (mouseGrb) + mouseGrb->releaseMouse(); + journalRec = SetWindowsHookEx(WH_JOURNALRECORD, (HOOKPROC)qJournalRecordProc, GetModuleHandle(0), 0); + Q_ASSERT(testAttribute(Qt::WA_WState_Created)); + SetCapture(effectiveWinId()); + mouseGrbCur = new QCursor(cursor); + SetCursor(mouseGrbCur->handle()); + mouseGrb = this; + } +} +#endif + +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_WS_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 = Qt::WindowFlags(GetWindowLong(internalWinId(), GWL_STYLE)); +#ifndef Q_FLATTEN_EXPOSE + UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; +#else + UINT style = WS_POPUP; +#endif + if (ulong(d->topData()->savedFlags) & WS_SYSMENU) + style |= WS_SYSMENU; + if (isVisible()) + style |= WS_VISIBLE; + SetWindowLong(internalWinId(), GWL_STYLE, style); + QRect r = QApplication::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; + SetWindowLong(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_WS_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_WS_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_WS_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; + } + + if (data.window_flags & Qt::Window) { + QTLWExtra *extra = topData(); + if (!extra->hotkeyRegistered) { + // Try to set the hotkey using information from STARTUPINFO + STARTUPINFO startupInfo; + GetStartupInfo(&startupInfo); + // If STARTF_USEHOTKEY is set, hStdInput is the virtual keycode + if (startupInfo.dwFlags & 0x00000200) { + WPARAM hotKey = (WPARAM)startupInfo.hStdInput; + SendMessage(data.winid, WM_SETHOTKEY, hotKey, 0); + } + extra->hotkeyRegistered = 1; + } + } + + 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; + // This is to resolve the problem where popups are opened from the + // system tray and not being implicitly activated + if (q->windowType() == Qt::Popup && + !q->parentWidget() && !qApp->activeWindow()) + q->activateWindow(); + } + + winSetupGestures(); + + invalidateBuffer(q->rect()); +} +#endif //Q_WS_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, const QRect &) +{ + 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); + +#ifdef Q_WS_WINCE + // On Windows CE we can't just fiddle around with the window state. + // Too much magic in setWindowState. + if (isResize && q->isMaximized()) + q->setWindowState(q->windowState() & ~Qt::WindowMaximized); +#else + if (isResize) + data.window_state &= ~Qt::WindowMaximized; +#endif + + 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; + SetWindowLong(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) + && GetWindowLong(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_WS_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 & Qt::WindowMaximized) { + qt_wince_maximize(q); + } else { +#endif + MoveWindow(q->internalWinId(), fs.x(), fs.y(), fs.width(), fs.height(), true); + } + 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 the user explicitly asked for the maximize button, we try to add + // it even if the window has fixed size. + if (data.window_flags & Qt::CustomizeWindowHint && + data.window_flags & Qt::WindowMaximizeButtonHint) + return true; + 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_WS_WINCE + Q_Q(QWidget); + + if (!q->isWindow() || !q->testAttribute(Qt::WA_TranslucentBackground)) + return; + + if ((data.window_flags & Qt::FramelessWindowHint) == 0) + return; + + if (!isOpaque && ptrUpdateLayeredWindowIndirect) { + SetWindowLong(q->internalWinId(), GWL_EXSTYLE, + GetWindowLong(q->internalWinId(), GWL_EXSTYLE) | Q_WS_EX_LAYERED); + } else { + SetWindowLong(q->internalWinId(), GWL_EXSTYLE, + GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & ~Q_WS_EX_LAYERED); + } +#endif +} + +void QWidgetPrivate::setConstraints_sys() +{ +#ifndef Q_WS_WINCE_WM + Q_Q(QWidget); + if (q->isWindow() && q->testAttribute(Qt::WA_WState_Created)) { + int style = GetWindowLong(q->internalWinId(), GWL_STYLE); + if (shouldShowMaximizeButton()) + style |= WS_MAXIMIZEBOX; + else + style &= ~WS_MAXIMIZEBOX; + SetWindowLong(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()); + } +} + +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 = GetDC(0); + 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"); + } + ReleaseDC(0, gdc); + } + return val; +} + +void QWidgetPrivate::createSysExtra() +{ +#ifndef QT_NO_DRAGANDDROP + extra->dropTarget = 0; +#endif +} + +#ifndef Q_WS_WINCE +void QWidgetPrivate::deleteSysExtra() +{ +} +#endif //Q_WS_WINCE + +void QWidgetPrivate::createTLSysExtra() +{ + extra->topextra->hotkeyRegistered = 0; + extra->topextra->savedFlags = 0; + extra->topextra->winIconBig = 0; + extra->topextra->winIconSmall = 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 ®ion) +{ + 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)); + if (!SetWindowRgn(data.winid, wr, true)) + DeleteObject(wr); +} + +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 = GetWindowLong(q->internalWinId(), GWL_EXSTYLE); + uint style = GetWindowLong(q->internalWinId(), GWL_STYLE); +#ifndef Q_WS_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_WS_WINCE +void QWidgetPrivate::setWindowOpacity_sys(qreal level) +{ + Q_Q(QWidget); + + if (!isOpaque && ptrUpdateLayeredWindow && (data.window_flags & Qt::FramelessWindowHint)) { + if (GetWindowLong(q->internalWinId(), GWL_EXSTYLE) & Q_WS_EX_LAYERED) { + BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * level), AC_SRC_ALPHA}; + ptrUpdateLayeredWindow(q->internalWinId(), NULL, NULL, NULL, NULL, NULL, 0, &blend, Q_ULW_ALPHA); + } + return; + } + + static bool function_resolved = false; + if (!function_resolved) { + ptrSetLayeredWindowAttributes = + (PtrSetLayeredWindowAttributes) QSystemLibrary::resolve(QLatin1String("user32"), + "SetLayeredWindowAttributes"); + function_resolved = true; + } + + if (!ptrSetLayeredWindowAttributes) + return; + + int wl = GetWindowLong(q->internalWinId(), GWL_EXSTYLE); + + if (level != 1.0) { + if ((wl&Q_WS_EX_LAYERED) == 0) + SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl | Q_WS_EX_LAYERED); + } else if (wl&Q_WS_EX_LAYERED) { + SetWindowLong(q->internalWinId(), GWL_EXSTYLE, wl & ~Q_WS_EX_LAYERED); + } + ptrSetLayeredWindowAttributes(q->internalWinId(), 0, (int)(level * 255), Q_LWA_ALPHA); +} +#endif //Q_WS_WINCE + +// class QGlobalRasterPaintEngine: public QRasterPaintEngine +// { +// public: +// inline QGlobalRasterPaintEngine() : QRasterPaintEngine() { setFlushOnEnd(false); } +// }; +// Q_GLOBAL_STATIC(QGlobalRasterPaintEngine, globalRasterPaintEngine) + + +#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, + QApplication::desktop()->width(), + QApplication::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_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); + return new QRasterWindowSurface(q); +} + +void QWidgetPrivate::setModal_sys() +{ +} + +void QWidgetPrivate::registerTouchWindow() +{ + Q_Q(QWidget); + + // enable WM_TOUCH* messages on our window + if (q->testAttribute(Qt::WA_WState_Created) + && QApplicationPrivate::RegisterTouchWindow + && q->windowType() != Qt::Desktop) + QApplicationPrivate::RegisterTouchWindow(q->effectiveWinId(), 0); +} + +void QWidgetPrivate::winSetupGestures() +{ +#if !defined(QT_NO_GESTURES) && !defined(QT_NO_NATIVE_GESTURES) + Q_Q(QWidget); + if (!q || !q->isVisible() || !nativeGesturePanEnabled) + return; + + if (!QApplicationPrivate::HasTouchSupport) + return; + QApplicationPrivate *qAppPriv = QApplicationPrivate::instance(); + if (!qAppPriv->SetGestureConfig) + return; + WId winid = q->internalWinId(); + + bool needh = false; + bool needv = false; + bool singleFingerPanEnabled = false; + +#ifndef QT_NO_SCROLLAREA + if (QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea*>(q->parent())) { + QScrollBar *hbar = asa->horizontalScrollBar(); + QScrollBar *vbar = asa->verticalScrollBar(); + Qt::ScrollBarPolicy hbarpolicy = asa->horizontalScrollBarPolicy(); + Qt::ScrollBarPolicy vbarpolicy = asa->verticalScrollBarPolicy(); + needh = (hbarpolicy == Qt::ScrollBarAlwaysOn || + (hbarpolicy == Qt::ScrollBarAsNeeded && hbar->minimum() < hbar->maximum())); + needv = (vbarpolicy == Qt::ScrollBarAlwaysOn || + (vbarpolicy == Qt::ScrollBarAsNeeded && vbar->minimum() < vbar->maximum())); + singleFingerPanEnabled = asa->d_func()->singleFingerPanEnabled; + if (!winid) { + winid = q->winId(); // enforces the native winid on the viewport + } + } +#endif //QT_NO_SCROLLAREA + if (winid) { + GESTURECONFIG gc[1]; + memset(gc, 0, sizeof(gc)); + gc[0].dwID = GID_PAN; + if (nativeGesturePanEnabled) { + gc[0].dwWant = GC_PAN; + if (needv && singleFingerPanEnabled) + gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; + else + gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; + if (needh && singleFingerPanEnabled) + gc[0].dwWant |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; + else + gc[0].dwBlock |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; + } else { + gc[0].dwBlock = GC_PAN; + } + + qAppPriv->SetGestureConfig(winid, 0, sizeof(gc)/sizeof(gc[0]), gc, sizeof(gc[0])); + } +#endif +} + +QT_END_NAMESPACE + +#ifdef Q_WS_WINCE +# include "qwidget_wince.cpp" +#endif diff --git a/src/gui/platforms/win/qwidget_wince.cpp b/src/gui/platforms/win/qwidget_wince.cpp new file mode 100644 index 0000000000..7676182ef0 --- /dev/null +++ b/src/gui/platforms/win/qwidget_wince.cpp @@ -0,0 +1,675 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef Q_WS_WINCE + +#include "qguifunctions_wince.h" + +QT_BEGIN_NAMESPACE + +const QString qt_reg_winclass(QWidget *w); // defined in qapplication_win.cpp +extern "C" LRESULT QT_WIN_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); + +#ifndef QT_NO_TABLETEVENT +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; +} +#endif // QT_NO_TABLETEVENT + + +// 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; + + QString title; + int style = WS_CHILD; + int exsty = WS_EX_NOPARENTNOTIFY; + + if (topLevel) { + if (!(flags & Qt::FramelessWindowHint) && !tool && !q->testAttribute(Qt::WA_DontShowOnScreen)) + 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_WS_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) { + title = q->isWindow() ? qAppName() : q->objectName(); + } + + // 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 = SetWindowLong(window, GWL_STYLE, style); + if (!res) + qErrnoWarning("QWidget::create: Failed to set window style"); + + res = SetWindowLong( 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); + id = CreateWindow(reinterpret_cast<const wchar_t *>(windowClassName.utf16()), + reinterpret_cast<const wchar_t *>(title.utf16()), 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); + } + } + + id = CreateWindowEx(exsty, reinterpret_cast<const wchar_t *>(windowClassName.utf16()), + reinterpret_cast<const wchar_t *>(title.utf16()), 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 + id = CreateWindowEx(exsty, (wchar_t*)windowClassName.utf16(), (wchar_t*)title.utf16(), 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; + 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); + } + +#ifndef QT_NO_TABLETEVENT + if (q != qt_tablet_widget && QWidgetPrivate::mapper) + qt_tablet_init_wce(); +#endif // QT_NO_TABLETEVENT + + 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_WS_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 +#else + // Imitate minimizing on Windows mobile by hiding the widget. + if (q->isMinimized()) + sm = SW_HIDE; +#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; + } + + ShowWindow(q->internalWinId(), sm); + + if (q->isMaximized() && q->isWindow()) + qt_wince_maximize(q); + +#ifndef Q_WS_WINCE_WM + if (!qt_wince_is_mobile() && q->isFullScreen()) { + HWND handle = FindWindow(L"HHTaskBar", L""); + if (handle) { + ShowWindow(handle, SW_HIDE); + 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 normal = SW_SHOWNOACTIVATE; + + if ((oldstate & Qt::WindowMinimized) && !(newstate & Qt::WindowMinimized)) + newstate |= Qt::WindowActive; + if (newstate & Qt::WindowActive) + 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 (!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 = GetWindowLong(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); + qt_wince_unmaximize(this); + } + 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 = (Qt::WindowFlags)GetWindowLong(internalWinId(), GWL_STYLE); + UINT style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP; + if (isVisible()) + style |= WS_VISIBLE; + SetWindowLong(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; + SetWindowLong(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 (newstate & Qt::WindowMinimized) + qt_wince_minimize(internalWinId()); + else if (newstate & Qt::WindowMaximized) { + ShowWindow(internalWinId(), max); + qt_wince_maximize(this); + } else { + ShowWindow(internalWinId(), normal); + } + } + } + data->window_state = newstate; + QWindowStateChangeEvent e(oldstate); + QApplication::sendEvent(this, &e); +} + +void QWidgetPrivate::deleteSysExtra() +{ + Q_Q(QWidget); + if (!qt_wince_is_mobile() && q->isFullScreen()) { + HWND handle = FindWindow(L"HHTaskBar", L""); + if (handle) { + ShowWindow(handle, SW_SHOWNORMAL); + 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 QT_WIN_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_WS_WINCE diff --git a/src/gui/platforms/win/qwindowdefs_win.h b/src/gui/platforms/win/qwindowdefs_win.h new file mode 100644 index 0000000000..a4dd38410c --- /dev/null +++ b/src/gui/platforms/win/qwindowdefs_win.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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_CORE_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/platforms/win/qwinnativepangesturerecognizer_win.cpp b/src/gui/platforms/win/qwinnativepangesturerecognizer_win.cpp new file mode 100644 index 0000000000..0d13bafc0c --- /dev/null +++ b/src/gui/platforms/win/qwinnativepangesturerecognizer_win.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qwinnativepangesturerecognizer_win_p.h" + +#include "qevent.h" +#include "qgraphicsitem.h" +#include "qgesture.h" + +#include "private/qgesture_p.h" +#include "private/qevent_p.h" +#include "private/qapplication_p.h" +#include "private/qwidget_p.h" + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_NATIVE_GESTURES) + +QWinNativePanGestureRecognizer::QWinNativePanGestureRecognizer() +{ +} + +QGesture *QWinNativePanGestureRecognizer::create(QObject *target) +{ + if (!target) + return new QPanGesture; // a special case + if (!target->isWidgetType()) + return 0; + if (qobject_cast<QGraphicsObject *>(target)) + return 0; + + QWidget *q = static_cast<QWidget *>(target); + QWidgetPrivate *d = q->d_func(); + d->nativeGesturePanEnabled = true; + d->winSetupGestures(); + + return new QPanGesture; +} + +QGestureRecognizer::Result QWinNativePanGestureRecognizer::recognize(QGesture *state, + QObject *, + QEvent *event) +{ + QPanGesture *q = static_cast<QPanGesture*>(state); + QPanGesturePrivate *d = q->d_func(); + + QGestureRecognizer::Result result = QGestureRecognizer::Ignore; + if (event->type() == QEvent::NativeGesture) { + QNativeGestureEvent *ev = static_cast<QNativeGestureEvent*>(event); + switch(ev->gestureType) { + case QNativeGestureEvent::GestureBegin: + break; + case QNativeGestureEvent::Pan: + result = QGestureRecognizer::TriggerGesture; + event->accept(); + break; + case QNativeGestureEvent::GestureEnd: + if (q->state() == Qt::NoGesture) + return QGestureRecognizer::Ignore; // some other gesture has ended + result = QGestureRecognizer::FinishGesture; + break; + default: + return QGestureRecognizer::Ignore; + } + if (q->state() == Qt::NoGesture) { + d->lastOffset = d->offset = QPointF(); + d->startPosition = ev->position; + } else { + d->lastOffset = d->offset; + d->offset = QPointF(ev->position.x() - d->startPosition.x(), + ev->position.y() - d->startPosition.y()); + } + } + return result; +} + +void QWinNativePanGestureRecognizer::reset(QGesture *state) +{ + QPanGesture *pan = static_cast<QPanGesture*>(state); + QPanGesturePrivate *d = pan->d_func(); + + d->lastOffset = d->offset = QPointF(); + d->startPosition = QPoint(); + d->acceleration = 0; + + QGestureRecognizer::reset(state); +} + +#endif // QT_NO_NATIVE_GESTURES + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES diff --git a/src/gui/platforms/win/qwinnativepangesturerecognizer_win_p.h b/src/gui/platforms/win/qwinnativepangesturerecognizer_win_p.h new file mode 100644 index 0000000000..6d23e41ce3 --- /dev/null +++ b/src/gui/platforms/win/qwinnativepangesturerecognizer_win_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H +#define QWINNATIVEPANGESTURERECOGNIZER_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. +// + +#include <QGestureRecognizer> + +#ifndef QT_NO_GESTURES + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_NATIVE_GESTURES) + +class QWinNativePanGestureRecognizer : public QGestureRecognizer +{ +public: + QWinNativePanGestureRecognizer(); + + QGesture *create(QObject *target); + QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event); + void reset(QGesture *state); +}; + +#endif // QT_NO_NATIVE_GESTURES + +QT_END_NAMESPACE + +#endif // QT_NO_GESTURES + +#endif // QWINNATIVEPANGESTURERECOGNIZER_WIN_P_H diff --git a/src/gui/platforms/x11/qapplication_x11.cpp b/src/gui/platforms/x11/qapplication_x11.cpp new file mode 100644 index 0000000000..20542ea328 --- /dev/null +++ b/src/gui/platforms/x11/qapplication_x11.cpp @@ -0,0 +1,6239 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// ### 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/qcrashhandler_p.h> +#include <private/qcolor_p.h> +#include <private/qcursor_p.h> +#include <private/qiconloader_p.h> +#include <qgtkstyle.h> +#include "qstyle.h" +#include "qmetaobject.h" +#include "qtimer.h" +#include "qlibrary.h" +#include <private/qgraphicssystemfactory_p.h> +#include "qguiplatformplugin_p.h" +#include "qkde_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 + +#ifndef QT_GUI_DOUBLE_CLICK_RADIUS +#define QT_GUI_DOUBLE_CLICK_RADIUS 5 +#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> +#if !defined(QT_NO_XINPUT) +#include <X11/extensions/XI.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <locale.h> + +#include "qwidget_p.h" + +#include <private/qbackingstore_p.h> + +#ifdef QT_RX71_MULTITOUCH +# include <qsocketnotifier.h> +# include <linux/input.h> +# include <errno.h> +#endif + +#if _POSIX_VERSION+0 < 200112L && !defined(Q_OS_BSD4) +# define QT_NO_UNSETENV +#endif + +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" + "_NET_WM_SYNC_REQUEST\0" + "_NET_WM_SYNC_REQUEST_COUNTER\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" + "SAVE_TARGETS\0" + "CLIP_TEMPORARY\0" + "_QT_SELECTION\0" + "_QT_CLIPBOARD_SENTINEL\0" + "_QT_SELECTION_SENTINEL\0" + "CLIPBOARD_MANAGER\0" + + "RESOURCE_MANAGER\0" + + "_XSETROOT_ID\0" + + "_QT_SCROLL_DONE\0" + "_QT_INPUT_ENCODING\0" + + "_MOTIF_WM_HINTS\0" + + "DTWM_IS_RUNNING\0" + "ENLIGHTENMENT_DESKTOP\0" + "_DT_SAVE_MODE\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" + + "_NET_ACTIVE_WINDOW\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" + + // Wacom old. (before version 0.10) + "Wacom Stylus\0" + "Wacom Cursor\0" + "Wacom Eraser\0" + + // Tablet + "STYLUS\0" + "ERASER\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 + +typedef bool(*QX11FilterFunction)(XEvent *event); + +Q_GLOBAL_STATIC(QList<QX11FilterFunction>, x11Filters) + +Q_GUI_EXPORT void qt_installX11EventFilter(QX11FilterFunction func) +{ + Q_ASSERT(func); + + if (QList<QX11FilterFunction> *list = x11Filters()) + list->append(func); +} + +Q_GUI_EXPORT void qt_removeX11EventFilter(QX11FilterFunction func) +{ + Q_ASSERT(func); + + if (QList<QX11FilterFunction> *list = x11Filters()) + list->removeOne(func); +} + + +static bool qt_x11EventFilter(XEvent* ev) +{ + long unused; + if (qApp->filterEvent(ev, &unused)) + return true; + if (const QList<QX11FilterFunction> *list = x11Filters()) { + for (QList<QX11FilterFunction>::const_iterator it = list->constBegin(); it != list->constEnd(); ++it) { + if ((*it)(ev)) + 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); + 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: + QWidgetPrivate* d_func() { return QWidget::d_func(); } + 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) +{ + if (X11->display != dpy) { + // only handle X errors for our display + return 0; + } + + 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(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; + + default: +#if !defined(QT_NO_XINPUT) + if (err->request_code == X11->xinput_major + && err->error_code == (X11->xinput_errorbase + XI_BadDevice) + && err->minor_code == 3 /* X_OpenDevice */) { + return 0; + } +#endif + 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"; +#ifndef QT_NO_XKB + else if(err->request_code == X11->xkb_major) + extensionName = "XKEYBOARD"; +#endif + + 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 + +#ifndef QT_NO_XSYNC +struct qt_sync_request_event_data +{ + WId window; +}; + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +static Bool qt_sync_request_scanner(Display*, XEvent *event, XPointer arg) +{ + qt_sync_request_event_data *data = + reinterpret_cast<qt_sync_request_event_data*>(arg); + if (event->type == ClientMessage && + event->xany.window == data->window && + event->xclient.message_type == ATOM(WM_PROTOCOLS) && + (Atom)event->xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST)) { + QWidget *w = QWidget::find(event->xany.window); + if (QTLWExtra *tlw = ((QETWidget*)w)->d_func()->maybeTopData()) { + const ulong timestamp = (const ulong) event->xclient.data.l[1]; + if (timestamp > X11->time) + X11->time = timestamp; + if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) { + tlw->syncRequestTimestamp = timestamp; + tlw->newCounterValueLo = event->xclient.data.l[2]; + tlw->newCounterValueHi = event->xclient.data.l[3]; + } + } + return true; + } + return false; +} + +#if defined(Q_C_CALLBACKS) +} +#endif +#endif // QT_NO_XSYNC + +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 + 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])); + } + + // ### Fix properly for 4.6 + bool usingGtkSettings = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle"); + if (!usingGtkSettings) { + if (groupCount == QPalette::NColorGroups) + QApplicationPrivate::setSystemPalette(pal); + } + + if (!appFont) { + // ### Fix properly for 4.6 + if (!usingGtkSettings) { + QFont font(QApplication::font()); + QString fontDescription; + // Override Qt font if KDE4 settings can be used + if (X11->desktopVersion == 4) { + QSettings kdeSettings(QKde::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.isNull() && X11->use_xrender) { + stylename = qt_guiPlatformPlugin()->styleName(); + } + + static QString currentStyleName = stylename; + if (QCoreApplication::startingUp()) { + if (!stylename.isEmpty() && QApplicationPrivate::styleOverride.isNull()) + QApplicationPrivate::styleOverride = stylename; + } else { + if (currentStyleName != stylename) { + currentStyleName = stylename; + 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"); + + 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); +} + +// 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); + } + // QGtkStyle sets it's own system palette + bool gtkStyle = QApplicationPrivate::app_style && QApplicationPrivate::app_style->inherits("QGtkStyle"); + bool kdeColors = (QApplication::desktopSettingsAware() && X11->desktopEnvironment == DE_KDE); + if (!gtkStyle && (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; + } + + 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); + } + + 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); + } + + pal = qt_guiPlatformPlugin()->palette().resolve(pal); + 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"))); + } + + QIconLoader::instance()->updateSystemTheme(); +} + + +// 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 *) ×tamp, 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; +Q_GLOBAL_STATIC(QByteArray, wacomDeviceName) +#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; + // otherwise use fontconfig to convert the string to integer + else + FcNameConstant((FcChar8 *) str, val); + } +} + +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; + + X11->use_xkb = false; + X11->xkb_major = 0; + X11->xkb_eventbase = 0; + X11->xkb_errorbase = 0; + + // MIT-SHM + X11->use_mitshm = false; + X11->use_mitshm_pixmaps = 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 = 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) + + if (QApplication::testAttribute(Qt::AA_X11InitThreads)) + XInitThreads(); + + // 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; + + int widthMM = DisplayWidthMM(X11->display, s); + if (widthMM != 0) { + screen->dpiX = (DisplayWidth(X11->display, s) * 254 + widthMM * 5) / (widthMM * 10); + } else { + screen->dpiX = 72; + } + + int heightMM = DisplayHeightMM(X11->display, s); + if (heightMM != 0) { + screen->dpiY = (DisplayHeight(X11->display, s) * 254 + heightMM * 5) / (heightMM * 10); + } else { + screen->dpiY = 72; + } + + 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)); + + // MITSHM only works for local displays, so do a quick check here + // to determine whether the display is local or not (not 100 % accurate). + // BGR server layouts are not supported either, since it requires the raster + // engine to work on a QImage with BGR layout. + bool local = displayName.isEmpty() || displayName.lastIndexOf(QLatin1Char(':')) == 0; + if (local && (qgetenv("QT_X11_NO_MITSHM").toInt() == 0)) { + Visual *defaultVisual = DefaultVisual(X11->display, DefaultScreen(X11->display)); + X11->use_mitshm = ((defaultVisual->red_mask == 0xff0000 + || defaultVisual->red_mask == 0xf800) + && (defaultVisual->green_mask == 0xff00 + || defaultVisual->green_mask == 0x7e0) + && (defaultVisual->blue_mask == 0xff + || defaultVisual->blue_mask == 0x1f)); + X11->use_mitshm_pixmaps = 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"); + X11->ptrXRRSizes = + (PtrXRRSizes) xrandrLib.resolve("XRRSizes"); + } +# else + X11->ptrXRRSelectInput = XRRSelectInput; + X11->ptrXRRUpdateConfiguration = XRRUpdateConfiguration; + X11->ptrXRRRootToScreen = XRRRootToScreen; + X11->ptrXRRQueryExtension = XRRQueryExtension; + X11->ptrXRRSizes = XRRSizes; +# 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; + } + } +#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_XSYNC + int xsync_evbase, xsync_errbase; + int major, minor; + if (XSyncQueryExtension(X11->display, &xsync_evbase, &xsync_errbase)) + XSyncInitialize(X11->display, &major, &minor); +#endif // QT_NO_XSYNC + +#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 + +#ifndef QT_NO_XKB + int xkblibMajor = XkbMajorVersion; + int xkblibMinor = XkbMinorVersion; + X11->use_xkb = XkbQueryExtension(X11->display, + &X11->xkb_major, + &X11->xkb_eventbase, + &X11->xkb_errorbase, + &xkblibMajor, + &xkblibMinor); + if (X11->use_xkb) { + // 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); + + // select for group change events + XkbSelectEventDetails(X11->display, + XkbUseCoreKbd, + XkbStateNotify, + XkbAllStateComponentsMask, + XkbGroupStateMask); + + // current group state is queried when creating the keymapper, no need to do it here + } +#endif + + +#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 !defined(QT_NO_XRENDER) && (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 + X11->fc_hint_style = -1; + 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(); + + // 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 + +#ifndef QT_NO_XFIXES + if (X11->ptrXFixesSelectSelectionInput) + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(), ATOM(_NET_WM_CM_S0), + XFixesSetSelectionOwnerNotifyMask + | XFixesSelectionWindowDestroyNotifyMask + | XFixesSelectionClientCloseNotifyMask); +#endif // QT_NO_XFIXES + X11->compositingManagerRunning = XGetSelectionOwner(X11->display, + ATOM(_NET_WM_CM_S0)); + X11->desktopEnvironment = DE_UNKNOWN; + X11->desktopVersion = 0; + + Atom type; + int format; + unsigned long length, after; + uchar *data = 0; + int rc; + + do { + if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { + X11->desktopEnvironment = DE_KDE; + X11->desktopVersion = qgetenv("KDE_SESSION_VERSION").toInt(); + break; + } + + if (qgetenv("DESKTOP_SESSION") == "gnome") { + X11->desktopEnvironment = DE_GNOME; + break; + } + + // GNOME_DESKTOP_SESSION_ID is deprecated for some reason, but still check it + if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) { + X11->desktopEnvironment = DE_GNOME; + break; + } + + rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(_DT_SAVE_MODE), + 0, 2, False, XA_STRING, &type, &format, &length, + &after, &data); + if (rc == Success && length) { + if (!strcmp(reinterpret_cast<char *>(data), "xfce4")) { + // Pretend that xfce4 is gnome, as it uses the same libraries. + // The detection above is stolen from xdg-open. + X11->desktopEnvironment = DE_GNOME; + break; + } + + // We got the property but it wasn't xfce4. Free data before it gets overwritten. + XFree(data); + data = 0; + } + + rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), ATOM(DTWM_IS_RUNNING), + 0, 1, False, AnyPropertyType, &type, &format, &length, + &after, &data); + if (rc == Success && length) { + // DTWM is running, meaning most likely CDE is running... + X11->desktopEnvironment = DE_CDE; + break; + } + + rc = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), + ATOM(_SGI_DESKS_MANAGER), 0, 1, False, XA_WINDOW, + &type, &format, &length, &after, &data); + if (rc == Success && length) { + X11->desktopEnvironment = DE_4DWM; + break; + } + + if (XGetWindowProperty(X11->display, QX11Info::appRootWindow(), + ATOM(_NET_SUPPORTING_WM_CHECK), + 0, 1024, False, XA_WINDOW, &type, + &format, &length, &after, &data) == Success) { + if (type == XA_WINDOW && format == 32) { + Window windowManagerWindow = *((Window*) data); + XFree(data); + data = 0; + + if (windowManagerWindow != XNone) { + Atom utf8atom = ATOM(UTF8_STRING); + if (XGetWindowProperty(QX11Info::display(), windowManagerWindow, ATOM(_NET_WM_NAME), + 0, 1024, False, utf8atom, &type, + &format, &length, &after, &data) == Success) { + if (type == utf8atom && format == 8) { + if (qstrcmp((const char *)data, "MCompositor") == 0) + X11->desktopEnvironment = DE_MEEGO_COMPOSITOR; + } + } + } + } + } + + } while(0); + + if (data) + XFree((char *)data); + +#if !defined(QT_NO_STYLE_GTK) + if (X11->desktopEnvironment == DE_GNOME) { + static bool menusHaveIcons = QGtkStyle::getGConfBool(QLatin1String("/desktop/gnome/interface/menus_have_icons"), true); + QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, !menusHaveIcons); + } +#endif + 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 (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; + +#if defined(Q_OS_IRIX) + QString devName = QString::fromLocal8Bit(devs->name).toLower(); + if (devName == QLatin1String(WACOM_NAME)) { + deviceType = QTabletEvent::Stylus; + gotStylus = true; + } +#else + if (devs->type == ATOM(XWacomStylus) || devs->type == ATOM(XTabletStylus)) { + deviceType = QTabletEvent::Stylus; + if (wacomDeviceName()->isEmpty()) + wacomDeviceName()->append(devs->name); + gotStylus = true; + } else if (devs->type == ATOM(XWacomEraser) || devs->type == ATOM(XTabletEraser)) { + 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"); + if (X11->startupId) { +#ifndef QT_NO_UNSETENV + unsetenv("DESKTOP_STARTUP_ID"); +#else + // it's a small memory leak, however we won't crash if Qt is + // unloaded and someones tries to use the envoriment. + putenv(strdup("DESKTOP_STARTUP_ID=")); +#endif + } + } 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. + // 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 +} + +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 + } + +#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 + *****************************************************************************/ + +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(); + if (!XTranslateCoordinates(X11->display, + QX11Info::appRootWindow(screen), + ctarget, x, y, &unused, &unused, &ctarget) + || X11->badwindow()) + break; + if (ctarget == wid) { + // Found! + w = widget; + break; + } + } + } + if (w) + break; + } + } + } + return w ? w->window() : 0; +#endif +} + +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; +} + +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 && amw->testAttribute(Qt::WA_X11DoNotAcceptFocus)) + amw = 0; + 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); + } +#ifndef QT_NO_XSYNC + } else if (a == ATOM(_NET_WM_SYNC_REQUEST)) { + const ulong timestamp = (const ulong) event->xclient.data.l[1]; + if (timestamp > X11->time) + X11->time = timestamp; + if (QTLWExtra *tlw = w->d_func()->maybeTopData()) { + if (timestamp == CurrentTime || timestamp > tlw->syncRequestTimestamp) { + tlw->syncRequestTimestamp = timestamp; + tlw->newCounterValueLo = event->xclient.data.l[2]; + tlw->newCounterValueHi = event->xclient.data.l[3]; + } + } +#endif + } + } 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; +} + +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; + if (req->selection == ATOM(_NET_WM_CM_S0)) + X11->compositingManagerRunning = req->owner; + } +#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; + } +#ifndef QT_NO_XKB + else if (X11->use_xkb && event->type == X11->xkb_eventbase) { + XkbAnyEvent *xkbevent = (XkbAnyEvent *) event; + switch (xkbevent->xkb_type) { + case XkbStateNotify: + { + XkbStateNotifyEvent *xkbstateevent = (XkbStateNotifyEvent *) xkbevent; + if ((xkbstateevent->changed & XkbGroupStateMask) != 0) { + qt_keymapper_private()->xkb_currentGroup = xkbstateevent->group; + QKeyMapper::changeKeyboard(); + } + break; + } + default: + break; + } + } +#endif + + 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); + QDesktopWidget *desktop = QApplication::desktop(); + 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)); + QResizeEvent e(w->size(), oldSize); + QApplication::sendEvent(w, &e); + if (w != desktop) + QApplication::sendEvent(desktop, &e); + } +#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()) { + // if we got a MapNotify when we were not waiting for it, it most + // likely means the user has already asked to hide the window before + // it ever being shown, so we try to withdraw a window after sending + // the QShowEvent. + bool pendingHide = widget->testAttribute(Qt::WA_WState_ExplicitShowHide) && widget->testAttribute(Qt::WA_WState_Hidden); + 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); + } + } + if (pendingHide) // hide the window + XWithdrawWindow(X11->display, widget->internalWinId(), widget->x11Info().screen()); + } + 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 don't 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(); + + // emit the workAreaResized() signal + QDesktopWidget *desktop = QApplication::desktop(); + int numScreens = desktop->numScreens(); + for (int i = 0; i < numScreens; ++i) + emit desktop->workAreaResized(i); + } + } else if (widget) { + widget->translatePropertyEvent(event); + } else { + return -1; // don't know this window + } + break; + + default: + break; + } + + return 0; +} + +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) || + (nextEvent.xclient.message_type == ATOM(WM_PROTOCOLS) && + (Atom)nextEvent.xclient.data.l[0] == ATOM(_NET_WM_SYNC_REQUEST))))) { + 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) < QT_GUI_DOUBLE_CLICK_RADIUS && + qAbs(event->xbutton.y - mouseYPos) < QT_GUI_DOUBLE_CLICK_RADIUS) { + 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(); +#ifndef QT_NO_WHEELEVENT + QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient); + if (QApplication::sendSpontaneousEvent(widget, &e)) +#endif + 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(); +#ifndef QT_NO_WHEELEVENT + QWheelEvent e(pos, globalPos, delta, buttons, modifiers, orient); + if (QApplication::sendSpontaneousEvent(widget, &e)) +#endif + 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; + WACOMDEVICE *device = ptrWacomConfigOpenDevice (config, wacomDeviceName()->constData()); + 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 + +struct qt_tablet_motion_data +{ + bool filterByWidget; + const QWidget *widget; + const QWidget *etWidget; + int tabletMotionType; + bool error; // found a reason to stop searching +}; + +static Bool qt_mouseMotion_scanner(Display *, XEvent *event, XPointer arg) +{ + qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg; + if (data->error) + return false; + + if (event->type == MotionNotify) + return true; + + data->error = event->type != data->tabletMotionType; // we stop compression when another event gets in between. + return false; +} + +static Bool qt_tabletMotion_scanner(Display *, XEvent *event, XPointer arg) +{ + qt_tablet_motion_data *data = (qt_tablet_motion_data *) arg; + if (data->error) + return false; + if (event->type == data->tabletMotionType) { + const XDeviceMotionEvent *const motion = reinterpret_cast<const XDeviceMotionEvent*>(event); + if (data->filterByWidget) { + const QPoint curr(motion->x, motion->y); + const QWidget *w = data->etWidget; + const QWidget *const child = w->childAt(curr); + if (child) { + w = child; + } + if (w == data->widget) + return true; + } else { + return true; + } + } + + data->error = event->type != MotionNotify; // we stop compression when another event gets in between. + return false; +} + +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; + const XDeviceMotionEvent *motion = 0; + XDeviceButtonEvent *button = 0; + const XProximityNotifyEvent *proximity = 0; + QEvent::Type t; + Qt::KeyboardModifiers modifiers = 0; +#if !defined (Q_OS_IRIX) + XID device_id; +#endif + + if (ev->type == tablet->xinput_motion) { + motion = reinterpret_cast<const XDeviceMotionEvent*>(ev); + 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 + + if (tablet->widgetToGetPress) { + w = tablet->widgetToGetPress; + } else { + 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); + const bool accepted = e.isAccepted(); + if (!accepted && ev->type == tablet->xinput_motion) { + // If the widget does not accept tablet events, we drop the next ones from the event queue + // for this widget so it is not overloaded with the numerous tablet events. + qt_tablet_motion_data tabletMotionData; + tabletMotionData.tabletMotionType = tablet->xinput_motion; + tabletMotionData.widget = w; + tabletMotionData.etWidget = this; + // if nothing is pressed, the events are filtered by position + tabletMotionData.filterByWidget = (tablet->widgetToGetPress == 0); + + bool reinsertMouseEvent = false; + XEvent mouseMotionEvent; + while (true) { + // Find first mouse event since we expect them in pairs inside Qt + tabletMotionData.error =false; + if (XCheckIfEvent(X11->display, &mouseMotionEvent, &qt_mouseMotion_scanner, (XPointer) &tabletMotionData)) { + reinsertMouseEvent = true; + } else { + break; + } + + // Now discard any duplicate tablet events. + tabletMotionData.error = false; + XEvent dummy; + while (XCheckIfEvent(X11->display, &dummy, &qt_tabletMotion_scanner, (XPointer) &tabletMotionData)) { + // just discard the event + } + } + + if (reinsertMouseEvent) { + XPutBackEvent(X11->display, &mouseMotionEvent); + } + } + } + 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()); + bool isCPos = false; + + 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; + isCPos = true; + } + 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; + isCPos = true; + } + } +#ifndef QT_NO_XSYNC + qt_sync_request_event_data sync_event; + sync_event.window = internalWinId(); + for (XEvent ev;;) { + if (!XCheckIfEvent(X11->display, &ev, &qt_sync_request_scanner, (XPointer)&sync_event)) + break; + } +#endif // QT_NO_XSYNC + } + + if (!isCPos) { + // we didn't get an updated position of the toplevel. + // either we haven't moved or there is a bug in the window manager. + // anyway, let's query the position to be certain. + int x, y; + Window child; + XTranslateCoordinates(X11->display, internalWinId(), + QApplication::desktop()->screen(d->xinfo.screen())->internalWinId(), + 0, 0, &x, &y, &child); + newCPos.rx() = x; + newCPos.ry() = y; + } + + 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) { + if (isVisible() && data->crect.size() != oldSize) { + Q_ASSERT(d->extra->topextra); + QWidgetBackingStore *bs = d->extra->topextra->backingStore.data(); + 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 (!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; + } +#ifndef QT_NO_XSYNC + if (QTLWExtra *tlwExtra = d->maybeTopData()) { + if (tlwExtra->newCounterValueLo != 0 || tlwExtra->newCounterValueHi != 0) { + XSyncValue value; + XSyncIntsToValue(&value, + tlwExtra->newCounterValueLo, + tlwExtra->newCounterValueHi); + + XSyncSetCounter(X11->display, tlwExtra->syncUpdateCounter, value); + tlwExtra->newCounterValueHi = 0; + tlwExtra->newCounterValueLo = 0; + } + } +#endif + 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; +} + +#ifndef QT_NO_WHEELEVENT +void QApplication::setWheelScrollLines(int n) +{ + QApplicationPrivate::wheel_scroll_lines = n; +} + +int QApplication::wheelScrollLines() +{ + return QApplicationPrivate::wheel_scroll_lines; +} +#endif + +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), + restartHint(QSessionManager::RestartIfRunning), 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] = ∝ + 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(_POSIX_THREAD_SAFE_FUNCTIONS) && (_POSIX_THREAD_SAFE_FUNCTIONS - 0 > 0) + QVarLengthArray<char, 1024> buf(qMax<long>(sysconf(_SC_GETPW_R_SIZE_MAX), 1024L)); + struct passwd entry; + while (getpwuid_r(geteuid(), &entry, buf.data(), buf.size(), &entryPtr) == ERANGE) { + if (buf.size() >= 32768) { + // too big already, fail + static char badusername[] = ""; + entryPtr = &entry; + entry.pw_name = badusername; + break; + } + + // retry with a bigger buffer + buf.resize(buf.size() * 2); + } +#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 + +#if defined(QT_RX71_MULTITOUCH) + +static inline int testBit(const char *array, int bit) +{ + return (array[bit/8] & (1<<(bit%8))); +} + +static int openRX71Device(const QByteArray &deviceName) +{ + int fd = open(deviceName, O_RDONLY | O_NONBLOCK); + if (fd == -1) { + fd = -errno; + return fd; + } + + // fetch the event type mask and check that the device reports absolute coordinates + char eventTypeMask[(EV_MAX + sizeof(char) - 1) * sizeof(char) + 1]; + memset(eventTypeMask, 0, sizeof(eventTypeMask)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(eventTypeMask)), eventTypeMask) < 0) { + close(fd); + return -1; + } + if (!testBit(eventTypeMask, EV_ABS)) { + close(fd); + return -1; + } + + // make sure that we can get the absolute X and Y positions from the device + char absMask[(ABS_MAX + sizeof(char) - 1) * sizeof(char) + 1]; + memset(absMask, 0, sizeof(absMask)); + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absMask)), absMask) < 0) { + close(fd); + return -1; + } + if (!testBit(absMask, ABS_X) || !testBit(absMask, ABS_Y)) { + close(fd); + return -1; + } + + return fd; +} + +void QApplicationPrivate::initializeMultitouch_sys() +{ + Q_Q(QApplication); + + QByteArray deviceName = QByteArray("/dev/input/event"); + int currentDeviceNumber = 0; + for (;;) { + int fd = openRX71Device(QByteArray(deviceName + QByteArray::number(currentDeviceNumber++))); + if (fd == -ENOENT) { + // no more devices + break; + } + if (fd < 0) { + // not a touch device + continue; + } + + struct input_absinfo abs_x, abs_y, abs_z; + ioctl(fd, EVIOCGABS(ABS_X), &abs_x); + ioctl(fd, EVIOCGABS(ABS_Y), &abs_y); + ioctl(fd, EVIOCGABS(ABS_Z), &abs_z); + + int deviceNumber = allRX71TouchPoints.count(); + + QSocketNotifier *socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); + QObject::connect(socketNotifier, SIGNAL(activated(int)), q, SLOT(_q_readRX71MultiTouchEvents())); + + RX71TouchPointState touchPointState = { + socketNotifier, + QTouchEvent::TouchPoint(deviceNumber), + + abs_x.minimum, abs_x.maximum, q->desktop()->screenGeometry().width(), + abs_y.minimum, abs_y.maximum, q->desktop()->screenGeometry().height(), + abs_z.minimum, abs_z.maximum + }; + allRX71TouchPoints.append(touchPointState); + } + + hasRX71MultiTouch = allRX71TouchPoints.count() > 1; + if (!hasRX71MultiTouch) { + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); + } +} + +void QApplicationPrivate::cleanupMultitouch_sys() +{ + hasRX71MultiTouch = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) { + QSocketNotifier *socketNotifier = allRX71TouchPoints.at(i).socketNotifier; + close(socketNotifier->socket()); + delete socketNotifier; + } + allRX71TouchPoints.clear(); +} + +bool QApplicationPrivate::readRX71MultiTouchEvents(int deviceNumber) +{ + RX71TouchPointState &touchPointState = allRX71TouchPoints[deviceNumber]; + QSocketNotifier *socketNotifier = touchPointState.socketNotifier; + int fd = socketNotifier->socket(); + + QTouchEvent::TouchPoint &touchPoint = touchPointState.touchPoint; + + bool down = touchPoint.state() != Qt::TouchPointReleased; + if (down) + touchPoint.setState(Qt::TouchPointStationary); + + bool changed = false; + for (;;) { + struct input_event inputEvent; + int bytesRead = read(fd, &inputEvent, sizeof(inputEvent)); + if (bytesRead <= 0) + break; + if (bytesRead != sizeof(inputEvent)) { + qWarning("Qt: INTERNAL ERROR: short read in readRX71MultiTouchEvents()"); + return false; + } + + switch (inputEvent.type) { + case EV_SYN: + changed = true; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + case Qt::TouchPointReleased: + // make sure we don't compress pressed and releases with any other events + return changed; + default: + break; + } + continue; + case EV_KEY: + case EV_ABS: + break; + default: + qWarning("Qt: WARNING: unknown event type %d on multitouch device", inputEvent.type); + continue; + } + + QPointF screenPos = touchPoint.screenPos(); + switch (inputEvent.code) { + case BTN_TOUCH: + if (!down && inputEvent.value != 0) + touchPoint.setState(Qt::TouchPointPressed); + else if (down && inputEvent.value == 0) + touchPoint.setState(Qt::TouchPointReleased); + break; + case ABS_TOOL_WIDTH: + case ABS_VOLUME: + case ABS_PRESSURE: + // ignore for now + break; + case ABS_X: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minX) + / qreal(touchPointState.maxX - touchPointState.minX)) + * touchPointState.scaleX); + screenPos.rx() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Y: + { + qreal newValue = ((qreal(inputEvent.value - touchPointState.minY) + / qreal(touchPointState.maxY - touchPointState.minY)) + * touchPointState.scaleY); + screenPos.ry() = newValue; + touchPoint.setScreenPos(screenPos); + break; + } + case ABS_Z: + { + // map Z (signal strength) to pressure for now + qreal newValue = (qreal(inputEvent.value - touchPointState.minZ) + / qreal(touchPointState.maxZ - touchPointState.minZ)); + touchPoint.setPressure(newValue); + break; + } + default: + qWarning("Qt: WARNING: unknown event code %d on multitouch device", inputEvent.code); + continue; + } + } + + if (down && touchPoint.state() != Qt::TouchPointReleased) + touchPoint.setState(changed ? Qt::TouchPointMoved : Qt::TouchPointStationary); + + return changed; +} + +void QApplicationPrivate::_q_readRX71MultiTouchEvents() +{ + // read touch events from all devices + bool changed = false; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + changed = readRX71MultiTouchEvents(i) || changed; + if (!changed) + return; + + QList<QTouchEvent::TouchPoint> touchPoints; + for (int i = 0; i < allRX71TouchPoints.count(); ++i) + touchPoints.append(allRX71TouchPoints.at(i).touchPoint); + + translateRawTouchEvent(0, QTouchEvent::TouchScreen, touchPoints); +} + +#else // !QT_RX71_MULTITOUCH + +void QApplicationPrivate::initializeMultitouch_sys() +{ } +void QApplicationPrivate::cleanupMultitouch_sys() +{ } + +#endif // QT_RX71_MULTITOUCH + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qclipboard_x11.cpp b/src/gui/platforms/x11/qclipboard_x11.cpp new file mode 100644 index 0000000000..d566c86e04 --- /dev/null +++ b/src/gui/platforms/x11/qclipboard_x11.cpp @@ -0,0 +1,1539 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// #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 "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 "qelapsedtimer.h" +#include "qvariant.h" +#include "qdnd_p.h" +#include <private/qwidget_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")); + // We don't need this internal widgets to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) { + QWidgetPrivate::allWidgets->remove(owner); + QWidgetPrivate::allWidgets->remove(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(); + +#ifndef QT_NO_XFIXES + if (X11->use_xfixes && X11->ptrXFixesSelectSelectionInput) { + const unsigned long eventMask = + XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask; + for (int i = 0; i < X11->screenCount; ++i) { + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), + XA_PRIMARY, eventMask); + X11->ptrXFixesSelectSelectionInput(X11->display, QX11Info::appRootWindow(i), + ATOM(CLIPBOARD), eventMask); + } + } +#endif // QT_NO_XFIXES + + 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(); + // We need this value just for completeness, we don't use it. + long 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) +{ + QElapsedTimer started; + started.start(); + QElapsedTimer 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.start(); + + 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.start(); + + 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) +{ + 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; + buffer->resize(newSize); + + bool ok = (buffer->size() == newSize); + VDEBUG("QClipboard: read_property(): buffer resized to %d", buffer->size()); + + if (ok && newSize) { + // 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 = buffer_offset; + textprop.value = (unsigned char *) buffer->data(); + + char **list_ret = 0; + int count; + if (XmbTextPropertyToTextList(display, &textprop, &list_ret, + &count) == Success && count && list_ret) { + offset = buffer_offset = strlen(list_ret[0]); + buffer->resize(offset); + memcpy(buffer->data(), list_ret[0], offset); + } + if (list_ret) XFreeStringList(list_ret); + } + } + + // 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)) { + 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")); + // We don't need this internal widget to appear in QApplication::topLevelWidgets() + if (QWidgetPrivate::allWidgets) + QWidgetPrivate::allWidgets->remove(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)); + types.append(ATOM(SAVE_TARGETS)); + + 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()) { // 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 property; + } + + // 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) { + // That means application exits and we need to give clipboard + // content to the clipboard manager. + // First we check if there is a clipboard manager. + if (XGetSelectionOwner(X11->display, ATOM(CLIPBOARD_MANAGER)) == XNone + || !owner) + return true; + + Window ownerId = owner->internalWinId(); + Q_ASSERT(ownerId); + // we delete the property so the manager saves all TARGETS. + XDeleteProperty(X11->display, ownerId, ATOM(_QT_SELECTION)); + XConvertSelection(X11->display, ATOM(CLIPBOARD_MANAGER), ATOM(SAVE_TARGETS), + ATOM(_QT_SELECTION), ownerId, X11->time); + XSync(dpy, false); + + XEvent event; + // waiting until the clipboard manager fetches the content. + if (!X11->clipboardWaitForEvent(ownerId, SelectionNotify, &event, 10000)) { + qWarning("QClipboard: Unable to receive an event from the " + "clipboard manager in a reasonable time"); + } + + 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) + || 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)) { + 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/platforms/x11/qcolormap_x11.cpp b/src/gui/platforms/x11/qcolormap_x11.cpp new file mode 100644 index 0000000000..05eefa455b --- /dev/null +++ b/src/gui/platforms/x11/qcolormap_x11.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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; + +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; + } +} + +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/platforms/x11/qcursor_x11.cpp b/src/gui/platforms/x11/qcursor_x11.cpp new file mode 100644 index 0000000000..d0ed98e1fe --- /dev/null +++ b/src/gui/platforms/x11/qcursor_x11.cpp @@ -0,0 +1,637 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qdebug.h> +#include <qdatastream.h> +#include <private/qcursor_p.h> +#include <private/qt_x11_p.h> +#include <private/qapplication_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" +#include <private/qpixmap_x11_p.h> + +QT_BEGIN_NAMESPACE + +// Define QT_USE_APPROXIMATE_CURSORS when compiling if you REALLY want to +// use the ugly X11 cursors. + +/***************************************************************************** + 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", + "copy", + "move", + "link" + }; + +#ifndef QT_NO_XCURSOR + if (X11->ptrXcursorLibraryLoadCursor) { + // special case for non-standard dnd-* cursors + switch (cshape) { + case Qt::DragCopyCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-copy"); + break; + case Qt::DragMoveCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-move"); + break; + case Qt::DragLinkCursor: + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, "dnd-link"); + break; + default: + break; + } + if (!hcurs) + hcurs = X11->ptrXcursorLibraryLoadCursor(dpy, cursorNames[cshape]); + } + if (hcurs) + return; +#endif // QT_NO_XCURSOR + + 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 }; + + // Non-standard X11 cursors are created from bitmaps + +#ifndef QT_USE_APPROXIMATE_CURSORS + 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 *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 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 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}; + + static const uchar * 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 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}; + + 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_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, reinterpret_cast<const char*>(cursor_bits16[i]), 16, 16); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char*>(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, reinterpret_cast<const char *>(cursor_bits32[i]), 32, 32); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(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, reinterpret_cast<const char *>(cursor_bits20[i]), 20, 20); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(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, reinterpret_cast<const char *>(open ? openhand_bits : closedhand_bits), 16, 16); + pmm = XCreateBitmapFromData(dpy, rootwin, reinterpret_cast<const char *>(open ? openhandm_bits : closedhandm_bits), 16, 16); + hcurs = XCreatePixmapCursor(dpy, pm, pmm, &fg, &bg, 8, 8); + } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor + || cshape == Qt::DragLinkCursor) { + XColor bg, fg; + bg.red = 255 << 8; + bg.green = 255 << 8; + bg.blue = 255 << 8; + fg.red = 0; + fg.green = 0; + fg.blue = 0; + QImage image = QApplicationPrivate::instance()->getPixmapCursor(cshape).toImage(); + pm = QX11PixmapData::createBitmapFromImage(image); + pmm = QX11PixmapData::createBitmapFromImage(image.createAlphaMask().convertToFormat(QImage::Format_MonoLSB)); + 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; + case Qt::DragCopyCursor: + sh = XC_tcross; + break; + case Qt::DragLinkCursor: + sh = XC_center_ptr; + break; + case Qt::DragMoveCursor: + sh = XC_top_left_arrow; + 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/platforms/x11/qdesktopwidget_x11.cpp b/src/gui/platforms/x11/qdesktopwidget_x11.cpp new file mode 100644 index 0000000000..b0f12903a1 --- /dev/null +++ b/src/gui/platforms/x11/qdesktopwidget_x11.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 = q_check_ptr((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 (X11->isSupportedByWM(ATOM(_NET_WORKAREA))) { + int x11Screen = isVirtualDesktop() ? DefaultScreen(X11->display) : screen; + + Atom ret; + int format, e; + unsigned char *data = 0; + unsigned long nitems, after; + + e = XGetWindowProperty(X11->display, + QX11Info::appRootWindow(x11Screen), + ATOM(_NET_WORKAREA), 0, 4, False, XA_CARDINAL, + &ret, &format, &nitems, &after, &data); + + QRect workArea; + if (e == Success && ret == XA_CARDINAL && + format == 32 && nitems == 4) { + long *workarea = (long *) data; + workArea = QRect(workarea[0], workarea[1], workarea[2], workarea[3]); + } else { + workArea = screenGeometry(screen); + } + + if (isVirtualDesktop()) { + // intersect the workarea (which spawns all Xinerama screens) with the rect for the + // requested screen + workArea &= screenGeometry(screen); + } + + d->workareas[screen] = workArea; + + 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); + int oldScreenCount = d->screenCount; + QVector<QRect> oldRects(oldScreenCount); + for (int i = 0; i < oldScreenCount; ++i) { + oldRects[i] = d->rects[i]; + } + + d->init(); + + for (int i = 0; i < qMin(oldScreenCount, d->screenCount); ++i) { + if (oldRects.at(i) != d->rects[i]) + emit resized(i); + } + + if (oldScreenCount != d->screenCount) { + emit screenCountChanged(d->screenCount); + } + + qt_desktopwidget_workarea_dirty = true; + QWidget::resizeEvent(event); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qdnd_x11.cpp b/src/gui/platforms/x11/qdnd_x11.cpp new file mode 100644 index 0000000000..9ff1543e51 --- /dev/null +++ b/src/gui/platforms/x11/qdnd_x11.cpp @@ -0,0 +1,2072 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "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 "qiodevice.h" +#include "qpointer.h" +#include "qcursor.h" +#include "qelapsedtimer.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 "qapplication_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); + ret = true; + + 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; + ret = true; + } + } else { + DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName)); + } + } + return ret && data != 0; +} + +//$$$ +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::fromRawData((const QChar *)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()); + if (!xpm) + return QByteArray(); + QPixmap qpm = QPixmap::fromX11Pixmap(xpm); + QImageWriter imageWriter; + imageWriter.setFormat("PPMRAW"); + QImage imageToWrite = qpm.toImage(); + 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 = 0; + XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0, + qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval); + if (retval) { + Atom *data = (Atom *)retval; + for (; j<qt_xdnd_max_type && j < (int)n; j++) { + qt_xdnd_types[j] = data[j]; + } + 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; + + QDragManager *manager = QDragManager::self(); + + XClientMessageEvent leave; + leave.type = ClientMessage; + leave.window = qt_xdnd_current_target; + leave.format = 32; + leave.message_type = ATOM(XdndLeave); + leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId(); + 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 + 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; +} + +// TODO: remove and use QApplication::currentKeyboardModifiers() in Qt 4.8. +static Qt::KeyboardModifiers currentKeyboardModifiers() +{ + Window root; + Window child; + int root_x, root_y, win_x, win_y; + uint keybstate; + for (int i = 0; i < ScreenCount(X11->display); ++i) { + if (XQueryPointer(X11->display, QX11Info::appRootWindow(i), &root, &child, + &root_x, &root_y, &win_x, &win_y, &keybstate)) + return X11->translateModifiers(keybstate & 0x00ff); + } + return 0; +} + +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; + + // Drop coming from another app? Update keyboard modifiers. + if (!qt_xdnd_dragging) { + QApplicationPrivate::modifier_buttons = currentKeyboardModifiers(); + } + + 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::ShortcutOverride) { + // prevent accelerators from firing while dragging + e->accept(); + 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: + return true; + default: + return false; + } +} + +void QDragManager::updateCursor() +{ + if (!noDropCursor) { +#ifndef QT_NO_CURSOR + noDropCursor = new QCursor(Qt::ForbiddenCursor); + moveCursor = new QCursor(Qt::DragMoveCursor); + copyCursor = new QCursor(Qt::DragCopyCursor); + linkCursor = new QCursor(Qt::DragLinkCursor); +#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)) { + 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(); + + QElapsedTimer timer; + timer.start(); + do { + XEvent event; + if (XCheckTypedEvent(X11->display, ClientMessage, &event)) + qApp->x11ProcessEvent(&event); + + // 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 && timer.hasExpired(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/platforms/x11/qeventdispatcher_x11.cpp b/src/gui/platforms/x11/qeventdispatcher_x11.cpp new file mode 100644 index 0000000000..110786a378 --- /dev/null +++ b/src/gui/platforms/x11/qeventdispatcher_x11.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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/platforms/x11/qeventdispatcher_x11_p.h b/src/gui/platforms/x11/qeventdispatcher_x11_p.h new file mode 100644 index 0000000000..cfdd2a5fa6 --- /dev/null +++ b/src/gui/platforms/x11/qeventdispatcher_x11_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/platforms/x11/qfont_x11.cpp b/src/gui/platforms/x11/qfont_x11.cpp new file mode 100644 index 0000000000..c72a5fade5 --- /dev/null +++ b/src/gui/platforms/x11/qfont_x11.cpp @@ -0,0 +1,368 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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; + +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); +} + +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::Monospace: + return QString::fromLatin1("Courier New"); + + case QFont::Cursive: + return QString::fromLatin1("Comic Sans MS"); + + case QFont::Fantasy: + return QString::fromLatin1("Impact"); + + 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/platforms/x11/qfontdatabase_x11.cpp b/src/gui/platforms/x11/qfontdatabase_x11.cpp new file mode 100644 index 0000000000..0c0c4c8343 --- /dev/null +++ b/src/gui/platforms/x11/qfontdatabase_x11.cpp @@ -0,0 +1,2146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qplatformdefs.h> + +#include <qdebug.h> +#include <qpaintdevice.h> +#include <qelapsedtimer.h> + +#include <private/qt_x11_p.h> +#include "qx11info_x11.h" +#include <qdebug.h> +#include <qfile.h> +#include <qtemporaryfile.h> +#include <qabstractfileengine.h> +#include <qmath.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); + +// from qapplication.cpp +extern bool qt_is_gui_used; + +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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0 }, + // *-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, 0 }, + // 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, 0 } + +}; + +// ----- 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 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 != QLatin1String("*") && (!desc || desc->family->count > 1)) + fd->family += + QLatin1String(" [") + foundry + QLatin1Char(']'); + + 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; + + fontDef.hintingPreference = request.hintingPreference; + 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 = qt_defaultDpiY(); + } + + double size; + if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) + fontDef.pixelSize = 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 + "" // N'Ko +}; +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 + 0x7ca // N'Ko +}; +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 + 0 // N'Ko +}; +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 + 0x7ca // N'Ko +}; +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 + "nko " // N'Ko +}; +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; + + FcPattern *pattern = FcPatternCreate(); + FcDefaultSubstitute(pattern); + FcChar8 *lang = 0; + if (FcPatternGetString(pattern, FC_LANG, 0, &lang) == FcResultMatch) + db->systemLang = QString::fromUtf8((const char *) lang); + FcPatternDestroy(pattern); + + 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, bool forceXLFD = false) +{ + if (X11->has_fontconfig && !forceXLFD) { + initializeDb(); + return; + } + +#ifdef QFONTDATABASE_DEBUG + QElapsedTimer 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; + + QElapsedTimer 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", int(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; + FcPatternDel(pattern, FC_WEIGHT); + 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; + FcPatternDel(pattern, FC_SLANT); + FcPatternAddInteger(pattern, FC_SLANT, slant_value); + + double size_value = qMax(qreal(1.), request.pixelSize); + FcPatternDel(pattern, FC_PIXEL_SIZE); + FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); + + int stretch = request.stretch; + if (!stretch) + stretch = 100; + FcPatternDel(pattern, FC_WIDTH); + FcPatternAddInteger(pattern, FC_WIDTH, stretch); + + if (X11->display && QX11Info::appDepth(screen) <= 8) { + FcPatternDel(pattern, FC_ANTIALIAS); + // can't do antialiasing on 8bpp + FcPatternAddBool(pattern, FC_ANTIALIAS, false); + } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { + FcPatternDel(pattern, FC_ANTIALIAS); + FcPatternAddBool(pattern, FC_ANTIALIAS, + !(request.styleStrategy & QFont::NoAntialias)); + } + + if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { + Q_ASSERT(script < QUnicodeTables::ScriptCount); + FcLangSet *ls = FcLangSetCreate(); + FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); + FcPatternDel(pattern, FC_LANG); + 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 if (*specialLanguages[script] != '\0'){ + 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, QList<int>(), true); + 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) { + if (desc.encoding->encoding >= 0) + 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 || e->encoding < 0) + 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) && enc->id >= 0) { + 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; +} + +#if (defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6)) && defined(Q_CC_GNU) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) +#define NEEDS_GCC_BUG_WORKAROUND +#endif + +#ifdef NEEDS_GCC_BUG_WORKAROUND +static inline void gccBugWorkaround(const QFontDef &req) +{ + char buffer[8]; + snprintf(buffer, 8, "%f", req.pixelSize); +} +#endif + +/*! \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 = qFloor(qt_pixelSize(req.pointSize, d->dpi) * 100.0 + 0.5) * 0.01; + if (req.pixelSize < 1) + req.pixelSize = 1; + +#ifdef NEEDS_GCC_BUG_WORKAROUND + // req.pixelSize ends up with a bogus value unless this workaround is called + gccBugWorkaround(req); +#endif + + 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 + if (req.pointSize < 0) + 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 && qt_is_gui_used) { + 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-existent"); + 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; + QFontDatabasePrivate *db = privateDb(); + + 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, *familylang = 0; + int i, n = 0; + for (i = 0; ; i++) { + if (FcPatternGetString(pattern, FC_FAMILYLANG, i, &familylang) != FcResultMatch) + break; + QString familyLang = QString::fromUtf8((const char *) familylang); + if (familyLang.compare(db->systemLang, Qt::CaseInsensitive) == 0) { + n = i; + break; + } + } + + if (FcPatternGetString(pattern, FC_FAMILY, n, &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 +} + +QString QFontDatabase::resolveFontFamilyAlias(const QString &family) +{ +#if defined(QT_NO_FONTCONFIG) + return family; +#else + FcPattern *pattern = FcPatternCreate(); + if (!pattern) + return family; + + FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) family.toUtf8().data()); + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcChar8 *familyAfterSubstitution; + FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution); + QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution); + FcPatternDestroy(pattern); + + return resolved; +#endif +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qfontengine_x11.cpp b/src/gui/platforms/x11/qfontengine_x11.cpp new file mode 100644 index 0000000000..4260b85b11 --- /dev/null +++ b/src/gui/platforms/x11/qfontengine_x11.cpp @@ -0,0 +1,1215 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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).startsWith(QLatin1Char('/'))) + 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 performance + 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); + // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel + 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] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); + } 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) { + // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel + // XCharStruct::width is defined as the advance + 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 { + face_id.index = 0; + face_id.filename = '-' + QFontEngine::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 = QApplication::testAttribute(Qt::AA_X11InitThreads) || (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; + } + } + + if (fd.hintingPreference != QFont::PreferDefaultHinting) { + switch (fd.hintingPreference) { + case QFont::PreferNoHinting: + default_hint_style = HintNone; + break; + case QFont::PreferVerticalHinting: + default_hint_style = HintLight; + break; + case QFont::PreferFullHinting: + default: + default_hint_style = HintFull; + break; + } + } +#ifdef FC_HINT_STYLE + else { + int hint_style = 0; + // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match + // the behavior of cairo + if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME) + hint_style = X11->fc_hint_style; + else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch + && X11->fc_hint_style > -1) + 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 +} + +QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const +{ + QFontDef fontDef; + fontDef.pixelSize = pixelSize; + QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef); + if (!fe->initFromFontEngine(this)) { + delete fe; + return 0; + } else { + fe->xglyph_format = xglyph_format; + return fe; + } +} + +#endif // QT_NO_FONTCONFIG + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qfontengine_x11_p.h b/src/gui/platforms/x11/qfontengine_x11_p.h new file mode 100644 index 0000000000..d7eb39daaa --- /dev/null +++ b/src/gui/platforms/x11/qfontengine_x11_p.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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(const QFontDef &fontDef) : QFontEngineFT(fontDef) {} + explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen); + ~QFontEngineX11FT(); + + QFontEngine *cloneWithSize(qreal pixelSize) const; + +#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/platforms/x11/qkde.cpp b/src/gui/platforms/x11/qkde.cpp new file mode 100644 index 0000000000..7d333feb9a --- /dev/null +++ b/src/gui/platforms/x11/qkde.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qkde_p.h" +#include <QtCore/QLibrary> +#include <QtCore/QDir> +#include <QtCore/qdebug.h> +#include <QtCore/QSettings> +#include "QtGui/qstylefactory.h" +#include "qt_x11_p.h" + +#if defined(Q_WS_X11) + +QT_BEGIN_NAMESPACE + +/*! \internal +Gets the current KDE home path +like "/home/troll/.kde" +*/ +QString QKde::kdeHome() +{ + static QString kdeHomePath; + if (kdeHomePath.isEmpty()) { + kdeHomePath = QString::fromLocal8Bit(qgetenv("KDEHOME")); + if (kdeHomePath.isEmpty()) { + QDir homeDir(QDir::homePath()); + QString kdeConfDir(QLatin1String("/.kde")); + if (4 == X11->desktopVersion && homeDir.exists(QLatin1String(".kde4"))) + kdeConfDir = QLatin1String("/.kde4"); + kdeHomePath = QDir::homePath() + kdeConfDir; + } + } + return kdeHomePath; +} + +/*!\internal + Reads the color from the config, and store it in the palette with the given color role if found + */ +static bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QSettings &kdeSettings, const QString &kde4Key, const QString &kde3Key = QString()) +{ + QVariant variant = kdeSettings.value(kde4Key); + if (!variant.isValid()) + QVariant variant = kdeSettings.value(kde3Key); + 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(); + pal->setBrush(role, QColor(r, g, b)); + return true; + } + } + return false; +} + + +/*!\internal + Returns the KDE palette +*/ +QPalette QKde::kdePalette() +{ + const QSettings theKdeSettings(QKde::kdeHome() + + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat); + QPalette pal; + + // Setup KDE palette + kdeColor(&pal, QPalette::Button, theKdeSettings, QLatin1String("Colors:Button/BackgroundNormal"), QLatin1String("buttonBackground")); + kdeColor(&pal, QPalette::Window, theKdeSettings, QLatin1String("Colors:Window/BackgroundNormal"), QLatin1String("background")); + kdeColor(&pal, QPalette::Text, theKdeSettings, QLatin1String("Colors:View/ForegroundNormal"), QLatin1String("foreground")); + kdeColor(&pal, QPalette::WindowText, theKdeSettings, QLatin1String("Colors:Window/ForegroundNormal"), QLatin1String("windowForeground")); + kdeColor(&pal, QPalette::Base, theKdeSettings, QLatin1String("Colors:View/BackgroundNormal"), QLatin1String("windowBackground")); + kdeColor(&pal, QPalette::Highlight, theKdeSettings, QLatin1String("Colors:Selection/BackgroundNormal"), QLatin1String("selectBackground")); + kdeColor(&pal, QPalette::HighlightedText, theKdeSettings, QLatin1String("Colors:Selection/ForegroundNormal"), QLatin1String("selectForeground")); + kdeColor(&pal, QPalette::AlternateBase, theKdeSettings, QLatin1String("Colors:View/BackgroundAlternate"), QLatin1String("alternateBackground")); + kdeColor(&pal, QPalette::ButtonText, theKdeSettings, QLatin1String("Colors:Button/ForegroundNormal"), QLatin1String("buttonForeground")); + kdeColor(&pal, QPalette::Link, theKdeSettings, QLatin1String("Colors:View/ForegroundLink"), QLatin1String("linkColor")); + kdeColor(&pal, QPalette::LinkVisited, theKdeSettings, QLatin1String("Colors:View/ForegroundVisited"), QLatin1String("visitedLinkColor")); + //## TODO tooltip color + + return pal; +} + +/*!\internal + Returns the name of the QStyle to use. + (read from the kde config if needed) +*/ +QString QKde::kdeStyle() +{ + if (X11->desktopVersion >= 4) { + QSettings kdeSettings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), QSettings::IniFormat); + QString style = kdeSettings.value(QLatin1String("widgetStyle"), QLatin1String("Oxygen")).toString(); + + QStringList availableStyles = QStyleFactory::keys(); + if(availableStyles.contains(style, Qt::CaseInsensitive)) + return style; + } + + if (X11->use_xrender) + return QLatin1String("plastique"); + else + return QLatin1String("windows"); +} + + +int QKde::kdeToolButtonStyle() +{ + QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), + QSettings::IniFormat); + settings.beginGroup(QLatin1String("Toolbar style")); + QString toolbarStyle = settings.value(QLatin1String("ToolButtonStyle"), QLatin1String("TextBesideIcon")).toString(); + if (toolbarStyle == QLatin1String("TextBesideIcon")) + return Qt::ToolButtonTextBesideIcon; + else if (toolbarStyle == QLatin1String("TextOnly")) + return Qt::ToolButtonTextOnly; + else if (toolbarStyle == QLatin1String("TextUnderIcon")) + return Qt::ToolButtonTextUnderIcon; + + return Qt::ToolButtonTextBesideIcon; +} + +int QKde::kdeToolBarIconSize() +{ + static int iconSize = -1; + if (iconSize == -1) { + QSettings settings(QKde::kdeHome() + QLatin1String("/share/config/kdeglobals"), + QSettings::IniFormat); + settings.beginGroup(QLatin1String("ToolbarIcons")); + iconSize = settings.value(QLatin1String("Size")).toInt(); + } + return iconSize; +} + +QT_END_NAMESPACE + +#endif //Q_WS_X11 + diff --git a/src/gui/platforms/x11/qkde_p.h b/src/gui/platforms/x11/qkde_p.h new file mode 100644 index 0000000000..4e108f6e9e --- /dev/null +++ b/src/gui/platforms/x11/qkde_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QKDE_H +#define QKDE_H + +#include <QtCore/qglobal.h> +#include <QtGui/QPalette> +#include <QtGui/QIcon> + +// +// 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_WS_X11) + + +QT_BEGIN_NAMESPACE + +/*!\internal + This namespace contains helper function to help KDE integration + They are only used if we detect the use of KDE and the KDE platform plugin is not found (old KDE version) + Or if the detected KDE version is KDE3 +*/ +namespace QKde { + QString kdeHome(); + QString kdeStyle(); + QPalette kdePalette(); + int kdeToolButtonStyle(); + int kdeToolBarIconSize(); +} + + +QT_END_NAMESPACE + +#endif // Q_WS_X11 +#endif // QKDE_H diff --git a/src/gui/platforms/x11/qkeymapper_x11.cpp b/src/gui/platforms/x11/qkeymapper_x11.cpp new file mode 100644 index 0000000000..5383bfd456 --- /dev/null +++ b/src/gui/platforms/x11/qkeymapper_x11.cpp @@ -0,0 +1,1869 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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> + +#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 + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_XKB + +// bring in the auto-generated xkbLayoutData +#include "qkeymapper_x11_p.cpp" + +QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName) +{ + int i = 0; + while (xkbLayoutData[i].layout != 0) { + if (layoutName == xkbLayoutData[i].layout && variantName == xkbLayoutData[i].variant) + return QLocale(xkbLayoutData[i].language, xkbLayoutData[i].country); + ++i; + } + return QLocale::c(); +} +#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), xkb_currentGroup(0) +{ + memset(&coreDesc, 0, sizeof(coreDesc)); + +#ifndef QT_NO_XKB + if (X11->use_xkb) { + // get the current group + XkbStateRec xkbState; + if (XkbGetState(X11->display, XkbUseCoreKbd, &xkbState) == Success) + xkb_currentGroup = xkbState.group; + } +#endif +} + +QKeyMapperPrivate::~QKeyMapperPrivate() +{ + if (coreDesc.keysyms) + XFree(coreDesc.keysyms); +} + +QList<int> QKeyMapperPrivate::possibleKeys(QKeyEvent *event) +{ +#ifndef QT_NO_XKB + if (X11->use_xkb) + 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 == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab + code = Qt::Key_Backtab; + text = QString(); + } + + 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 == Qt::Key_Tab && (baseModifiers & Qt::ShiftModifier)) { + // map shift+tab to shift+backtab + code = Qt::Key_Backtab; + text = QString(); + } + + 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 (X11->use_xkb) { + // 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); + + // the layout names and variants are saved in the _XKB_RULES_NAMES property as a comma separated list + QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(','); + if (uint(xkb_currentGroup) < uint(layoutNames.count())) + layoutName = layoutNames.at(xkb_currentGroup); + QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(','); + if (uint(xkb_currentGroup) < uint(variantNames.count())) + variantName = variantNames.at(xkb_currentGroup); + } + + // ### ??? + // if (keyboardLayoutName.isEmpty()) + // qWarning("Qt: unable to determine keyboard layout, please talk to qt-bugs@trolltech.com"); ? + + keyboardInputLocale = q_getKeyboardLocale(layoutName, variantName); + keyboardInputDirection = keyboardInputLocale.textDirection(); + +#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 + 1, + &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 (X11->use_xkb) { + 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 on 10/2009 from X.org (X11/XF86keysym.h), defining some special +// multimedia keys. They are included here as not every system has them. +#define XF86XK_MonBrightnessUp 0x1008FF02 +#define XF86XK_MonBrightnessDown 0x1008FF03 +#define XF86XK_KbdLightOnOff 0x1008FF04 +#define XF86XK_KbdBrightnessUp 0x1008FF05 +#define XF86XK_KbdBrightnessDown 0x1008FF06 +#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_Mail 0x1008FF19 +#define XF86XK_Start 0x1008FF1A +#define XF86XK_Search 0x1008FF1B +#define XF86XK_AudioRecord 0x1008FF1C +#define XF86XK_Calculator 0x1008FF1D +#define XF86XK_Memo 0x1008FF1E +#define XF86XK_ToDoList 0x1008FF1F +#define XF86XK_Calendar 0x1008FF20 +#define XF86XK_PowerDown 0x1008FF21 +#define XF86XK_ContrastAdjust 0x1008FF22 +#define XF86XK_Back 0x1008FF26 +#define XF86XK_Forward 0x1008FF27 +#define XF86XK_Stop 0x1008FF28 +#define XF86XK_Refresh 0x1008FF29 +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_WakeUp 0x1008FF2B +#define XF86XK_Eject 0x1008FF2C +#define XF86XK_ScreenSaver 0x1008FF2D +#define XF86XK_WWW 0x1008FF2E +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Favorites 0x1008FF30 +#define XF86XK_AudioPause 0x1008FF31 +#define XF86XK_AudioMedia 0x1008FF32 +#define XF86XK_MyComputer 0x1008FF33 +#define XF86XK_LightBulb 0x1008FF35 +#define XF86XK_Shop 0x1008FF36 +#define XF86XK_History 0x1008FF37 +#define XF86XK_OpenURL 0x1008FF38 +#define XF86XK_AddFavorite 0x1008FF39 +#define XF86XK_HotLinks 0x1008FF3A +#define XF86XK_BrightnessAdjust 0x1008FF3B +#define XF86XK_Finance 0x1008FF3C +#define XF86XK_Community 0x1008FF3D +#define XF86XK_AudioRewind 0x1008FF3E +#define XF86XK_BackForward 0x1008FF3F +#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 +#define XF86XK_ApplicationLeft 0x1008FF50 +#define XF86XK_ApplicationRight 0x1008FF51 +#define XF86XK_Book 0x1008FF52 +#define XF86XK_CD 0x1008FF53 +#define XF86XK_Calculater 0x1008FF54 +#define XF86XK_Clear 0x1008FF55 +#define XF86XK_ClearGrab 0x1008FE21 +#define XF86XK_Close 0x1008FF56 +#define XF86XK_Copy 0x1008FF57 +#define XF86XK_Cut 0x1008FF58 +#define XF86XK_Display 0x1008FF59 +#define XF86XK_DOS 0x1008FF5A +#define XF86XK_Documents 0x1008FF5B +#define XF86XK_Excel 0x1008FF5C +#define XF86XK_Explorer 0x1008FF5D +#define XF86XK_Game 0x1008FF5E +#define XF86XK_Go 0x1008FF5F +#define XF86XK_iTouch 0x1008FF60 +#define XF86XK_LogOff 0x1008FF61 +#define XF86XK_Market 0x1008FF62 +#define XF86XK_Meeting 0x1008FF63 +#define XF86XK_MenuKB 0x1008FF65 +#define XF86XK_MenuPB 0x1008FF66 +#define XF86XK_MySites 0x1008FF67 +#define XF86XK_News 0x1008FF69 +#define XF86XK_OfficeHome 0x1008FF6A +#define XF86XK_Option 0x1008FF6C +#define XF86XK_Paste 0x1008FF6D +#define XF86XK_Phone 0x1008FF6E +#define XF86XK_Reply 0x1008FF72 +#define XF86XK_Reload 0x1008FF73 +#define XF86XK_RotateWindows 0x1008FF74 +#define XF86XK_RotationPB 0x1008FF75 +#define XF86XK_RotationKB 0x1008FF76 +#define XF86XK_Save 0x1008FF77 +#define XF86XK_Send 0x1008FF7B +#define XF86XK_Spell 0x1008FF7C +#define XF86XK_SplitScreen 0x1008FF7D +#define XF86XK_Support 0x1008FF7E +#define XF86XK_TaskPane 0x1008FF7F +#define XF86XK_Terminal 0x1008FF80 +#define XF86XK_Tools 0x1008FF81 +#define XF86XK_Travel 0x1008FF82 +#define XF86XK_Video 0x1008FF87 +#define XF86XK_Word 0x1008FF89 +#define XF86XK_Xfer 0x1008FF8A +#define XF86XK_ZoomIn 0x1008FF8B +#define XF86XK_ZoomOut 0x1008FF8C +#define XF86XK_Away 0x1008FF8D +#define XF86XK_Messenger 0x1008FF8E +#define XF86XK_WebCam 0x1008FF8F +#define XF86XK_MailForward 0x1008FF90 +#define XF86XK_Pictures 0x1008FF91 +#define XF86XK_Music 0x1008FF92 +#define XF86XK_Battery 0x1008FF93 +#define XF86XK_Bluetooth 0x1008FF94 +#define XF86XK_WLAN 0x1008FF95 +#define XF86XK_UWB 0x1008FF96 +#define XF86XK_AudioForward 0x1008FF97 +#define XF86XK_AudioRepeat 0x1008FF98 +#define XF86XK_AudioRandomPlay 0x1008FF99 +#define XF86XK_Subtitle 0x1008FF9A +#define XF86XK_AudioCycleTrack 0x1008FF9B +#define XF86XK_Time 0x1008FF9F +#define XF86XK_Select 0x1008FFA0 +#define XF86XK_View 0x1008FFA1 +#define XF86XK_TopMenu 0x1008FFA2 +#define XF86XK_Suspend 0x1008FFA7 +#define XF86XK_Hibernate 0x1008FFA8 + + +// 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 keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + 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, + 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, + XF86XK_Mail, Qt::Key_LaunchMail, + XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 5: remap properly + XF86XK_Calculator, Qt::Key_Launch1, + XF86XK_Memo, Qt::Key_Memo, + XF86XK_ToDoList, Qt::Key_ToDoList, + XF86XK_Calendar, Qt::Key_Calendar, + XF86XK_PowerDown, Qt::Key_PowerDown, + XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust, + XF86XK_Standby, Qt::Key_Standby, + XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp, + XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown, + XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff, + XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, + XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, + XF86XK_PowerOff, Qt::Key_PowerOff, + XF86XK_WakeUp, Qt::Key_WakeUp, + XF86XK_Eject, Qt::Key_Eject, + XF86XK_ScreenSaver, Qt::Key_ScreenSaver, + XF86XK_WWW, Qt::Key_WWW, + XF86XK_Sleep, Qt::Key_Sleep, + XF86XK_LightBulb, Qt::Key_LightBulb, + XF86XK_Shop, Qt::Key_Shop, + XF86XK_History, Qt::Key_History, + XF86XK_AddFavorite, Qt::Key_AddFavorite, + XF86XK_HotLinks, Qt::Key_HotLinks, + XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust, + XF86XK_Finance, Qt::Key_Finance, + XF86XK_Community, Qt::Key_Community, + XF86XK_AudioRewind, Qt::Key_AudioRewind, + XF86XK_BackForward, Qt::Key_BackForward, + XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft, + XF86XK_ApplicationRight, Qt::Key_ApplicationRight, + XF86XK_Book, Qt::Key_Book, + XF86XK_CD, Qt::Key_CD, + XF86XK_Calculater, Qt::Key_Calculator, + XF86XK_Clear, Qt::Key_Clear, + XF86XK_ClearGrab, Qt::Key_ClearGrab, + XF86XK_Close, Qt::Key_Close, + XF86XK_Copy, Qt::Key_Copy, + XF86XK_Cut, Qt::Key_Cut, + XF86XK_Display, Qt::Key_Display, + XF86XK_DOS, Qt::Key_DOS, + XF86XK_Documents, Qt::Key_Documents, + XF86XK_Excel, Qt::Key_Excel, + XF86XK_Explorer, Qt::Key_Explorer, + XF86XK_Game, Qt::Key_Game, + XF86XK_Go, Qt::Key_Go, + XF86XK_iTouch, Qt::Key_iTouch, + XF86XK_LogOff, Qt::Key_LogOff, + XF86XK_Market, Qt::Key_Market, + XF86XK_Meeting, Qt::Key_Meeting, + XF86XK_MenuKB, Qt::Key_MenuKB, + XF86XK_MenuPB, Qt::Key_MenuPB, + XF86XK_MySites, Qt::Key_MySites, + XF86XK_News, Qt::Key_News, + XF86XK_OfficeHome, Qt::Key_OfficeHome, + XF86XK_Option, Qt::Key_Option, + XF86XK_Paste, Qt::Key_Paste, + XF86XK_Phone, Qt::Key_Phone, + XF86XK_Reply, Qt::Key_Reply, + XF86XK_Reload, Qt::Key_Reload, + XF86XK_RotateWindows, Qt::Key_RotateWindows, + XF86XK_RotationPB, Qt::Key_RotationPB, + XF86XK_RotationKB, Qt::Key_RotationKB, + XF86XK_Save, Qt::Key_Save, + XF86XK_Send, Qt::Key_Send, + XF86XK_Spell, Qt::Key_Spell, + XF86XK_SplitScreen, Qt::Key_SplitScreen, + XF86XK_Support, Qt::Key_Support, + XF86XK_TaskPane, Qt::Key_TaskPane, + XF86XK_Terminal, Qt::Key_Terminal, + XF86XK_Tools, Qt::Key_Tools, + XF86XK_Travel, Qt::Key_Travel, + XF86XK_Video, Qt::Key_Video, + XF86XK_Word, Qt::Key_Word, + XF86XK_Xfer, Qt::Key_Xfer, + XF86XK_ZoomIn, Qt::Key_ZoomIn, + XF86XK_ZoomOut, Qt::Key_ZoomOut, + XF86XK_Away, Qt::Key_Away, + XF86XK_Messenger, Qt::Key_Messenger, + XF86XK_WebCam, Qt::Key_WebCam, + XF86XK_MailForward, Qt::Key_MailForward, + XF86XK_Pictures, Qt::Key_Pictures, + XF86XK_Music, Qt::Key_Music, + XF86XK_Battery, Qt::Key_Battery, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_WLAN, Qt::Key_WLAN, + XF86XK_UWB, Qt::Key_UWB, + XF86XK_AudioForward, Qt::Key_AudioForward, + XF86XK_AudioRepeat, Qt::Key_AudioRepeat, + XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay, + XF86XK_Subtitle, Qt::Key_Subtitle, + XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack, + XF86XK_Time, Qt::Key_Time, + XF86XK_Select, Qt::Key_Select, + XF86XK_View, Qt::Key_View, + XF86XK_TopMenu, Qt::Key_TopMenu, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_Suspend, Qt::Key_Suspend, + XF86XK_Hibernate, Qt::Key_Hibernate, + XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 5: remap properly + 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, + XF86XK_LaunchE, Qt::Key_LaunchG, + XF86XK_LaunchF, Qt::Key_LaunchH, + + // 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/platforms/x11/qkeymapper_x11_p.cpp b/src/gui/platforms/x11/qkeymapper_x11_p.cpp new file mode 100644 index 0000000000..2dbe1e77a4 --- /dev/null +++ b/src/gui/platforms/x11/qkeymapper_x11_p.cpp @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This file is auto-generated, do not edit! +// (Generated using util/xkbdatagen) + +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::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:lyx, description = Israel + { "il", "lyx", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:si1452, description = Israel + { "il", "si1452", Qt::RightToLeft, QLocale::Hebrew, QLocale::Israel }, + // name = il:phonetic, description = Israel + { "il", "phonetic", Qt::RightToLeft, 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::Syriac, QLocale::SyrianArabRepublic }, + // name = sy:syc, description = Syria + { "sy", "syc", Qt::RightToLeft, QLocale::Syriac, QLocale::SyrianArabRepublic }, + // name = sy:syc_phonetic, description = Syria + { "sy", "syc_phonetic", Qt::RightToLeft, QLocale::Syriac, 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/platforms/x11/qmotifdnd_x11.cpp b/src/gui/platforms/x11/qmotifdnd_x11.cpp new file mode 100644 index 0000000000..eef4cc470b --- /dev/null +++ b/src/gui/platforms/x11/qmotifdnd_x11.cpp @@ -0,0 +1,1031 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* The following copyright notice pertains to the code as contributed +to Qt, not to Nokia'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; + + // squelch potential valgrind errors about uninitialized reads + memset(&receiver_prop, 0, sizeof(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)) { + } + } + + // 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/platforms/x11/qpaintdevice_x11.cpp b/src/gui/platforms/x11/qpaintdevice_x11.cpp new file mode 100644 index 0000000000..690dea99d7 --- /dev/null +++ b/src/gui/platforms/x11/qpaintdevice_x11.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 + +/*! \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; +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qpaintengine_x11.cpp b/src/gui/platforms/x11/qpaintengine_x11.cpp new file mode 100644 index 0000000000..1256996491 --- /dev/null +++ b/src/gui/platforms/x11/qpaintengine_x11.cpp @@ -0,0 +1,2507 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" + +#include "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 <private/qpainterpath_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 + +#include <private/qhexstring_p.h> + +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_GUI_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 = QLatin1Literal("$qt-alpha-brush$") + % HexString<uchar>(alpha) + % HexString<int>(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 = q_check_ptr((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 = qrand() % 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.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 ®ion); + 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.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) { + d->brush_pm.detach(); + QX11PixmapData *brushData = static_cast<QX11PixmapData*>(d->brush_pm.data.data()); + brushData->convertToARGB32(); + } +#endif + vals.tile = (d->brush_pm.depth() == d->pdev_depth + ? d->brush_pm.handle() + : static_cast<QX11PixmapData*>(d->brush_pm.data.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) +{ + if (rect.isEmpty()) { + drawRects(&rect, 1); + return; + } + + 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 + XFillArc(d->dpy, d->hd, d->gc_brush, x, y, w, h, 0, 360*64); + if (!d->has_pen) // make smoother outline + XDrawArc(d->dpy, d->hd, d->gc_brush, x, y, w-1, h-1, 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) +{ + Q_Q(QX11PaintEngine); + + 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 > 200000) { + QPolygon poly; + for (int i = 0; i < clippedCount; ++i) + poly << QPoint(qFloor(clippedPoints[i].x), qFloor(clippedPoints[i].y)); + + const QRect bounds = poly.boundingRect(); + const QRect aligned = bounds + & QRect(QPoint(), QSize(pdev->width(), pdev->height())); + + QImage img(aligned.size(), QImage::Format_ARGB32_Premultiplied); + img.fill(0); + + QPainter painter(&img); + painter.translate(-aligned.x(), -aligned.y()); + painter.setPen(Qt::NoPen); + painter.setBrush(fill); + if (gcMode == BrushGC) + painter.setBrushOrigin(q->painter()->brushOrigin()); + painter.drawPolygon(poly); + painter.end(); + + q->drawImage(aligned, img, img.rect(), Qt::AutoColor); + } else 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; + QRectF deviceRect(0, 0, d->pdev->width(), d->pdev->height()); + // necessary to get aliased alphablended primitives to be drawn correctly + if (d->cpen.isCosmetic() || d->has_scaling_xform) { + if (d->cpen.isCosmetic()) + stroker.setWidth(width == 0 ? 1 : width); + else + stroker.setWidth(width * d->xform_scale); + stroker.d_ptr->stroker.setClipRect(deviceRect); + stroke = stroker.createStroke(path * d->matrix); + if (stroke.isEmpty()) + return; + stroke.setFillRule(Qt::WindingFill); + d->fillPath(stroke, QX11PaintEnginePrivate::PenGC, false); + } else { + stroker.setWidth(width); + stroker.d_ptr->stroker.setClipRect(d->matrix.inverted().mapRect(deviceRect)); + 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; + int image_byte_order = ImageByteOrder(X11->display); + if ((QSysInfo::ByteOrder == QSysInfo::BigEndian && ((image_byte_order == LSBFirst) || bgr_layout)) + || (image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) + || (image_byte_order == LSBFirst && bgr_layout)) + { + 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 && image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + while (p < end) { + *p = ((*p << 8) & 0xffffff00) | ((*p >> 24) & 0x000000ff); + p++; + } + } else if ((image_byte_order == LSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) + || (image_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 ((image_byte_order == MSBFirst && QSysInfo::ByteOrder == QSysInfo::BigEndian) + || (image_byte_order == LSBFirst && bgr_layout)) + { + 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(pixmap.isNull()) + return; + + 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.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.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.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 if (mono_dst && !mono_src) { + QBitmap bitmap(pixmap); + XCopyArea(d->dpy, bitmap.handle(), d->hd, d->gc, sx, sy, sw, sh, x, y); + } 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.data())->x11_mask; + Pixmap dst_mask = static_cast<QX11PixmapData*>(px->data.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.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.constData(), glyphs.size(), positions.constData(), 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 = 0; + if (ft->xglyph_format != PictStandardA1) + 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; + + QPoint delta(qRound(d->matrix.dx()), qRound(d->matrix.dy())); + QRect clip(d->polygonClipper.boundingRect()); + 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; + + QRect rect = QRect(x + delta.x(), y + delta.y(), w, h); + rect = rect.intersected(clip); + if (rect.isEmpty()) + continue; + + rects[num_rects].x = short(rect.x()); + rects[num_rects].y = short(rect.y()); + rects[num_rects].width = ushort(rect.width()); + rects[num_rects].height = ushort(rect.height()); + ++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/platforms/x11/qpaintengine_x11_p.h b/src/gui/platforms/x11/qpaintengine_x11_p.h new file mode 100644 index 0000000000..897c69f122 --- /dev/null +++ b/src/gui/platforms/x11/qpaintengine_x11_p.h @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 ®ion, 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; + xform_scale = 1; +#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/platforms/x11/qpixmap_x11.cpp b/src/gui/platforms/x11/qpixmap_x11.cpp new file mode 100644 index 0000000000..bc468cb7ec --- /dev/null +++ b/src/gui/platforms/x11/qpixmap_x11.cpp @@ -0,0 +1,2419 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// 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 <private/qimage_p.h> +#include <private/qimagepixmapcleanuphooks_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 + *****************************************************************************/ + +QBasicAtomicInt qt_pixmap_serial = Q_BASIC_ATOMIC_INITIALIZER(0); +int Q_GUI_EXPORT qt_x11_preferred_pixmap_depth = 0; + +QX11PixmapData::QX11PixmapData(PixelType type) + : QPixmapData(type, X11Class), gl_surface(0), hd(0), + flags(Uninitialized), x11_mask(0), picture(0), mask_picture(0), hd2(0), + share_mode(QPixmap::ImplicitlyShared), pengine(0) +{ +} + +QPixmapData *QX11PixmapData::createCompatiblePixmapData() const +{ + return new QX11PixmapData(pixelType()); +} + +void QX11PixmapData::resize(int width, int height) +{ + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); + + w = width; + h = height; + is_null = (w <= 0 || h <= 0); + + 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; + is_null = true; + 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 +} + +struct QX11AlphaDetector +{ + bool hasAlpha() const { + if (checked) + return has; + // Will implicitly also check format and return quickly for opaque types... + checked = true; + has = image->isNull() ? false : const_cast<QImage *>(image)->data_ptr()->checkForAlphaPixels(); + return has; + } + + bool hasXRenderAndAlpha() const { + if (!X11->use_xrender) + return false; + return hasAlpha(); + } + + QX11AlphaDetector(const QImage *i, Qt::ImageConversionFlags flags) + : image(i), checked(false), has(false) + { + if (flags & Qt::NoOpaqueDetection) { + checked = true; + has = image->hasAlphaChannel(); + } + } + + const QImage *image; + mutable bool checked; + mutable bool has; +}; + +void QX11PixmapData::fromImage(const QImage &img, + Qt::ImageConversionFlags flags) +{ + setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); + + w = img.width(); + h = img.height(); + d = img.depth(); + is_null = (w <= 0 || h <= 0); + + if (is_null) { + w = h = 0; + return; + } + + 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; + is_null = true; + return; + } + + QX11AlphaDetector alphaCheck(&img, flags); + int dd = alphaCheck.hasXRenderAndAlpha() ? 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.colorCount() == 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.byteCount(); + uchar *newbits= 0; + +#ifndef QT_NO_XRENDER + if (alphaCheck.hasXRenderAndAlpha()) { + 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.colorCount(); 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.colorCount() == 0) + image.setColorCount(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.colorCount(); i++) { // compute number of colors + if (pop[i] > 0) + ncols++; + } + for (int i = cimage.colorCount(); 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 (alphaCheck.hasAlpha()) { + QBitmap m = QBitmap::fromImage(image.createAlphaMask(flags)); + setMask(m); + } +} + +Qt::HANDLE QX11PixmapData::createBitmapFromImage(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; + int w = img.width(); + int h = img.height(); + 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; + } + Qt::HANDLE hd = (Qt::HANDLE)XCreateBitmapFromData(X11->display, + QX11Info::appRootWindow(), + bits, w, h); + if (tmp_bits) // Avoid purify complaint + delete [] tmp_bits; + return hd; +} + +void QX11PixmapData::bitmapFromImage(const QImage &image) +{ + w = image.width(); + h = image.height(); + d = 1; + is_null = (w <= 0 || h <= 0); + hd = createBitmapFromImage(image); +#ifndef QT_NO_XRENDER + if (X11->use_xrender) + picture = XRenderCreatePicture(X11->display, hd, + XRenderFindStandardFormat(X11->display, PictStandardA1), 0, 0); +#endif // QT_NO_XRENDER +} + +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() +{ + // Cleanup hooks have to be called before the handles are freed + if (is_cached) { + QImagePixmapCleanupHooks::executePixmapDataDestructionHooks(this); + is_cached = false; + } + + release(); +} + +void QX11PixmapData::release() +{ + delete pengine; + pengine = 0; + + if (!X11) { + // At this point, the X server will already have freed our resources, + // so there is nothing to do. + 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 (!(flags & Readonly)) + XFreePixmap(xinfo.display(), hd); + hd = 0; + } +} + +QPixmap QX11PixmapData::alphaChannel() const +{ + if (!hasAlphaChannel()) { + QPixmap pm(w, h); + pm.fill(Qt::white); + return pm; + } + 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; + // the new QX11PixmapData object isn't referenced yet, so + // ref it + ref.ref(); + + // the below is to make sure the QX11PixmapData destructor + // doesn't delete our newly created render picture + 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; + } +} + +struct QXImageWrapper +{ + XImage *xi; +}; + +bool QX11PixmapData::canTakeQImageFromXImage(const QXImageWrapper &xiWrapper) const +{ + XImage *xi = xiWrapper.xi; + + // ARGB32_Premultiplied + if (picture && depth() == 32) + return true; + + Visual *visual = (Visual *)xinfo.visual(); + + // RGB32 + if (depth() == 24 && xi->bits_per_pixel == 32 && visual->red_mask == 0xff0000 + && visual->green_mask == 0xff00 && visual->blue_mask == 0xff) + return true; + + // RGB16 + if (depth() == 16 && xi->bits_per_pixel == 16 && visual->red_mask == 0xf800 + && visual->green_mask == 0x7e0 && visual->blue_mask == 0x1f) + return true; + + return false; +} + +QImage QX11PixmapData::takeQImageFromXImage(const QXImageWrapper &xiWrapper) const +{ + XImage *xi = xiWrapper.xi; + + QImage::Format format = QImage::Format_ARGB32_Premultiplied; + if (depth() == 24) + format = QImage::Format_RGB32; + else if (depth() == 16) + format = QImage::Format_RGB16; + + QImage image((uchar *)xi->data, xi->width, xi->height, xi->bytes_per_line, format); + // take ownership + image.data_ptr()->own_data = true; + xi->data = 0; + + // we may have to swap the byte order + if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst) + || (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst)) + { + for (int i=0; i < image.height(); i++) { + if (depth() == 16) { + ushort *p = (ushort*)image.scanLine(i); + ushort *end = p + image.width(); + while (p < end) { + *p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff); + p++; + } + } else { + uint *p = (uint*)image.scanLine(i); + uint *end = p + image.width(); + while (p < end) { + *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) + | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); + p++; + } + } + } + } + + // fix-up alpha channel + if (format == QImage::Format_RGB32) { + QRgb *p = (QRgb *)image.bits(); + for (int y = 0; y < xi->height; ++y) { + for (int x = 0; x < xi->width; ++x) + p[x] |= 0xff000000; + p += xi->bytes_per_line / 4; + } + } + + XDestroyImage(xi); + return image; +} + +QImage QX11PixmapData::toImage(const QRect &rect) const +{ + QXImageWrapper xiWrapper; + xiWrapper.xi = XGetImage(X11->display, hd, rect.x(), rect.y(), rect.width(), rect.height(), + AllPlanes, (depth() == 1) ? XYPixmap : ZPixmap); + + Q_CHECK_PTR(xiWrapper.xi); + if (!xiWrapper.xi) + return QImage(); + + if (!x11_mask && canTakeQImageFromXImage(xiWrapper)) + return takeQImageFromXImage(xiWrapper); + + QImage image = toImage(xiWrapper, rect); + qSafeXDestroyImage(xiWrapper.xi); + return image; +} + +/*! + 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 +{ + return toImage(QRect(0, 0, w, h)); +} + +QImage QX11PixmapData::toImage(const QXImageWrapper &xiWrapper, const QRect &rect) const +{ + XImage *xi = xiWrapper.xi; + + 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; + } + + 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(xi->width, xi->height, format); + if (image.isNull()) // could not create image + return image; + + QImage alpha; + if (x11_mask) { + if (rect.contains(QRect(0, 0, w, h))) + alpha = mask().toImage(); + else + alpha = mask().toImage().copy(rect); + } + 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 < xi->height; ++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 < xi->width; 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 = xi->width; // leave loop + y = xi->height; + 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<xi->height; 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.setColorCount(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 < xi->height; i++) { + uchar* asrc = alpha.scanLine(i); + p = image.scanLine(i); + if (ale) { + for (int x = 0; x < xi->width; x++) { + if (asrc[x >> 3] & (1 << (x & 7))) + use[*p] = 1; + ++p; + } + } else { + for (int x = 0; x < xi->width; x++) { + if (asrc[x >> 3] & (0x80 >> (x & 7))) + use[*p] = 1; + ++p; + } + } + } + } else { + for (int i = 0; i < xi->height; 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 < xi->height; 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.setColorCount(ncols); // create color table + image.setColor(trans, 0x00000000); + } else { + image.setColorCount(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 < xi->height; i++) { + uchar* asrc = alpha.scanLine(i); + p = image.scanLine(i); + if (ale) { + for (int x = 0; x < xi->width; x++) { + if (!(asrc[x >> 3] & (1 << (x & 7)))) + *p = trans; + ++p; + } + } else { + for (int x = 0; x < xi->width; x++) { + if (!(asrc[x >> 3] & (1 << (7 -(x & 7))))) + *p = trans; + ++p; + } + } + } + } else { + image.setColorCount(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()); + } + } + + 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 + QX11PixmapData *x11Data = new QX11PixmapData(QPixmapData::PixmapType); + QPixmap pm(x11Data); + x11Data->flags &= ~QX11PixmapData::Uninitialized; + x11Data->xinfo = xinfo; + x11Data->d = d; + x11Data->w = w; + x11Data->h = h; + x11Data->is_null = (w <= 0 || h <= 0); + x11Data->hd = (Qt::HANDLE)XCreatePixmap(X11->display, + RootWindow(X11->display, xinfo.screen()), + w, h, d); + x11Data->setSerialNumber(qt_pixmap_serial.fetchAndAddRelaxed(1)); + +#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; + } +} + +int QPixmap::x11SetDefaultScreen(int screen) +{ + int old = defaultScreen; + defaultScreen = screen; + return old; +} + +void QPixmap::x11SetScreen(int screen) +{ + if (paintingActive()) { + qWarning("QPixmap::x11SetScreen(): Cannot change screens during painting"); + return; + } + + if (isNull()) + return; + + if (data->classId() != QPixmapData::X11Class) + return; + + if (screen < 0) + screen = QX11Info::appScreen(); + + QX11PixmapData *x11Data = static_cast<QX11PixmapData*>(data.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->flags &= ~QX11PixmapData::Uninitialized; + 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; +} + +const QX11Info &QPixmap::x11Info() const +{ + if (data && data->classId() == QPixmapData::X11Class) + return static_cast<QX11PixmapData*>(data.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 ((flags & Readonly) && 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->flags &= ~QX11PixmapData::Readonly; + } + + if (!that->pengine) + that->pengine = new QX11PaintEngine; + return that->pengine; +} + +Qt::HANDLE QPixmap::x11PictureHandle() const +{ +#ifndef QT_NO_XRENDER + if (data && data->classId() == QPixmapData::X11Class) + return static_cast<const QX11PixmapData*>(data.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.fetchAndAddRelaxed(1)); + + flags &= ~Uninitialized; + xinfo = x11Data->xinfo; + d = x11Data->d; + w = rect.width(); + h = rect.height(); + is_null = (w <= 0 || h <= 0); + 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); + } +} + +bool QX11PixmapData::scroll(int dx, int dy, const QRect &rect) +{ + GC gc = XCreateGC(X11->display, hd, 0, 0); + XCopyArea(X11->display, hd, hd, gc, + rect.left(), rect.top(), rect.width(), rect.height(), + rect.left() + dx, rect.top() + dy); + XFreeGC(X11->display, gc); + return true; +} + +#if !defined(QT_NO_XRENDER) +void QX11PixmapData::convertToARGB32(bool preserveContents) +{ + if (!X11->use_xrender) + return; + + // Q_ASSERT(count == 1); + if ((flags & Readonly) && 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 (!(flags & Readonly)) + XRenderFreePicture(X11->display, picture); + } + if (hd && !(flags & Readonly)) + 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.fetchAndAddRelaxed(1)); + data->flags = QX11PixmapData::Readonly; + data->share_mode = mode; + data->w = width; + data->h = height; + data->is_null = (width <= 0 || height <= 0); + 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/platforms/x11/qpixmap_x11_p.h b/src/gui/platforms/x11/qpixmap_x11_p.h new file mode 100644 index 0000000000..eb8e5819ad --- /dev/null +++ b/src/gui/platforms/x11/qpixmap_x11_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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; + +struct QXImageWrapper; + +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(); + + QPixmapData *createCompatiblePixmapData() const; + + void resize(int width, int height); + void fromImage(const QImage &image, Qt::ImageConversionFlags flags); + void copy(const QPixmapData *data, const QRect &rect); + bool scroll(int dx, int dy, 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; + QImage toImage(const QRect &rect) const; + QPaintEngine* paintEngine() const; + + Qt::HANDLE handle() const { return hd; } + Qt::HANDLE x11ConvertToDefaultDepth(); + + static Qt::HANDLE createBitmapFromImage(const QImage &image); + + void* gl_surface; +#ifndef QT_NO_XRENDER + void convertToARGB32(bool preserveContents = true); +#endif + +protected: + int metric(QPaintDevice::PaintDeviceMetric metric) const; + +private: + friend class QPixmap; + friend class QBitmap; + friend class QX11PaintEngine; + friend class QX11WindowSurface; + friend class QRasterWindowSurface; + friend class QGLContextPrivate; // Needs to access xinfo, gl_surface & flags + friend class QEglContext; // Needs gl_surface + friend class QGLContext; // Needs gl_surface + friend class QX11GLPixmapData; // Needs gl_surface + friend class QMeeGoLivePixmapData; // Needs gl_surface and flags + friend bool qt_createEGLSurfaceForPixmap(QPixmapData*, bool); // Needs gl_surface + + void release(); + + QImage toImage(const QXImageWrapper &xi, const QRect &rect) const; + + QBitmap mask_to_bitmap(int screen) const; + static Qt::HANDLE bitmap_to_mask(const QBitmap &, int screen); + void bitmapFromImage(const QImage &image); + + bool canTakeQImageFromXImage(const QXImageWrapper &xi) const; + QImage takeQImageFromXImage(const QXImageWrapper &xi) const; + + Qt::HANDLE hd; + + enum Flag { + NoFlags = 0x0, + Uninitialized = 0x1, + Readonly = 0x2, + InvertedWhenBoundToTexture = 0x4, + GlSurfaceCreatedWithAlpha = 0x8 + }; + uint flags; + + QX11Info xinfo; + Qt::HANDLE x11_mask; + Qt::HANDLE picture; + Qt::HANDLE mask_picture; + Qt::HANDLE hd2; // sorted in the default display depth + QPixmap::ShareMode share_mode; + + QX11PaintEngine *pengine; +}; + +QT_END_NAMESPACE + +#endif // QPIXMAPDATA_X11_P_H + diff --git a/src/gui/platforms/x11/qregion_x11.cpp b/src/gui/platforms/x11/qregion_x11.cpp new file mode 100644 index 0000000000..ef4e844bfa --- /dev/null +++ b/src/gui/platforms/x11/qregion_x11.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/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/platforms/x11/qsound_x11.cpp b/src/gui/platforms/x11/qsound_x11.cpp new file mode 100644 index 0000000000..12c06f0aa1 --- /dev/null +++ b/src/gui/platforms/x11/qsound_x11.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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/platforms/x11/qt_x11_p.h b/src/gui/platforms/x11/qt_x11_p.h new file mode 100644 index 0000000000..69079cfaad --- /dev/null +++ b/src/gui/platforms/x11/qt_x11_p.h @@ -0,0 +1,757 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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 +#ifdef Q_OS_VXWORS +# ifdef open +# undef open +# endif +# ifdef getpid +# undef getpid +# endif +#endif // Q_OS_VXWORKS +#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 + +#ifndef QT_NO_XSYNC +extern "C" { +# include "X11/extensions/sync.h" +} +#endif + +// #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 *); +typedef XRRScreenSize *(*PtrXRRSizes)(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 Q_GUI_EXPORT QX11Data *qt_x11Data; + +enum DesktopEnvironment { + DE_UNKNOWN, + DE_KDE, + DE_GNOME, + DE_CDE, + DE_MEEGO_COMPOSITOR, + 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); + 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; + bool use_mitshm_pixmaps; + 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; + + // for XKEYBOARD support + bool use_xkb; + int xkb_major; + int xkb_eventbase; + int xkb_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; + + DesktopEnvironment desktopEnvironment : 8; + uint desktopVersion : 8; /* Used only for KDE */ + + /* 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, + _NET_WM_SYNC_REQUEST, + _NET_WM_SYNC_REQUEST_COUNTER, + + // 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, + SAVE_TARGETS, + CLIP_TEMPORARY, + _QT_SELECTION, + _QT_CLIPBOARD_SENTINEL, + _QT_SELECTION_SENTINEL, + CLIPBOARD_MANAGER, + + RESOURCE_MANAGER, + + _XSETROOT_ID, + + _QT_SCROLL_DONE, + _QT_INPUT_ENCODING, + + _MOTIF_WM_HINTS, + + DTWM_IS_RUNNING, + ENLIGHTENMENT_DESKTOP, + _DT_SAVE_MODE, + _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, + + _NET_ACTIVE_WINDOW, + + // 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, + + XWacomStylus, + XWacomCursor, + XWacomEraser, + + XTabletStylus, + XTabletEraser, + + 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; + PtrXRRSizes ptrXRRSizes; +#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/platforms/x11/qwidget_x11.cpp b/src/gui/platforms/x11/qwidget_x11.cpp new file mode 100644 index 0000000000..5ece7d65c6 --- /dev/null +++ b/src/gui/platforms/x11/qwidget_x11.cpp @@ -0,0 +1,3146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "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 "qelapsedtimer.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); +} + +void qt_x11_wait_for_window_manager(QWidget *w, bool sendPostedEvents) +{ + if (!w || (!w->isWindow() && !w->internalWinId())) + return; + QApplication::flush(); + XEvent ev; + QElapsedTimer t; + t.start(); + static const int maximumWaitTime = 2000; + if (!w->testAttribute(Qt::WA_WState_Created)) + return; + + WId winid = w->internalWinId(); + + // first deliver events that are already in the local queue + if (sendPostedEvents) + QApplication::sendPostedEvents(); + + // the normal sequence is: + // ... ConfigureNotify ... ReparentNotify ... MapNotify ... Expose + // with X11BypassWindowManagerHint: + // ConfigureNotify ... MapNotify ... Expose + + enum State { + Initial, Mapped + } state = Initial; + + do { + if (XEventsQueued(X11->display, QueuedAlready)) { + XNextEvent(X11->display, &ev); + qApp->x11ProcessEvent(&ev); + + switch (state) { + case Initial: + if (ev.type == MapNotify && ev.xany.window == winid) + state = Mapped; + break; + case Mapped: + if (ev.type == Expose && ev.xany.window == winid) + return; + break; + } + } else { + if (!XEventsQueued(X11->display, QueuedAfterFlush)) + qApp->syncX(); // non-busy wait + } + if (t.elapsed() > maximumWaitTime) + return; + } while(1); +} + +Q_GUI_EXPORT void qt_x11_wait_for_window_manager(QWidget *w) +{ + qt_x11_wait_for_window_manager(w, true); +} + +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); + propertyData = 0; + + // 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)); + } + if (propertyData) + 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; + + QX11Info *parentXinfo = parentWidget ? &parentWidget->d_func()->xinfo : 0; + + 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 (parentXinfo && (parentXinfo->screen() != xinfo.screen() + || (parentXinfo->visual() != xinfo.visual() + && !q->inherits("QGLWidget")))) + { + // QGLWidgets have to be excluded here as they have a + // specially crafted QX11Info structure which can't be swapped + // out with the parent widgets QX11Info. The parent visual, + // for instance, might not even be GL capable. + xinfo = *parentXinfo; + } + + //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)) { + QDesktopWidget *desktopWidget = qApp->desktop(); + if (desktopWidget->isVirtualDesktop()) { + QRect r = desktopWidget->screenGeometry(); + sw = r.width(); + sh = r.height(); + } + + 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 = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : 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[5]; + 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 +#ifndef QT_NO_XSYNC + protocols[n++] = ATOM(_NET_WM_SYNC_REQUEST); // support _NET_WM_SYNC_REQUEST protocol +#endif // QT_NO_XSYNC + 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->internalWinId()) { + qt_x11_enforce_cursor(q); + if (QWidget *p = q->parentWidget()) // reset the cursor on the native parent + qt_x11_enforce_cursor(p); + } + + 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); + if (QTLWExtra *topData = maybeTopData()) { +#ifndef QT_NO_XSYNC + if (topData->syncUpdateCounter) + XSyncDestroyCounter(dpy, topData->syncUpdateCounter); +#endif + // we destroyed our old window - reset the top-level state + createTLSysExtra(); + } + } + + // 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 +} + +static void qt_x11_recreateWidget(QWidget *widget) +{ + if (widget->inherits("QGLWidget")) { + // We send QGLWidgets a ParentChange event which causes them to + // recreate their GL context, which in turn causes them to choose + // their visual again. Now that WA_TranslucentBackground is set, + // QGLContext::chooseVisual will select an ARGB visual. + QEvent e(QEvent::ParentChange); + QApplication::sendEvent(widget, &e); + } else { + // For regular widgets, reparent them with their parent which + // also triggers a recreation of the native window + QPoint pos = widget->pos(); + bool visible = widget->isVisible(); + if (visible) + widget->hide(); + + widget->setParent(widget->parentWidget(), widget->windowFlags()); + widget->move(pos); + if (visible) + widget->show(); + } +} + +static void qt_x11_recreateNativeWidgetsRecursive(QWidget *widget) +{ + if (widget->internalWinId()) + qt_x11_recreateWidget(widget); + + const QObjectList &children = widget->children(); + for (int i = 0; i < children.size(); ++i) { + QWidget *child = qobject_cast<QWidget*>(children.at(i)); + if (child) + qt_x11_recreateNativeWidgetsRecursive(child); + } +} + +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) + { + qt_x11_recreateNativeWidgetsRecursive(q); + } +#endif +} + +/* + 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; +} + +void QWidget::destroy(bool destroyWindow, bool destroySubWindows) +{ + Q_D(QWidget); + d->aboutToDestroy(); + if (!isWindow() && parentWidget()) + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(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); + } + QT_TRY { + d->setWinId(0); + } QT_CATCH (const std::bad_alloc &) { + // swallow - destructors must not throw + } + + 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 = QApplicationPrivate::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(effectiveRectFor(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) + 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 QWidgetPrivate::mapToGlobal(const QPoint &pos) const +{ + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { + QPoint p = pos + q->data->crect.topLeft(); + //cannot trust that !isWindow() implies parentWidget() before create + return (q->isWindow() || !q->parentWidget()) ? p : q->parentWidget()->d_func()->mapToGlobal(p); + } + int x, y; + Window child; + QPoint p = mapToWS(pos); + XTranslateCoordinates(X11->display, q->internalWinId(), + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), + p.x(), p.y(), &x, &y, &child); + return QPoint(x, y); +} + +QPoint QWidgetPrivate::mapFromGlobal(const QPoint &pos) const +{ + Q_Q(const QWidget); + if (!q->testAttribute(Qt::WA_WState_Created) || !q->internalWinId()) { + //cannot trust that !isWindow() implies parentWidget() before create + QPoint p = (q->isWindow() || !q->parentWidget()) ? pos : q->parentWidget()->d_func()->mapFromGlobal(pos); + return p - q->data->crect.topLeft(); + } + int x, y; + Window child; + XTranslateCoordinates(X11->display, + QApplication::desktop()->screen(xinfo.screen())->internalWinId(), + q->internalWinId(), pos.x(), pos.y(), &x, &y, &child); + return mapFromWS(QPoint(x, y)); +} + +QPoint QWidget::mapToGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos + offset; + + return d->mapToGlobal(pos); +} + +QPoint QWidget::mapFromGlobal(const QPoint &pos) const +{ + Q_D(const QWidget); + QPoint offset = data->crect.topLeft(); + const QWidget *w = this; + const QWidget *p = w->parentWidget(); + while (!w->isWindow() && p) { + w = p; + p = p->parentWidget(); + offset += w->data->crect.topLeft(); + } + + const QWidgetPrivate *wd = w->d_func(); + QTLWExtra *tlw = wd->topData(); + if (!tlw->embedded) + return pos - offset; + + return d->mapFromGlobal(pos); +} + +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) { + if (QX11Info::isCompositingManagerRunning() + && q->testAttribute(Qt::WA_TranslucentBackground) + && !(q->parent())) + XSetWindowBackground(X11->display, q->internalWinId(), + QColormap::instance(xinfo.screen()).pixel(Qt::transparent)); + else + 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.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; + + // preparing images to set the _NET_WM_ICON property + QIcon icon = q->windowIcon(); + QVector<long> icon_data; + Qt::HANDLE pixmap_handle = 0; + 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)); + } + 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.byteCount()); + } 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()) { + 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 QPixmap(qt_toX11Pixmap(QBitmap(icon.pixmap(QSize(64,64))))); + pixmap_handle = 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)))); + pixmap_handle = static_cast<QX11PixmapData*>(topData->iconPixmap->data.data())->x11ConvertToDefaultDepth(); + } + } + } + + if (!q->internalWinId()) + return; + + if (!icon_data.isEmpty()) { + XChangeProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) icon_data.data(), + icon_data.size()); + } else { + XDeleteProperty(X11->display, q->internalWinId(), ATOM(_NET_WM_ICON)); + } + + XWMHints *h = XGetWMHints(X11->display, q->internalWinId()); + XWMHints wm_hints; + if (!h) { + memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy + h = &wm_hints; + } + + if (pixmap_handle) { + h->icon_pixmap = pixmap_handle; + h->flags |= IconPixmapHint; + } else { + h->icon_pixmap = 0; + h->flags &= ~(IconPixmapHint | IconMaskHint); + } + + 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() +{ + 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); + + if (X11->isSupportedByWM(ATOM(_NET_ACTIVE_WINDOW)) + && !(tlw->windowFlags() & Qt::X11BypassWindowManagerHint)) { + XEvent e; + e.xclient.type = ClientMessage; + e.xclient.message_type = ATOM(_NET_ACTIVE_WINDOW); + e.xclient.display = X11->display; + e.xclient.window = tlw->internalWinId(); + e.xclient.format = 32; + e.xclient.data.l[0] = 1; // 1 == application + e.xclient.data.l[1] = X11->userTime; + if (QWidget *aw = QApplication::activeWindow()) + e.xclient.data.l[2] = aw->internalWinId(); + else + e.xclient.data.l[2] = XNone; + e.xclient.data.l[3] = 0; + e.xclient.data.l[4] = 0; + XSendEvent(X11->display, RootWindow(X11->display, tlw->x11Info().screen()), + false, SubstructureNotifyMask | SubstructureRedirectMask, &e); + } else { + if (!qt_widget_private(tlw)->topData()->waitingForMapNotify) + XSetInputFocus(X11->display, tlw->internalWinId(), XRevertToParent, X11->time); + } + } +} + +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; + } + + if (mwmhints.decorations == MWM_DECOR_ALL) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + mwmhints.decorations = (MWM_DECOR_BORDER + | MWM_DECOR_TITLE + | MWM_DECOR_MENU); + } else { + mwmhints.decorations &= ~MWM_DECOR_RESIZEH; + } + + if (q->windowFlags() & Qt::WindowMinimizeButtonHint) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + mwmhints.decorations |= MWM_DECOR_MINIMIZE; + mwmhints.functions |= MWM_FUNC_MINIMIZE; + } + if (q->windowFlags() & Qt::WindowMaximizeButtonHint) { + mwmhints.flags |= MWM_HINTS_DECORATIONS; + 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"; + if (!netWmState.contains(ATOM(_NET_WM_STATE_ABOVE))) + netWmState.append(ATOM(_NET_WM_STATE_ABOVE)); + if (!netWmState.contains(ATOM(_NET_WM_STATE_STAYS_ON_TOP))) + netWmState.append(ATOM(_NET_WM_STATE_STAYS_ON_TOP)); + } else if (flags & Qt::WindowStaysOnBottomHint) { + if (!netWmState.contains(ATOM(_NET_WM_STATE_BELOW))) + netWmState.append(ATOM(_NET_WM_STATE_BELOW)); + } + if (q->isFullScreen()) { + if (!netWmState.contains(ATOM(_NET_WM_STATE_FULLSCREEN))) + netWmState.append(ATOM(_NET_WM_STATE_FULLSCREEN)); + } + if (q->isMaximized()) { + if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ))) + netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)); + if (!netWmState.contains(ATOM(_NET_WM_STATE_MAXIMIZED_VERT))) + netWmState.append(ATOM(_NET_WM_STATE_MAXIMIZED_VERT)); + } + if (data.window_modality != Qt::NonModal) { + if (!netWmState.contains(ATOM(_NET_WM_STATE_MODAL))) + 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); + +#ifndef QT_NO_XSYNC + if (!topData()->syncUpdateCounter) { + XSyncValue value; + XSyncIntToValue(&value, 0); + topData()->syncUpdateCounter = XSyncCreateCounter(X11->display, value); + + XChangeProperty(X11->display, q->internalWinId(), + ATOM(_NET_WM_SYNC_REQUEST_COUNTER), + XA_CARDINAL, + 32, PropModeReplace, + (uchar *) &topData()->syncUpdateCounter, 1); + + topData()->newCounterValueHi = 0; + topData()->newCounterValueLo = 0; + } +#endif + + 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, const QRect &) +{ + 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(); + else if (!valid_rect) + dirty.translate(dx, dy); + + 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->compress_events = true; + extra->xDndProxy = 0; +} + +void QWidgetPrivate::deleteSysExtra() +{ +} + +void QWidgetPrivate::createTLSysExtra() +{ + extra->topextra->spont_unmapped = 0; + extra->topextra->dnd = 0; + extra->topextra->validWMState = 0; + extra->topextra->waitingForMapNotify = 0; + extra->topextra->parentWinId = 0; + extra->topextra->userTimeWindow = 0; +#ifndef QT_NO_XSYNC + extra->topextra->syncUpdateCounter = 0; + extra->topextra->syncRequestTimestamp = 0; + extra->topextra->newCounterValueHi = 0; + extra->topextra->newCounterValueLo = 0; +#endif +} + +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 ®ion) +{ + 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); +} + +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()); +} + +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 = qrand() % 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); +} + +void QWidgetPrivate::updateX11AcceptFocus() +{ + Q_Q(QWidget); + if (!q->isWindow() || !q->internalWinId()) + return; + + XWMHints *h = XGetWMHints(X11->display, q->internalWinId()); + XWMHints wm_hints; + if (!h) { + memset(&wm_hints, 0, sizeof(wm_hints)); // make valgrind happy + h = &wm_hints; + } + h->flags |= InputHint; + h->input = q->testAttribute(Qt::WA_X11DoNotAcceptFocus) ? False : True; + + XSetWMHints(X11->display, q->internalWinId(), h); + if (h != &wm_hints) + XFree((char *)h); +} + +QT_END_NAMESPACE diff --git a/src/gui/platforms/x11/qwidgetcreate_x11.cpp b/src/gui/platforms/x11/qwidgetcreate_x11.cpp new file mode 100644 index 0000000000..16bd6abf9a --- /dev/null +++ b/src/gui/platforms/x11/qwidgetcreate_x11.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwidget.h" +#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/platforms/x11/qx11embed_x11.cpp b/src/gui/platforms/x11/qx11embed_x11.cpp new file mode 100644 index 0000000000..49a819469e --- /dev/null +++ b/src/gui/platforms/x11/qx11embed_x11.cpp @@ -0,0 +1,1808 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qx11embed_x11.h" +#include <qapplication.h> +#include <qevent.h> +#include <qpainter.h> +#include <qlayout.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qelapsedtimer.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 corresponding KDE 3 + 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 + corresponding KDE 3 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; + XFree(retval); + if (state == 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); + + long 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(x11Info().screen()), 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(x11Info().screen())) + ? 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(x11Info().screen())) { + 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 (((long * )prop_return)[1] & XEMBED_MAPPED) { + XMapWindow(x11Info().display(), internalWinId()); + } else { + XUnmapWindow(x11Info().display(), internalWinId()); + } + } + if (prop_return) + XFree(prop_return); + } + } + + 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->createWinId(); + 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(x11Info().screen()), 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 whether 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. + */ + QElapsedTimer 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->clientIsXEmbed) { + sendXEmbedMessage(d->client, x11Info().display(), XEMBED_WINDOW_ACTIVATE); + } else { + d->checkGrab(); + if (hasFocus()) + XSetInputFocus(x11Info().display(), d->client, XRevertToParent, x11Time()); + } + if (!d->isEmbedded()) + d->moveInputToProxy(); + } + 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(x11Info().screen()), 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) { + long 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) { + long 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(q->x11Info().screen()), 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; + + long *p = (long *)prop_return; + if (nitems_return >= 2) + clientversion = (unsigned int)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); + // Following Owen Taylor's advice from the XEmbed specification to + // always use CurrentTime when no explicit user action is involved. + XSetInputFocus(q->x11Info().display(), focusProxy->internalWinId(), XRevertToParent, CurrentTime); +} + +/*! \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/platforms/x11/qx11embed_x11.h b/src/gui/platforms/x11/qx11embed_x11.h new file mode 100644 index 0000000000..30929f7ba9 --- /dev/null +++ b/src/gui/platforms/x11/qx11embed_x11.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/platforms/x11/qx11info_x11.cpp b/src/gui/platforms/x11/qx11info_x11.cpp new file mode 100644 index 0000000000..f52443befc --- /dev/null +++ b/src/gui/platforms/x11/qx11info_x11.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwidget.h" +#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->ref = 0; + 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/platforms/x11/qx11info_x11.h b/src/gui/platforms/x11/qx11info_x11.h new file mode 100644 index 0000000000..ece85740d2 --- /dev/null +++ b/src/gui/platforms/x11/qx11info_x11.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef 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/text/qfont_mac.cpp b/src/gui/text/qfont_mac.cpp deleted file mode 100644 index daf68c03ea..0000000000 --- a/src/gui/text/qfont_mac.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfont.h" -#include "qfont_p.h" -#include "qfontengine_p.h" -#include "qfontengine_mac_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::Monospace: - return QString::fromLatin1("Courier"); - case QFont::Decorative: - return QString::fromLatin1("Bookman Old Style"); - case QFont::Cursive: - return QString::fromLatin1("Apple Chancery"); - case QFont::Fantasy: - return QString::fromLatin1("Papyrus"); - 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_s60.cpp b/src/gui/text/qfont_s60.cpp deleted file mode 100644 index 114191d765..0000000000 --- a/src/gui/text/qfont_s60.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfont.h" -#include "qfont_p.h" -#include <private/qt_s60_p.h> -#include <private/qpixmap_s60_p.h> -#include "qmutex.h" - -QT_BEGIN_NAMESPACE - -#ifdef QT_NO_FREETYPE -Q_GLOBAL_STATIC(QMutex, lastResortFamilyMutex); -#endif // QT_NO_FREETYPE - -extern QStringList qt_symbian_fontFamiliesOnFontServer(); // qfontdatabase_s60.cpp -Q_GLOBAL_STATIC_WITH_INITIALIZER(QStringList, fontFamiliesOnFontServer, { - // We are only interested in the initial font families. No Application fonts. - // Therefore, we are allowed to cache the list. - x->append(qt_symbian_fontFamiliesOnFontServer()); -}); - -QString QFont::lastResortFont() const -{ - // Symbian's font Api does not distinguish between font and family. - // Therefore we try to get a "Family" first, then fall back to "Sans". - static QString font = lastResortFamily(); - if (font.isEmpty()) - font = QLatin1String("Sans"); - return font; -} - -QString QFont::lastResortFamily() const -{ -#ifdef QT_NO_FREETYPE - QMutexLocker locker(lastResortFamilyMutex()); - static QString family; - if (family.isEmpty()) { - - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - - CFont *font; - const TInt err = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec()); - Q_ASSERT(err == KErrNone); - const TFontSpec spec = font->FontSpecInTwips(); - family = QString((const QChar *)spec.iTypeface.iName.Ptr(), spec.iTypeface.iName.Length()); - S60->screenDevice()->ReleaseFont(font); - - lock.relock(); - } - return family; -#else // QT_NO_FREETYPE - // For the FreeType case we just hard code the face name, since otherwise on - // East Asian systems we may get a name for a stroke based (non-ttf) font. - - // TODO: Get the type face name in a proper way - - const bool isJapaneseOrChineseSystem = - User::Language() == ELangJapanese || User::Language() == ELangPrcChinese; - - static QString family; - if (family.isEmpty()) { - QStringList families = qt_symbian_fontFamiliesOnFontServer(); - const char* const preferredFamilies[] = {"Nokia Sans S60", "Series 60 Sans"}; - for (int i = 0; i < sizeof preferredFamilies / sizeof preferredFamilies[0]; ++i) { - const QString preferredFamily = QLatin1String(preferredFamilies[i]); - if (families.contains(preferredFamily)) { - family = preferredFamily; - break; - } - } - } - - return QLatin1String(isJapaneseOrChineseSystem?"Heisei Kaku Gothic S60":family.toLatin1()); -#endif // QT_NO_FREETYPE -} - -QString QFont::defaultFamily() const -{ -#ifdef QT_NO_FREETYPE - switch(d->request.styleHint) { - case QFont::SansSerif: { - static const char* const preferredSansSerif[] = {"Nokia Sans S60", "Series 60 Sans"}; - for (int i = 0; i < sizeof preferredSansSerif / sizeof preferredSansSerif[0]; ++i) { - const QString sansSerif = QLatin1String(preferredSansSerif[i]); - if (fontFamiliesOnFontServer()->contains(sansSerif)) - return sansSerif; - } - } - // No break. Intentional fall through. - default: - return lastResortFamily(); - } -#endif // QT_NO_FREETYPE - return lastResortFamily(); -} - -QT_END_NAMESPACE diff --git a/src/gui/text/qfont_win.cpp b/src/gui/text/qfont_win.cpp deleted file mode 100644 index 3ef761bfa5..0000000000 --- a/src/gui/text/qfont_win.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 -extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp - -// ### maybe move to qapplication_win -QFont qt_LOGFONTtoQFont(LOGFONT& lf, bool /*scale*/) -{ - QString family = QString::fromWCharArray(lf.lfFaceName); - QFont qf(family); - qf.setItalic(lf.lfItalic); - if (lf.lfWeight != FW_DONTCARE) - qf.setWeight(weightFromInteger(lf.lfWeight)); - 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: - case QFont::Monospace: - return QString::fromLatin1("Courier New"); - case QFont::Decorative: - return QString::fromLatin1("Bookman Old Style"); - case QFont::Cursive: - return QString::fromLatin1("Comic Sans MS"); - case QFont::Fantasy: - return QString::fromLatin1("Impact"); - 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 deleted file mode 100644 index c72a5fade5..0000000000 --- a/src/gui/text/qfont_x11.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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; - -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); -} - -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::Monospace: - return QString::fromLatin1("Courier New"); - - case QFont::Cursive: - return QString::fromLatin1("Comic Sans MS"); - - case QFont::Fantasy: - return QString::fromLatin1("Impact"); - - 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_mac.cpp b/src/gui/text/qfontdatabase_mac.cpp deleted file mode 100644 index 5ba236b5f7..0000000000 --- a/src/gui/text/qfontdatabase_mac.cpp +++ /dev/null @@ -1,466 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qt_mac_p.h> -#include "qfontengine_p.h" -#include <qfile.h> -#include <qabstractfileengine.h> -#include <stdlib.h> -#include <qendian.h> -#include <private/qfontengine_coretext_p.h> -#include <private/qfontengine_mac_p.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 - -#ifndef QT_MAC_USE_COCOA -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 = qt_determine_writing_systems_from_truetype_bits(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; -} -#endif - -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 QT_MAC_USE_COCOA - 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; -} - -static inline float weightToFloat(unsigned int weight) -{ - return (weight - 50) / 100.0; -} - -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 = familyList(req); - - 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(); - -#if defined(QT_MAC_USE_COCOA) - QCFString fontName = NULL, familyName = NULL; -#else - ATSFontFamilyRef familyRef = 0; - ATSFontRef fontRef = 0; -#endif - - 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(); -#if defined(QT_MAC_USE_COCOA) - QCFType<CTFontRef> ctFont = CTFontCreateWithName(QCFString(db->families[k]->name), 12, NULL); - if (ctFont) { - fontName = CTFontCopyFullName(ctFont); - familyName = CTFontCopyFamilyName(ctFont); - goto FamilyFound; - } -#else - familyRef = ATSFontFamilyFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - if (familyRef) { - fontRef = ATSFontFindFromName(QCFString(db->families[k]->name), kATSOptionFlagsDefault); - goto FamilyFound; - } -#endif - } - } - } -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); - -#ifdef QT_MAC_USE_COCOA - fontDef.family = familyName; - QFontEngine *engine = new QCoreTextFontEngineMulti(fontName, fontDef, d->kerning); -#else - QCFString actualName; - if (ATSFontFamilyGetName(familyRef, kATSOptionFlagsDefault, &actualName) == noErr) - fontDef.family = actualName; - QFontEngine *engine = new QFontEngineMacMulti(familyRef, fontRef, fontDef, d->kerning); -#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(); -#if defined(QT_MAC_USE_COCOA) - // Make sure that the family name set on the font matches what - // kCTFontFamilyNameAttribute returns in initializeDb(). - // So far the best solution seems find the installed font - // using CoreText and get the family name from it. - // (ATSFontFamilyGetName appears to be the correct API, but also - // returns the font display name.) - for(int i = 0; i < containedFonts.size(); ++i) { - QCFString fontPostScriptName; - ATSFontGetPostScriptName(containedFonts[i], kATSOptionFlagsDefault, &fontPostScriptName); - QCFType<CTFontDescriptorRef> font = CTFontDescriptorCreateWithNameAndSize(fontPostScriptName, 14); - QCFString familyName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); - fnt->families.append(familyName); - } -#else - for(int i = 0; i < containedFonts.size(); ++i) { - QCFString family; - ATSFontGetName(containedFonts[i], kATSOptionFlagsDefault, &family); - fnt->families.append(family); - } -#endif - - 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_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp deleted file mode 100644 index 1db4a7d359..0000000000 --- a/src/gui/text/qfontdatabase_s60.cpp +++ /dev/null @@ -1,1091 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qapplication_p.h> -#include "qdir.h" -#include "qfont_p.h" -#include "qfontengine_s60_p.h" -#include "qabstractfileengine.h" -#include "qdesktopservices.h" -#include "qtemporaryfile.h" -#include "qtextcodec.h" -#include <private/qpixmap_s60_p.h> -#include <private/qt_s60_p.h> -#include "qendian.h" -#include <private/qcore_symbian_p.h> -#ifdef QT_NO_FREETYPE -#include <openfont.h> -#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS -#include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file -#endif // SYMBIAN_ENABLE_SPLIT_HEADERS -#endif // QT_NO_FREETYPE - -QT_BEGIN_NAMESPACE - -QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp -{ - QStringList result; - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - const int numTypeFaces = S60->screenDevice()->NumTypefaces(); - for (int i = 0; i < numTypeFaces; i++) { - TTypefaceSupport typefaceSupport; - S60->screenDevice()->TypefaceSupport(typefaceSupport, i); - const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); - result.append(familyName); - } - lock.relock(); - return result; -} - -QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters, - QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort, - bool uniqueFileNames = true) -{ - QFileInfoList result; - - // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z: - QStringList driveStrings; - foreach (const QFileInfo &drive, QDir::drives()) - driveStrings.append(drive.absolutePath()); - driveStrings.sort(); - const QString zDriveString(QLatin1String("Z:/")); - driveStrings.removeAll(zDriveString); - driveStrings.prepend(zDriveString); - - QStringList uniqueFileNameList; - for (int i = driveStrings.count() - 1; i >= 0; --i) { - const QDir dirOnDrive(driveStrings.at(i) + path); - const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort); - if (uniqueFileNames) { - foreach(const QFileInfo &entry, entriesOnDrive) { - if (!uniqueFileNameList.contains(entry.fileName())) { - uniqueFileNameList.append(entry.fileName()); - result.append(entry); - } - } - } else { - result.append(entriesOnDrive); - } - } - return result; -} - -#ifdef QT_NO_FREETYPE -class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras -{ -public: - QSymbianFontDatabaseExtrasImplementation(); - ~QSymbianFontDatabaseExtrasImplementation(); - - const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const; - void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt); - static inline bool appFontLimitReached(); - TUid addFontFileToFontStore(const QFileInfo &fontFileInfo); - static void clear(); - - static inline QString tempAppFontFolder(); - static const QString appFontMarkerPrefix; - static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>' - - struct CFontFromFontStoreReleaser { - static inline void cleanup(CFont *font) - { - if (!font) - return; - const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras); - dbExtras->m_store->ReleaseFont(font); - } - }; - - struct CFontFromScreenDeviceReleaser { - static inline void cleanup(CFont *font) - { - if (!font) - return; - S60->screenDevice()->ReleaseFont(font); - } - }; - -// m_heap, m_store, m_rasterizer and m_extras are used if Symbian -// does not provide the Font Table API - RHeap* m_heap; - CFontStore *m_store; - COpenFontRasterizer *m_rasterizer; - mutable QList<const QSymbianTypeFaceExtras *> m_extras; - - mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash; - mutable QSet<QString> m_applicationFontFamilies; -}; - -const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix = - QLatin1String("Q"); - -inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() -{ - return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\'); -} - -QString QSymbianFontDatabaseExtrasImplementation::appFontMarker() -{ - static QString result; - if (result.isEmpty()) { - quint16 id = 0; - if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { - // We are allowed to load app fonts even from previous, crashed runs - // of this application, since we can access the font tables. - const quint32 uid = RProcess().Type().MostDerived().iUid; - id = static_cast<quint16>(uid + (uid >> 16)); - } else { - // If no font table Api is available, we must not even load a font - // from a previous (crashed) run of this application. Reason: we - // won't get the font tables, they are not in the CFontStore. - // So, we use the pid, for more uniqueness. - id = static_cast<quint16>(RProcess().Id().Id()); - } - result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0')); - Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4); - } - return result; -} - -static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName) -{ - const int idLength = 3; // Keep in sync with id length in appFontMarker(). - const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix; - if (fontName.length() < prefix.length() + idLength - || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix) - return false; - // Testing if the the id is base32 data - for (int i = fontName.length() - idLength; i < fontName.length(); ++i) { - const QChar &c = fontName.at(i); - if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9') - || c >= QLatin1Char('a') && c <= QLatin1Char('v'))) - return false; - } - return true; -} - -// If fontName is an application font of this app, prepend the app font marker -QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName) -{ - QFontDatabasePrivate *db = privateDb(); - Q_ASSERT(db); - const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - return dbExtras->m_applicationFontFamilies.contains(fontName) ? - fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker() - : fontName; -} - -static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName) -{ - return markedFontName.left(markedFontName.length() - - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length()); -} - -QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation() -{ - if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { - QStringList filters; - filters.append(QLatin1String("*.ttf")); - filters.append(QLatin1String("*.ccc")); - filters.append(QLatin1String("*.ltt")); - const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters); - - const TInt heapMinLength = 0x1000; - const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength); - m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength); - QT_TRAP_THROWING( - m_store = CFontStore::NewL(m_heap); - m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); - CleanupStack::PushL(m_rasterizer); - m_store->InstallRasterizerL(m_rasterizer); - CleanupStack::Pop(m_rasterizer);); - - foreach (const QFileInfo &fontFileInfo, fontFiles) - addFontFileToFontStore(fontFileInfo); - } -} - -void QSymbianFontDatabaseExtrasImplementation::clear() -{ - QFontDatabasePrivate *db = privateDb(); - if (!db) - return; - const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - if (!dbExtras) - return; // initializeDb() has never been called - if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { - qDeleteAll(dbExtras->m_extrasHash); - } else { - typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator; - for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) { - dbExtras->m_store->ReleaseFont((*p)->fontOwner()); - delete *p; - } - dbExtras->m_extras.clear(); - } - dbExtras->m_extrasHash.clear(); -} - -void qt_cleanup_symbianFontDatabase() -{ - QFontDatabasePrivate *db = privateDb(); - if (!db) - return; - - QSymbianFontDatabaseExtrasImplementation::clear(); - - if (!db->applicationFonts.isEmpty()) { - QFontDatabase::removeAllApplicationFonts(); - // We remove the left over temporary font files of Qt application. - // Active fonts are undeletable since the font server holds a handle - // on them, so we do not need to worry to delete other running - // applications' fonts. - const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()); - const QStringList filter( - QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf")); - foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter)) - QFile(ttfFile.absoluteFilePath()).remove(); - db->applicationFonts.clear(); - } -} - -QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation() -{ - qt_cleanup_symbianFontDatabase(); - if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { - delete m_store; - m_heap->Close(); - } -} - -#ifndef FNTSTORE_H_INLINES_SUPPORT_FMM -/* - Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()' - that returns a private data member. The header will change between SDKs. But Qt has - to build on any SDK version and run on other versions of Symbian OS. - This function performs the needed pointer arithmetic to get the right COpenFont* -*/ -COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont) -{ - const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private - const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont); - return (valueIOpenFont & 1) ? - (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset - (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer -} -#endif // FNTSTORE_H_INLINES_SUPPORT_FMM - -const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface, - bool bold, bool italic) const -{ - const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface); - const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic)); - if (!m_extrasHash.contains(searchKey)) { - TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1); - if (bold) - searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); - if (italic) - searchSpec.iFontStyle.SetPosture(EPostureItalic); - - CFont* font = NULL; - if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { - const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec); - Q_ASSERT(err == KErrNone && font); - QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font); - QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font); - sFont.take(); - m_extrasHash.insert(searchKey, extras); - } else { - const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec); - Q_ASSERT(err == KErrNone && font); - const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font); - COpenFont *openFont = -#ifdef FNTSTORE_H_INLINES_SUPPORT_FMM - bitmapFont->OpenFont(); -#else // FNTSTORE_H_INLINES_SUPPORT_FMM - OpenFontFromBitmapFont(bitmapFont); -#endif // FNTSTORE_H_INLINES_SUPPORT_FMM - const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib(); - const QString foundKey = - QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length()); - if (!m_extrasHash.contains(foundKey)) { - QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font); - QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont); - sFont.take(); - m_extras.append(extras); - m_extrasHash.insert(searchKey, extras); - m_extrasHash.insert(foundKey, extras); - } else { - m_store->ReleaseFont(font); - m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey)); - } - } - } - return m_extrasHash.value(searchKey); -} - -void QSymbianFontDatabaseExtrasImplementation::removeAppFontData( - QFontDatabasePrivate::ApplicationFont *fnt) -{ - clear(); - if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable() - && fnt->fontStoreFontFileUid.iUid != 0) - m_store->RemoveFile(fnt->fontStoreFontFileUid); - if (!fnt->families.isEmpty()) - m_applicationFontFamilies.remove(fnt->families.first()); - if (fnt->screenDeviceFontFileId != 0) - S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); - QFile::remove(fnt->temporaryFileName); - *fnt = QFontDatabasePrivate::ApplicationFont(); -} - -bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() -{ - QFontDatabasePrivate *db = privateDb(); - if (!db) - return false; - const int maxAppFonts = 5; - int registeredAppFonts = 0; - foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts) - if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts) - return true; - return false; -} - -TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) -{ - Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()); - const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath()); - const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); - TUid fontUid = {0}; - TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr)); - return fontUid; -} - -#else // QT_NO_FREETYPE -class QFontEngineFTS60 : public QFontEngineFT -{ -public: - QFontEngineFTS60(const QFontDef &fd); -}; - -QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd) - : QFontEngineFT(fd) -{ - default_hint_style = HintFull; -} -#endif // QT_NO_FREETYPE - -/* - QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60 - and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the - Freetype based font rendering need them, they are here. -*/ -qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation) -{ - CWsScreenDevice* device = S60->screenDevice(); - return (orientation == Qt::Horizontal? - device->HorizontalPixelsToTwips(pixels) - :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint; -} - -qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation) -{ - CWsScreenDevice* device = S60->screenDevice(); - const int twips = points * KTwipsPerPoint; - return orientation == Qt::Horizontal? - device->HorizontalTwipsToPixels(twips) - :device->VerticalTwipsToPixels(twips); -} - -QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies) - : QFontEngineMulti(fallbackFamilies.size() + 1) - , m_script(script) - , m_fallbackFamilies(fallbackFamilies) -{ - engines[0] = first; - first->ref.ref(); - fontDef = engines[0]->fontDef; -} - -void QFontEngineMultiS60::loadEngine(int at) -{ - Q_ASSERT(at < engines.size()); - Q_ASSERT(engines.at(at) == 0); - - QFontDef request = fontDef; - request.styleStrategy |= QFont::NoFontMerging; - request.family = m_fallbackFamilies.at(at-1); - engines[at] = QFontDatabase::findFont(m_script, - /*fontprivate*/0, - request); - Q_ASSERT(engines[at]); -} - -#ifdef QT_NO_FREETYPE -static bool registerScreenDeviceFont(int screenDeviceFontIndex, - const QSymbianFontDatabaseExtrasImplementation *dbExtras) -{ - TTypefaceSupport typefaceSupport; - S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex); - - QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); - if (qt_symbian_fontNameHasAppFontMarker(familyName)) { - const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); - if (familyName.endsWith(marker)) { - familyName = qt_symbian_appFontNameWithoutMarker(familyName); - dbExtras->m_applicationFontFamilies.insert(familyName); - } else { - return false; // This was somebody else's application font. Skip it. - } - } - - CFont *font; // We have to get a font instance in order to know all the details - TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11); - if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone) - return false; - QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font); - if (font->TypeUid() != KCFbsFontUid) - return false; - TOpenFontFaceAttrib faceAttrib; - const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font); - cfbsFont->GetFaceAttrib(faceAttrib); - - QtFontStyle::Key styleKey; - styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal; - styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal; - - QtFontFamily *family = privateDb()->family(familyName, true); - family->fixedPitch = faceAttrib.IsMonoWidth(); - QtFontFoundry *foundry = family->foundry(QString(), true); - QtFontStyle *style = foundry->style(styleKey, true); - style->smoothScalable = typefaceSupport.iIsScalable; - style->pixelSize(0, true); - - const QSymbianTypeFaceExtras *typeFaceExtras = - dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic()); - const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2')); - const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData()); - const unsigned char* ulUnicodeRange = data + 42; - quint32 unicodeRange[4] = { - qFromBigEndian<quint32>(ulUnicodeRange), - qFromBigEndian<quint32>(ulUnicodeRange + 4), - qFromBigEndian<quint32>(ulUnicodeRange + 8), - qFromBigEndian<quint32>(ulUnicodeRange + 12) - }; - const unsigned char* ulCodePageRange = data + 78; - quint32 codePageRange[2] = { - qFromBigEndian<quint32>(ulCodePageRange), - qFromBigEndian<quint32>(ulCodePageRange + 4) - }; - const QList<QFontDatabase::WritingSystem> writingSystems = - qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); - foreach (const QFontDatabase::WritingSystem system, writingSystems) - family->writingSystems[system] = QtFontFamily::Supported; - return true; -} -#endif - -static void initializeDb() -{ - QFontDatabasePrivate *db = privateDb(); - if(!db || db->count) - return; - -#ifdef QT_NO_FREETYPE - if (!db->symbianExtras) - db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation; - - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - - const int numTypeFaces = S60->screenDevice()->NumTypefaces(); - const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - for (int i = 0; i < numTypeFaces; i++) - registerScreenDeviceFont(i, dbExtras); - - // We have to clear/release all CFonts, here, in case one of the fonts is - // an application font of another running Qt app. Otherwise the other Qt app - // cannot remove it's application font, anymore -> "Zombie Font". - QSymbianFontDatabaseExtrasImplementation::clear(); - - lock.relock(); - -#else // QT_NO_FREETYPE - QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation)); - dir.setNameFilters(QStringList() << QLatin1String("*.ttf") - << QLatin1String("*.ttc") << QLatin1String("*.pfa") - << QLatin1String("*.pfb")); - for (int i = 0; i < int(dir.count()); ++i) { - const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); - db->addTTFile(file); - } -#endif // QT_NO_FREETYPE -} - -static inline void load(const QString &family = QString(), int script = -1) -{ - Q_UNUSED(family) - Q_UNUSED(script) - initializeDb(); -} - -struct OffsetTable { - quint32 sfntVersion; - quint16 numTables, searchRange, entrySelector, rangeShift; -}; - -struct TableRecord { - quint32 tag, checkSum, offset, length; -}; - -struct NameTableHead { - quint16 format, count, stringOffset; -}; - -struct NameRecord { - quint16 platformID, encodingID, languageID, nameID, length, offset; -}; - -static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount) -{ - quint32 result = 0; - const quint32 *ptr = reinterpret_cast<const quint32*>(data); - const quint32 *endPtr = - ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32); - while (ptr < endPtr) { - const quint32 unit32Value = *ptr++; - result += qFromBigEndian(unit32Value); - } - return result; -} - -static inline quint32 toDWordBoundary(quint32 value) -{ - return (value + 3) & ~3; -} - -static inline quint32 dWordPadding(quint32 value) -{ - return (4 - (value & 3)) & 3; -} - -static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker) -{ - const quint32 tableLength = static_cast<quint32>(table.size()); - - if (tableLength > 50000 // hard limit - || tableLength < sizeof(NameTableHead)) // corrupt name table - return false; - - const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData()); - const quint16 count = qFromBigEndian(head->count); - const quint16 stringOffset = qFromBigEndian(head->stringOffset); - if (count > 200 // hard limit - || stringOffset >= tableLength // corrupt name table - || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table - return false; - - QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader); - const QByteArray markerUtf16BE = encoder.fromUnicode(marker); - const QByteArray markerAscii = marker.toAscii(); - - QByteArray markedTable; - markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra - markedTable.append(table, stringOffset); - QByteArray markedStrings; - quint32 stringDataCount = stringOffset; - for (quint16 i = 0; i < count; ++i) { - const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i; - NameRecord *nameRecord = - reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset); - const quint16 nameID = qFromBigEndian(nameRecord->nameID); - const quint16 platformID = qFromBigEndian(nameRecord->platformID); - const quint16 encodingID = qFromBigEndian(nameRecord->encodingID); - const quint16 offset = qFromBigEndian(nameRecord->offset); - const quint16 length = qFromBigEndian(nameRecord->length); - stringDataCount += length; - if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string. - || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds - return false; - const bool needsMarker = - nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21; - const bool isUnicode = - platformID == 0 || platformID == 3 && encodingID == 1; - const QByteArray originalString = - QByteArray::fromRawData(table.constData() + stringOffset + offset, length); - QByteArray markedString; - if (needsMarker) { - const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1); - markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii); - } else { - markedString = originalString; - } - nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length())); - nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length())); - markedStrings.append(markedString); - } - markedTable.append(markedStrings); - table = markedTable; - return true; -} - -const quint32 ttfMaxFileSize = 3500000; - -static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker) -{ - const quint32 ttfChecksumNumber = 0xb1b0afba; - const quint32 alignment = 4; - const quint32 ttfLength = static_cast<quint32>(ttf.size()); - if (ttfLength > ttfMaxFileSize // hard limit - || ttfLength % alignment != 0 // ttf sizes are always factors of 4 - || ttfLength <= sizeof(OffsetTable) // ttf too short - || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid - return false; - - const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData()); - const quint16 numTables = qFromBigEndian(offsetTable->numTables); - const quint32 recordsLength = - toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord)); - if (numTables > 30 // hard limit - || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty. - return false; - - QByteArray markedTtf; - markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra - markedTtf.append(ttf.constData(), recordsLength); - - const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head' - int indexOfHeadTable = -1; - quint32 ttfDataSize = recordsLength; - typedef QPair<quint32, quint32> Range; - QList<Range> memoryRanges; - memoryRanges.reserve(numTables); - for (int i = 0; i < numTables; ++i) { - TableRecord *tableRecord = - reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); - const quint32 offset = qFromBigEndian(tableRecord->offset); - const quint32 length = qFromBigEndian(tableRecord->length); - const quint32 lengthAligned = toDWordBoundary(length); - ttfDataSize += lengthAligned; - if (offset < recordsLength // must not intersect ttf header/records - || offset % alignment != 0 // must be aligned - || offset > ttfLength - alignment // table out of bounds - || offset + lengthAligned > ttfLength // table out of bounds - || ttfDataSize > ttfLength) // tables would not fit into the ttf - return false; - - foreach (const Range &range, memoryRanges) - if (offset < range.first + range.second && offset + lengthAligned > range.first) - return false; // Overlaps with another table - memoryRanges.append(Range(offset, lengthAligned)); - - quint32 checkSum = qFromBigEndian(tableRecord->checkSum); - if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) { - if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32)) - return false; // Invalid 'head' table - const quint32 *checkSumAdjustmentTag = - reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset); - const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag); - checkSum += checkSumAdjustment; - indexOfHeadTable = i; // For the ttf checksum re-calculation, later - } - if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length)) - return false; // Table checksum is invalid - - bool updateTableChecksum = false; - QByteArray table; - if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) { - table = QByteArray(ttf.constData() + offset, length); - if (!ttfMarkNameTable(table, marker)) - return false; // Name table was not markable. - updateTableChecksum = true; - } else { - table = QByteArray::fromRawData(ttf.constData() + offset, length); - } - - tableRecord->offset = qToBigEndian(markedTtf.size()); - tableRecord->length = qToBigEndian(table.size()); - markedTtf.append(table); - markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding - if (updateTableChecksum) { - TableRecord *tableRecord = // Need to recalculate, since markedTtf changed - reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); - const quint32 offset = qFromBigEndian(tableRecord->offset); - const quint32 length = qFromBigEndian(tableRecord->length); - tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length)); - } - } - if (indexOfHeadTable == -1 // 'head' table is mandatory - || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian. - return false; - TableRecord *headRecord = - reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord)); - quint32 *checkSumAdjustmentTag = - reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset); - *checkSumAdjustmentTag = 0; - const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count()); - *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum); - ttf = markedTtf; - return true; -} - -static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName) -{ - bool result = false; - QString ttfFileName; - QFile tempFileGuard; - QFileInfo info(fileName); - if (!data.isEmpty()) { - QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() - + QSymbianFontDatabaseExtrasImplementation::appFontMarker() - + QLatin1String("XXXXXX.ttf")); - if (!tempfile.open() || tempfile.write(data) == -1) - return false; - ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath()); - tempfile.setAutoRemove(false); - tempfile.close(); - tempFileGuard.setFileName(ttfFileName); - if (!tempFileGuard.open(QIODevice::ReadOnly)) - return false; - } else if (info.isFile()) { - ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath()); - } else { - return false; - } - - CFontStore *store = 0; - RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000); - if (heap) { - QT_TRAP_THROWING( - CleanupClosePushL(*heap); - store = CFontStore::NewL(heap); - CleanupStack::PushL(store); - COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); - CleanupStack::PushL(rasterizer); - store->InstallRasterizerL(rasterizer); - CleanupStack::Pop(rasterizer); - TUid fontUid = {-1}; - TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName))); - if (fontUid.iUid != -1) - result = true; - CleanupStack::PopAndDestroy(2, heap); // heap, store - ); - } - - if (tempFileGuard.isOpen()) - tempFileGuard.remove(); - - return result; -} - -static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) -{ - if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() - || fnt->data.size() > ttfMaxFileSize // hard limit - || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf - || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit - return; - -// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower). -// Therefore, not using it for now, but eventually in a later version. -// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName)) -// return; - - QFontDatabasePrivate *db = privateDb(); - if (!db) - return; - - if (!db->count) - initializeDb(); - - QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - if (!dbExtras) - return; - - const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); - - // The QTemporaryFile object being used in the following section must be - // destructed before letting Symbian load the TTF file. Symbian would not - // load it otherwise, because QTemporaryFile will still keep some handle - // on it. The scope is used to reduce the life time of the QTemporaryFile. - // In order to prevent other processes from modifying the file between the - // moment where the QTemporaryFile is destructed and the file is loaded by - // Symbian, we have a QFile "tempFileGuard" outside the scope which opens - // the file in ReadOnly mode while the QTemporaryFile is still alive. - QFile tempFileGuard; - { - QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() - + marker + QLatin1String("XXXXXX.ttf")); - if (!tempfile.open()) - return; - const QString tempFileName = QFileInfo(tempfile).canonicalFilePath(); - if (fnt->data.isEmpty()) { - QFile sourceFile(fnt->fileName); - if (!sourceFile.open(QIODevice::ReadOnly)) - return; - fnt->data = sourceFile.readAll(); - } - if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1) - return; - tempfile.setAutoRemove(false); - tempfile.close(); // Tempfile still keeps a file handle, forbidding write access - fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore. - tempFileGuard.setFileName(tempFileName); - if (!tempFileGuard.open(QIODevice::ReadOnly)) - return; - fnt->temporaryFileName = tempFileName; - } - - const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName); - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer(); - const TInt err = - S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId); - tempFileGuard.close(); // Did its job - const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer(); - if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device? - int fontOnServerIndex = fontsOnServerAfter.count() - 1; - for (int i = 0; i < fontsOnServerBefore.count(); i++) { - if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) { - fontOnServerIndex = i; - break; - } - } - - // Must remove all font engines with their CFonts, first. - QFontCache::instance()->clear(); - db->free(); - QSymbianFontDatabaseExtrasImplementation::clear(); - - if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) - fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName)); - - const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex); - fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName)); - if (!qt_symbian_fontNameHasAppFontMarker(appFontName) - || !registerScreenDeviceFont(fontOnServerIndex, dbExtras)) - dbExtras->removeAppFontData(fnt); - } else { - if (fnt->screenDeviceFontFileId > 0) - S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open! - QFile::remove(fnt->temporaryFileName); - *fnt = QFontDatabasePrivate::ApplicationFont(); - } - lock.relock(); -} - -bool QFontDatabase::removeApplicationFont(int handle) -{ - QMutexLocker locker(fontDatabaseMutex()); - - QFontDatabasePrivate *db = privateDb(); - if (!db || handle < 0 || handle >= db->applicationFonts.count()) - return false; - QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - if (!dbExtras) - return false; - - QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle]; - if (fnt->families.isEmpty()) - return true; // Nothing to remove. Return peacefully. - - // Must remove all font engines with their CFonts, first - QFontCache::instance()->clear(); - db->free(); - dbExtras->removeAppFontData(fnt); - - db->invalidate(); // This will just emit 'fontDatabaseChanged()' - return true; -} - -bool QFontDatabase::removeAllApplicationFonts() -{ - QMutexLocker locker(fontDatabaseMutex()); - - const int applicationFontsCount = privateDb()->applicationFonts.count(); - for (int i = 0; i < applicationFontsCount; ++i) - if (!removeApplicationFont(i)) - return false; - return true; -} - -bool QFontDatabase::supportsThreadedFontRendering() -{ - return false; -} - -static -QFontDef cleanedFontDef(const QFontDef &req) -{ - QFontDef result = req; - if (result.pixelSize <= 0) { - result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize)); - result.pointSize = 0; - } - return result; -} - -QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req) -{ - const QFontCache::Key key(cleanedFontDef(req), script); - - if (!privateDb()->count) - initializeDb(); - - QFontEngine *fe = QFontCache::instance()->findEngine(key); - if (!fe) { - // Making sure that fe->fontDef.family will be an existing font. - initializeDb(); - QFontDatabasePrivate *db = privateDb(); - QtFontDesc desc; - QList<int> blacklistedFamilies; - match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies); - if (!desc.family) // falling back to application font - desc.family = db->family(QApplication::font().defaultFamily()); - Q_ASSERT(desc.family); - - // Making sure that desc.family supports the requested script - QtFontDesc mappedDesc; - bool supportsScript = false; - do { - match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); - if (mappedDesc.family == desc.family) { - supportsScript = true; - break; - } - blacklistedFamilies.append(mappedDesc.familyIndex); - } while (mappedDesc.family); - if (!supportsScript) { - blacklistedFamilies.clear(); - match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); - if (mappedDesc.family) - desc = mappedDesc; - } - - const QString fontFamily = desc.family->name; - QFontDef request = req; - request.family = fontFamily; -#ifdef QT_NO_FREETYPE - const QSymbianFontDatabaseExtrasImplementation *dbExtras = - static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); - const QSymbianTypeFaceExtras *typeFaceExtras = - dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal); - - // We need a valid pixelSize, e.g. for lineThickness() - if (request.pixelSize < 0) - request.pixelSize = request.pointSize * d->dpi / 72; - - fe = new QFontEngineS60(request, typeFaceExtras); -#else // QT_NO_FREETYPE - Q_UNUSED(d) - QFontEngine::FaceId faceId; - const QtFontFamily * const reqQtFontFamily = db->family(fontFamily); - faceId.filename = reqQtFontFamily->fontFilename; - faceId.index = reqQtFontFamily->fontFileIndex; - - QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request)); - if (fte->init(faceId, true, QFontEngineFT::Format_A8)) - fe = fte; - else - delete fte; -#endif // QT_NO_FREETYPE - - Q_ASSERT(fe); - if (script == QUnicodeTables::Common - && !(req.styleStrategy & QFont::NoFontMerging) - && !fe->symbol) { - - QStringList commonFonts; - for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) { - if (scriptForWritingSystem[ws] != script) - continue; - for (int i = 0; i < db->count; ++i) { - if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported) - commonFonts.append(db->families[i]->name); - } - } - - // Hack: Prioritize .ccc fonts - const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60")); - if (commonFonts.removeAll(niceEastAsianFont) > 0) - commonFonts.prepend(niceEastAsianFont); - - fe = new QFontEngineMultiS60(fe, script, commonFonts); - } - } - fe->ref.ref(); - QFontCache::instance()->insertEngine(key, fe); - return fe; -} - -void QFontDatabase::load(const QFontPrivate *d, int script) -{ - QFontEngine *fe = 0; - QFontDef req = d->request; - - if (!d->engineData) { - const QFontCache::Key key(cleanedFontDef(req), script); - getEngineData(d, key); - } - - // the cached engineData could have already loaded the engine we want - if (d->engineData->engines[script]) - fe = d->engineData->engines[script]; - - if (!fe) { - if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { - fe = new QTestFontEngine(req.pixelSize); - fe->fontDef = req; - } else { - fe = findFont(script, d, req); - } - d->engineData->engines[script] = fe; - } -} - -QT_END_NAMESPACE diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp deleted file mode 100644 index 05b7509bf6..0000000000 --- a/src/gui/text/qfontdatabase_win.cpp +++ /dev/null @@ -1,1348 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qt_windows.h" -#include <qmath.h> -#include <private/qapplication_p.h> -#include "qfont_p.h" -#include "qfontengine_p.h" -#include "qpaintdevice.h" -#include <private/qsystemlibrary_p.h> -#include "qabstractfileengine.h" -#include "qendian.h" - -#if !defined(QT_NO_DIRECTWRITE) -# include "qsettings.h" -# include "qfontenginedirectwrite_p.h" -#endif - -#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 ); - LOGFONT lf; - memset(&lf, 0, sizeof(LOGFONT)); - memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length()) * sizeof(wchar_t)); - lf.lfCharSet = DEFAULT_CHARSET; - HFONT hfont = CreateFontIndirect(&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; -} - -extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp - -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::fromWCharArray(f->elfScript); -// qDebug("script=%s", escript.latin1()); - - 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; - - // 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; - styleKey.weight = weightFromInteger(weight); - - 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_WS_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 = qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange); - - for (int i = 0; i < systems.count(); ++i) { - QFontDatabase::WritingSystem writingSystem = systems.at(i); - - // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains - // the symbol for Baht, and Windows thus reports that it supports the Thai script. - // Since it's the default UI font on this platform, most widgets will be unable to - // display Thai text by default. As a temporary work around, we special case Segoe UI - // and remove the Thai script from its list of supported writing systems. - if (writingSystem != QFontDatabase::Thai || familyName != QLatin1String("Segoe UI")) - family->writingSystems[writingSystem] = 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 = QString::fromWCharArray(f->elfLogFont.lfFaceName); - QString script = QString::fromWCharArray(f->elfScript); - - FONTSIGNATURE signature = textmetric->ntmFontSig; - - // 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); - - LOGFONT lf; - lf.lfCharSet = DEFAULT_CHARSET; - if (fam.isNull()) { - lf.lfFaceName[0] = 0; - } else { - memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded - } - lf.lfPitchAndFamily = 0; - - EnumFontFamiliesEx(dummy, &lf, - (FONTENUMPROC)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); - LOGFONT lf; - memset(&lf, 0, sizeof(LOGFONT)); - memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE, familyName.size())); - lf.lfCharSet = DEFAULT_CHARSET; - HFONT hfont = CreateFontIndirect(&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", qPrintable(family->name), 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, HDC fontHdc, int dpi) -{ - fe->fontDef = request; // most settings are equal - - HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fontHdc) ? fontHdc : shared_dc(); - SelectObject(dc, fe->hfont); - wchar_t n[64]; - GetTextFace(dc, 64, n); - fe->fontDef.family = QString::fromWCharArray(n); - fe->fontDef.fixedPitch = !(fe->tm.tmPitchAndFamily & TMPF_FIXED_PITCH); - if (fe->fontDef.pointSize < 0) { - fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; - } else if (fe->fontDef.pixelSize == -1) { - fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); - } -} - -#if !defined(QT_NO_DIRECTWRITE) -static void initFontInfo(QFontEngineDirectWrite *fe, const QFontDef &request, - int dpi, IDWriteFont *font) -{ - fe->fontDef = request; - - IDWriteFontFamily *fontFamily = NULL; - HRESULT hr = font->GetFontFamily(&fontFamily); - - IDWriteLocalizedStrings *familyNames = NULL; - if (SUCCEEDED(hr)) - hr = fontFamily->GetFamilyNames(&familyNames); - - UINT32 index = 0; - BOOL exists = false; - - wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; - - if (SUCCEEDED(hr)) { - int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH); - - if (defaultLocaleSuccess) - hr = familyNames->FindLocaleName(localeName, &index, &exists); - - if (SUCCEEDED(hr) && !exists) - hr = familyNames->FindLocaleName(L"en-us", &index, &exists); - } - - if (!exists) - index = 0; - - UINT32 length = 0; - if (SUCCEEDED(hr)) - hr = familyNames->GetStringLength(index, &length); - - wchar_t *name = new (std::nothrow) wchar_t[length+1]; - if (name == NULL) - hr = E_OUTOFMEMORY; - - // Get the family name. - if (SUCCEEDED(hr)) - hr = familyNames->GetString(index, name, length + 1); - - if (SUCCEEDED(hr)) - fe->fontDef.family = QString::fromWCharArray(name); - - delete[] name; - if (familyNames != NULL) - familyNames->Release(); - - if (FAILED(hr)) - qErrnoWarning(hr, "initFontInfo: Failed to get family name"); - - if (fe->fontDef.pointSize < 0) - fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / dpi; - else if (fe->fontDef.pixelSize == -1) - fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * dpi / 72.); -} -#endif - -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; - -#if !defined(QT_NO_DIRECTWRITE) -static QString fontNameSubstitute(const QString &familyName) -{ - QLatin1String key("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\" - "FontSubstitutes"); - return QSettings(key, QSettings::NativeFormat).value(familyName, familyName).toString(); -} -#endif - -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 QFontDef &request, - HDC fontHdc, int dpi, bool rawMode, - const QtFontDesc *desc, - const QStringList &family_list) -{ - LOGFONT lf; - memset(&lf, 0, sizeof(LOGFONT)); - - bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fontHdc; - - HDC hdc = shared_dc(); - QString font_name = desc != 0 ? desc->family->name : request.family; - - if (useDevice) { - hdc = fontHdc; - font_name = request.family; - } - - bool stockFont = false; - bool preferClearTypeAA = false; - - HFONT hfont = 0; - - -#if !defined(QT_NO_DIRECTWRITE) - bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting) - || (request.hintingPreference == QFont::PreferVerticalHinting); - IDWriteFont *directWriteFont = 0; -#else - bool useDirectWrite = false; -#endif - - if (rawMode) { // will choose a stock font - int f, deffnt = SYSTEM_FONT; - QString fam = desc != 0 ? desc->family->name.toLower() : request.family.toLower(); - if (fam == QLatin1String("default")) - f = deffnt; - else if (fam == QLatin1String("system")) - f = SYSTEM_FONT; -#ifndef Q_WS_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 = -qRound(request.pixelSize); - lf.lfWidth = 0; - lf.lfEscapement = 0; - lf.lfOrientation = 0; - if (desc == 0 || desc->style->key.weight == 50) - lf.lfWeight = FW_DONTCARE; - else - lf.lfWeight = (desc->style->key.weight*900)/99; - lf.lfItalic = (desc != 0 && 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_WS_WINCE - } else if (request.styleStrategy & QFont::PreferDevice) { - strat = OUT_DEVICE_PRECIS; - } else if (request.styleStrategy & QFont::PreferOutline) { - strat = OUT_OUTLINE_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_WS_WINCE - else if (request.styleStrategy & QFont::PreferQuality) - qual = PROOF_QUALITY; -#endif - - if (request.styleStrategy & QFont::PreferAntialias) { - if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) { - qual = CLEARTYPE_QUALITY; - preferClearTypeAA = true; - } 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"); - - memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded - - hfont = CreateFontIndirect(&lf); - if (!hfont) - qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed"); - - stockFont = (hfont == 0); - bool ttf = false; - int avWidth = 0; - BOOL res; - HGDIOBJ oldObj = SelectObject(hdc, hfont); - - TEXTMETRIC tm; - res = GetTextMetrics(hdc, &tm); - avWidth = tm.tmAveCharWidth; - ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; - SelectObject(hdc, oldObj); - - if (!ttf || !useDirectWrite) { - useDirectWrite = false; - - if (hfont && (!ttf || request.stretch != 100)) { - DeleteObject(hfont); - if (!res) - qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); - lf.lfWidth = avWidth * request.stretch/100; - hfont = CreateFontIndirect(&lf); - if (!hfont) - qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); - } - -#ifndef Q_WS_WINCE - if (hfont == 0) { - hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); - stockFont = true; - } -#else - if (hfont == 0) { - hfont = (HFONT)GetStockObject(SYSTEM_FONT); - stockFont = true; - } -#endif - - } - -#if !defined(QT_NO_DIRECTWRITE) - else { - // Default to false for DirectWrite (and re-enable once/if everything - // turns out okay) - useDirectWrite = false; - - QFontDatabasePrivate *db = privateDb(); - if (db->directWriteFactory == 0) { - HRESULT hr = DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown **>(&db->directWriteFactory) - ); - if (FAILED(hr)) { - qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed"); - } else { - hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop); - if (FAILED(hr)) - qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed"); - } - } - - if (db->directWriteGdiInterop != 0) { - QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName)); - memcpy(lf.lfFaceName, nameSubstitute.utf16(), - sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); - - HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT( - &lf, - &directWriteFont); - if (FAILED(hr)) { -#ifndef QT_NO_DEBUG - qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed " - "for %ls (0x%lx)", - lf.lfFaceName, hr); -#endif - } else { - DeleteObject(hfont); - useDirectWrite = true; - } - } - } -#endif - - } - - QFontEngine *fe = 0; - if (!useDirectWrite) { - QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); - if (preferClearTypeAA) - few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; - - // 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; - } - } - - initFontInfo(few, request, fontHdc, dpi); - fe = few; - } - -#if !defined(QT_NO_DIRECTWRITE) - else { - QFontDatabasePrivate *db = privateDb(); - - IDWriteFontFace *directWriteFontFace = NULL; - HRESULT hr = directWriteFont->CreateFontFace(&directWriteFontFace); - if (SUCCEEDED(hr)) { - QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(db->directWriteFactory, - directWriteFontFace, - request.pixelSize); - - initFontInfo(fedw, request, dpi, directWriteFont); - - fe = fedw; - } else { - qErrnoWarning(hr, "QFontEngine::loadEngine: CreateFontFace failed"); - } - } - - if (directWriteFont != 0) - directWriteFont->Release(); -#endif - - if(script == QUnicodeTables::Common - && !(request.styleStrategy & QFont::NoFontMerging) - && desc != 0 - && !(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(fe, list); - mfe->fontDef = fe->fontDef; - fe = mfe; - } - return fe; -} - -QFontEngine *qt_load_font_engine_win(const QFontDef &request) -{ - // From qfont.cpp - extern int qt_defaultDpi(); - - QFontCache::Key key(request, QUnicodeTables::Common); - QFontEngine *fe = QFontCache::instance()->findEngine(key); - if (fe != 0) - return fe; - else - return loadEngine(QUnicodeTables::Common, request, 0, qt_defaultDpi(), false, 0, - QStringList()); -} - -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); - - 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, req, d->hdc, d->dpi, d->rawMode, &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 = floor((100.0 * req.pointSize * d->dpi) / 72. + 0.5) / 100; - if (req.pixelSize < 1) - req.pixelSize = 1; - 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 - if (req.pointSize < 0) - 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); - } else { - memset(&signature, 0, sizeof(signature)); - } - appFont->signatures << signature; - } -} - -static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) -{ - if(!fnt->data.isEmpty()) { -#ifndef Q_OS_WINCE - PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QSystemLibrary::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 - wchar_t lpBuffer[MAX_PATH]; - GetTempPath(MAX_PATH, lpBuffer); - QString s = QString::fromWCharArray(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 // QT_NO_TEMPORARYFILE - 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 // Q_OS_WINCE - - 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 - PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"), - "AddFontResourceExW"); - if (!ptrAddFontResourceExW - || ptrAddFontResourceExW((wchar_t*)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0) - return; -#endif // Q_OS_WINCE - - 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)QSystemLibrary::resolve(QLatin1String("gdi32"), - "RemoveFontMemResourceEx"); - if (!ptrRemoveFontMemResourceEx - || !ptrRemoveFontMemResourceEx(font.handle)) - return false; -#endif // Q_OS_WINCE - } else { -#ifdef Q_OS_WINCE - if (!RemoveFontResource((LPCWSTR)font.fileName.utf16())) - return false; -#else - PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QSystemLibrary::resolve(QLatin1String("gdi32"), - "RemoveFontResourceExW"); - if (!ptrRemoveFontResourceExW - || !ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0)) - return false; -#endif // Q_OS_WINCE - } - - 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 deleted file mode 100644 index 0c0c4c8343..0000000000 --- a/src/gui/text/qfontdatabase_x11.cpp +++ /dev/null @@ -1,2146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <qplatformdefs.h> - -#include <qdebug.h> -#include <qpaintdevice.h> -#include <qelapsedtimer.h> - -#include <private/qt_x11_p.h> -#include "qx11info_x11.h" -#include <qdebug.h> -#include <qfile.h> -#include <qtemporaryfile.h> -#include <qabstractfileengine.h> -#include <qmath.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); - -// from qapplication.cpp -extern bool qt_is_gui_used; - -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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0 }, - // *-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, 0 }, - // 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, 0 } - -}; - -// ----- 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 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 != QLatin1String("*") && (!desc || desc->family->count > 1)) - fd->family += - QLatin1String(" [") + foundry + QLatin1Char(']'); - - 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; - - fontDef.hintingPreference = request.hintingPreference; - 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 = qt_defaultDpiY(); - } - - double size; - if (FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &size) == FcResultMatch) - fontDef.pixelSize = 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 - "" // N'Ko -}; -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 - 0x7ca // N'Ko -}; -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 - 0 // N'Ko -}; -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 - 0x7ca // N'Ko -}; -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 - "nko " // N'Ko -}; -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; - - FcPattern *pattern = FcPatternCreate(); - FcDefaultSubstitute(pattern); - FcChar8 *lang = 0; - if (FcPatternGetString(pattern, FC_LANG, 0, &lang) == FcResultMatch) - db->systemLang = QString::fromUtf8((const char *) lang); - FcPatternDestroy(pattern); - - 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, bool forceXLFD = false) -{ - if (X11->has_fontconfig && !forceXLFD) { - initializeDb(); - return; - } - -#ifdef QFONTDATABASE_DEBUG - QElapsedTimer 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; - - QElapsedTimer 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", int(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; - FcPatternDel(pattern, FC_WEIGHT); - 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; - FcPatternDel(pattern, FC_SLANT); - FcPatternAddInteger(pattern, FC_SLANT, slant_value); - - double size_value = qMax(qreal(1.), request.pixelSize); - FcPatternDel(pattern, FC_PIXEL_SIZE); - FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size_value); - - int stretch = request.stretch; - if (!stretch) - stretch = 100; - FcPatternDel(pattern, FC_WIDTH); - FcPatternAddInteger(pattern, FC_WIDTH, stretch); - - if (X11->display && QX11Info::appDepth(screen) <= 8) { - FcPatternDel(pattern, FC_ANTIALIAS); - // can't do antialiasing on 8bpp - FcPatternAddBool(pattern, FC_ANTIALIAS, false); - } else if (request.styleStrategy & (QFont::PreferAntialias|QFont::NoAntialias)) { - FcPatternDel(pattern, FC_ANTIALIAS); - FcPatternAddBool(pattern, FC_ANTIALIAS, - !(request.styleStrategy & QFont::NoAntialias)); - } - - if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') { - Q_ASSERT(script < QUnicodeTables::ScriptCount); - FcLangSet *ls = FcLangSetCreate(); - FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]); - FcPatternDel(pattern, FC_LANG); - 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 if (*specialLanguages[script] != '\0'){ - 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, QList<int>(), true); - 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) { - if (desc.encoding->encoding >= 0) - 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 || e->encoding < 0) - 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) && enc->id >= 0) { - 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; -} - -#if (defined(QT_ARCH_ARM) || defined(QT_ARCH_ARMV6)) && defined(Q_CC_GNU) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3) -#define NEEDS_GCC_BUG_WORKAROUND -#endif - -#ifdef NEEDS_GCC_BUG_WORKAROUND -static inline void gccBugWorkaround(const QFontDef &req) -{ - char buffer[8]; - snprintf(buffer, 8, "%f", req.pixelSize); -} -#endif - -/*! \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 = qFloor(qt_pixelSize(req.pointSize, d->dpi) * 100.0 + 0.5) * 0.01; - if (req.pixelSize < 1) - req.pixelSize = 1; - -#ifdef NEEDS_GCC_BUG_WORKAROUND - // req.pixelSize ends up with a bogus value unless this workaround is called - gccBugWorkaround(req); -#endif - - 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 - if (req.pointSize < 0) - 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 && qt_is_gui_used) { - 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-existent"); - 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; - QFontDatabasePrivate *db = privateDb(); - - 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, *familylang = 0; - int i, n = 0; - for (i = 0; ; i++) { - if (FcPatternGetString(pattern, FC_FAMILYLANG, i, &familylang) != FcResultMatch) - break; - QString familyLang = QString::fromUtf8((const char *) familylang); - if (familyLang.compare(db->systemLang, Qt::CaseInsensitive) == 0) { - n = i; - break; - } - } - - if (FcPatternGetString(pattern, FC_FAMILY, n, &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 -} - -QString QFontDatabase::resolveFontFamilyAlias(const QString &family) -{ -#if defined(QT_NO_FONTCONFIG) - return family; -#else - FcPattern *pattern = FcPatternCreate(); - if (!pattern) - return family; - - FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) family.toUtf8().data()); - FcConfigSubstitute(0, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcChar8 *familyAfterSubstitution; - FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution); - QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution); - FcPatternDestroy(pattern); - - return resolved; -#endif -} - -QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_coretext.mm b/src/gui/text/qfontengine_coretext.mm deleted file mode 100644 index d4df2183ed..0000000000 --- a/src/gui/text/qfontengine_coretext.mm +++ /dev/null @@ -1,880 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfontengine_coretext_p.h" - -#include <QtCore/qendian.h> -#include <QtCore/qsettings.h> - -#include <private/qimage_p.h> - -#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) - -QT_BEGIN_NAMESPACE - -static float SYNTHETIC_ITALIC_SKEW = tanf(14 * acosf(0) / 90); - -static void loadAdvancesForGlyphs(CTFontRef ctfont, - QVarLengthArray<CGGlyph> &cgGlyphs, - QGlyphLayout *glyphs, int len, - QTextEngine::ShaperFlags flags, - const QFontDef &fontDef) -{ - Q_UNUSED(flags); - QVarLengthArray<CGSize> advances(len); - CTFontGetAdvancesForGlyphs(ctfont, kCTFontHorizontalOrientation, cgGlyphs.data(), advances.data(), len); - - for (int i = 0; i < len; ++i) { - if (glyphs->glyphs[i] & 0xff000000) - continue; - glyphs->advances_x[i] = QFixed::fromReal(advances[i].width); - glyphs->advances_y[i] = QFixed::fromReal(advances[i].height); - } - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - for (int i = 0; i < len; ++i) { - glyphs->advances_x[i] = glyphs->advances_x[i].round(); - glyphs->advances_y[i] = glyphs->advances_y[i].round(); - } - } -} - -QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning) - : 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; - } - - transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - } - - QCFType<CTFontDescriptorRef> descriptor = CTFontDescriptorCreateWithNameAndSize(name, fontDef.pixelSize); - QCFType<CTFontRef> baseFont = CTFontCreateWithFontDescriptor(descriptor, fontDef.pixelSize, &transform); - ctfont = CTFontCreateCopyWithSymbolicTraits(baseFont, fontDef.pixelSize, &transform, 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); - } - init(kerning); -} - -QCoreTextFontEngineMulti::QCoreTextFontEngineMulti(CGFontRef cgFontRef, const QFontDef &fontDef, bool kerning) - : QFontEngineMulti(0) -{ - this->fontDef = fontDef; - - transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) { - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - } - - ctfont = CTFontCreateWithGraphicsFont(cgFontRef, fontDef.pixelSize, &transform, NULL); - init(kerning); -} - -QCoreTextFontEngineMulti::~QCoreTextFontEngineMulti() -{ - CFRelease(ctfont); -} - -void QCoreTextFontEngineMulti::init(bool kerning) -{ - Q_ASSERT(ctfont != NULL); - attributeDict = CFDictionaryCreateMutable(0, 2, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(attributeDict, NSFontAttributeName, ctfont); - if (!kerning) { - float zero = 0.0; - QCFType<CFNumberRef> noKern = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &zero); - CFDictionaryAddValue(attributeDict, kCTKernAttributeName, noKern); - } - - QCoreTextFontEngine *fe = new QCoreTextFontEngine(ctfont, fontDef); - fe->ref.ref(); - engines.append(fe); -} - -uint QCoreTextFontEngineMulti::fontIndexForFont(CTFontRef font) const -{ - for (int i = 0; i < engines.count(); ++i) { - if (CFEqual(engineAt(i)->ctfont, font)) - return i; - } - - QCoreTextFontEngineMulti *that = const_cast<QCoreTextFontEngineMulti *>(this); - QCoreTextFontEngine *fe = new QCoreTextFontEngine(font, fontDef); - 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 flags, - unsigned short *logClusters, const HB_CharAttributes *, - QScriptItem *si) const -{ - QCFType<CFStringRef> cfstring = CFStringCreateWithCharactersNoCopy(0, - reinterpret_cast<const UniChar *>(str), - len, kCFAllocatorNull); - QCFType<CFAttributedStringRef> attributedString = CFAttributedStringCreate(0, cfstring, attributeDict); - QCFType<CTTypesetterRef> typeSetter; - -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - if (flags & QTextEngine::RightToLeft) { - const void *optionKeys[] = { kCTTypesetterOptionForcedEmbeddingLevel }; - const short rtlForcedEmbeddingLevelValue = 1; - const void *rtlOptionValues[] = { CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &rtlForcedEmbeddingLevelValue) }; - QCFType<CFDictionaryRef> options = CFDictionaryCreate(kCFAllocatorDefault, optionKeys, rtlOptionValues, 1, - &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - typeSetter = CTTypesetterCreateWithAttributedStringAndOptions(attributedString, options); - } else -#else - Q_UNUSED(flags); -#endif - 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) { - // CoreText failed to shape the text we gave it, so we assume one glyph - // per character and build a list of invalid glyphs with zero advance - *nglyphs = len; - for (int i = 0; i < len; ++i) { - outGlyphs[i] = 0; - if (logClusters) - logClusters[i] = i; - outAdvances_x[i] = QFixed(); - outAdvances_y[i] = QFixed(); - outAttributes[i].clusterStart = true; - } - return true; - } - - 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); - CFRange stringRange = CTRunGetStringRange(run); - int prepend = 0; -#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 - UniChar beginGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location); - QChar dir = QChar::direction(beginGlyph); - bool beginWithOverride = dir == QChar::DirLRO || dir == QChar::DirRLO || dir == QChar::DirLRE || dir == QChar::DirRLE; - if (beginWithOverride) { - logClusters[stringRange.location] = 0; - outGlyphs[0] = 0xFFFF; - outAdvances_x[0] = 0; - outAdvances_y[0] = 0; - outAttributes[0].clusterStart = true; - outAttributes[0].dontPrint = true; - outGlyphs++; - outAdvances_x++; - outAdvances_y++; - outAttributes++; - prepend = 1; - } -#endif - UniChar endGlyph = CFStringGetCharacterAtIndex(cfstring, stringRange.location + stringRange.length - 1); - bool endWithPDF = QChar::direction(endGlyph) == QChar::DirPDF; - if (endWithPDF) - glyphCount++; - - 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)); - uint fontIndex = fontIndexForFont(runFont); - const QFontEngine *engine = engineAt(fontIndex); - fontIndex <<= 24; - si->ascent = qMax(engine->ascent(), si->ascent); - si->descent = qMax(engine->descent(), si->descent); - si->leading = qMax(engine->leading(), si->leading); - //NSLog(@"Run Font Name = %@", CTFontCopyFamilyName(runFont)); - if (endWithPDF) - glyphCount--; - - 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 + prepend; - (i < stringRange.location + stringRange.length) && (k < glyphCount); ++i) { - if (tmpIndices[k * rtlSign + rtlOffset] == i || i == stringRange.location + prepend) { - 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); - // Use negative y advance for flipped coordinate system - outAdvances_y[idx] = QFixed::fromReal(tmpPoints[i].y - tmpPoints[i + 1].y); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - outAdvances_x[idx] = outAdvances_x[idx].round(); - outAdvances_y[idx] = outAdvances_y[idx].round(); - } - } - CGSize lastGlyphAdvance; - CTFontGetAdvancesForGlyphs(runFont, kCTFontHorizontalOrientation, tmpGlyphs + glyphCount - 1, &lastGlyphAdvance, 1); - - outGlyphs[rtl ? 0 : (glyphCount - 1)] = tmpGlyphs[glyphCount - 1] | fontIndex; - outAdvances_x[rtl ? 0 : (glyphCount - 1)] = - (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(lastGlyphAdvance.width).round() - : QFixed::fromReal(lastGlyphAdvance.width); - - if (endWithPDF) { - logClusters[stringRange.location + stringRange.length - 1] = glyphCount + prepend; - outGlyphs[glyphCount] = 0xFFFF; - outAdvances_x[glyphCount] = 0; - outAdvances_y[glyphCount] = 0; - outAttributes[glyphCount].clusterStart = true; - outAttributes[glyphCount].dontPrint = true; - glyphCount++; - } - } - 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 -{ - *nglyphs = len; - QCFType<CFStringRef> cfstring; - - QVarLengthArray<CGGlyph> cgGlyphs(len); - CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); - - for (int i = 0; i < len; ++i) { - if (cgGlyphs[i]) { - glyphs->glyphs[i] = cgGlyphs[i]; - } else { - if (!cfstring) - cfstring = CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar *>(str), len, kCFAllocatorNull); - QCFType<CTFontRef> substituteFont = CTFontCreateForString(ctfont, cfstring, CFRangeMake(i, 1)); - CGGlyph substituteGlyph = 0; - CTFontGetGlyphsForCharacters(substituteFont, (const UniChar*)str + i, &substituteGlyph, 1); - if (substituteGlyph) { - const uint fontIndex = (fontIndexForFont(substituteFont) << 24); - glyphs->glyphs[i] = substituteGlyph | fontIndex; - if (!(flags & QTextEngine::GlyphIndicesOnly)) { - CGSize advance; - CTFontGetAdvancesForGlyphs(substituteFont, kCTFontHorizontalOrientation, &substituteGlyph, &advance, 1); - glyphs->advances_x[i] = QFixed::fromReal(advance.width); - glyphs->advances_y[i] = QFixed::fromReal(advance.height); - } - } - } - } - - if (flags & QTextEngine::GlyphIndicesOnly) - return true; - - loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); - return true; -} - -void QCoreTextFontEngineMulti::loadEngine(int) -{ - // Do nothing - Q_ASSERT(false); -} - -extern int qt_antialiasing_threshold; // from qapplication.cpp - -static inline CGAffineTransform transformFromFontDef(const QFontDef &fontDef) -{ - CGAffineTransform transform = CGAffineTransformIdentity; - if (fontDef.stretch != 100) - transform = CGAffineTransformMakeScale(float(fontDef.stretch) / float(100), 1); - return transform; -} - -QCoreTextFontEngine::QCoreTextFontEngine(CTFontRef font, const QFontDef &def) -{ - fontDef = def; - transform = transformFromFontDef(fontDef); - ctfont = font; - CFRetain(ctfont); - cgFont = CTFontCopyGraphicsFont(font, NULL); - init(); -} - -QCoreTextFontEngine::QCoreTextFontEngine(CGFontRef font, const QFontDef &def) -{ - fontDef = def; - transform = transformFromFontDef(fontDef); - cgFont = font; - // Keep reference count balanced - CFRetain(cgFont); - ctfont = CTFontCreateWithGraphicsFont(font, fontDef.pixelSize, &transform, NULL); - init(); -} - -QCoreTextFontEngine::~QCoreTextFontEngine() -{ - CFRelease(cgFont); - CFRelease(ctfont); -} - -extern QFont::Weight weightFromInteger(int weight); // qfontdatabase.cpp - -int getTraitValue(CFDictionaryRef allTraits, CFStringRef trait) -{ - if (CFDictionaryContainsKey(allTraits, trait)) { - CFNumberRef traitNum = (CFNumberRef) CFDictionaryGetValue(allTraits, trait); - float v = 0; - CFNumberGetValue(traitNum, kCFNumberFloatType, &v); - // the value we get from CFNumberRef is from -1.0 to 1.0 - int value = v * 500 + 500; - return value; - } - - return 0; -} - -void QCoreTextFontEngine::init() -{ - Q_ASSERT(ctfont != NULL); - Q_ASSERT(cgFont != NULL); - - QCFString family = CTFontCopyFamilyName(ctfont); - fontDef.family = family; - - synthesisFlags = 0; - CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(ctfont); - if (traits & kCTFontItalicTrait) - fontDef.style = QFont::StyleItalic; - - CFDictionaryRef allTraits = CTFontCopyTraits(ctfont); - fontDef.weight = weightFromInteger(getTraitValue(allTraits, kCTFontWeightTrait)); - int slant = getTraitValue(allTraits, kCTFontSlantTrait); - if (slant > 500 && !(traits & kCTFontItalicTrait)) - fontDef.style = QFont::StyleOblique; - CFRelease(allTraits); - - if (fontDef.weight >= QFont::Bold && !(traits & kCTFontBoldTrait)) - synthesisFlags |= SynthesizedBold; - // XXX: we probably don't need to synthesis italic for oblique font - if (fontDef.style != QFont::StyleNormal && !(traits & kCTFontItalicTrait)) - synthesisFlags |= SynthesizedItalic; - - avgCharWidth = 0; - QByteArray os2Table = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); - unsigned emSize = CTFontGetUnitsPerEm(ctfont); - if (os2Table.size() >= 10) { - fsType = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 8)); - // qAbs is a workaround for weird fonts like Lucida Grande - qint16 width = qAbs(qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(os2Table.constData() + 2))); - avgCharWidth = QFixed::fromReal(width * fontDef.pixelSize / emSize); - } else - avgCharWidth = QFontEngine::averageCharWidth(); - - ctMaxCharWidth = ctMinLeftBearing = ctMinRightBearing = 0; - QByteArray hheaTable = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a')); - if (hheaTable.size() >= 16) { - quint16 width = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 10)); - ctMaxCharWidth = width * fontDef.pixelSize / emSize; - qint16 bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 12)); - ctMinLeftBearing = bearing * fontDef.pixelSize / emSize; - bearing = qFromBigEndian<qint16>(reinterpret_cast<const uchar *>(hheaTable.constData() + 14)); - ctMinRightBearing = bearing * fontDef.pixelSize / emSize; - } -} - -bool QCoreTextFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, - int *nglyphs, QTextEngine::ShaperFlags flags) const -{ - *nglyphs = len; - QCFType<CFStringRef> cfstring; - - QVarLengthArray<CGGlyph> cgGlyphs(len); - CTFontGetGlyphsForCharacters(ctfont, (const UniChar*)str, cgGlyphs.data(), len); - - for (int i = 0; i < len; ++i) - if (cgGlyphs[i]) - glyphs->glyphs[i] = cgGlyphs[i]; - - if (flags & QTextEngine::GlyphIndicesOnly) - return true; - - loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, len, flags, fontDef); - return true; -} - -glyph_metrics_t QCoreTextFontEngine::boundingBox(const QGlyphLayout &glyphs) -{ - QFixed w; - bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; - - for (int i = 0; i < glyphs.numGlyphs; ++i) { - w += round ? glyphs.effectiveAdvance(i).round() - : glyphs.effectiveAdvance(i); - } - return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), 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); - if (synthesisFlags & QFontEngine::SynthesizedItalic) { - rect.size.width += rect.size.height * SYNTHETIC_ITALIC_SKEW; - } - 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); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - ret.xoff = ret.xoff.round(); - ret.yoff = ret.yoff.round(); - } - - return ret; -} - -QFixed QCoreTextFontEngine::ascent() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetAscent(ctfont)).round() - : QFixed::fromReal(CTFontGetAscent(ctfont)); -} -QFixed QCoreTextFontEngine::descent() const -{ - QFixed d = QFixed::fromReal(CTFontGetDescent(ctfont)); - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - d = d.round(); - - // subtract a pixel to even out the historical +1 in QFontMetrics::height(). - // Fix in Qt 5. - return d - 1; -} -QFixed QCoreTextFontEngine::leading() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetLeading(ctfont)).round() - : QFixed::fromReal(CTFontGetLeading(ctfont)); -} -QFixed QCoreTextFontEngine::xHeight() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? QFixed::fromReal(CTFontGetXHeight(ctfont)).round() - : QFixed::fromReal(CTFontGetXHeight(ctfont)); -} - -QFixed QCoreTextFontEngine::averageCharWidth() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? avgCharWidth.round() : avgCharWidth; -} - -qreal QCoreTextFontEngine::maxCharWidth() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? qRound(ctMaxCharWidth) : ctMaxCharWidth; -} - -qreal QCoreTextFontEngine::minLeftBearing() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? qRound(ctMinLeftBearing) : ctMinLeftBearing; -} - -qreal QCoreTextFontEngine::minRightBearing() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? qRound(ctMinRightBearing) : ctMinLeftBearing; -} - -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, -SYNTHETIC_ITALIC_SKEW, 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, -SYNTHETIC_ITALIC_SKEW, 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::imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int /*margin*/, bool aa) -{ - 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 = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - uint cgflags = kCGImageAlphaNoneSkipFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#endif - CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(), - 8, im.bytesPerLine(), colorspace, - cgflags); - CGContextSetFontSize(ctx, fontDef.pixelSize); - CGContextSetShouldAntialias(ctx, aa || - (fontDef.pointSize > qt_antialiasing_threshold - && !(fontDef.styleStrategy & QFont::NoAntialias))); - CGContextSetShouldSmoothFonts(ctx, aa); - 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, SYNTHETIC_ITALIC_SKEW, 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() + subPixelPosition.toReal(); - qreal pos_y = im.height() + br.y.toReal() - 1; - 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); - - return im; -} - -QImage QCoreTextFontEngine::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) -{ - QImage im = imageForGlyph(glyph, subPixelPosition, 0, false); - - 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; -} - -QImage QCoreTextFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, const QTransform &x) -{ - if (x.type() >= QTransform::TxScale) - return QFontEngine::alphaRGBMapForGlyph(glyph, subPixelPosition, margin, x); - - QImage im = imageForGlyph(glyph, subPixelPosition, margin, true); - qGamma_correct_back_to_linear_cs(&im); - return im; -} - -void QCoreTextFontEngine::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const -{ - int i, numGlyphs = glyphs->numGlyphs; - QVarLengthArray<CGGlyph> cgGlyphs(numGlyphs); - - for (i = 0; i < numGlyphs; ++i) { - if (glyphs->glyphs[i] & 0xff000000) - cgGlyphs[i] = 0; - else - cgGlyphs[i] = glyphs->glyphs[i]; - } - - loadAdvancesForGlyphs(ctfont, cgGlyphs, glyphs, numGlyphs, flags, fontDef); -} - -QFontEngine::FaceId QCoreTextFontEngine::faceId() const -{ - return QFontEngine::FaceId(); -} - -bool QCoreTextFontEngine::canRender(const QChar *string, int len) -{ - QVarLengthArray<CGGlyph> cgGlyphs(len); - return CTFontGetGlyphsForCharacters(ctfont, (const UniChar *) string, cgGlyphs.data(), len); -} - -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 *) -{ - // ### -} - -QFixed QCoreTextFontEngine::emSquareSize() const -{ - return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); -} - -QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const -{ - QFontDef newFontDef = fontDef; - newFontDef.pixelSize = pixelSize; - newFontDef.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - - return new QCoreTextFontEngine(cgFont, fontDef); -} - -QT_END_NAMESPACE - -#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) - diff --git a/src/gui/text/qfontengine_coretext_p.h b/src/gui/text/qfontengine_coretext_p.h deleted file mode 100644 index bb80a9b2f3..0000000000 --- a/src/gui/text/qfontengine_coretext_p.h +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QFONTENGINE_CORETEXT_P_H -#define QFONTENGINE_CORETEXT_P_H - -#include <private/qfontengine_p.h> - -#if !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) - -class QRawFontPrivate; -class QCoreTextFontEngineMulti; -class QCoreTextFontEngine : public QFontEngine -{ -public: - QCoreTextFontEngine(CTFontRef font, const QFontDef &def); - QCoreTextFontEngine(CGFontRef font, const QFontDef &def); - ~QCoreTextFontEngine(); - - 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 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 bool supportsSubPixelPositions() const { return true; } - - 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, QFixed subPixelPosition); - virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); - virtual qreal minRightBearing() const; - virtual qreal minLeftBearing() const; - virtual QFixed emSquareSize() const; - - virtual QFontEngine *cloneWithSize(qreal pixelSize) const; - -private: - friend class QRawFontPrivate; - - void init(); - QImage imageForGlyph(glyph_t glyph, QFixed subPixelPosition, int margin, bool colorful); - CTFontRef ctfont; - CGFontRef cgFont; - int synthesisFlags; - CGAffineTransform transform; - QFixed avgCharWidth; - qreal ctMaxCharWidth; - qreal ctMinLeftBearing; - qreal ctMinRightBearing; - friend class QCoreTextFontEngineMulti; -}; - -class QCoreTextFontEngineMulti : public QFontEngineMulti -{ -public: - QCoreTextFontEngineMulti(const QCFString &name, const QFontDef &fontDef, bool kerning); - QCoreTextFontEngineMulti(CGFontRef cgFontRef, 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, - QScriptItem *si) const; - - virtual const char *name() const { return "CoreText"; } -protected: - virtual void loadEngine(int at); - -private: - void init(bool kerning); - inline const QCoreTextFontEngine *engineAt(int i) const - { return static_cast<const QCoreTextFontEngine *>(engines.at(i)); } - - uint fontIndexForFont(CTFontRef font) const; - CTFontRef ctfont; - mutable QCFType<CFMutableDictionaryRef> attributeDict; - CGAffineTransform transform; - friend class QFontDialogPrivate; -}; - -#endif// !defined(Q_WS_MAC) || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) - -#endif // QFONTENGINE_CORETEXT_P_H diff --git a/src/gui/text/qfontengine_mac.mm b/src/gui/text/qfontengine_mac.mm deleted file mode 100644 index 9f094ad7d1..0000000000 --- a/src/gui/text/qfontengine_mac.mm +++ /dev/null @@ -1,1236 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfontengine_mac_p.h" - -#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 <qglobal.h> -#include <qpixmap.h> -#include <qpixmapcache.h> -#include <qvarlengtharray.h> -#include <qdebug.h> -#include <qendian.h> -#include <qmath.h> -#include <private/qimage_p.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; -} - - -#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; - unsigned int styleStrategy; -}; - -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); - } -#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 (nfo->styleStrategy & QFont::ForceIntegerMetrics) { - yAdvance = yAdvance.round(); - xAdvance = xAdvance.round(); - } - - if (glyphId != 0xffff || i == 0) { - if (i < nfo->glyphs->numGlyphs) - { - 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, QScriptItem *) 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; - nfo.styleStrategy = fontDef.styleStrategy; - - int prevNumGlyphs = *nglyphs; - - 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 (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); - if (prevNumGlyphs < *nfo.numGlyphs) - return false; - 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; -} - -// Not used directly for shaping, only used to calculate m_averageCharWidth -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); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - glyphs->advances_x[i] = glyphs->advances_x[i].round(); - glyphs->advances_y[i] = glyphs->advances_y[i].round(); - } - } -} - -glyph_metrics_t QFontEngineMac::boundingBox(const QGlyphLayout &glyphs) -{ - QFixed w; - bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; - for (int i = 0; i < glyphs.numGlyphs; ++i) { - w += round ? glyphs.effectiveAdvance(i).round() - : glyphs.effectiveAdvance(i); - } - return glyph_metrics_t(0, -(ascent()), w - lastRightBearing(glyphs, round), 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); - - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { - gm.x = gm.x.floor(); - gm.y = gm.y.floor(); - gm.xoff = gm.xoff.round(); - gm.yoff = gm.yoff.round(); - } - - return gm; -} - -QFixed QFontEngineMac::ascent() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? m_ascent.round() - : m_ascent; -} - -QFixed QFontEngineMac::descent() const -{ - // subtract a pixel to even out the historical +1 in QFontMetrics::height(). - // Fix in Qt 5. - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? m_descent.round() - 1 - : m_descent; -} - -QFixed QFontEngineMac::leading() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? m_leading.round() - : m_leading; -} - -qreal QFontEngineMac::maxCharWidth() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? qRound(m_maxCharWidth) - : m_maxCharWidth; -} - -QFixed QFontEngineMac::xHeight() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? m_xHeight.round() - : m_xHeight; -} - -QFixed QFontEngineMac::averageCharWidth() const -{ - return (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - ? m_averageCharWidth.round() - : 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); -} - - -/*! - Helper function for alphaMapForGlyph and alphaRGBMapForGlyph. The two are identical, except for - the subpixel antialiasing... -*/ -QImage QFontEngineMac::imageForGlyph(glyph_t glyph, int margin, bool colorful) -{ - const glyph_metrics_t br = boundingBox(glyph); - QImage im(qRound(br.width)+2, qRound(br.height)+4, QImage::Format_RGB32); - im.fill(0xff000000); - - CGColorSpaceRef colorspace = QCoreGraphicsPaintEngine::macGenericColorSpace(); - uint cgflags = kCGImageAlphaNoneSkipFirst; -#ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version - cgflags |= kCGBitmapByteOrder32Host; -#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, colorful); - 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); - - return im; -} - -QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) -{ - QImage im = imageForGlyph(glyph, 2, false); - - 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; -} - -QImage QFontEngineMac::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, const QTransform &t) -{ - QImage im = imageForGlyph(glyph, margin, true); - - if (t.type() >= QTransform::TxScale) { - im = im.transformed(t); - } - - qGamma_correct_back_to_linear_cs(&im); - - return im; -} - - -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) -if (QSysInfo::MacintoshVersion >= QSysInfo::MV_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 -#endif -{ - 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()); -} - 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 = QFontEngine::convertToPostscriptFontFamilyName(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_mac_p.h b/src/gui/text/qfontengine_mac_p.h deleted file mode 100644 index 292ea98d9a..0000000000 --- a/src/gui/text/qfontengine_mac_p.h +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QFONTENGINE_MAC_P_H -#define QFONTENGINE_MAC_P_H - -#include <private/qfontengine_p.h> - -#ifndef QT_MAC_USE_COCOA -class QFontEngineMacMulti; -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); - virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); - -private: - QImage imageForGlyph(glyph_t glyph, int margin, bool colorful); - - 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, QScriptItem *) 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 // QFONTENGINE_MAC_P_H diff --git a/src/gui/text/qfontengine_s60.cpp b/src/gui/text/qfontengine_s60.cpp deleted file mode 100644 index e9b54e350f..0000000000 --- a/src/gui/text/qfontengine_s60.cpp +++ /dev/null @@ -1,569 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qfontengine_s60_p.h" -#include "qtextengine_p.h" -#include "qendian.h" -#include "qglobal.h" -#include <private/qapplication_p.h> -#include "qimage.h" -#include <private/qt_s60_p.h> -#include <private/qpixmap_s60_p.h> - -#include <e32base.h> -#include <e32std.h> -#include <eikenv.h> -#include <gdi.h> -#if defined(Q_SYMBIAN_HAS_GLYPHOUTLINE_API) -#include <graphics/gdi/gdiplatapi.h> -#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API - -// Replication of TGetFontTableParam & friends. -// There is unfortunately no compile time flag like SYMBIAN_FONT_TABLE_API -// that would help us to only replicate these things for Symbian versions -// that do not yet have the font table Api. Symbian's public SDK does -// generally not define any usable macros. -class QSymbianTGetFontTableParam -{ -public: - TUint32 iTag; - TAny *iContent; - TInt iLength; -}; -const TUid QSymbianKFontGetFontTable = {0x102872C1}; -const TUid QSymbianKFontReleaseFontTable = {0x2002AC24}; - -QT_BEGIN_NAMESPACE - -QSymbianTypeFaceExtras::QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont) - : m_cFont(cFont) - , m_symbolCMap(false) - , m_openFont(openFont) -{ - if (!symbianFontTableApiAvailable()) { - TAny *trueTypeExtension = NULL; - m_openFont->ExtendedInterface(KUidOpenFontTrueTypeExtension, trueTypeExtension); - m_trueTypeExtension = static_cast<MOpenFontTrueTypeExtension*>(trueTypeExtension); - Q_ASSERT(m_trueTypeExtension); - } -} - -QSymbianTypeFaceExtras::~QSymbianTypeFaceExtras() -{ - if (symbianFontTableApiAvailable()) - S60->screenDevice()->ReleaseFont(m_cFont); -} - -QByteArray QSymbianTypeFaceExtras::getSfntTable(uint tag) const -{ - if (symbianFontTableApiAvailable()) { - QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 }; - if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { - const char* const fontTableContent = - static_cast<const char *>(fontTableParams.iContent); - const QByteArray fontTable(fontTableContent, fontTableParams.iLength); - m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); - return fontTable; - } - return QByteArray(); - } else { - Q_ASSERT(m_trueTypeExtension->HasTrueTypeTable(tag)); - TInt error = KErrNone; - TInt tableByteLength = 0; - TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); - Q_CHECK_PTR(table); - const QByteArray result(static_cast<const char*>(table), tableByteLength); - m_trueTypeExtension->ReleaseTrueTypeTable(table); - return result; - } -} - -bool QSymbianTypeFaceExtras::getSfntTableData(uint tag, uchar *buffer, uint *length) const -{ - bool result = true; - if (symbianFontTableApiAvailable()) { - QSymbianTGetFontTableParam fontTableParams = { tag, 0, 0 }; - if (m_cFont->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { - if (*length > 0 && *length < fontTableParams.iLength) { - result = false; // Caller did not allocate enough memory - } else { - *length = fontTableParams.iLength; - if (buffer) - memcpy(buffer, fontTableParams.iContent, fontTableParams.iLength); - } - m_cFont->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); - } else { - result = false; - } - } else { - if (!m_trueTypeExtension->HasTrueTypeTable(tag)) - return false; - - TInt error = KErrNone; - TInt tableByteLength; - TAny *table = m_trueTypeExtension->GetTrueTypeTable(error, tag, &tableByteLength); - Q_CHECK_PTR(table); - - if (error != KErrNone) { - return false; - } else if (*length > 0 && *length < tableByteLength) { - result = false; // Caller did not allocate enough memory - } else { - *length = tableByteLength; - if (buffer) - memcpy(buffer, table, tableByteLength); - } - - m_trueTypeExtension->ReleaseTrueTypeTable(table); - } - return result; -} - -const uchar *QSymbianTypeFaceExtras::cmap() const -{ - if (m_cmapTable.isNull()) { - const QByteArray cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); - int size = 0; - const uchar *cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *> - (cmapTable.constData()), cmapTable.size(), &m_symbolCMap, &size); - m_cmapTable = QByteArray(reinterpret_cast<const char *>(cmap), size); - } - return reinterpret_cast<const uchar *>(m_cmapTable.constData()); -} - -bool QSymbianTypeFaceExtras::isSymbolCMap() const -{ - return m_symbolCMap; -} - -CFont *QSymbianTypeFaceExtras::fontOwner() const -{ - return m_cFont; -} - -QFixed QSymbianTypeFaceExtras::unitsPerEm() const -{ - if (m_unitsPerEm.value() != 0) - return m_unitsPerEm; - const QByteArray head = getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')); - const int unitsPerEmOffset = 18; - if (head.size() > unitsPerEmOffset + sizeof(quint16)) { - const uchar* tableData = reinterpret_cast<const uchar*>(head.constData()); - const uchar* unitsPerEm = tableData + unitsPerEmOffset; - m_unitsPerEm = qFromBigEndian<quint16>(unitsPerEm); - } else { - // Bitmap font? Corrupt font? - // We return -1 and let the QFontEngineS60 return the pixel size. - m_unitsPerEm = -1; - } - return m_unitsPerEm; -} - -bool QSymbianTypeFaceExtras::symbianFontTableApiAvailable() -{ - enum FontTableApiAvailability { - Unknown, - Available, - Unavailable - }; - static FontTableApiAvailability availability = - QSysInfo::symbianVersion() < QSysInfo::SV_SF_3 ? - Unavailable : Unknown; - if (availability == Unknown) { - // Actually, we should ask CFeatureDiscovery::IsFeatureSupportedL() - // with FfFontTable here. But since at the time of writing, the - // FfFontTable flag check either gave false positives or false - // negatives. Here comes an implicit check via CFont::ExtendedFunction. - QSymbianTGetFontTableParam fontTableParams = { - MAKE_TAG('O', 'S', '/', '2'), 0, 0 }; - QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); - CFont *font; - const TInt getFontErr = S60->screenDevice()->GetNearestFontInTwips(font, TFontSpec()); - Q_ASSERT(getFontErr == KErrNone); - if (font->ExtendedFunction(QSymbianKFontGetFontTable, &fontTableParams) == KErrNone) { - font->ExtendedFunction(QSymbianKFontReleaseFontTable, &fontTableParams); - availability = Available; - } else { - availability = Unavailable; - } - S60->screenDevice()->ReleaseFont(font); - lock.relock(); - } - return availability == Available; -} - -// duplicated from qfontengine_xyz.cpp -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; -} - -extern QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName); // qfontdatabase_s60.cpp - -CFont *QFontEngineS60::fontWithSize(qreal size) const -{ - CFont *result = 0; - const QString family = qt_symbian_fontNameWithAppFontMarker(QFontEngine::fontDef.family); - TFontSpec fontSpec(qt_QString2TPtrC(family), TInt(size)); - fontSpec.iFontStyle.SetBitmapType(EAntiAliasedGlyphBitmap); - fontSpec.iFontStyle.SetPosture(QFontEngine::fontDef.style == QFont::StyleNormal?EPostureUpright:EPostureItalic); - fontSpec.iFontStyle.SetStrokeWeight(QFontEngine::fontDef.weight > QFont::Normal?EStrokeWeightBold:EStrokeWeightNormal); - const TInt errorCode = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(result, fontSpec); - Q_ASSERT(result && (errorCode == 0)); - return result; -} - -void QFontEngineS60::setFontScale(qreal scale) -{ - if (qFuzzyCompare(scale, qreal(1))) { - if (!m_originalFont) - m_originalFont = fontWithSize(m_originalFontSizeInPixels); - m_activeFont = m_originalFont; - } else { - const qreal scaledFontSizeInPixels = m_originalFontSizeInPixels * scale; - if (!m_scaledFont || - (TInt(scaledFontSizeInPixels) != TInt(m_scaledFontSizeInPixels))) { - releaseFont(m_scaledFont); - m_scaledFontSizeInPixels = scaledFontSizeInPixels; - m_scaledFont = fontWithSize(m_scaledFontSizeInPixels); - } - m_activeFont = m_scaledFont; - } -} - -void QFontEngineS60::releaseFont(CFont *&font) -{ - if (font) { - S60->screenDevice()->ReleaseFont(font); - font = 0; - } -} - -QFontEngineS60::QFontEngineS60(const QFontDef &request, const QSymbianTypeFaceExtras *extras) - : m_extras(extras) - , m_originalFont(0) - , m_originalFontSizeInPixels((request.pixelSize >= 0)? - request.pixelSize:pointsToPixels(request.pointSize)) - , m_scaledFont(0) - , m_scaledFontSizeInPixels(0) - , m_activeFont(0) -{ - QFontEngine::fontDef = request; - setFontScale(1.0); - cache_cost = sizeof(QFontEngineS60); -} - -QFontEngineS60::~QFontEngineS60() -{ - releaseFont(m_originalFont); - releaseFont(m_scaledFont); -} - -QFixed QFontEngineS60::emSquareSize() const -{ - const QFixed unitsPerEm = m_extras->unitsPerEm(); - return unitsPerEm.toInt() == -1 ? - QFixed::fromReal(m_originalFontSizeInPixels) : unitsPerEm; -} - -bool QFontEngineS60::stringToCMap(const QChar *characters, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const -{ - if (*nglyphs < len) { - *nglyphs = len; - return false; - } - - HB_Glyph *g = glyphs->glyphs; - const unsigned char* cmap = m_extras->cmap(); - const bool isRtl = (flags & QTextEngine::RightToLeft); - for (int i = 0; i < len; ++i) { - const unsigned int uc = getChar(characters, i, len); - *g++ = QFontEngine::getTrueTypeGlyphIndex(cmap, - (isRtl && !m_extras->isSymbolCMap()) ? QChar::mirroredChar(uc) : uc); - } - - glyphs->numGlyphs = g - glyphs->glyphs; - *nglyphs = glyphs->numGlyphs; - - if (flags & QTextEngine::GlyphIndicesOnly) - return true; - - recalcAdvances(glyphs, flags); - return true; -} - -void QFontEngineS60::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const -{ - Q_UNUSED(flags); - TOpenFontCharMetrics metrics; - const TUint8 *glyphBitmapBytes; - TSize glyphBitmapSize; - for (int i = 0; i < glyphs->numGlyphs; i++) { - getCharacterData(glyphs->glyphs[i], metrics, glyphBitmapBytes, glyphBitmapSize); - glyphs->advances_x[i] = metrics.HorizAdvance(); - glyphs->advances_y[i] = 0; - } -} - -#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API -static bool parseGlyphPathData(const char *dataStr, const char *dataEnd, QPainterPath &path, - qreal fontPixelSize, const QPointF &offset, bool hinted); -#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API - -void QFontEngineS60::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, - int nglyphs, QPainterPath *path, - QTextItem::RenderFlags flags) -{ -#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API - Q_UNUSED(flags) - RGlyphOutlineIterator iterator; - const TInt error = iterator.Open(*m_activeFont, glyphs, nglyphs); - if (KErrNone != error) - return; - const qreal fontSizeInPixels = qreal(m_activeFont->HeightInPixels()); - int count = 0; - do { - const TUint8* outlineUint8 = iterator.Outline(); - const char* const outlineChar = reinterpret_cast<const char*>(outlineUint8); - const char* const outlineEnd = outlineChar + iterator.OutlineLength(); - parseGlyphPathData(outlineChar, outlineEnd, *path, fontSizeInPixels, - positions[count++].toPointF(), false); - } while(KErrNone == iterator.Next() && count <= nglyphs); - iterator.Close(); -#else // Q_SYMBIAN_HAS_GLYPHOUTLINE_API - QFontEngine::addGlyphsToPath(glyphs, positions, nglyphs, path, flags); -#endif //Q_SYMBIAN_HAS_GLYPHOUTLINE_API -} - -QImage QFontEngineS60::alphaMapForGlyph(glyph_t glyph) -{ - // Note: On some Symbian versions (apparently <= Symbian^1), this - // function will return gray values 0x00, 0x10 ... 0xe0, 0xf0 due - // to a bug. The glyphs are nowhere perfectly opaque. - // This has been fixed for Symbian^3. - - TOpenFontCharMetrics metrics; - const TUint8 *glyphBitmapBytes; - TSize glyphBitmapSize; - getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize); - QImage result(glyphBitmapBytes, glyphBitmapSize.iWidth, glyphBitmapSize.iHeight, glyphBitmapSize.iWidth, QImage::Format_Indexed8); - result.setColorTable(grayPalette()); - return result; -} - -glyph_metrics_t QFontEngineS60::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 - lastRightBearing(glyphs), ascent()+descent()+1, w, 0); -} - -glyph_metrics_t QFontEngineS60::boundingBox_const(glyph_t glyph) const -{ - TOpenFontCharMetrics metrics; - const TUint8 *glyphBitmapBytes; - TSize glyphBitmapSize; - getCharacterData(glyph, metrics, glyphBitmapBytes, glyphBitmapSize); - const glyph_metrics_t result( - metrics.HorizBearingX(), - -metrics.HorizBearingY(), - metrics.Width(), - metrics.Height(), - metrics.HorizAdvance(), - 0 - ); - return result; -} - -glyph_metrics_t QFontEngineS60::boundingBox(glyph_t glyph) -{ - return boundingBox_const(glyph); -} - -QFixed QFontEngineS60::ascent() const -{ - // Workaround for QTBUG-8013 - // Stroke based fonts may return an incorrect FontMaxAscent of 0. - const QFixed ascent = m_originalFont->FontMaxAscent(); - return (ascent > 0) ? ascent : QFixed::fromReal(m_originalFontSizeInPixels) - descent(); -} - -QFixed QFontEngineS60::descent() const -{ - return m_originalFont->FontMaxDescent(); -} - -QFixed QFontEngineS60::leading() const -{ - return 0; -} - -qreal QFontEngineS60::maxCharWidth() const -{ - return m_originalFont->MaxCharWidthInPixels(); -} - -const char *QFontEngineS60::name() const -{ - return "QFontEngineS60"; -} - -bool QFontEngineS60::canRender(const QChar *string, int len) -{ - const unsigned char *cmap = m_extras->cmap(); - for (int i = 0; i < len; ++i) { - const unsigned int uc = getChar(string, i, len); - if (QFontEngine::getTrueTypeGlyphIndex(cmap, uc) == 0) - return false; - } - return true; -} - -QByteArray QFontEngineS60::getSfntTable(uint tag) const -{ - return m_extras->getSfntTable(tag); -} - -bool QFontEngineS60::getSfntTableData(uint tag, uchar *buffer, uint *length) const -{ - return m_extras->getSfntTableData(tag, buffer, length); -} - -QFontEngine::Type QFontEngineS60::type() const -{ - return QFontEngine::S60FontEngine; -} - -void QFontEngineS60::getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const -{ - // Setting the most significant bit tells GetCharacterData - // that 'code' is a Glyph ID, rather than a UTF-16 value - const TUint specialCode = (TUint)glyph | 0x80000000; - - const CFont::TCharacterDataAvailability availability = - m_activeFont->GetCharacterData(specialCode, metrics, bitmap, bitmapSize); - const glyph_t fallbackGlyph = '?'; - if (availability != CFont::EAllCharacterData) { - const CFont::TCharacterDataAvailability fallbackAvailability = - m_activeFont->GetCharacterData(fallbackGlyph, metrics, bitmap, bitmapSize); - Q_ASSERT(fallbackAvailability == CFont::EAllCharacterData); - } -} - -#ifdef Q_SYMBIAN_HAS_GLYPHOUTLINE_API -static inline void skipSpacesAndComma(const char* &str, const char* const strEnd) -{ - while (str <= strEnd && (*str == ' ' || *str == ',')) - ++str; -} - -static bool parseGlyphPathData(const char *svgPath, const char *svgPathEnd, QPainterPath &path, - qreal fontPixelSize, const QPointF &offset, bool hinted) -{ - Q_UNUSED(hinted) - QPointF p1, p2, firstSubPathPoint; - qreal *elementValues[] = - {&p1.rx(), &p1.ry(), &p2.rx(), &p2.ry()}; - const int unitsPerEm = 2048; // See: http://en.wikipedia.org/wiki/Em_%28typography%29 - const qreal resizeFactor = fontPixelSize / unitsPerEm; - - while (svgPath < svgPathEnd) { - skipSpacesAndComma(svgPath, svgPathEnd); - const char pathElem = *svgPath++; - skipSpacesAndComma(svgPath, svgPathEnd); - - if (pathElem != 'Z') { - char *endStr = 0; - int elementValuesCount = 0; - for (int i = 0; i < 4; ++i) { // 4 = size of elementValues[] - qreal coordinateValue = strtod(svgPath, &endStr); - if (svgPath == endStr) - break; - if (i % 2) // Flip vertically - coordinateValue = -coordinateValue; - *elementValues[i] = coordinateValue * resizeFactor; - elementValuesCount++; - svgPath = endStr; - skipSpacesAndComma(svgPath, svgPathEnd); - } - p1 += offset; - if (elementValuesCount == 2) - p2 = firstSubPathPoint; - else - p2 += offset; - } - - switch (pathElem) { - case 'M': - firstSubPathPoint = p1; - path.moveTo(p1); - break; - case 'Z': - path.closeSubpath(); - break; - case 'L': - path.lineTo(p1); - break; - case 'Q': - path.quadTo(p1, p2); - break; - default: - return false; - } - } - return true; -} -#endif // Q_SYMBIAN_HAS_GLYPHOUTLINE_API - -QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_s60_p.h b/src/gui/text/qfontengine_s60_p.h deleted file mode 100644 index c0eeef5264..0000000000 --- a/src/gui/text/qfontengine_s60_p.h +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QFONTENGINE_S60_P_H -#define QFONTENGINE_S60_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 "qconfig.h" -#include <private/qfontengine_p.h> -#include "qsize.h" -#include <openfont.h> - -// The glyph outline code is intentionally disabled. It will be reactivated as -// soon as the glyph outline API is backported from Symbian(^4) to Symbian(^3). -#if 0 -#define Q_SYMBIAN_HAS_GLYPHOUTLINE_API -#endif - -class CFont; - -QT_BEGIN_NAMESPACE - -// ..gives us access to truetype tables -class QSymbianTypeFaceExtras -{ -public: - QSymbianTypeFaceExtras(CFont* cFont, COpenFont *openFont = 0); - ~QSymbianTypeFaceExtras(); - - QByteArray getSfntTable(uint tag) const; - bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; - const uchar *cmap() const; - CFont *fontOwner() const; - bool isSymbolCMap() const; - QFixed unitsPerEm() const; - static bool symbianFontTableApiAvailable(); - -private: - CFont* m_cFont; - mutable bool m_symbolCMap; - mutable QByteArray m_cmapTable; - mutable QFixed m_unitsPerEm; - - // m_openFont and m_openFont are used if Symbian does not provide - // the Font Table API - COpenFont *m_openFont; - mutable MOpenFontTrueTypeExtension *m_trueTypeExtension; -}; - -class QFontEngineS60 : public QFontEngine -{ -public: - QFontEngineS60(const QFontDef &fontDef, const QSymbianTypeFaceExtras *extras); - ~QFontEngineS60(); - - QFixed emSquareSize() const; - bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; - void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; - - void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, - QPainterPath *path, QTextItem::RenderFlags flags); - - QImage alphaMapForGlyph(glyph_t glyph); - - glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); - glyph_metrics_t boundingBox_const(glyph_t glyph) const; // Const correctnes quirk. - glyph_metrics_t boundingBox(glyph_t glyph); - - QFixed ascent() const; - QFixed descent() const; - QFixed leading() const; - qreal maxCharWidth() const; - qreal minLeftBearing() const { return 0; } - qreal minRightBearing() const { return 0; } - - QByteArray getSfntTable(uint tag) const; - bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; - - static qreal pixelsToPoints(qreal pixels, Qt::Orientation orientation = Qt::Horizontal); - static qreal pointsToPixels(qreal points, Qt::Orientation orientation = Qt::Horizontal); - - const char *name() const; - - bool canRender(const QChar *string, int len); - - Type type() const; - - void getCharacterData(glyph_t glyph, TOpenFontCharMetrics& metrics, const TUint8*& bitmap, TSize& bitmapSize) const; - void setFontScale(qreal scale); - -private: - friend class QFontPrivate; - friend class QSymbianVGFontGlyphCache; - - QFixed glyphAdvance(HB_Glyph glyph) const; - CFont *fontWithSize(qreal size) const; - static void releaseFont(CFont *&font); - - const QSymbianTypeFaceExtras *m_extras; - CFont* m_originalFont; - const qreal m_originalFontSizeInPixels; - CFont* m_scaledFont; - qreal m_scaledFontSizeInPixels; - CFont* m_activeFont; -}; - -class QFontEngineMultiS60 : public QFontEngineMulti -{ -public: - QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies); - void loadEngine(int at); - - int m_script; - QStringList m_fallbackFamilies; -}; - -QT_END_NAMESPACE - -#endif // QFONTENGINE_S60_P_H diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp deleted file mode 100644 index 54d7ec2980..0000000000 --- a/src/gui/text/qfontengine_win.cpp +++ /dev/null @@ -1,1339 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#if _WIN32_WINNT < 0x0500 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0500 -#endif - -#include "qfontengine_p.h" -#include "qtextengine_p.h" -#include <qglobal.h> -#include "qt_windows.h" -#include <private/qapplication_p.h> - -#include <private/qsystemlibrary_p.h> -#include <qpaintdevice.h> -#include <qpainter.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 "qpaintengine.h" -#include "qvarlengtharray.h" -#include <private/qpaintengine_raster_p.h> -#include <private/qnativeimage_p.h> - -#if defined(Q_WS_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)) \ - ) - -// 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 - -#ifndef Q_WS_WINCE -typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); -static PtrGetCharWidthI ptrGetCharWidthI = 0; -static bool resolvedGetCharWidthI = false; - -static void resolveGetCharWidthI() -{ - if (resolvedGetCharWidthI) - return; - resolvedGetCharWidthI = true; - ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI"); -} -#endif // !defined(Q_WS_WINCE) - -// 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; -} - -// general font engine - -QFixed QFontEngineWin::lineThickness() const -{ - if(lineWidth > 0) - return lineWidth; - - return QFontEngine::lineThickness(); -} - -static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) -{ - int size; - size = GetOutlineTextMetrics(hdc, 0, 0); - OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)malloc(size); - GetOutlineTextMetrics(hdc, size, otm); - return otm; -} - -void QFontEngineWin::getCMap() -{ - ttf = (bool)(tm.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) { - OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); - designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); - unitsPerEm = otm->otmEMSquare; - x_height = (int)otm->otmsXHeight; - loadKerningPairs(designToDevice); - _faceId.filename = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFullName)).toLatin1(); - lineWidth = otm->otmsUnderscoreSize; - fsType = otm->otmfsType; - free(otm); - } else { - unitsPerEm = tm.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_WS_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 - wchar_t first = tm.tmFirstChar; - wchar_t last = tm.tmLastChar; - - for (; i < numChars; ++i, ++glyph_pos) { - uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); - if ( -#ifdef Q_WS_WINCE - tm.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_WS_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 - wchar_t first = tm.tmFirstChar; - wchar_t last = tm.tmLastChar; - - for (; i < numChars; ++i, ++glyph_pos) { - uint uc = getChar(str, i, numChars); - if ( -#ifdef Q_WS_WINCE - tm.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 = GetTextMetrics(hdc, &tm); - fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); - if (!res) { - qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); - ZeroMemory(&tm, sizeof(TEXTMETRIC)); - } - - cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; - getCMap(); - - widthCache = 0; - widthCacheSize = 0; - designAdvances = 0; - designAdvancesSize = 0; - -#ifndef Q_WS_WINCE - if (!resolvedGetCharWidthI) - resolveGetCharWidthI(); -#endif -} - -QFontEngineWin::~QFontEngineWin() -{ - if (designAdvances) - free(designAdvances); - - if (widthCache) - free(widthCache); - - // make sure we aren't by accident still selected - SelectObject(shared_dc(), (HFONT)GetStockObject(SYSTEM_FONT)); - - if (!stockFont) { - if (!DeleteObject(hfont)) - qErrnoWarning("QFontEngineWin: failed to delete non-stock font..."); - } -} - -HGDIOBJ QFontEngineWin::selectDesignFont() const -{ - LOGFONT f = logfont; - f.lfHeight = unitsPerEm; - HFONT designFont = CreateFontIndirect(&f); - return SelectObject(shared_dc(), designFont); -} - -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; - - recalcAdvances(glyphs, flags); - return true; -} - -inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) -{ -#if defined(Q_WS_WINCE) - GetCharWidth32(hdc, glyph, glyph, &width); -#else - if (ptrGetCharWidthI) - ptrGetCharWidthI(hdc, glyph, 1, 0, &width); -#endif -} - -void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const -{ - HGDIOBJ oldFont = 0; - HDC hdc = shared_dc(); - if (ttf && (flags & QTextEngine::DesignMetrics)) { - 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 = q_check_ptr((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(); - - int width = 0; - calculateTTFGlyphWidth(hdc, glyph, width); - designAdvances[glyph] = QFixed(width) / designToDevice; - } - glyphs->advances_x[i] = designAdvances[glyph]; - glyphs->advances_y[i] = 0; - } - if(oldFont) - DeleteObject(SelectObject(hdc, oldFont)); - } else { - 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 = q_check_ptr((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}; - GetTextExtentPoint32(hdc, (wchar_t *)ch, chrLen, &size); - width = size.cx; - } else { - calculateTTFGlyphWidth(hdc, glyph, width); - } - 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.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); -} - -#ifndef Q_WS_WINCE -bool QFontEngineWin::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const -{ - Q_ASSERT(metrics != 0); - - HDC hdc = shared_dc(); - - GLYPHMETRICS gm; - 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); - } - - uint format = GGO_METRICS; - if (ttf) - format |= GGO_GLYPH_INDEX; - res = GetGlyphOutline(hdc, glyph, format, &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) { - *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, - (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY); - return true; - } else { - return false; - } -} -#endif - -glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t) -{ -#ifndef Q_WS_WINCE - HDC hdc = shared_dc(); - SelectObject(hdc, hfont); - - glyph_metrics_t glyphMetrics; - bool success = getOutlineMetrics(glyph, t, &glyphMetrics); - - if (!ttf && !success) { - // Bitmap fonts - wchar_t ch = glyph; - ABCFLOAT abc; - GetCharABCWidthsFloat(hdc, ch, ch, &abc); - int width = qRound(abc.abcfB); - - return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); - } - - return glyphMetrics; -#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.tmMaxCharWidth; - advance = width; - } - - SelectObject(hdc, oldFont); - return glyph_metrics_t(0, -tm.tmAscent, width, tm.tmHeight, advance, 0).transformed(t); -#endif -} - -QFixed QFontEngineWin::ascent() const -{ - return tm.tmAscent; -} - -QFixed QFontEngineWin::descent() const -{ - // ### we substract 1 to even out the historical +1 in QFontMetrics's - // ### height=asc+desc+1 equation. Fix in Qt5. - return tm.tmDescent - 1; -} - -QFixed QFontEngineWin::leading() const -{ - return tm.tmExternalLeading; -} - - -QFixed QFontEngineWin::xHeight() const -{ - if(x_height >= 0) - return x_height; - return QFontEngine::xHeight(); -} - -QFixed QFontEngineWin::averageCharWidth() const -{ - return tm.tmAveCharWidth; -} - -qreal QFontEngineWin::maxCharWidth() const -{ - return tm.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); - -#ifndef Q_CC_MINGW -void QFontEngineWin::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) -{ - HDC hdc = shared_dc(); - SelectObject(hdc, hfont); - -#ifndef Q_WS_WINCE - if (ttf) -#endif - - { - ABC abcWidths; - GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); - if (leftBearing) - *leftBearing = abcWidths.abcA; - if (rightBearing) - *rightBearing = abcWidths.abcC; - } - -#ifndef Q_WS_WINCE - else { - QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); - } -#endif -} -#endif // Q_CC_MINGW - -qreal QFontEngineWin::minLeftBearing() const -{ - if (lbearing == SHRT_MIN) - minRightBearing(); // calculates both - - return lbearing; -} - -qreal QFontEngineWin::minRightBearing() const -{ -#ifdef Q_WS_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 = tm.tmLastChar - tm.tmFirstChar; - if (n <= max_font_count) { - abc = new ABC[n+1]; - GetCharABCWidths(hdc, tm.tmFirstChar, tm.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; - } - 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 = tm.tmLastChar - tm.tmFirstChar; - if (n <= max_font_count) { - abc = new ABC[n+1]; - GetCharABCWidths(hdc, tm.tmFirstChar, tm.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 { - ABCFLOAT *abc = 0; - int n = tm.tmLastChar - tm.tmFirstChar+1; - if (n <= max_font_count) { - abc = new ABCFLOAT[n]; - GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.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; - } - 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 { - while(len--) { - if (tm.tmFirstChar > string->unicode() || tm.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_WS_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_WS_WINCE) - bufferSize = GetGlyphOutline(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_WS_WINCE) - ret = GetGlyphOutline(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 = CreateFontIndirect(&lf); - 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_WS_WINCE) - if(tm.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.tmItalic && !(macStyle & 2)) - synthesized_flags = SynthesizedItalic; - if (fontDef.stretch != 100 && ttf) - synthesized_flags |= SynthesizedStretch; - if (tm.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 = CreateFontIndirect(&lf); - HDC hdc = shared_dc(); - HGDIOBJ oldfont = SelectObject(hdc, hf); - OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); - Properties p; - p.emSquare = unitsPerEm; - p.italicAngle = otm->otmItalicAngle; - p.postscriptName = QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpFamilyName)).toLatin1(); - p.postscriptName += QString::fromWCharArray((wchar_t *)((char *)otm + (quintptr)otm->otmpStyleName)).toLatin1(); - p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(p.postscriptName); - 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 = CreateFontIndirect(&lf); - 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 - -extern bool qt_cleartype_enabled; - -QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, - const QTransform &t, QImage::Format mask_format) -{ - Q_UNUSED(mask_format) - 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_WS_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; - - if (GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat) == 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 + 4, - ih + 2 * margin + 4, - QNativeImage::systemFormat(), !qt_cleartype_enabled); - - /*If cleartype is enabled we use the standard system format even on Windows CE - and not the special textbuffer format we have to use if cleartype is disabled*/ - - 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); - ExtTextOut(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0); - } else -#endif - { - ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0); - } - - SelectObject(hdc, old_font); - return ni; -} - - -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 = CreateFontIndirect(&lf); - } - QImage::Format mask_format = QNativeImage::systemFormat(); -#ifndef Q_OS_WINCE - mask_format = QImage::Format_RGB32; -#endif - - QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); - 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 don't 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->image.format() == QImage::Format_RGB16) { - const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y); - for (int x=0; x<mask->width(); ++x) - dest[x] = 255 - qGray(src[x]); - } 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 - if (QNativeImage::systemFormat() == QImage::Format_RGB16) - 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, QFixed, 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, QImage::Format_RGB32); - 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; -} - -// From qfontdatabase_win.cpp -extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); -QFontEngine *QFontEngineWin::cloneWithSize(qreal pixelSize) const -{ - QFontDef request = fontDef; - QString actualFontName = request.family; - if (!uniqueFamilyName.isEmpty()) - request.family = uniqueFamilyName; - request.pixelSize = pixelSize; - - QFontEngine *fontEngine = qt_load_font_engine_win(request); - if (fontEngine != NULL) - fontEngine->fontDef.family = actualFontName; - - return fontEngine; -} - -// -------------------------------------- Multi font engine - -QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks) - : QFontEngineMulti(fallbacks.size()+1), - fallbacks(fallbacks) -{ - engines[0] = first; - first->ref.ref(); - fontDef = engines[0]->fontDef; - cache_cost = first->cache_cost; -} - -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; - memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded - HFONT hfont = CreateFontIndirect(&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; - - // TODO: increase cost in QFontCache for the font engine loaded here -} - -QT_END_NAMESPACE diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h deleted file mode 100644 index 114149d61f..0000000000 --- a/src/gui/text/qfontengine_win_p.h +++ /dev/null @@ -1,164 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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() 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, QFixed subPixelPosition, int margin, const QTransform &xform); - - virtual QFontEngine *cloneWithSize(qreal pixelSize) const; - -#ifndef Q_CC_MINGW - virtual void getGlyphBearings(glyph_t glyph, qreal *leftBearing = 0, qreal *rightBearing = 0); -#endif - - int getGlyphIndexes(const QChar *ch, int numChars, QGlyphLayout *glyphs, bool mirrored) const; - void getCMap(); - -#ifndef Q_WS_WINCE - bool getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const; -#endif - - QString _name; - QString uniqueFamilyName; - HFONT hfont; - LOGFONT logfont; - uint stockFont : 1; - uint ttf : 1; - uint hasOutline : 1; - TEXTMETRIC 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, - QImage::Format mask_format); - -}; - -class QFontEngineMultiWin : public QFontEngineMulti -{ -public: - QFontEngineMultiWin(QFontEngine *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 deleted file mode 100644 index 4260b85b11..0000000000 --- a/src/gui/text/qfontengine_x11.cpp +++ /dev/null @@ -1,1215 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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).startsWith(QLatin1Char('/'))) - 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 performance - 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); - // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel - 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] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); - } 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) { - // XCharStruct::rbearing is defined as distance from left edge to rightmost pixel - // XCharStruct::width is defined as the advance - 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 { - face_id.index = 0; - face_id.filename = '-' + QFontEngine::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 = QApplication::testAttribute(Qt::AA_X11InitThreads) || (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; - } - } - - if (fd.hintingPreference != QFont::PreferDefaultHinting) { - switch (fd.hintingPreference) { - case QFont::PreferNoHinting: - default_hint_style = HintNone; - break; - case QFont::PreferVerticalHinting: - default_hint_style = HintLight; - break; - case QFont::PreferFullHinting: - default: - default_hint_style = HintFull; - break; - } - } -#ifdef FC_HINT_STYLE - else { - int hint_style = 0; - // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match - // the behavior of cairo - if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME) - hint_style = X11->fc_hint_style; - else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch - && X11->fc_hint_style > -1) - 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 -} - -QFontEngine *QFontEngineX11FT::cloneWithSize(qreal pixelSize) const -{ - QFontDef fontDef; - fontDef.pixelSize = pixelSize; - QFontEngineX11FT *fe = new QFontEngineX11FT(fontDef); - if (!fe->initFromFontEngine(this)) { - delete fe; - return 0; - } else { - fe->xglyph_format = xglyph_format; - return fe; - } -} - -#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 deleted file mode 100644 index d7eb39daaa..0000000000 --- a/src/gui/text/qfontengine_x11_p.h +++ /dev/null @@ -1,180 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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(const QFontDef &fontDef) : QFontEngineFT(fontDef) {} - explicit QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int screen); - ~QFontEngineX11FT(); - - QFontEngine *cloneWithSize(qreal pixelSize) const; - -#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/qrawfont_mac.cpp b/src/gui/text/qrawfont_mac.cpp deleted file mode 100644 index 1ed4185a5d..0000000000 --- a/src/gui/text/qrawfont_mac.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qglobal.h> - -#if !defined(QT_NO_RAWFONT) - -#include "qrawfont_p.h" -#include "qfontengine_coretext_p.h" - -QT_BEGIN_NAMESPACE - -void QRawFontPrivate::platformCleanUp() -{ -} - -extern int qt_defaultDpi(); - -void QRawFontPrivate::platformLoadFromData(const QByteArray &fontData, - int pixelSize, - QFont::HintingPreference hintingPreference) -{ - // Mac OS X ignores it - Q_UNUSED(hintingPreference); - - QCFType<CGDataProviderRef> dataProvider = CGDataProviderCreateWithData(NULL, - fontData.constData(), fontData.size(), NULL); - - CGFontRef cgFont = CGFontCreateWithDataProvider(dataProvider); - - if (cgFont == NULL) { - qWarning("QRawFont::platformLoadFromData: CGFontCreateWithDataProvider failed"); - } else { - QFontDef def; - def.pixelSize = pixelSize; - def.pointSize = pixelSize * 72.0 / qt_defaultDpi(); - fontEngine = new QCoreTextFontEngine(cgFont, def); - CFRelease(cgFont); - fontEngine->ref.ref(); - } -} - -QT_END_NAMESPACE - -#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qrawfont_win.cpp b/src/gui/text/qrawfont_win.cpp deleted file mode 100644 index d8acf57431..0000000000 --- a/src/gui/text/qrawfont_win.cpp +++ /dev/null @@ -1,707 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qrawfont_p.h" -#include <private/qsystemlibrary_p.h> - -#if !defined(QT_NO_DIRECTWRITE) -# include "qfontenginedirectwrite_p.h" -# include <dwrite.h> -#endif - -#if !defined(QT_NO_RAWFONT) - -QT_BEGIN_NAMESPACE - -namespace { - - template<typename T> - struct BigEndian - { - quint8 data[sizeof(T)]; - - operator T() const - { - T littleEndian = 0; - for (int i=0; i<sizeof(T); ++i) { - littleEndian |= data[i] << ((sizeof(T) - i - 1) * 8); - } - - return littleEndian; - } - - BigEndian<T> &operator=(const T &t) - { - for (int i=0; i<sizeof(T); ++i) { - data[i] = ((t >> (sizeof(T) - i - 1) * 8) & 0xff); - } - - return *this; - } - }; - -# pragma pack(1) - - // Common structure for all formats of the "name" table - struct NameTable - { - BigEndian<quint16> format; - BigEndian<quint16> count; - BigEndian<quint16> stringOffset; - }; - - struct NameRecord - { - BigEndian<quint16> platformID; - BigEndian<quint16> encodingID; - BigEndian<quint16> languageID; - BigEndian<quint16> nameID; - BigEndian<quint16> length; - BigEndian<quint16> offset; - }; - - struct OffsetSubTable - { - BigEndian<quint32> scalerType; - BigEndian<quint16> numTables; - BigEndian<quint16> searchRange; - BigEndian<quint16> entrySelector; - BigEndian<quint16> rangeShift; - }; - - struct TableDirectory - { - BigEndian<quint32> identifier; - BigEndian<quint32> checkSum; - BigEndian<quint32> offset; - BigEndian<quint32> length; - }; - - struct OS2Table - { - BigEndian<quint16> version; - BigEndian<qint16> avgCharWidth; - BigEndian<quint16> weightClass; - BigEndian<quint16> widthClass; - BigEndian<quint16> type; - BigEndian<qint16> subscriptXSize; - BigEndian<qint16> subscriptYSize; - BigEndian<qint16> subscriptXOffset; - BigEndian<qint16> subscriptYOffset; - BigEndian<qint16> superscriptXSize; - BigEndian<qint16> superscriptYSize; - BigEndian<qint16> superscriptXOffset; - BigEndian<qint16> superscriptYOffset; - BigEndian<qint16> strikeOutSize; - BigEndian<qint16> strikeOutPosition; - BigEndian<qint16> familyClass; - quint8 panose[10]; - BigEndian<quint32> unicodeRanges[4]; - quint8 vendorID[4]; - BigEndian<quint16> selection; - BigEndian<quint16> firstCharIndex; - BigEndian<quint16> lastCharIndex; - BigEndian<qint16> typoAscender; - BigEndian<qint16> typoDescender; - BigEndian<qint16> typoLineGap; - BigEndian<quint16> winAscent; - BigEndian<quint16> winDescent; - BigEndian<quint32> codepageRanges[2]; - BigEndian<qint16> height; - BigEndian<qint16> capHeight; - BigEndian<quint16> defaultChar; - BigEndian<quint16> breakChar; - BigEndian<quint16> maxContext; - }; - -# pragma pack() - - class EmbeddedFont - { - public: - EmbeddedFont(const QByteArray &fontData); - - QString changeFamilyName(const QString &newFamilyName); - QByteArray data() const { return m_fontData; } - TableDirectory *tableDirectoryEntry(const QByteArray &tagName); - QString familyName(TableDirectory *nameTableDirectory = 0); - - private: - QByteArray m_fontData; - }; - - EmbeddedFont::EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) - { - } - - TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName) - { - Q_ASSERT(tagName.size() == 4); - - const BigEndian<quint32> *tagIdPtr = - reinterpret_cast<const BigEndian<quint32> *>(tagName.constData()); - quint32 tagId = *tagIdPtr; - - OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data()); - TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1); - - TableDirectory *nameTableDirectoryEntry = 0; - for (int i=0; i<offsetSubTable->numTables; ++i, ++tableDirectory) { - if (tableDirectory->identifier == tagId) { - nameTableDirectoryEntry = tableDirectory; - break; - } - } - - return nameTableDirectoryEntry; - } - - QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) - { - QString name; - - if (nameTableDirectoryEntry == 0) - nameTableDirectoryEntry = tableDirectoryEntry("name"); - - if (nameTableDirectoryEntry != 0) { - NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() - + nameTableDirectoryEntry->offset); - NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); - for (int i=0; i<nameTable->count; ++i, ++nameRecord) { - if (nameRecord->nameID == 1 - && nameRecord->platformID == 3 // Windows - && nameRecord->languageID == 0x0409) { // US English - const void *ptr = reinterpret_cast<const quint8 *>(nameTable) - + nameTable->stringOffset - + nameRecord->offset; - - const BigEndian<quint16> *s = reinterpret_cast<const BigEndian<quint16> *>(ptr); - for (int j=0; j<nameRecord->length / sizeof(quint16); ++j) - name += QChar(s[j]); - - break; - } - } - } - - return name; - } - - QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) - { - TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); - if (nameTableDirectoryEntry == 0) - return QString(); - - QString oldFamilyName = familyName(nameTableDirectoryEntry); - - // Reserve size for name table header, five required name records and string - const int requiredRecordCount = 5; - quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 }; - - int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount; - int newFamilyNameSize = newFamilyName.size() * sizeof(quint16); - - const QString regularString = QString::fromLatin1("Regular"); - int regularStringSize = regularString.size() * sizeof(quint16); - - // Align table size of table to 32 bits (pad with 0) - int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4; - - QByteArray newNameTable(fullSize, char(0)); - - { - NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data()); - nameTable->count = requiredRecordCount; - nameTable->stringOffset = sizeOfHeader; - - NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1); - for (int i=0; i<requiredRecordCount; ++i, nameRecord++) { - nameRecord->nameID = nameIds[i]; - nameRecord->encodingID = 1; - nameRecord->languageID = 0x0409; - nameRecord->platformID = 3; - nameRecord->length = newFamilyNameSize; - - // Special case for sub-family - if (nameIds[i] == 4) { - nameRecord->offset = newFamilyNameSize; - nameRecord->length = regularStringSize; - } - } - - // nameRecord now points to string data - BigEndian<quint16> *stringStorage = reinterpret_cast<BigEndian<quint16> *>(nameRecord); - const quint16 *sourceString = newFamilyName.utf16(); - for (int i=0; i<newFamilyName.size(); ++i) - stringStorage[i] = sourceString[i]; - stringStorage += newFamilyName.size(); - - sourceString = regularString.utf16(); - for (int i=0; i<regularString.size(); ++i) - stringStorage[i] = sourceString[i]; - } - - quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data()); - quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize); - - quint32 checkSum = 0; - while (p < tableEnd) - checkSum += *(p++); - - nameTableDirectoryEntry->checkSum = checkSum; - nameTableDirectoryEntry->offset = m_fontData.size(); - nameTableDirectoryEntry->length = fullSize; - - m_fontData.append(newNameTable); - - return oldFamilyName; - } - -#if !defined(QT_NO_DIRECTWRITE) - - class DirectWriteFontFileStream: public IDWriteFontFileStream - { - public: - DirectWriteFontFileStream(const QByteArray &fontData) - : m_fontData(fontData) - , m_referenceCount(0) - { - } - - ~DirectWriteFontFileStream() - { - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset, - UINT64 fragmentSize, OUT void **fragmentContext); - void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext); - HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize); - HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime); - - private: - QByteArray m_fontData; - ULONG m_referenceCount; - }; - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object) - { - if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { - *object = this; - AddRef(); - return S_OK; - } else { - *object = NULL; - return E_NOINTERFACE; - } - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef() - { - return InterlockedIncrement(&m_referenceCount); - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release() - { - ULONG newCount = InterlockedDecrement(&m_referenceCount); - if (newCount == 0) - delete this; - return newCount; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment( - const void **fragmentStart, - UINT64 fileOffset, - UINT64 fragmentSize, - OUT void **fragmentContext) - { - *fragmentContext = NULL; - if (fragmentSize + fileOffset <= m_fontData.size()) { - *fragmentStart = m_fontData.data() + fileOffset; - return S_OK; - } else { - *fragmentStart = NULL; - return E_FAIL; - } - } - - void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *) - { - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize) - { - *fileSize = m_fontData.size(); - return S_OK; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) - { - *lastWriteTime = 0; - return E_NOTIMPL; - } - - class DirectWriteFontFileLoader: public IDWriteFontFileLoader - { - public: - DirectWriteFontFileLoader() : m_referenceCount(0) {} - - ~DirectWriteFontFileLoader() - { - } - - inline void addKey(const void *key, const QByteArray &fontData) - { - Q_ASSERT(!m_fontDatas.contains(key)); - m_fontDatas.insert(key, fontData); - } - - inline void removeKey(const void *key) - { - m_fontDatas.remove(key); - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object); - ULONG STDMETHODCALLTYPE AddRef(); - ULONG STDMETHODCALLTYPE Release(); - - HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey, - UINT32 fontFileReferenceKeySize, - OUT IDWriteFontFileStream **fontFileStream); - - private: - ULONG m_referenceCount; - QHash<const void *, QByteArray> m_fontDatas; - }; - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, - void **object) - { - if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { - *object = this; - AddRef(); - return S_OK; - } else { - *object = NULL; - return E_NOINTERFACE; - } - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef() - { - return InterlockedIncrement(&m_referenceCount); - } - - ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release() - { - ULONG newCount = InterlockedDecrement(&m_referenceCount); - if (newCount == 0) - delete this; - return newCount; - } - - HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey( - void const *fontFileReferenceKey, - UINT32 fontFileReferenceKeySize, - IDWriteFontFileStream **fontFileStream) - { - Q_UNUSED(fontFileReferenceKeySize); - - if (fontFileReferenceKeySize != sizeof(const void *)) { - qWarning("DirectWriteFontFileLoader::CreateStreamFromKey: Wrong key size"); - return E_FAIL; - } - - const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey); - *fontFileStream = NULL; - if (!m_fontDatas.contains(key)) - return E_FAIL; - - QByteArray fontData = m_fontDatas.value(key); - DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData); - stream->AddRef(); - *fontFileStream = stream; - - return S_OK; - } - - class CustomFontFileLoader - { - public: - CustomFontFileLoader() : m_directWriteFactory(0), m_directWriteFontFileLoader(0) - { - HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown **>(&m_directWriteFactory)); - if (FAILED(hres)) { - qErrnoWarning(hres, "CustomFontFileLoader::CustomFontFileLoader: " - "DWriteCreateFactory failed."); - } else { - m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); - m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); - } - } - - ~CustomFontFileLoader() - { - if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) - m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); - - if (m_directWriteFactory != 0) - m_directWriteFactory->Release(); - } - - void addKey(const void *key, const QByteArray &fontData) - { - if (m_directWriteFontFileLoader != 0) - m_directWriteFontFileLoader->addKey(key, fontData); - } - - void removeKey(const void *key) - { - if (m_directWriteFontFileLoader != 0) - m_directWriteFontFileLoader->removeKey(key); - } - - IDWriteFontFileLoader *loader() const - { - return m_directWriteFontFileLoader; - } - - private: - IDWriteFactory *m_directWriteFactory; - DirectWriteFontFileLoader *m_directWriteFontFileLoader; - }; - -#endif - -} // Anonymous namespace - - -// From qfontdatabase_win.cpp -extern QFontEngine *qt_load_font_engine_win(const QFontDef &request); -// From qfontdatabase.cpp -extern QFont::Weight weightFromInteger(int weight); - -void QRawFontPrivate::platformCleanUp() -{ - if (fontHandle != NULL) { - if (ptrRemoveFontMemResourceEx == NULL) { - void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); - ptrRemoveFontMemResourceEx = - reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func); - } - - if (ptrRemoveFontMemResourceEx == NULL) { - qWarning("QRawFont::platformCleanUp: Can't find RemoveFontMemResourceEx in gdi32"); - fontHandle = NULL; - } else { - ptrRemoveFontMemResourceEx(fontHandle); - fontHandle = NULL; - } - } -} - -void QRawFontPrivate::platformLoadFromData(const QByteArray &_fontData, - int pixelSize, - QFont::HintingPreference hintingPreference) -{ - QByteArray fontData(_fontData); - EmbeddedFont font(fontData); - -#if !defined(QT_NO_DIRECTWRITE) - if (hintingPreference == QFont::PreferDefaultHinting - || hintingPreference == QFont::PreferFullHinting) -#endif - { - GUID guid; - CoCreateGuid(&guid); - - QString uniqueFamilyName = QString::fromLatin1("f") - + QString::number(guid.Data1, 36) + QLatin1Char('-') - + QString::number(guid.Data2, 36) + QLatin1Char('-') - + QString::number(guid.Data3, 36) + QLatin1Char('-') - + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); - - QString actualFontName = font.changeFamilyName(uniqueFamilyName); - if (actualFontName.isEmpty()) { - qWarning("QRawFont::platformLoadFromData: Can't change family name of font"); - return; - } - - if (ptrAddFontMemResourceEx == NULL || ptrRemoveFontMemResourceEx == NULL) { - void *func = QSystemLibrary::resolve(QLatin1String("gdi32"), "RemoveFontMemResourceEx"); - ptrRemoveFontMemResourceEx = - reinterpret_cast<QRawFontPrivate::PtrRemoveFontMemResourceEx>(func); - - func = QSystemLibrary::resolve(QLatin1String("gdi32"), "AddFontMemResourceEx"); - ptrAddFontMemResourceEx = - reinterpret_cast<QRawFontPrivate::PtrAddFontMemResourceEx>(func); - } - - Q_ASSERT(fontHandle == NULL); - if (ptrAddFontMemResourceEx != NULL && ptrRemoveFontMemResourceEx != NULL) { - DWORD count = 0; - fontData = font.data(); - fontHandle = ptrAddFontMemResourceEx(fontData.data(), fontData.size(), 0, &count); - - if (count == 0 && fontHandle != NULL) { - ptrRemoveFontMemResourceEx(fontHandle); - fontHandle = NULL; - } - } - - if (fontHandle == NULL) { - qWarning("QRawFont::platformLoadFromData: AddFontMemResourceEx failed"); - } else { - QFontDef request; - request.family = uniqueFamilyName; - request.pixelSize = pixelSize; - request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; - request.hintingPreference = hintingPreference; - - fontEngine = qt_load_font_engine_win(request); - if (request.family != fontEngine->fontDef.family) { - qWarning("QRawFont::platformLoadFromData: Failed to load font. " - "Got fallback instead: %s", qPrintable(fontEngine->fontDef.family)); - if (fontEngine->cache_count == 0 && fontEngine->ref == 0) - delete fontEngine; - fontEngine = 0; - } else { - Q_ASSERT(fontEngine->cache_count == 0 && fontEngine->ref == 0); - - // Override the generated font name - static_cast<QFontEngineWin *>(fontEngine)->uniqueFamilyName = uniqueFamilyName; - fontEngine->fontDef.family = actualFontName; - fontEngine->ref.ref(); - } - } - } -#if !defined(QT_NO_DIRECTWRITE) - else { - CustomFontFileLoader fontFileLoader; - fontFileLoader.addKey(this, fontData); - - IDWriteFactory *factory = NULL; - HRESULT hres = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown **>(&factory)); - if (FAILED(hres)) { - qErrnoWarning(hres, "QRawFont::platformLoadFromData: DWriteCreateFactory failed"); - return; - } - - IDWriteFontFile *fontFile = NULL; - void *key = this; - - hres = factory->CreateCustomFontFileReference(&key, sizeof(void *), - fontFileLoader.loader(), &fontFile); - if (FAILED(hres)) { - qErrnoWarning(hres, "QRawFont::platformLoadFromData: " - "CreateCustomFontFileReference failed"); - factory->Release(); - return; - } - - BOOL isSupportedFontType; - DWRITE_FONT_FILE_TYPE fontFileType; - DWRITE_FONT_FACE_TYPE fontFaceType; - UINT32 numberOfFaces; - fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces); - if (!isSupportedFontType) { - fontFile->Release(); - factory->Release(); - return; - } - - IDWriteFontFace *directWriteFontFace = NULL; - hres = factory->CreateFontFace(fontFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, - &directWriteFontFace); - if (FAILED(hres)) { - qErrnoWarning(hres, "QRawFont::platformLoadFromData: CreateFontFace failed"); - fontFile->Release(); - factory->Release(); - return; - } - - fontFile->Release(); - - fontEngine = new QFontEngineDirectWrite(factory, directWriteFontFace, pixelSize); - - // Get font family from font data - fontEngine->fontDef.family = font.familyName(); - fontEngine->ref.ref(); - - directWriteFontFace->Release(); - factory->Release(); - } -#endif - - // Get style and weight info - if (fontEngine != 0) { - TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); - if (os2TableEntry != 0) { - const OS2Table *os2Table = - reinterpret_cast<const OS2Table *>(fontData.constData() - + os2TableEntry->offset); - - bool italic = os2Table->selection & 1; - bool oblique = os2Table->selection & 128; - - if (italic) - fontEngine->fontDef.style = QFont::StyleItalic; - else if (oblique) - fontEngine->fontDef.style = QFont::StyleOblique; - else - fontEngine->fontDef.style = QFont::StyleNormal; - - fontEngine->fontDef.weight = weightFromInteger(os2Table->weightClass); - } - } -} - -QT_END_NAMESPACE - -#endif // QT_NO_RAWFONT diff --git a/src/gui/text/qtextengine_mac.cpp b/src/gui/text/qtextengine_mac.cpp deleted file mode 100644 index 2c6e579b45..0000000000 --- a/src/gui/text/qtextengine_mac.cpp +++ /dev/null @@ -1,656 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtextengine_p.h" - -#include <private/qfontengine_coretext_p.h> -#include <private/qfontengine_mac_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_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, &si.leading); - 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); - } - - ensureSpace(num_glyphs); - num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used; - - QGlyphLayout g = availableGlyphs(&si); - g.numGlyphs = num_glyphs; - unsigned short *log_clusters = logClusters(&si); - - bool stringToCMapFailed = false; - if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) { - ensureSpace(num_glyphs); - g = availableGlyphs(&si); - stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, - attributes(), &si); - } - - if (!stringToCMapFailed) { - heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs); - - 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 -- cgit v1.2.3