From 520f091dbc85d9bf12f021c8030428fd2262a031 Mon Sep 17 00:00:00 2001 From: feiyangqingyun Date: Sun, 24 May 2020 16:43:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BD=91=E7=BB=9C=E4=B8=AD?= =?UTF-8?q?=E8=BD=AC=E6=9C=8D=E5=8A=A1=E5=99=A8+designer=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {snap => 0snap}/battery.gif | Bin {snap => 0snap}/bgdemo.gif | Bin {snap => 0snap}/buttondefence.gif | Bin {snap => 0snap}/colorwidget.gif | Bin {snap => 0snap}/comtool.jpg | Bin {snap => 0snap}/countcode.gif | Bin {snap => 0snap}/dbpage.png | Bin 0snap/designer.png | Bin 0 -> 238230 bytes {snap => 0snap}/devicesizetable.gif | Bin {snap => 0snap}/echartgauge.gif | Bin {snap => 0snap}/emailtool.jpg | Bin {snap => 0snap}/ffmpegdemo.png | Bin {snap => 0snap}/flatui.gif | Bin {snap => 0snap}/framelesswidget.gif | Bin {snap => 0snap}/gifwidget.gif | Bin {snap => 0snap}/imageswitch.gif | Bin {snap => 0snap}/ipaddress.gif | Bin {snap => 0snap}/lightbutton.gif | Bin {snap => 0snap}/lineeditnext.gif | Bin {snap => 0snap}/lunarcalendarwidget.gif | Bin {snap => 0snap}/maskwidget.gif | Bin {snap => 0snap}/mouseline.gif | Bin {snap => 0snap}/movewidget.gif | Bin {snap => 0snap}/navbutton.gif | Bin 0snap/netserver.jpg | Bin 0 -> 131177 bytes {snap => 0snap}/nettool.gif | Bin {snap => 0snap}/ntpclient.gif | Bin {snap => 0snap}/pngtool.gif | Bin 0snap/qwt.png | Bin 0 -> 41567 bytes {snap => 0snap}/qwtdemo.jpg | Bin {snap => 0snap}/screenwidget.gif | Bin {snap => 0snap}/styledemo_flatwhite.png | Bin {snap => 0snap}/styledemo_lightblue.png | Bin {snap => 0snap}/styledemo_psblack.png | Bin {snap => 0snap}/videopanel.gif | Bin {snap => 0snap}/videowidget.gif | Bin {snap => 0snap}/vlcdemo.png | Bin {snap => 0snap}/zhtopy.gif | Bin README.md | 74 +- designer/designer/appfontdialog.cpp | 429 + designer/designer/appfontdialog.h | 101 + designer/designer/assistantclient.cpp | 175 + designer/designer/assistantclient.h | 83 + designer/designer/designer.pro | 69 + designer/designer/designer.qrc | 12 + designer/designer/designer_enums.h | 52 + designer/designer/fontpanel.cpp | 308 + designer/designer/fontpanel.h | 108 + designer/designer/images/back.png | Bin 0 -> 678 bytes designer/designer/images/designer.png | Bin 0 -> 4205 bytes designer/designer/images/designer_zh_CN.qm | Bin 0 -> 112340 bytes designer/designer/images/down.png | Bin 0 -> 594 bytes designer/designer/images/forward.png | Bin 0 -> 655 bytes designer/designer/images/minus.png | Bin 0 -> 250 bytes designer/designer/images/plus.png | Bin 0 -> 462 bytes designer/designer/images/up.png | Bin 0 -> 692 bytes designer/designer/main.cpp | 73 + designer/designer/main.ico | Bin 0 -> 67646 bytes designer/designer/main.rc | 41 + designer/designer/mainwindow.cpp | 419 + designer/designer/mainwindow.h | 187 + designer/designer/newform.cpp | 229 + designer/designer/newform.h | 104 + designer/designer/preferencesdialog.cpp | 118 + designer/designer/preferencesdialog.h | 82 + designer/designer/preferencesdialog.ui | 91 + designer/designer/qdesigner.cpp | 323 + designer/designer/qdesigner.h | 102 + designer/designer/qdesigner_actions.cpp | 1437 ++ designer/designer/qdesigner_actions.h | 231 + .../designer/qdesigner_appearanceoptions.cpp | 167 + .../designer/qdesigner_appearanceoptions.h | 136 + .../designer/qdesigner_appearanceoptions.ui | 57 + designer/designer/qdesigner_formwindow.cpp | 289 + designer/designer/qdesigner_formwindow.h | 97 + designer/designer/qdesigner_pch.h | 59 + designer/designer/qdesigner_server.cpp | 156 + designer/designer/qdesigner_server.h | 89 + designer/designer/qdesigner_settings.cpp | 250 + designer/designer/qdesigner_settings.h | 94 + designer/designer/qdesigner_toolwindow.cpp | 438 + designer/designer/qdesigner_toolwindow.h | 123 + designer/designer/qdesigner_workbench.cpp | 1098 ++ designer/designer/qdesigner_workbench.h | 215 + designer/designer/qttoolbardialog.cpp | 1871 +++ designer/designer/qttoolbardialog.h | 138 + designer/designer/qttoolbardialog.ui | 207 + designer/designer/saveformastemplate.cpp | 173 + designer/designer/saveformastemplate.h | 77 + designer/designer/saveformastemplate.ui | 166 + designer/designer/versiondialog.cpp | 191 + designer/designer/versiondialog.h | 58 + .../extension/default_extensionfactory.cpp | 178 + .../lib/extension/default_extensionfactory.h | 86 + designer/lib/extension/extension.cpp | 186 + designer/lib/extension/extension.h | 109 + designer/lib/extension/extension.pri | 12 + designer/lib/extension/extension_global.h | 64 + designer/lib/extension/qextensionmanager.cpp | 174 + designer/lib/extension/qextensionmanager.h | 79 + designer/lib/lib.pro | 78 + designer/lib/lib_pch.h | 65 + designer/lib/sdk/abstractactioneditor.cpp | 123 + designer/lib/sdk/abstractactioneditor.h | 76 + designer/lib/sdk/abstractbrushmanager.h | 83 + designer/lib/sdk/abstractdialoggui.cpp | 161 + designer/lib/sdk/abstractdialoggui_p.h | 107 + designer/lib/sdk/abstractdnditem.h | 75 + designer/lib/sdk/abstractdnditem.qdoc | 98 + designer/lib/sdk/abstractformeditor.cpp | 630 + designer/lib/sdk/abstractformeditor.h | 159 + designer/lib/sdk/abstractformeditorplugin.cpp | 86 + designer/lib/sdk/abstractformeditorplugin.h | 73 + designer/lib/sdk/abstractformwindow.cpp | 814 ++ designer/lib/sdk/abstractformwindow.h | 183 + designer/lib/sdk/abstractformwindowcursor.cpp | 252 + designer/lib/sdk/abstractformwindowcursor.h | 109 + .../lib/sdk/abstractformwindowmanager.cpp | 502 + designer/lib/sdk/abstractformwindowmanager.h | 122 + designer/lib/sdk/abstractformwindowtool.cpp | 106 + designer/lib/sdk/abstractformwindowtool.h | 85 + designer/lib/sdk/abstracticoncache.h | 83 + designer/lib/sdk/abstracticoncache.qdoc | 116 + designer/lib/sdk/abstractintegration.cpp | 54 + designer/lib/sdk/abstractintegration.h | 76 + designer/lib/sdk/abstractintrospection.cpp | 548 + designer/lib/sdk/abstractintrospection_p.h | 174 + designer/lib/sdk/abstractlanguage.h | 100 + designer/lib/sdk/abstractmetadatabase.cpp | 170 + designer/lib/sdk/abstractmetadatabase.h | 99 + designer/lib/sdk/abstractnewformwidget.cpp | 117 + designer/lib/sdk/abstractnewformwidget_p.h | 88 + designer/lib/sdk/abstractobjectinspector.cpp | 110 + designer/lib/sdk/abstractobjectinspector.h | 73 + designer/lib/sdk/abstractoptionspage_p.h | 79 + .../lib/sdk/abstractpromotioninterface.cpp | 113 + designer/lib/sdk/abstractpromotioninterface.h | 91 + designer/lib/sdk/abstractpropertyeditor.cpp | 193 + designer/lib/sdk/abstractpropertyeditor.h | 84 + designer/lib/sdk/abstractresourcebrowser.cpp | 57 + designer/lib/sdk/abstractresourcebrowser.h | 75 + designer/lib/sdk/abstractsettings_p.h | 87 + designer/lib/sdk/abstractwidgetbox.cpp | 340 + designer/lib/sdk/abstractwidgetbox.h | 142 + designer/lib/sdk/abstractwidgetdatabase.cpp | 360 + designer/lib/sdk/abstractwidgetdatabase.h | 137 + designer/lib/sdk/abstractwidgetfactory.cpp | 112 + designer/lib/sdk/abstractwidgetfactory.h | 79 + designer/lib/sdk/dynamicpropertysheet.h | 81 + designer/lib/sdk/dynamicpropertysheet.qdoc | 80 + designer/lib/sdk/extrainfo.cpp | 116 + designer/lib/sdk/extrainfo.h | 84 + designer/lib/sdk/layoutdecoration.h | 99 + designer/lib/sdk/layoutdecoration.qdoc | 149 + designer/lib/sdk/membersheet.h | 89 + designer/lib/sdk/membersheet.qdoc | 249 + designer/lib/sdk/propertysheet.h | 90 + designer/lib/sdk/propertysheet.qdoc | 288 + designer/lib/sdk/script.cpp | 109 + designer/lib/sdk/script_p.h | 83 + designer/lib/sdk/sdk.pri | 58 + designer/lib/sdk/sdk_global.h | 64 + designer/lib/sdk/taskmenu.h | 72 + designer/lib/sdk/taskmenu.qdoc | 138 + designer/lib/shared/actioneditor.cpp | 824 ++ designer/lib/shared/actioneditor_p.h | 168 + designer/lib/shared/actionprovider_p.h | 108 + designer/lib/shared/actionrepository.cpp | 663 + designer/lib/shared/actionrepository_p.h | 269 + designer/lib/shared/addlinkdialog.ui | 112 + designer/lib/shared/codedialog.cpp | 262 + designer/lib/shared/codedialog_p.h | 100 + designer/lib/shared/connectionedit.cpp | 1612 +++ designer/lib/shared/connectionedit_p.h | 324 + designer/lib/shared/csshighlighter.cpp | 188 + designer/lib/shared/csshighlighter_p.h | 82 + designer/lib/shared/defaultgradients.xml | 498 + designer/lib/shared/deviceprofile.cpp | 467 + designer/lib/shared/deviceprofile_p.h | 152 + designer/lib/shared/dialoggui.cpp | 265 + designer/lib/shared/dialoggui_p.h | 107 + designer/lib/shared/extensionfactory_p.h | 120 + designer/lib/shared/filterwidget.cpp | 252 + designer/lib/shared/filterwidget_p.h | 151 + designer/lib/shared/formlayoutmenu.cpp | 534 + designer/lib/shared/formlayoutmenu_p.h | 100 + designer/lib/shared/formlayoutrowdialog.ui | 166 + designer/lib/shared/formwindowbase.cpp | 502 + designer/lib/shared/formwindowbase_p.h | 205 + designer/lib/shared/grid.cpp | 186 + designer/lib/shared/grid_p.h | 118 + designer/lib/shared/gridpanel.cpp | 121 + designer/lib/shared/gridpanel.ui | 144 + designer/lib/shared/gridpanel_p.h | 101 + designer/lib/shared/htmlhighlighter.cpp | 198 + designer/lib/shared/htmlhighlighter_p.h | 101 + designer/lib/shared/iconloader.cpp | 79 + designer/lib/shared/iconloader_p.h | 72 + designer/lib/shared/iconselector.cpp | 543 + designer/lib/shared/iconselector_p.h | 142 + designer/lib/shared/invisible_widget.cpp | 57 + designer/lib/shared/invisible_widget_p.h | 75 + designer/lib/shared/layout.cpp | 1326 ++ designer/lib/shared/layout_p.h | 152 + designer/lib/shared/layoutinfo.cpp | 312 + designer/lib/shared/layoutinfo_p.h | 114 + designer/lib/shared/metadatabase.cpp | 295 + designer/lib/shared/metadatabase_p.h | 142 + designer/lib/shared/morphmenu.cpp | 635 + designer/lib/shared/morphmenu_p.h | 97 + designer/lib/shared/newactiondialog.cpp | 197 + designer/lib/shared/newactiondialog.ui | 277 + designer/lib/shared/newactiondialog_p.h | 124 + designer/lib/shared/newformwidget.cpp | 586 + designer/lib/shared/newformwidget.ui | 192 + designer/lib/shared/newformwidget_p.h | 143 + designer/lib/shared/orderdialog.cpp | 188 + designer/lib/shared/orderdialog.ui | 198 + designer/lib/shared/orderdialog_p.h | 114 + designer/lib/shared/plaintexteditor.cpp | 119 + designer/lib/shared/plaintexteditor_p.h | 89 + designer/lib/shared/plugindialog.cpp | 207 + designer/lib/shared/plugindialog.ui | 136 + designer/lib/shared/plugindialog_p.h | 92 + designer/lib/shared/pluginmanager.cpp | 786 ++ designer/lib/shared/pluginmanager_p.h | 159 + .../lib/shared/previewconfigurationwidget.cpp | 366 + .../lib/shared/previewconfigurationwidget.ui | 91 + .../lib/shared/previewconfigurationwidget_p.h | 96 + designer/lib/shared/previewmanager.cpp | 943 ++ designer/lib/shared/previewmanager_p.h | 184 + designer/lib/shared/promotionmodel.cpp | 220 + designer/lib/shared/promotionmodel_p.h | 98 + designer/lib/shared/promotiontaskmenu.cpp | 361 + designer/lib/shared/promotiontaskmenu_p.h | 151 + designer/lib/shared/propertylineedit.cpp | 96 + designer/lib/shared/propertylineedit_p.h | 85 + designer/lib/shared/qdesigner_command.cpp | 2968 ++++ designer/lib/shared/qdesigner_command2.cpp | 159 + designer/lib/shared/qdesigner_command2_p.h | 101 + designer/lib/shared/qdesigner_command_p.h | 1136 ++ designer/lib/shared/qdesigner_dnditem.cpp | 300 + designer/lib/shared/qdesigner_dnditem_p.h | 147 + designer/lib/shared/qdesigner_dockwidget.cpp | 140 + designer/lib/shared/qdesigner_dockwidget_p.h | 87 + designer/lib/shared/qdesigner_formbuilder.cpp | 498 + designer/lib/shared/qdesigner_formbuilder_p.h | 181 + .../shared/qdesigner_formeditorcommand.cpp | 64 + .../shared/qdesigner_formeditorcommand_p.h | 83 + .../shared/qdesigner_formwindowcommand.cpp | 151 + .../shared/qdesigner_formwindowcommand_p.h | 98 + .../shared/qdesigner_formwindowmanager.cpp | 167 + .../shared/qdesigner_formwindowmanager_p.h | 99 + designer/lib/shared/qdesigner_integration.cpp | 496 + designer/lib/shared/qdesigner_integration_p.h | 152 + .../lib/shared/qdesigner_introspection.cpp | 372 + .../lib/shared/qdesigner_introspection_p.h | 84 + designer/lib/shared/qdesigner_membersheet.cpp | 371 + designer/lib/shared/qdesigner_membersheet_p.h | 120 + designer/lib/shared/qdesigner_menu.cpp | 1390 ++ designer/lib/shared/qdesigner_menu_p.h | 208 + designer/lib/shared/qdesigner_menubar.cpp | 979 ++ designer/lib/shared/qdesigner_menubar_p.h | 179 + .../lib/shared/qdesigner_objectinspector.cpp | 80 + .../lib/shared/qdesigner_objectinspector_p.h | 103 + designer/lib/shared/qdesigner_promotion.cpp | 373 + designer/lib/shared/qdesigner_promotion_p.h | 98 + .../lib/shared/qdesigner_promotiondialog.cpp | 448 + .../lib/shared/qdesigner_promotiondialog_p.h | 161 + .../lib/shared/qdesigner_propertycommand.cpp | 1503 +++ .../lib/shared/qdesigner_propertycommand_p.h | 313 + .../lib/shared/qdesigner_propertyeditor.cpp | 169 + .../lib/shared/qdesigner_propertyeditor_p.h | 112 + .../lib/shared/qdesigner_propertysheet.cpp | 1648 +++ .../lib/shared/qdesigner_propertysheet_p.h | 266 + designer/lib/shared/qdesigner_qsettings.cpp | 94 + designer/lib/shared/qdesigner_qsettings_p.h | 88 + designer/lib/shared/qdesigner_stackedbox.cpp | 399 + designer/lib/shared/qdesigner_stackedbox_p.h | 164 + designer/lib/shared/qdesigner_tabwidget.cpp | 572 + designer/lib/shared/qdesigner_tabwidget_p.h | 153 + designer/lib/shared/qdesigner_taskmenu.cpp | 777 ++ designer/lib/shared/qdesigner_taskmenu_p.h | 132 + designer/lib/shared/qdesigner_toolbar.cpp | 488 + designer/lib/shared/qdesigner_toolbar_p.h | 135 + designer/lib/shared/qdesigner_toolbox.cpp | 437 + designer/lib/shared/qdesigner_toolbox_p.h | 140 + designer/lib/shared/qdesigner_utils.cpp | 734 + designer/lib/shared/qdesigner_utils_p.h | 481 + designer/lib/shared/qdesigner_widget.cpp | 108 + designer/lib/shared/qdesigner_widget_p.h | 122 + designer/lib/shared/qdesigner_widgetbox.cpp | 181 + designer/lib/shared/qdesigner_widgetbox_p.h | 101 + designer/lib/shared/qdesigner_widgetitem.cpp | 345 + designer/lib/shared/qdesigner_widgetitem_p.h | 147 + designer/lib/shared/qlayout_widget.cpp | 2100 +++ designer/lib/shared/qlayout_widget_p.h | 292 + designer/lib/shared/qscripthighlighter.cpp | 468 + designer/lib/shared/qscripthighlighter_p.h | 84 + designer/lib/shared/qsimpleresource.cpp | 418 + designer/lib/shared/qsimpleresource_p.h | 164 + .../lib/shared/qtresourceeditordialog.cpp | 2223 +++ designer/lib/shared/qtresourceeditordialog.ui | 177 + .../lib/shared/qtresourceeditordialog_p.h | 130 + designer/lib/shared/qtresourcemodel.cpp | 646 + designer/lib/shared/qtresourcemodel_p.h | 145 + designer/lib/shared/qtresourceview.cpp | 906 ++ designer/lib/shared/qtresourceview_p.h | 141 + designer/lib/shared/richtexteditor.cpp | 758 ++ designer/lib/shared/richtexteditor_p.h | 102 + designer/lib/shared/scriptcommand.cpp | 103 + designer/lib/shared/scriptcommand_p.h | 93 + designer/lib/shared/scriptdialog.cpp | 130 + designer/lib/shared/scriptdialog_p.h | 90 + designer/lib/shared/scripterrordialog.cpp | 108 + designer/lib/shared/scripterrordialog_p.h | 83 + designer/lib/shared/selectsignaldialog.ui | 93 + designer/lib/shared/shared.pri | 189 + designer/lib/shared/shared.qrc | 20 + designer/lib/shared/shared_enums_p.h | 99 + designer/lib/shared/shared_global_p.h | 76 + designer/lib/shared/shared_settings.cpp | 321 + designer/lib/shared/shared_settings_p.h | 142 + designer/lib/shared/sheet_delegate.cpp | 112 + designer/lib/shared/sheet_delegate_p.h | 85 + designer/lib/shared/signalslotdialog.cpp | 526 + designer/lib/shared/signalslotdialog.ui | 129 + designer/lib/shared/signalslotdialog_p.h | 173 + designer/lib/shared/spacer_widget.cpp | 280 + designer/lib/shared/spacer_widget_p.h | 117 + designer/lib/shared/stylesheeteditor.cpp | 408 + designer/lib/shared/stylesheeteditor_p.h | 144 + .../240x320/Dialog_with_Buttons_Bottom.ui | 67 + .../240x320/Dialog_with_Buttons_Right.ui | 67 + .../320x240/Dialog_with_Buttons_Bottom.ui | 67 + .../320x240/Dialog_with_Buttons_Right.ui | 67 + .../480x640/Dialog_with_Buttons_Bottom.ui | 67 + .../480x640/Dialog_with_Buttons_Right.ui | 67 + .../640x480/Dialog_with_Buttons_Bottom.ui | 67 + .../640x480/Dialog_with_Buttons_Right.ui | 67 + .../forms/Dialog_with_Buttons_Bottom.ui | 71 + .../forms/Dialog_with_Buttons_Right.ui | 71 + .../templates/forms/Dialog_without_Buttons.ui | 18 + .../lib/shared/templates/forms/Main_Window.ui | 24 + designer/lib/shared/templates/forms/Widget.ui | 21 + designer/lib/shared/textpropertyeditor.cpp | 430 + designer/lib/shared/textpropertyeditor_p.h | 156 + designer/lib/shared/widgetdatabase.cpp | 865 ++ designer/lib/shared/widgetdatabase_p.h | 210 + designer/lib/shared/widgetfactory.cpp | 897 ++ designer/lib/shared/widgetfactory_p.h | 191 + designer/lib/shared/zoomwidget.cpp | 570 + designer/lib/shared/zoomwidget_p.h | 231 + designer/lib/uilib/abstractformbuilder.cpp | 3069 +++++ designer/lib/uilib/abstractformbuilder.h | 290 + designer/lib/uilib/container.h | 75 + designer/lib/uilib/container.qdoc | 172 + designer/lib/uilib/customwidget.h | 101 + designer/lib/uilib/customwidget.qdoc | 295 + designer/lib/uilib/formbuilder.cpp | 570 + designer/lib/uilib/formbuilder.h | 115 + designer/lib/uilib/formbuilderextra.cpp | 555 + designer/lib/uilib/formbuilderextra_p.h | 262 + designer/lib/uilib/formscriptrunner.cpp | 208 + designer/lib/uilib/formscriptrunner_p.h | 120 + designer/lib/uilib/properties.cpp | 681 + designer/lib/uilib/properties_p.h | 176 + designer/lib/uilib/qdesignerexportwidget.h | 66 + designer/lib/uilib/resourcebuilder.cpp | 169 + designer/lib/uilib/resourcebuilder_p.h | 104 + designer/lib/uilib/textbuilder.cpp | 84 + designer/lib/uilib/textbuilder_p.h | 93 + designer/lib/uilib/ui4.cpp | 11132 ++++++++++++++++ designer/lib/uilib/ui4_p.h | 3791 ++++++ designer/lib/uilib/uilib.pri | 32 + designer/lib/uilib/uilib_global.h | 64 + designer/lib/uilib/widgets.table | 148 + designer/readme.txt | 3 + netserver/api/api.pri | 11 + netserver/api/app.cpp | 94 + netserver/api/app.h | 29 + netserver/api/quiwidget.cpp | 2947 ++++ netserver/api/quiwidget.h | 646 + netserver/api/tcpserver1.cpp | 151 + netserver/api/tcpserver1.h | 74 + netserver/api/tcpserver2.cpp | 151 + netserver/api/tcpserver2.h | 74 + netserver/form/form.pri | 8 + netserver/form/frmmain.cpp | 269 + netserver/form/frmmain.h | 53 + netserver/form/frmmain.ui | 155 + netserver/head.h | 9 + netserver/main.cpp | 28 + netserver/netserver.pro | 30 + netserver/other/main.ico | Bin 0 -> 67646 bytes netserver/other/main.qrc | 7 + netserver/other/main.rc | 1 + netserver/other/qt_zh_CN.qm | Bin 0 -> 117340 bytes netserver/other/widgets.qm | Bin 0 -> 593 bytes netserver/readme.txt | 9 + 400 files changed, 106471 insertions(+), 35 deletions(-) rename {snap => 0snap}/battery.gif (100%) rename {snap => 0snap}/bgdemo.gif (100%) rename {snap => 0snap}/buttondefence.gif (100%) rename {snap => 0snap}/colorwidget.gif (100%) rename {snap => 0snap}/comtool.jpg (100%) rename {snap => 0snap}/countcode.gif (100%) rename {snap => 0snap}/dbpage.png (100%) create mode 100644 0snap/designer.png rename {snap => 0snap}/devicesizetable.gif (100%) rename {snap => 0snap}/echartgauge.gif (100%) rename {snap => 0snap}/emailtool.jpg (100%) rename {snap => 0snap}/ffmpegdemo.png (100%) rename {snap => 0snap}/flatui.gif (100%) rename {snap => 0snap}/framelesswidget.gif (100%) rename {snap => 0snap}/gifwidget.gif (100%) rename {snap => 0snap}/imageswitch.gif (100%) rename {snap => 0snap}/ipaddress.gif (100%) rename {snap => 0snap}/lightbutton.gif (100%) rename {snap => 0snap}/lineeditnext.gif (100%) rename {snap => 0snap}/lunarcalendarwidget.gif (100%) rename {snap => 0snap}/maskwidget.gif (100%) rename {snap => 0snap}/mouseline.gif (100%) rename {snap => 0snap}/movewidget.gif (100%) rename {snap => 0snap}/navbutton.gif (100%) create mode 100644 0snap/netserver.jpg rename {snap => 0snap}/nettool.gif (100%) rename {snap => 0snap}/ntpclient.gif (100%) rename {snap => 0snap}/pngtool.gif (100%) create mode 100644 0snap/qwt.png rename {snap => 0snap}/qwtdemo.jpg (100%) rename {snap => 0snap}/screenwidget.gif (100%) rename {snap => 0snap}/styledemo_flatwhite.png (100%) rename {snap => 0snap}/styledemo_lightblue.png (100%) rename {snap => 0snap}/styledemo_psblack.png (100%) rename {snap => 0snap}/videopanel.gif (100%) rename {snap => 0snap}/videowidget.gif (100%) rename {snap => 0snap}/vlcdemo.png (100%) rename {snap => 0snap}/zhtopy.gif (100%) create mode 100644 designer/designer/appfontdialog.cpp create mode 100644 designer/designer/appfontdialog.h create mode 100644 designer/designer/assistantclient.cpp create mode 100644 designer/designer/assistantclient.h create mode 100644 designer/designer/designer.pro create mode 100644 designer/designer/designer.qrc create mode 100644 designer/designer/designer_enums.h create mode 100644 designer/designer/fontpanel.cpp create mode 100644 designer/designer/fontpanel.h create mode 100644 designer/designer/images/back.png create mode 100644 designer/designer/images/designer.png create mode 100644 designer/designer/images/designer_zh_CN.qm create mode 100644 designer/designer/images/down.png create mode 100644 designer/designer/images/forward.png create mode 100644 designer/designer/images/minus.png create mode 100644 designer/designer/images/plus.png create mode 100644 designer/designer/images/up.png create mode 100644 designer/designer/main.cpp create mode 100644 designer/designer/main.ico create mode 100644 designer/designer/main.rc create mode 100644 designer/designer/mainwindow.cpp create mode 100644 designer/designer/mainwindow.h create mode 100644 designer/designer/newform.cpp create mode 100644 designer/designer/newform.h create mode 100644 designer/designer/preferencesdialog.cpp create mode 100644 designer/designer/preferencesdialog.h create mode 100644 designer/designer/preferencesdialog.ui create mode 100644 designer/designer/qdesigner.cpp create mode 100644 designer/designer/qdesigner.h create mode 100644 designer/designer/qdesigner_actions.cpp create mode 100644 designer/designer/qdesigner_actions.h create mode 100644 designer/designer/qdesigner_appearanceoptions.cpp create mode 100644 designer/designer/qdesigner_appearanceoptions.h create mode 100644 designer/designer/qdesigner_appearanceoptions.ui create mode 100644 designer/designer/qdesigner_formwindow.cpp create mode 100644 designer/designer/qdesigner_formwindow.h create mode 100644 designer/designer/qdesigner_pch.h create mode 100644 designer/designer/qdesigner_server.cpp create mode 100644 designer/designer/qdesigner_server.h create mode 100644 designer/designer/qdesigner_settings.cpp create mode 100644 designer/designer/qdesigner_settings.h create mode 100644 designer/designer/qdesigner_toolwindow.cpp create mode 100644 designer/designer/qdesigner_toolwindow.h create mode 100644 designer/designer/qdesigner_workbench.cpp create mode 100644 designer/designer/qdesigner_workbench.h create mode 100644 designer/designer/qttoolbardialog.cpp create mode 100644 designer/designer/qttoolbardialog.h create mode 100644 designer/designer/qttoolbardialog.ui create mode 100644 designer/designer/saveformastemplate.cpp create mode 100644 designer/designer/saveformastemplate.h create mode 100644 designer/designer/saveformastemplate.ui create mode 100644 designer/designer/versiondialog.cpp create mode 100644 designer/designer/versiondialog.h create mode 100644 designer/lib/extension/default_extensionfactory.cpp create mode 100644 designer/lib/extension/default_extensionfactory.h create mode 100644 designer/lib/extension/extension.cpp create mode 100644 designer/lib/extension/extension.h create mode 100644 designer/lib/extension/extension.pri create mode 100644 designer/lib/extension/extension_global.h create mode 100644 designer/lib/extension/qextensionmanager.cpp create mode 100644 designer/lib/extension/qextensionmanager.h create mode 100644 designer/lib/lib.pro create mode 100644 designer/lib/lib_pch.h create mode 100644 designer/lib/sdk/abstractactioneditor.cpp create mode 100644 designer/lib/sdk/abstractactioneditor.h create mode 100644 designer/lib/sdk/abstractbrushmanager.h create mode 100644 designer/lib/sdk/abstractdialoggui.cpp create mode 100644 designer/lib/sdk/abstractdialoggui_p.h create mode 100644 designer/lib/sdk/abstractdnditem.h create mode 100644 designer/lib/sdk/abstractdnditem.qdoc create mode 100644 designer/lib/sdk/abstractformeditor.cpp create mode 100644 designer/lib/sdk/abstractformeditor.h create mode 100644 designer/lib/sdk/abstractformeditorplugin.cpp create mode 100644 designer/lib/sdk/abstractformeditorplugin.h create mode 100644 designer/lib/sdk/abstractformwindow.cpp create mode 100644 designer/lib/sdk/abstractformwindow.h create mode 100644 designer/lib/sdk/abstractformwindowcursor.cpp create mode 100644 designer/lib/sdk/abstractformwindowcursor.h create mode 100644 designer/lib/sdk/abstractformwindowmanager.cpp create mode 100644 designer/lib/sdk/abstractformwindowmanager.h create mode 100644 designer/lib/sdk/abstractformwindowtool.cpp create mode 100644 designer/lib/sdk/abstractformwindowtool.h create mode 100644 designer/lib/sdk/abstracticoncache.h create mode 100644 designer/lib/sdk/abstracticoncache.qdoc create mode 100644 designer/lib/sdk/abstractintegration.cpp create mode 100644 designer/lib/sdk/abstractintegration.h create mode 100644 designer/lib/sdk/abstractintrospection.cpp create mode 100644 designer/lib/sdk/abstractintrospection_p.h create mode 100644 designer/lib/sdk/abstractlanguage.h create mode 100644 designer/lib/sdk/abstractmetadatabase.cpp create mode 100644 designer/lib/sdk/abstractmetadatabase.h create mode 100644 designer/lib/sdk/abstractnewformwidget.cpp create mode 100644 designer/lib/sdk/abstractnewformwidget_p.h create mode 100644 designer/lib/sdk/abstractobjectinspector.cpp create mode 100644 designer/lib/sdk/abstractobjectinspector.h create mode 100644 designer/lib/sdk/abstractoptionspage_p.h create mode 100644 designer/lib/sdk/abstractpromotioninterface.cpp create mode 100644 designer/lib/sdk/abstractpromotioninterface.h create mode 100644 designer/lib/sdk/abstractpropertyeditor.cpp create mode 100644 designer/lib/sdk/abstractpropertyeditor.h create mode 100644 designer/lib/sdk/abstractresourcebrowser.cpp create mode 100644 designer/lib/sdk/abstractresourcebrowser.h create mode 100644 designer/lib/sdk/abstractsettings_p.h create mode 100644 designer/lib/sdk/abstractwidgetbox.cpp create mode 100644 designer/lib/sdk/abstractwidgetbox.h create mode 100644 designer/lib/sdk/abstractwidgetdatabase.cpp create mode 100644 designer/lib/sdk/abstractwidgetdatabase.h create mode 100644 designer/lib/sdk/abstractwidgetfactory.cpp create mode 100644 designer/lib/sdk/abstractwidgetfactory.h create mode 100644 designer/lib/sdk/dynamicpropertysheet.h create mode 100644 designer/lib/sdk/dynamicpropertysheet.qdoc create mode 100644 designer/lib/sdk/extrainfo.cpp create mode 100644 designer/lib/sdk/extrainfo.h create mode 100644 designer/lib/sdk/layoutdecoration.h create mode 100644 designer/lib/sdk/layoutdecoration.qdoc create mode 100644 designer/lib/sdk/membersheet.h create mode 100644 designer/lib/sdk/membersheet.qdoc create mode 100644 designer/lib/sdk/propertysheet.h create mode 100644 designer/lib/sdk/propertysheet.qdoc create mode 100644 designer/lib/sdk/script.cpp create mode 100644 designer/lib/sdk/script_p.h create mode 100644 designer/lib/sdk/sdk.pri create mode 100644 designer/lib/sdk/sdk_global.h create mode 100644 designer/lib/sdk/taskmenu.h create mode 100644 designer/lib/sdk/taskmenu.qdoc create mode 100644 designer/lib/shared/actioneditor.cpp create mode 100644 designer/lib/shared/actioneditor_p.h create mode 100644 designer/lib/shared/actionprovider_p.h create mode 100644 designer/lib/shared/actionrepository.cpp create mode 100644 designer/lib/shared/actionrepository_p.h create mode 100644 designer/lib/shared/addlinkdialog.ui create mode 100644 designer/lib/shared/codedialog.cpp create mode 100644 designer/lib/shared/codedialog_p.h create mode 100644 designer/lib/shared/connectionedit.cpp create mode 100644 designer/lib/shared/connectionedit_p.h create mode 100644 designer/lib/shared/csshighlighter.cpp create mode 100644 designer/lib/shared/csshighlighter_p.h create mode 100644 designer/lib/shared/defaultgradients.xml create mode 100644 designer/lib/shared/deviceprofile.cpp create mode 100644 designer/lib/shared/deviceprofile_p.h create mode 100644 designer/lib/shared/dialoggui.cpp create mode 100644 designer/lib/shared/dialoggui_p.h create mode 100644 designer/lib/shared/extensionfactory_p.h create mode 100644 designer/lib/shared/filterwidget.cpp create mode 100644 designer/lib/shared/filterwidget_p.h create mode 100644 designer/lib/shared/formlayoutmenu.cpp create mode 100644 designer/lib/shared/formlayoutmenu_p.h create mode 100644 designer/lib/shared/formlayoutrowdialog.ui create mode 100644 designer/lib/shared/formwindowbase.cpp create mode 100644 designer/lib/shared/formwindowbase_p.h create mode 100644 designer/lib/shared/grid.cpp create mode 100644 designer/lib/shared/grid_p.h create mode 100644 designer/lib/shared/gridpanel.cpp create mode 100644 designer/lib/shared/gridpanel.ui create mode 100644 designer/lib/shared/gridpanel_p.h create mode 100644 designer/lib/shared/htmlhighlighter.cpp create mode 100644 designer/lib/shared/htmlhighlighter_p.h create mode 100644 designer/lib/shared/iconloader.cpp create mode 100644 designer/lib/shared/iconloader_p.h create mode 100644 designer/lib/shared/iconselector.cpp create mode 100644 designer/lib/shared/iconselector_p.h create mode 100644 designer/lib/shared/invisible_widget.cpp create mode 100644 designer/lib/shared/invisible_widget_p.h create mode 100644 designer/lib/shared/layout.cpp create mode 100644 designer/lib/shared/layout_p.h create mode 100644 designer/lib/shared/layoutinfo.cpp create mode 100644 designer/lib/shared/layoutinfo_p.h create mode 100644 designer/lib/shared/metadatabase.cpp create mode 100644 designer/lib/shared/metadatabase_p.h create mode 100644 designer/lib/shared/morphmenu.cpp create mode 100644 designer/lib/shared/morphmenu_p.h create mode 100644 designer/lib/shared/newactiondialog.cpp create mode 100644 designer/lib/shared/newactiondialog.ui create mode 100644 designer/lib/shared/newactiondialog_p.h create mode 100644 designer/lib/shared/newformwidget.cpp create mode 100644 designer/lib/shared/newformwidget.ui create mode 100644 designer/lib/shared/newformwidget_p.h create mode 100644 designer/lib/shared/orderdialog.cpp create mode 100644 designer/lib/shared/orderdialog.ui create mode 100644 designer/lib/shared/orderdialog_p.h create mode 100644 designer/lib/shared/plaintexteditor.cpp create mode 100644 designer/lib/shared/plaintexteditor_p.h create mode 100644 designer/lib/shared/plugindialog.cpp create mode 100644 designer/lib/shared/plugindialog.ui create mode 100644 designer/lib/shared/plugindialog_p.h create mode 100644 designer/lib/shared/pluginmanager.cpp create mode 100644 designer/lib/shared/pluginmanager_p.h create mode 100644 designer/lib/shared/previewconfigurationwidget.cpp create mode 100644 designer/lib/shared/previewconfigurationwidget.ui create mode 100644 designer/lib/shared/previewconfigurationwidget_p.h create mode 100644 designer/lib/shared/previewmanager.cpp create mode 100644 designer/lib/shared/previewmanager_p.h create mode 100644 designer/lib/shared/promotionmodel.cpp create mode 100644 designer/lib/shared/promotionmodel_p.h create mode 100644 designer/lib/shared/promotiontaskmenu.cpp create mode 100644 designer/lib/shared/promotiontaskmenu_p.h create mode 100644 designer/lib/shared/propertylineedit.cpp create mode 100644 designer/lib/shared/propertylineedit_p.h create mode 100644 designer/lib/shared/qdesigner_command.cpp create mode 100644 designer/lib/shared/qdesigner_command2.cpp create mode 100644 designer/lib/shared/qdesigner_command2_p.h create mode 100644 designer/lib/shared/qdesigner_command_p.h create mode 100644 designer/lib/shared/qdesigner_dnditem.cpp create mode 100644 designer/lib/shared/qdesigner_dnditem_p.h create mode 100644 designer/lib/shared/qdesigner_dockwidget.cpp create mode 100644 designer/lib/shared/qdesigner_dockwidget_p.h create mode 100644 designer/lib/shared/qdesigner_formbuilder.cpp create mode 100644 designer/lib/shared/qdesigner_formbuilder_p.h create mode 100644 designer/lib/shared/qdesigner_formeditorcommand.cpp create mode 100644 designer/lib/shared/qdesigner_formeditorcommand_p.h create mode 100644 designer/lib/shared/qdesigner_formwindowcommand.cpp create mode 100644 designer/lib/shared/qdesigner_formwindowcommand_p.h create mode 100644 designer/lib/shared/qdesigner_formwindowmanager.cpp create mode 100644 designer/lib/shared/qdesigner_formwindowmanager_p.h create mode 100644 designer/lib/shared/qdesigner_integration.cpp create mode 100644 designer/lib/shared/qdesigner_integration_p.h create mode 100644 designer/lib/shared/qdesigner_introspection.cpp create mode 100644 designer/lib/shared/qdesigner_introspection_p.h create mode 100644 designer/lib/shared/qdesigner_membersheet.cpp create mode 100644 designer/lib/shared/qdesigner_membersheet_p.h create mode 100644 designer/lib/shared/qdesigner_menu.cpp create mode 100644 designer/lib/shared/qdesigner_menu_p.h create mode 100644 designer/lib/shared/qdesigner_menubar.cpp create mode 100644 designer/lib/shared/qdesigner_menubar_p.h create mode 100644 designer/lib/shared/qdesigner_objectinspector.cpp create mode 100644 designer/lib/shared/qdesigner_objectinspector_p.h create mode 100644 designer/lib/shared/qdesigner_promotion.cpp create mode 100644 designer/lib/shared/qdesigner_promotion_p.h create mode 100644 designer/lib/shared/qdesigner_promotiondialog.cpp create mode 100644 designer/lib/shared/qdesigner_promotiondialog_p.h create mode 100644 designer/lib/shared/qdesigner_propertycommand.cpp create mode 100644 designer/lib/shared/qdesigner_propertycommand_p.h create mode 100644 designer/lib/shared/qdesigner_propertyeditor.cpp create mode 100644 designer/lib/shared/qdesigner_propertyeditor_p.h create mode 100644 designer/lib/shared/qdesigner_propertysheet.cpp create mode 100644 designer/lib/shared/qdesigner_propertysheet_p.h create mode 100644 designer/lib/shared/qdesigner_qsettings.cpp create mode 100644 designer/lib/shared/qdesigner_qsettings_p.h create mode 100644 designer/lib/shared/qdesigner_stackedbox.cpp create mode 100644 designer/lib/shared/qdesigner_stackedbox_p.h create mode 100644 designer/lib/shared/qdesigner_tabwidget.cpp create mode 100644 designer/lib/shared/qdesigner_tabwidget_p.h create mode 100644 designer/lib/shared/qdesigner_taskmenu.cpp create mode 100644 designer/lib/shared/qdesigner_taskmenu_p.h create mode 100644 designer/lib/shared/qdesigner_toolbar.cpp create mode 100644 designer/lib/shared/qdesigner_toolbar_p.h create mode 100644 designer/lib/shared/qdesigner_toolbox.cpp create mode 100644 designer/lib/shared/qdesigner_toolbox_p.h create mode 100644 designer/lib/shared/qdesigner_utils.cpp create mode 100644 designer/lib/shared/qdesigner_utils_p.h create mode 100644 designer/lib/shared/qdesigner_widget.cpp create mode 100644 designer/lib/shared/qdesigner_widget_p.h create mode 100644 designer/lib/shared/qdesigner_widgetbox.cpp create mode 100644 designer/lib/shared/qdesigner_widgetbox_p.h create mode 100644 designer/lib/shared/qdesigner_widgetitem.cpp create mode 100644 designer/lib/shared/qdesigner_widgetitem_p.h create mode 100644 designer/lib/shared/qlayout_widget.cpp create mode 100644 designer/lib/shared/qlayout_widget_p.h create mode 100644 designer/lib/shared/qscripthighlighter.cpp create mode 100644 designer/lib/shared/qscripthighlighter_p.h create mode 100644 designer/lib/shared/qsimpleresource.cpp create mode 100644 designer/lib/shared/qsimpleresource_p.h create mode 100644 designer/lib/shared/qtresourceeditordialog.cpp create mode 100644 designer/lib/shared/qtresourceeditordialog.ui create mode 100644 designer/lib/shared/qtresourceeditordialog_p.h create mode 100644 designer/lib/shared/qtresourcemodel.cpp create mode 100644 designer/lib/shared/qtresourcemodel_p.h create mode 100644 designer/lib/shared/qtresourceview.cpp create mode 100644 designer/lib/shared/qtresourceview_p.h create mode 100644 designer/lib/shared/richtexteditor.cpp create mode 100644 designer/lib/shared/richtexteditor_p.h create mode 100644 designer/lib/shared/scriptcommand.cpp create mode 100644 designer/lib/shared/scriptcommand_p.h create mode 100644 designer/lib/shared/scriptdialog.cpp create mode 100644 designer/lib/shared/scriptdialog_p.h create mode 100644 designer/lib/shared/scripterrordialog.cpp create mode 100644 designer/lib/shared/scripterrordialog_p.h create mode 100644 designer/lib/shared/selectsignaldialog.ui create mode 100644 designer/lib/shared/shared.pri create mode 100644 designer/lib/shared/shared.qrc create mode 100644 designer/lib/shared/shared_enums_p.h create mode 100644 designer/lib/shared/shared_global_p.h create mode 100644 designer/lib/shared/shared_settings.cpp create mode 100644 designer/lib/shared/shared_settings_p.h create mode 100644 designer/lib/shared/sheet_delegate.cpp create mode 100644 designer/lib/shared/sheet_delegate_p.h create mode 100644 designer/lib/shared/signalslotdialog.cpp create mode 100644 designer/lib/shared/signalslotdialog.ui create mode 100644 designer/lib/shared/signalslotdialog_p.h create mode 100644 designer/lib/shared/spacer_widget.cpp create mode 100644 designer/lib/shared/spacer_widget_p.h create mode 100644 designer/lib/shared/stylesheeteditor.cpp create mode 100644 designer/lib/shared/stylesheeteditor_p.h create mode 100644 designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui create mode 100644 designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui create mode 100644 designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui create mode 100644 designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui create mode 100644 designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui create mode 100644 designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui create mode 100644 designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui create mode 100644 designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui create mode 100644 designer/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui create mode 100644 designer/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui create mode 100644 designer/lib/shared/templates/forms/Dialog_without_Buttons.ui create mode 100644 designer/lib/shared/templates/forms/Main_Window.ui create mode 100644 designer/lib/shared/templates/forms/Widget.ui create mode 100644 designer/lib/shared/textpropertyeditor.cpp create mode 100644 designer/lib/shared/textpropertyeditor_p.h create mode 100644 designer/lib/shared/widgetdatabase.cpp create mode 100644 designer/lib/shared/widgetdatabase_p.h create mode 100644 designer/lib/shared/widgetfactory.cpp create mode 100644 designer/lib/shared/widgetfactory_p.h create mode 100644 designer/lib/shared/zoomwidget.cpp create mode 100644 designer/lib/shared/zoomwidget_p.h create mode 100644 designer/lib/uilib/abstractformbuilder.cpp create mode 100644 designer/lib/uilib/abstractformbuilder.h create mode 100644 designer/lib/uilib/container.h create mode 100644 designer/lib/uilib/container.qdoc create mode 100644 designer/lib/uilib/customwidget.h create mode 100644 designer/lib/uilib/customwidget.qdoc create mode 100644 designer/lib/uilib/formbuilder.cpp create mode 100644 designer/lib/uilib/formbuilder.h create mode 100644 designer/lib/uilib/formbuilderextra.cpp create mode 100644 designer/lib/uilib/formbuilderextra_p.h create mode 100644 designer/lib/uilib/formscriptrunner.cpp create mode 100644 designer/lib/uilib/formscriptrunner_p.h create mode 100644 designer/lib/uilib/properties.cpp create mode 100644 designer/lib/uilib/properties_p.h create mode 100644 designer/lib/uilib/qdesignerexportwidget.h create mode 100644 designer/lib/uilib/resourcebuilder.cpp create mode 100644 designer/lib/uilib/resourcebuilder_p.h create mode 100644 designer/lib/uilib/textbuilder.cpp create mode 100644 designer/lib/uilib/textbuilder_p.h create mode 100644 designer/lib/uilib/ui4.cpp create mode 100644 designer/lib/uilib/ui4_p.h create mode 100644 designer/lib/uilib/uilib.pri create mode 100644 designer/lib/uilib/uilib_global.h create mode 100644 designer/lib/uilib/widgets.table create mode 100644 designer/readme.txt create mode 100644 netserver/api/api.pri create mode 100644 netserver/api/app.cpp create mode 100644 netserver/api/app.h create mode 100644 netserver/api/quiwidget.cpp create mode 100644 netserver/api/quiwidget.h create mode 100644 netserver/api/tcpserver1.cpp create mode 100644 netserver/api/tcpserver1.h create mode 100644 netserver/api/tcpserver2.cpp create mode 100644 netserver/api/tcpserver2.h create mode 100644 netserver/form/form.pri create mode 100644 netserver/form/frmmain.cpp create mode 100644 netserver/form/frmmain.h create mode 100644 netserver/form/frmmain.ui create mode 100644 netserver/head.h create mode 100644 netserver/main.cpp create mode 100644 netserver/netserver.pro create mode 100644 netserver/other/main.ico create mode 100644 netserver/other/main.qrc create mode 100644 netserver/other/main.rc create mode 100644 netserver/other/qt_zh_CN.qm create mode 100644 netserver/other/widgets.qm create mode 100644 netserver/readme.txt diff --git a/snap/battery.gif b/0snap/battery.gif similarity index 100% rename from snap/battery.gif rename to 0snap/battery.gif diff --git a/snap/bgdemo.gif b/0snap/bgdemo.gif similarity index 100% rename from snap/bgdemo.gif rename to 0snap/bgdemo.gif diff --git a/snap/buttondefence.gif b/0snap/buttondefence.gif similarity index 100% rename from snap/buttondefence.gif rename to 0snap/buttondefence.gif diff --git a/snap/colorwidget.gif b/0snap/colorwidget.gif similarity index 100% rename from snap/colorwidget.gif rename to 0snap/colorwidget.gif diff --git a/snap/comtool.jpg b/0snap/comtool.jpg similarity index 100% rename from snap/comtool.jpg rename to 0snap/comtool.jpg diff --git a/snap/countcode.gif b/0snap/countcode.gif similarity index 100% rename from snap/countcode.gif rename to 0snap/countcode.gif diff --git a/snap/dbpage.png b/0snap/dbpage.png similarity index 100% rename from snap/dbpage.png rename to 0snap/dbpage.png diff --git a/0snap/designer.png b/0snap/designer.png new file mode 100644 index 0000000000000000000000000000000000000000..c17b2ae2a2bd8baeab4cf84ca87c8a8786088dff GIT binary patch literal 238230 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�XZJ@9K|h;DZ;! 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 literal 0 HcmV?d00001 diff --git a/snap/devicesizetable.gif b/0snap/devicesizetable.gif similarity index 100% rename from snap/devicesizetable.gif rename to 0snap/devicesizetable.gif diff --git a/snap/echartgauge.gif b/0snap/echartgauge.gif similarity index 100% rename from snap/echartgauge.gif rename to 0snap/echartgauge.gif diff --git a/snap/emailtool.jpg b/0snap/emailtool.jpg similarity index 100% rename from snap/emailtool.jpg rename to 0snap/emailtool.jpg diff --git a/snap/ffmpegdemo.png b/0snap/ffmpegdemo.png similarity index 100% rename from snap/ffmpegdemo.png rename to 0snap/ffmpegdemo.png diff --git a/snap/flatui.gif b/0snap/flatui.gif similarity index 100% rename from snap/flatui.gif rename to 0snap/flatui.gif diff --git a/snap/framelesswidget.gif b/0snap/framelesswidget.gif similarity index 100% rename from snap/framelesswidget.gif rename to 0snap/framelesswidget.gif diff --git a/snap/gifwidget.gif b/0snap/gifwidget.gif similarity index 100% rename from snap/gifwidget.gif rename to 0snap/gifwidget.gif diff --git a/snap/imageswitch.gif b/0snap/imageswitch.gif similarity index 100% rename from snap/imageswitch.gif rename to 0snap/imageswitch.gif diff --git a/snap/ipaddress.gif b/0snap/ipaddress.gif similarity index 100% rename from snap/ipaddress.gif rename to 0snap/ipaddress.gif diff --git a/snap/lightbutton.gif b/0snap/lightbutton.gif similarity index 100% rename from snap/lightbutton.gif rename to 0snap/lightbutton.gif diff --git a/snap/lineeditnext.gif b/0snap/lineeditnext.gif similarity index 100% rename from snap/lineeditnext.gif rename to 0snap/lineeditnext.gif diff --git a/snap/lunarcalendarwidget.gif b/0snap/lunarcalendarwidget.gif similarity index 100% rename from snap/lunarcalendarwidget.gif rename to 0snap/lunarcalendarwidget.gif diff --git a/snap/maskwidget.gif b/0snap/maskwidget.gif similarity index 100% rename from snap/maskwidget.gif rename to 0snap/maskwidget.gif diff --git a/snap/mouseline.gif b/0snap/mouseline.gif similarity index 100% rename from snap/mouseline.gif rename to 0snap/mouseline.gif diff --git a/snap/movewidget.gif b/0snap/movewidget.gif similarity index 100% rename from snap/movewidget.gif rename to 0snap/movewidget.gif diff --git a/snap/navbutton.gif b/0snap/navbutton.gif similarity index 100% rename from snap/navbutton.gif rename to 0snap/navbutton.gif diff --git a/0snap/netserver.jpg b/0snap/netserver.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63c0b23ea913f4491578ce523045960141bed3c4 GIT binary patch literal 131177 zcmd?Rbyyr(w>R2AfB=C2L4pN$cPDu8U=58EB)HQ!gg|f!0UC!OLAz;aoW_E?2G<04 z0tAm-GV{*7@5ni4&UgR0-*fkVx^}Iq>e{QeEcvZn^<(zOM*zNxqOu|Y1qB6QewzV5 z765X9yBHXl80dE~F)^{Q?qc5~!o7!sbB~Ph!F?hsa_UD^

&3|!1KbR6`Qlq|xm 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

m?#+w65MKF^dw;s!6mx=d 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%0A+@HP%1;EvsotrH^_lZN3ONy&vDM`G^<1t!iL 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 @pNq=QlAW1B0?ZnF|y8;8i!l246)ih9MU(H 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 E1KR8>{Qv*} literal 0 HcmV?d00001 diff --git a/designer/designer/images/down.png b/designer/designer/images/down.png new file mode 100644 index 0000000000000000000000000000000000000000..29d1d4439a139c662aecca94b6f43a465cfb9cc6 GIT binary patch literal 594 zcmV-Y0j z)Xz`TU>wKswOeUBH_Vo3LZ*V4p&U4v;LVFDq!ObUNJtQHC_UYOy}c$4_Z z287Mpy&>Gkk3$;%;XTGD)-SARcb^V+y#l_lys$a@k{nD+qgKLE+C6xLudGK{sd70w zcE71nDjtqr6rQslcH!s21HbzIZLG4Ku(F%O+U^xp_O4>4nBl-LJ{^?W2788E7ww3c$dW3qz>Ki(HSZqJlD~5#;x#SD}gQ7 zgv0(;bxhbL9Yezjn5K`uZiTiRwq2=|ckJ6DkxX7Tsy45p8>IMse%D zf;Vqf6vh<#P(J!fv{R}3IKcTOvuzkL=(>--JPth;j^KP+u2DCF7oBg1O2Gjh4cJAZUz`NyfQlF14XgvfeqKhRM8=g$nIpi07v2Pm$T0PG z+s5t5ev&IuzPy9iGfJ_*Szxjq-U_S#@di#sMFxj0Y^u zs2IrSA-xLzn@Xv~L10-`OD_1{xQ}!;h8VCMGZhEg7)ve(nH0=Nlth##z-1t-sznz( zI^knI8`}k}=1Q^8xg4a|;6+&Z7-tr^27CjyRJH7a$AX8**swS%Z2U-ZpmSmP5GMj$ z1y+C!prEQ%7kpz7mY14W%oynFgWlfyOj9X{G^0Np=uW$J^8**~h`aaUdlDkgAkhJB z2loJbfPHrEakyVc$6#mx5^=)7buY9XEP!Q$7GNCj4jnzl`B(FVY;?m5b-|rNMD_xg pfC->zKW))&VdLne22{OJfIn@@R^)Beh^qhq002ovPDHLkV1l$X9{d0R literal 0 HcmV?d00001 diff --git a/designer/designer/images/minus.png b/designer/designer/images/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f233d7399c4c07c6c66775f7806acac84b1870 GIT binary patch literal 250 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6P60k4u0Wb3;FYqWamwl0TmSYg zzPEV)f9L4=uDMg50*&A=3GxeOV9U{Bm1EIh)b!)dNY8G|=4l2>MR>Y6hE&9zJ@3ia zpuppBkxMsqLg3#2p{{ow4y^sCTR&@AtNcZ-!c0XiUIF(fS@*p4-Mvkxubuwr)McGB zHm7G5zmh%w<-tAQYwQ=f{_Q#i|$63?4Gdn(WDhGy5?S*zvKV>UH^L*-tUX z$Jv{I&ffC7bM{5Qj4e}Ezbj}vVjnSc;_|19_y1onFfAQuu24ykUoZnB3o9G9teS?7 zt9NpGMrLu#@0UVzfU@^IT^vIy;%X<}%xf{=arHKHYQL~@ZfotgwYUG*$MH^!I&%2V z@)sJL`rl4pesV%`$+ja*^=&3!b{Hmgs`72G$lh?~sgSmhV3T|L)#5pvnp5W$R3F&x zq^-&N)%9FO2FOm@bA*rKR&a{^p(h(SexKRzobg)f!S5Tb z0(ReeejodN_$Ob%pPGvI-{X5McZ=;}w|{;sE!Zmj;a8K;+WTSWrcUlO+qsq1zw`F> zL$~g{I69@}|MDjqpAy@zuKLv`b1gzCeQUnq?;S;@b8|k;l-+S#KOj|nW)fHH0-#SB NJYD@<);T3K0RYVY#HauO literal 0 HcmV?d00001 diff --git a/designer/designer/images/up.png b/designer/designer/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..e4373122171599c88b78c884b927c6a8b4a90c6a GIT binary patch literal 692 zcmV;l0!#ggP)p2raNh0iv$(l~TMx4kdC6q9nEA|`**D{}k#dX8|6LB>7#;)I^Ped=4Hzs5}YJfl=IMqVOwV3TOn<`fg+FtutHTOl+p4ItW@S@UCRT$s#e2Vdg=lo5D}~>p3$197_jRp z=YhPc7Gm8z$3=Kf7AcnG)$Gyx5pjP)J5;=W_SftyqWmZ>V+N`!8lA3I}LdVVyM axbX+reAIe(fQ}9T0000 +#include +#include + +#include + +QT_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + Q_INIT_RESOURCE(designer); + + QDesigner app(argc, argv); + app.setQuitOnLastWindowClosed(false); + +#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + return app.exec(); +} diff --git a/designer/designer/main.ico b/designer/designer/main.ico new file mode 100644 index 0000000000000000000000000000000000000000..bfacb89b1a0fa36b1834702adecf0c112ac50b35 GIT binary patch literal 67646 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 O@`D +# else +# include +# endif + +IDI_ICON1 ICON DISCARDABLE "main.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2018,8,26,0 + PRODUCTVERSION 2018,8,26,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "feiyangqingyun QQ:517216493\0" + VALUE "FileDescription", "Qt Designer\0" + VALUE "FileVersion", "2018.8.26.0\0" + VALUE "LegalCopyright", "Copyright (C) 2018\0" + VALUE "OriginalFilename", "designer.exe\0" + VALUE "ProductName", "designer\0" + VALUE "ProductVersion", "2018.8.26.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END + END +/* End of Version info */ + diff --git a/designer/designer/mainwindow.cpp b/designer/designer/mainwindow.cpp new file mode 100644 index 0000000..15f6df1 --- /dev/null +++ b/designer/designer/mainwindow.cpp @@ -0,0 +1,419 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "qdesigner.h" +#include "qdesigner_actions.h" +#include "qdesigner_workbench.h" +#include "qdesigner_formwindow.h" +#include "qdesigner_toolwindow.h" +#include "qdesigner_settings.h" +#include "qttoolbardialog.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const char *uriListMimeFormatC = "text/uri-list"; + +QT_BEGIN_NAMESPACE + +typedef QList ActionList; + +// Helpers for creating toolbars and menu + +static void addActionsToToolBar(const ActionList &actions, QToolBar *t) +{ + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) { + QAction *action = *it; + if (action->property(QDesignerActions::defaultToolbarPropertyName).toBool()) + t->addAction(action); + } +} +static QToolBar *createToolBar(const QString &title, const QString &objectName, const ActionList &actions) +{ + QToolBar *rc = new QToolBar; + rc->setObjectName(objectName); + rc->setWindowTitle(title); + addActionsToToolBar(actions, rc); + return rc; +} + +// ---------------- MainWindowBase + +MainWindowBase::MainWindowBase(QWidget *parent, Qt::WindowFlags flags) : + QMainWindow(parent, flags), + m_policy(AcceptCloseEvents) +{ +#ifndef Q_WS_MAC + setWindowIcon(qDesigner->windowIcon()); +#endif +} + +void MainWindowBase::closeEvent(QCloseEvent *e) +{ + switch (m_policy) { + case AcceptCloseEvents: + QMainWindow::closeEvent(e); + break; + case EmitCloseEventSignal: + emit closeEventReceived(e); + break; + } +} + +QList MainWindowBase::createToolBars(const QDesignerActions *actions, bool singleToolBar) +{ + // Note that whenever you want to add a new tool bar here, you also have to update the default + // action groups added to the toolbar manager in the mainwindow constructor + QList rc; + if (singleToolBar) { + //: Not currently used (main tool bar) + QToolBar *main = createToolBar(tr("Main"), QLatin1String("mainToolBar"), actions->fileActions()->actions()); + addActionsToToolBar(actions->editActions()->actions(), main); + addActionsToToolBar(actions->toolActions()->actions(), main); + addActionsToToolBar(actions->formActions()->actions(), main); + rc.push_back(main); + } else { + rc.push_back(createToolBar(tr("File"), QLatin1String("fileToolBar"), actions->fileActions()->actions())); + rc.push_back(createToolBar(tr("Edit"), QLatin1String("editToolBar"), actions->editActions()->actions())); + rc.push_back(createToolBar(tr("Tools"), QLatin1String("toolsToolBar"), actions->toolActions()->actions())); + rc.push_back(createToolBar(tr("Form"), QLatin1String("formToolBar"), actions->formActions()->actions())); + } + return rc; +} + +QString MainWindowBase::mainWindowTitle() +{ + return tr("Qt Designer"); +} + +// Use the minor Qt version as settings versions to avoid conflicts +int MainWindowBase::settingsVersion() +{ + const int version = QT_VERSION; + return (version & 0x00FF00) >> 8; +} + +// ----------------- DockedMdiArea + +DockedMdiArea::DockedMdiArea(const QString &extension, QWidget *parent) : + QMdiArea(parent), + m_extension(extension) +{ + setAcceptDrops(true); + setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); +} + +QStringList DockedMdiArea::uiFiles(const QMimeData *d) const +{ + // Extract dropped UI files from Mime data. + QStringList rc; + if (!d->hasFormat(QLatin1String(uriListMimeFormatC))) + return rc; + const QList urls = d->urls(); + if (urls.empty()) + return rc; + const QList::const_iterator cend = urls.constEnd(); + for (QList::const_iterator it = urls.constBegin(); it != cend; ++it) { + const QString fileName = it->toLocalFile(); + if (!fileName.isEmpty() && fileName.endsWith(m_extension)) + rc.push_back(fileName); + } + return rc; +} + +bool DockedMdiArea::event(QEvent *event) +{ + // Listen for desktop file manager drop and emit a signal once a file is + // dropped. + switch (event->type()) { + case QEvent::DragEnter: { + QDragEnterEvent *e = static_cast(event); + if (!uiFiles(e->mimeData()).empty()) { + e->acceptProposedAction(); + return true; + } + } + break; + case QEvent::Drop: { + QDropEvent *e = static_cast(event); + const QStringList files = uiFiles(e->mimeData()); + const QStringList::const_iterator cend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != cend; ++it) { + emit fileDropped(*it); + } + e->acceptProposedAction(); + return true; + } + break; + default: + break; + } + return QMdiArea::event(event); +} + +// ------------- ToolBarManager: + +static void addActionsToToolBarManager(const ActionList &al, const QString &title, QtToolBarManager *tbm) +{ + const ActionList::const_iterator cend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != cend; ++it) + tbm->addAction(*it, title); +} + +ToolBarManager::ToolBarManager(QMainWindow *configureableMainWindow, + QWidget *parent, + QMenu *toolBarMenu, + const QDesignerActions *actions, + const QList &toolbars, + const QList &toolWindows) : + QObject(parent), + m_configureableMainWindow(configureableMainWindow), + m_parent(parent), + m_toolBarMenu(toolBarMenu), + m_manager(new QtToolBarManager(this)), + m_configureAction(new QAction(tr("Configure Toolbars..."), this)), + m_toolbars(toolbars) +{ + m_configureAction->setMenuRole(QAction::NoRole); + m_configureAction->setObjectName(QLatin1String("__qt_configure_tool_bars_action")); + connect(m_configureAction, SIGNAL(triggered()), this, SLOT(configureToolBars())); + + m_manager->setMainWindow(configureableMainWindow); + + foreach(QToolBar *tb, m_toolbars) { + const QString title = tb->windowTitle(); + m_manager->addToolBar(tb, title); + addActionsToToolBarManager(tb->actions(), title, m_manager); + } + + addActionsToToolBarManager(actions->windowActions()->actions(), tr("Window"), m_manager); + addActionsToToolBarManager(actions->helpActions()->actions(), tr("Help"), m_manager); + + // Filter out the device profile preview actions which have int data(). + ActionList previewActions = actions->styleActions()->actions(); + ActionList::iterator it = previewActions.begin(); + for ( ; (*it)->isSeparator() || (*it)->data().type() == QVariant::Int; ++it) ; + previewActions.erase(previewActions.begin(), it); + addActionsToToolBarManager(previewActions, tr("Style"), m_manager); + + const QString dockTitle = tr("Dock views"); + foreach (QDesignerToolWindow *tw, toolWindows) { + if (QAction *action = tw->action()) + m_manager->addAction(action, dockTitle); + } + + QString category(tr("File")); + foreach(QAction *action, actions->fileActions()->actions()) + m_manager->addAction(action, category); + + category = tr("Edit"); + foreach(QAction *action, actions->editActions()->actions()) + m_manager->addAction(action, category); + + category = tr("Tools"); + foreach(QAction *action, actions->toolActions()->actions()) + m_manager->addAction(action, category); + + category = tr("Form"); + foreach(QAction *action, actions->formActions()->actions()) + m_manager->addAction(action, category); + + m_manager->addAction(m_configureAction, tr("Toolbars")); + updateToolBarMenu(); +} + +// sort function for sorting tool bars alphabetically by title [non-static since called from template] + +bool toolBarTitleLessThan(const QToolBar *t1, const QToolBar *t2) +{ + return t1->windowTitle() < t2->windowTitle(); +} + +void ToolBarManager::updateToolBarMenu() +{ + // Sort tool bars alphabetically by title + qStableSort(m_toolbars.begin(), m_toolbars.end(), toolBarTitleLessThan); + // add to menu + m_toolBarMenu->clear(); + foreach (QToolBar *tb, m_toolbars) + m_toolBarMenu->addAction(tb->toggleViewAction()); + m_toolBarMenu->addAction(m_configureAction); +} + +void ToolBarManager::configureToolBars() +{ +// QtToolBarDialog dlg(m_parent); +// dlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowContextHelpButtonHint); +// dlg.setToolBarManager(m_manager); +// dlg.exec(); +// updateToolBarMenu(); +} + +QByteArray ToolBarManager::saveState(int version) const +{ + return m_manager->saveState(version); +} + +bool ToolBarManager::restoreState(const QByteArray &state, int version) +{ + return m_manager->restoreState(state, version); +} + +// ---------- DockedMainWindow + +DockedMainWindow::DockedMainWindow(QDesignerWorkbench *wb, + QMenu *toolBarMenu, + const QList &toolWindows) : + m_toolBarManager(0) +{ + setObjectName(QLatin1String("MDIWindow")); + setWindowTitle(mainWindowTitle()); + + const QList toolbars = createToolBars(wb->actionManager(), false); + foreach (QToolBar *tb, toolbars) + addToolBar(tb); + DockedMdiArea *dma = new DockedMdiArea(wb->actionManager()->uiExtension()); + connect(dma, SIGNAL(fileDropped(QString)), + this, SIGNAL(fileDropped(QString))); + connect(dma, SIGNAL(subWindowActivated(QMdiSubWindow*)), + this, SLOT(slotSubWindowActivated(QMdiSubWindow*))); + setCentralWidget(dma); + + QStatusBar *sb = statusBar(); + Q_UNUSED(sb) + + m_toolBarManager = new ToolBarManager(this, this, toolBarMenu, wb->actionManager(), toolbars, toolWindows); +} + +QMdiArea *DockedMainWindow::mdiArea() const +{ + return static_cast(centralWidget()); +} + +void DockedMainWindow::slotSubWindowActivated(QMdiSubWindow* subWindow) +{ + if (subWindow) { + QWidget *widget = subWindow->widget(); + if (QDesignerFormWindow *fw = qobject_cast(widget)) { + emit formWindowActivated(fw); + mdiArea()->setActiveSubWindow(subWindow); + } + } +} + +// Create a MDI subwindow for the form. +QMdiSubWindow *DockedMainWindow::createMdiSubWindow(QWidget *fw, Qt::WindowFlags f, const QKeySequence &designerCloseActionShortCut) +{ + QMdiSubWindow *rc = mdiArea()->addSubWindow(fw, f); + // Make action shortcuts respond only if focused to avoid conflicts with + // designer menu actions + if (designerCloseActionShortCut == QKeySequence(QKeySequence::Close)) { + const ActionList systemMenuActions = rc->systemMenu()->actions(); + if (!systemMenuActions.empty()) { + const ActionList::const_iterator cend = systemMenuActions.constEnd(); + for (ActionList::const_iterator it = systemMenuActions.constBegin(); it != cend; ++it) { + if ( (*it)->shortcut() == designerCloseActionShortCut) { + (*it)->setShortcutContext(Qt::WidgetShortcut); + break; + } + } + } + } + return rc; +} + +DockedMainWindow::DockWidgetList DockedMainWindow::addToolWindows(const DesignerToolWindowList &tls) +{ + DockWidgetList rc; + foreach (QDesignerToolWindow *tw, tls) { + QDockWidget *dockWidget = new QDockWidget; + dockWidget->setObjectName(tw->objectName() + QLatin1String("_dock")); + dockWidget->setWindowTitle(tw->windowTitle()); + addDockWidget(tw->dockWidgetAreaHint(), dockWidget); + dockWidget->setWidget(tw); + rc.push_back(dockWidget); + } + return rc; +} + +// Settings consist of MainWindow state and tool bar manager state +void DockedMainWindow::restoreSettings(const QDesignerSettings &s, const DockWidgetList &dws, const QRect &desktopArea) +{ + const int version = settingsVersion(); + m_toolBarManager->restoreState(s.toolBarsState(DockedMode), version); + + // If there are no old geometry settings, show the window maximized + s.restoreGeometry(this, QRect(desktopArea.topLeft(), QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX))); + + const QByteArray mainWindowState = s.mainWindowState(DockedMode); + const bool restored = !mainWindowState.isEmpty() && restoreState(mainWindowState, version); + if (!restored) { + // Default: Tabify less relevant windows bottom/right. + tabifyDockWidget(dws.at(QDesignerToolWindow::SignalSlotEditor), + dws.at(QDesignerToolWindow::ActionEditor)); + tabifyDockWidget(dws.at(QDesignerToolWindow::ActionEditor), + dws.at(QDesignerToolWindow::ResourceEditor)); + } +} + +void DockedMainWindow::saveSettings(QDesignerSettings &s) const +{ + const int version = settingsVersion(); + s.setToolBarsState(DockedMode, m_toolBarManager->saveState(version)); + s.saveGeometryFor(this); + s.setMainWindowState(DockedMode, saveState(version)); +} + +QT_END_NAMESPACE diff --git a/designer/designer/mainwindow.h b/designer/designer/mainwindow.h new file mode 100644 index 0000000..68ce06f --- /dev/null +++ b/designer/designer/mainwindow.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerActions; +class QDesignerWorkbench; +class QDesignerToolWindow; +class QDesignerFormWindow; +class QDesignerSettings; + +class QtToolBarManager; +class QToolBar; +class QMdiArea; +class QMenu; +class QByteArray; +class QMimeData; + +/* A main window that has a configureable policy on handling close events. If + * enabled, it can forward the close event to external handlers. + * Base class for windows that can switch roles between tool windows + * and main windows. */ + +class MainWindowBase: public QMainWindow +{ + Q_DISABLE_COPY(MainWindowBase) + Q_OBJECT +protected: + explicit MainWindowBase(QWidget *parent = 0, Qt::WindowFlags flags = Qt::Window); + +public: + enum CloseEventPolicy { + /* Always accept close events */ + AcceptCloseEvents, + /* Emit a signal with the event, have it handled elsewhere */ + EmitCloseEventSignal }; + + CloseEventPolicy closeEventPolicy() const { return m_policy; } + void setCloseEventPolicy(CloseEventPolicy pol) { m_policy = pol; } + + static QList createToolBars(const QDesignerActions *actions, bool singleToolBar); + static QString mainWindowTitle(); + + // Use the minor Qt version as settings versions to avoid conflicts + static int settingsVersion(); + +signals: + void closeEventReceived(QCloseEvent *e); + +protected: + virtual void closeEvent(QCloseEvent *e); +private: + CloseEventPolicy m_policy; +}; + +/* An MdiArea that listens for desktop file manager file drop events and emits + * a signal to open a dropped file. */ +class DockedMdiArea : public QMdiArea +{ + Q_DISABLE_COPY(DockedMdiArea) + Q_OBJECT +public: + explicit DockedMdiArea(const QString &extension, QWidget *parent = 0); + +signals: + void fileDropped(const QString &); + +protected: + bool event (QEvent *event); + +private: + QStringList uiFiles(const QMimeData *d) const; + + const QString m_extension; +}; + +// Convenience class that manages a QtToolBarManager and an action to trigger +// it on a mainwindow. +class ToolBarManager : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ToolBarManager) +public: + explicit ToolBarManager(QMainWindow *configureableMainWindow, + QWidget *parent, + QMenu *toolBarMenu, + const QDesignerActions *actions, + const QList &toolbars, + const QList &toolWindows); + + QByteArray saveState(int version = 0) const; + bool restoreState(const QByteArray &state, int version = 0); + +private slots: + void configureToolBars(); + void updateToolBarMenu(); + +private: + QMainWindow *m_configureableMainWindow; + QWidget *m_parent; + QMenu *m_toolBarMenu; + QtToolBarManager *m_manager; + QAction *m_configureAction; + QList m_toolbars; +}; + +/* Main window to be used for docked mode */ +class DockedMainWindow : public MainWindowBase { + Q_OBJECT + Q_DISABLE_COPY(DockedMainWindow) +public: + typedef QList DesignerToolWindowList; + typedef QList DockWidgetList; + + explicit DockedMainWindow(QDesignerWorkbench *wb, + QMenu *toolBarMenu, + const DesignerToolWindowList &toolWindows); + + // Create a MDI subwindow for the form. + QMdiSubWindow *createMdiSubWindow(QWidget *fw, Qt::WindowFlags f, const QKeySequence &designerCloseActionShortCut); + + QMdiArea *mdiArea() const; + + DockWidgetList addToolWindows(const DesignerToolWindowList &toolWindows); + + void restoreSettings(const QDesignerSettings &s, const DockWidgetList &dws, const QRect &desktopArea); + void saveSettings(QDesignerSettings &) const; + +signals: + void fileDropped(const QString &); + void formWindowActivated(QDesignerFormWindow *); + +private slots: + void slotSubWindowActivated(QMdiSubWindow*); + +private: + ToolBarManager *m_toolBarManager; +}; + +QT_END_NAMESPACE + +#endif // MAINWINDOW_H diff --git a/designer/designer/newform.cpp b/designer/designer/newform.cpp new file mode 100644 index 0000000..b31f874 --- /dev/null +++ b/designer/designer/newform.cpp @@ -0,0 +1,229 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newform.h" +#include "qdesigner_workbench.h" +#include "qdesigner_actions.h" +#include "qdesigner_formwindow.h" +#include "qdesigner_settings.h" + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +NewForm::NewForm(QDesignerWorkbench *workbench, QWidget *parentWidget, const QString &fileName) + : QDialog(parentWidget, +#ifdef Q_WS_MAC + Qt::Tool | +#endif + Qt::WindowTitleHint | Qt::WindowSystemMenuHint), + m_fileName(fileName), + m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(workbench->core())), + m_workbench(workbench), + m_chkShowOnStartup(new QCheckBox(tr("Show this Dialog on Startup"))), + m_createButton(new QPushButton(QApplication::translate("NewForm", "C&reate", 0, QApplication::UnicodeUTF8))), + m_recentButton(new QPushButton(QApplication::translate("NewForm", "Recent", 0, QApplication::UnicodeUTF8))), + m_buttonBox(0) +{ + setWindowTitle(tr("New Form")); + QDesignerSettings settings(m_workbench->core()); + + QVBoxLayout *vBoxLayout = new QVBoxLayout; + + connect(m_newFormWidget, SIGNAL(templateActivated()), this, SLOT(slotTemplateActivated())); + connect(m_newFormWidget, SIGNAL(currentTemplateChanged(bool)), this, SLOT(slotCurrentTemplateChanged(bool))); + vBoxLayout->addWidget(m_newFormWidget); + + QFrame *horizontalLine = new QFrame; + horizontalLine->setFrameShape(QFrame::HLine); + horizontalLine->setFrameShadow(QFrame::Sunken); + vBoxLayout->addWidget(horizontalLine); + + m_chkShowOnStartup->setChecked(settings.showNewFormOnStartup()); + vBoxLayout->addWidget(m_chkShowOnStartup); + + m_buttonBox = createButtonBox(); + vBoxLayout->addWidget(m_buttonBox); + setLayout(vBoxLayout); + + resize(500, 400); + slotCurrentTemplateChanged(m_newFormWidget->hasCurrentTemplate()); +} + +QDialogButtonBox *NewForm::createButtonBox() +{ + // Dialog buttons with 'recent files' + QDialogButtonBox *buttonBox = new QDialogButtonBox; + buttonBox->addButton(QApplication::translate("NewForm", "&Close", 0, + QApplication::UnicodeUTF8), QDialogButtonBox::RejectRole); + buttonBox->addButton(m_createButton, QDialogButtonBox::AcceptRole); + buttonBox->addButton(QApplication::translate("NewForm", "&Open...", 0, + QApplication::UnicodeUTF8), QDialogButtonBox::ActionRole); + buttonBox->addButton(m_recentButton, QDialogButtonBox::ActionRole); + + QDesignerActions *da = m_workbench->actionManager(); + + QMenu *recentFilesMenu = new QMenu(tr("&Recent Forms"), m_recentButton); + // Pop the "Recent Files" stuff in here. + const QList recentActions = da->recentFilesActions()->actions(); + if (!recentActions.empty()) { + const QList::const_iterator acend = recentActions.constEnd(); + for (QList::const_iterator it = recentActions.constBegin(); it != acend; ++it) { + recentFilesMenu->addAction(*it); + connect(*it, SIGNAL(triggered()), this, SLOT(recentFileChosen())); + } + } + m_recentButton->setMenu(recentFilesMenu); + connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(slotButtonBoxClicked(QAbstractButton*))); + return buttonBox; +} + +NewForm::~NewForm() +{ + QDesignerSettings settings (m_workbench->core()); + settings.setShowNewFormOnStartup(m_chkShowOnStartup->isChecked()); +} + +void NewForm::recentFileChosen() +{ + QAction *action = qobject_cast(sender()); + if (!action) + return; + if (action->objectName() == QLatin1String("__qt_action_clear_menu_")) + return; + close(); +} + +void NewForm::slotCurrentTemplateChanged(bool templateSelected) +{ + if (templateSelected) { + m_createButton->setEnabled(true); + m_createButton->setDefault(true); + } else { + m_createButton->setEnabled(false); + } +} + +void NewForm::slotTemplateActivated() +{ + m_createButton->animateClick(0); +} + +void NewForm::slotButtonBoxClicked(QAbstractButton *btn) +{ + switch (m_buttonBox->buttonRole(btn)) { + case QDialogButtonBox::RejectRole: + reject(); + break; + case QDialogButtonBox::ActionRole: + if (btn != m_recentButton) { + m_fileName.clear(); + if (m_workbench->actionManager()->openForm(this)) + accept(); + } + break; + case QDialogButtonBox::AcceptRole: { + QString errorMessage; + if (openTemplate(&errorMessage)) { + accept(); + } else { + QMessageBox::warning(this, tr("Read error"), errorMessage); + } + } + break; + default: + break; + } +} + +bool NewForm::openTemplate(QString *ptrToErrorMessage) +{ + const QString contents = m_newFormWidget->currentTemplate(ptrToErrorMessage); + if (contents.isEmpty()) + return false; + // Write to temporary file and open + QString tempPattern = QDir::tempPath(); + if (!tempPattern.endsWith(QDir::separator())) // platform-dependant + tempPattern += QDir::separator(); + tempPattern += QLatin1String("XXXXXX.ui"); + QTemporaryFile tempFormFile(tempPattern); + + tempFormFile.setAutoRemove(true); + if (!tempFormFile.open()) { + *ptrToErrorMessage = tr("A temporary form file could not be created in %1.").arg(QDir::tempPath()); + return false; + } + const QString tempFormFileName = tempFormFile.fileName(); + tempFormFile.write(contents.toUtf8()); + if (!tempFormFile.flush()) { + *ptrToErrorMessage = tr("The temporary form file %1 could not be written.").arg(tempFormFileName); + return false; + } + tempFormFile.close(); + return m_workbench->openTemplate(tempFormFileName, m_fileName, ptrToErrorMessage); +} + +QImage NewForm::grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp) +{ + return qdesigner_internal::NewFormWidget::grabForm(core, file, workingDir, dp); +} + +QT_END_NAMESPACE diff --git a/designer/designer/newform.h b/designer/designer/newform.h new file mode 100644 index 0000000..c270622 --- /dev/null +++ b/designer/designer/newform.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWFORM_H +#define NEWFORM_H + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + class DeviceProfile; +} + +class QDesignerFormEditorInterface; +class QDesignerNewFormWidgetInterface; +class QDesignerWorkbench; + +class QCheckBox; +class QAbstractButton; +class QPushButton; +class QDialogButtonBox; +class QImage; +class QIODevice; + +class NewForm: public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY(NewForm) + +public: + NewForm(QDesignerWorkbench *workbench, + QWidget *parentWidget, + // Use that file name instead of a temporary one + const QString &fileName = QString()); + + virtual ~NewForm(); + + // Convenience for implementing file dialogs with preview + static QImage grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp); + +private slots: + void slotButtonBoxClicked(QAbstractButton *btn); + void recentFileChosen(); + void slotCurrentTemplateChanged(bool templateSelected); + void slotTemplateActivated(); + +private: + QDialogButtonBox *createButtonBox(); + bool openTemplate(QString *ptrToErrorMessage); + + QString m_fileName; + QDesignerNewFormWidgetInterface *m_newFormWidget; + QDesignerWorkbench *m_workbench; + QCheckBox *m_chkShowOnStartup; + QPushButton *m_createButton; + QPushButton *m_recentButton; + QDialogButtonBox *m_buttonBox; +}; + +QT_END_NAMESPACE + +#endif // NEWFORM_H diff --git a/designer/designer/preferencesdialog.cpp b/designer/designer/preferencesdialog.cpp new file mode 100644 index 0000000..2613556 --- /dev/null +++ b/designer/designer/preferencesdialog.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "preferencesdialog.h" +#include "ui_preferencesdialog.h" +#include "qdesigner_appearanceoptions.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +PreferencesDialog::PreferencesDialog(QDesignerFormEditorInterface *core, QWidget *parentWidget) : + QDialog(parentWidget), + m_ui(new Ui::PreferencesDialog()), + m_core(core) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->setupUi(this); + + m_optionsPages = core->optionsPages(); + + m_ui->m_optionTabWidget->clear(); + foreach (QDesignerOptionsPageInterface *optionsPage, m_optionsPages) { + QWidget *page = optionsPage->createPage(this); + m_ui->m_optionTabWidget->addTab(page, optionsPage->name()); + if (QDesignerAppearanceOptionsWidget *appearanceWidget = qobject_cast(page)) + connect(appearanceWidget, SIGNAL(uiModeChanged(bool)), this, SLOT(slotUiModeChanged(bool))); + } + + connect(m_ui->m_dialogButtonBox, SIGNAL(rejected()), this, SLOT(slotRejected())); + connect(m_ui->m_dialogButtonBox, SIGNAL(accepted()), this, SLOT(slotAccepted())); + connect(applyButton(), SIGNAL(clicked()), this, SLOT(slotApply())); +} + +PreferencesDialog::~PreferencesDialog() +{ + delete m_ui; +} + +QPushButton *PreferencesDialog::applyButton() const +{ + return m_ui->m_dialogButtonBox->button(QDialogButtonBox::Apply); +} + +void PreferencesDialog::slotApply() +{ + foreach (QDesignerOptionsPageInterface *optionsPage, m_optionsPages) + optionsPage->apply(); +} + +void PreferencesDialog::slotAccepted() +{ + slotApply(); + closeOptionPages(); + accept(); +} + +void PreferencesDialog::slotRejected() +{ + closeOptionPages(); + reject(); +} + +void PreferencesDialog::slotUiModeChanged(bool modified) +{ + // Cannot "apply" a ui mode change (destroy the dialogs parent) + applyButton()->setEnabled(!modified); +} + +void PreferencesDialog::closeOptionPages() +{ + foreach (QDesignerOptionsPageInterface *optionsPage, m_optionsPages) + optionsPage->finish(); +} + +QT_END_NAMESPACE diff --git a/designer/designer/preferencesdialog.h b/designer/designer/preferencesdialog.h new file mode 100644 index 0000000..5df0df4 --- /dev/null +++ b/designer/designer/preferencesdialog.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include + +QT_BEGIN_NAMESPACE + +class QPushButton; +class QDesignerFormEditorInterface; +class QDesignerOptionsPageInterface; + +namespace Ui { + class PreferencesDialog; +} + +class PreferencesDialog: public QDialog +{ + Q_OBJECT +public: + explicit PreferencesDialog(QDesignerFormEditorInterface *core, QWidget *parentWidget = 0); + ~PreferencesDialog(); + + +private slots: + void slotAccepted(); + void slotRejected(); + void slotApply(); + void slotUiModeChanged(bool modified); + +private: + QPushButton *applyButton() const; + void closeOptionPages(); + + Ui::PreferencesDialog *m_ui; + QDesignerFormEditorInterface *m_core; + QList m_optionsPages; +}; + +QT_END_NAMESPACE + +#endif // PREFERENCESDIALOG_H diff --git a/designer/designer/preferencesdialog.ui b/designer/designer/preferencesdialog.ui new file mode 100644 index 0000000..28d14bb --- /dev/null +++ b/designer/designer/preferencesdialog.ui @@ -0,0 +1,91 @@ + + + PreferencesDialog + + + + 0 + 0 + 474 + 304 + + + + + 0 + 0 + + + + Preferences + + + true + + + + + + + 0 + 0 + + + + 0 + + + + Tab 1 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Apply|QDialogButtonBox::Ok + + + + + + + + + m_dialogButtonBox + accepted() + PreferencesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + m_dialogButtonBox + rejected() + PreferencesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/designer/qdesigner.cpp b/designer/designer/qdesigner.cpp new file mode 100644 index 0000000..def7bcf --- /dev/null +++ b/designer/designer/qdesigner.cpp @@ -0,0 +1,323 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// designer +#include "qdesigner.h" +#include "qdesigner_actions.h" +#include "qdesigner_server.h" +#include "qdesigner_settings.h" +#include "qdesigner_workbench.h" +#include "mainwindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static const char *designerApplicationName = "Designer"; +static const char *designerWarningPrefix = "Designer: "; + +static void designerMessageHandler(QtMsgType type, const char *msg) +{ + // Only Designer warnings are displayed as box + QDesigner *designerApp = qDesigner; + if (type != QtWarningMsg || !designerApp || qstrncmp(designerWarningPrefix, msg, qstrlen(designerWarningPrefix))) { + qInstallMsgHandler(0); + qt_message_output(type, msg); + qInstallMsgHandler (designerMessageHandler); + return; + } + designerApp->showErrorMessage(msg); +} + +QDesigner::QDesigner(int &argc, char **argv) + : QApplication(argc, argv), + m_server(0), + m_client(0), + m_workbench(0), m_suppressNewFormShow(false) +{ + setOrganizationName(QLatin1String("Trolltech")); + setApplicationName(QLatin1String(designerApplicationName)); + QDesignerComponents::initializeResources(); + + initialize(); +} + +QDesigner::~QDesigner() +{ + if (m_workbench) { + delete m_workbench; + } + if (m_server) { + delete m_server; + } + if (m_client) { + delete m_client; + } +} + +void QDesigner::showErrorMessage(const char *message) +{ + // strip the prefix + const QString qMessage = QString::fromUtf8(message + qstrlen(designerWarningPrefix)); + // If there is no main window yet, just store the message. + // The QErrorMessage would otherwise be hidden by the main window. + if (m_mainWindow) { + showErrorMessageBox(qMessage); + } else { + qInstallMsgHandler(0); + qt_message_output(QtWarningMsg, message); // just in case we crash + qInstallMsgHandler (designerMessageHandler); + m_initializationErrors += qMessage; + m_initializationErrors += QLatin1Char('\n'); + } +} + +void QDesigner::showErrorMessageBox(const QString &msg) +{ + // Manually suppress consecutive messages. + // This happens if for example sth is wrong with custom widget creation. + // The same warning will be displayed by Widget box D&D and form Drop + // while trying to create instance. + if (m_errorMessageDialog && m_lastErrorMessage == msg) { + return; + } + + if (!m_errorMessageDialog) { + m_lastErrorMessage.clear(); + m_errorMessageDialog = new QErrorMessage(m_mainWindow); + const QString title = QCoreApplication::translate("QDesigner", "%1 - warning").arg(QLatin1String(designerApplicationName)); + m_errorMessageDialog->setWindowTitle(title); + m_errorMessageDialog->setMinimumSize(QSize(600, 250)); + m_errorMessageDialog->setWindowFlags(m_errorMessageDialog->windowFlags() & ~Qt::WindowContextHelpButtonHint); + } + m_errorMessageDialog->showMessage(msg); + m_lastErrorMessage = msg; +} + +QDesignerWorkbench *QDesigner::workbench() const +{ + return m_workbench; +} + +QDesignerServer *QDesigner::server() const +{ + return m_server; +} + +bool QDesigner::parseCommandLineArgs(QStringList &fileNames, QString &resourceDir) +{ + const QStringList args = arguments(); + const QStringList::const_iterator acend = args.constEnd(); + QStringList::const_iterator it = args.constBegin(); + for (++it; it != acend; ++it) { + const QString &argument = *it; + do { + // Arguments + if (!argument.startsWith(QLatin1Char('-'))) { + if (!fileNames.contains(argument)) { + fileNames.append(argument); + } + break; + } + // Options + if (argument == QLatin1String("-server")) { + m_server = new QDesignerServer(); + printf("%d\n", m_server->serverPort()); + fflush(stdout); + break; + } + if (argument == QLatin1String("-client")) { + bool ok = true; + if (++it == acend) { + qWarning("** WARNING The option -client requires an argument"); + return false; + } + const quint16 port = it->toUShort(&ok); + if (ok) { + m_client = new QDesignerClient(port, this); + } else { + qWarning("** WARNING Non-numeric argument specified for -client"); + return false; + } + break; + } + if (argument == QLatin1String("-resourcedir")) { + if (++it == acend) { + qWarning("** WARNING The option -resourcedir requires an argument"); + return false; + } + resourceDir = QFile::decodeName(it->toLocal8Bit()); + break; + } + if (argument == QLatin1String("-enableinternaldynamicproperties")) { + QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(true); + break; + } + const QString msg = QString::fromUtf8("** WARNING Unknown option %1").arg(argument); + qWarning("%s", qPrintable(msg)); + } while (false); + } + return true; +} + +void QDesigner::initialize() +{ + // initialize the sub components + QStringList files; + QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + parseCommandLineArgs(files, resourceDir); + + QTranslator *translator = new QTranslator(this); + translator->load(":/trolltech/designer/images/designer_zh_CN.qm"); + this->installTranslator(translator); + + if (QLibraryInfo::licensedProducts() == QLatin1String("Console")) { + QMessageBox::information(0, tr("Qt Designer"), + tr("This application cannot be used for the Console edition of Qt")); + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + return; + } + + m_workbench = new QDesignerWorkbench(); + + emit initialized(); + qInstallMsgHandler(designerMessageHandler); // Warn when loading faulty forms + + m_suppressNewFormShow = m_workbench->readInBackup(); + + if (!files.empty()) { + const QStringList::const_iterator cend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != cend; ++it) { + // Ensure absolute paths for recent file list to be unique + QString fileName = *it; + const QFileInfo fi(fileName); + if (fi.exists() && fi.isRelative()) { + fileName = fi.absoluteFilePath(); + } + m_workbench->readInForm(fileName); + } + } + if ( m_workbench->formWindowCount()) { + m_suppressNewFormShow = true; + } + + // Show up error box with parent now if something went wrong + if (m_initializationErrors.isEmpty()) { + if (!m_suppressNewFormShow && QDesignerSettings(m_workbench->core()).showNewFormOnStartup()) { + QTimer::singleShot(100, this, SLOT(callCreateForm())); // won't show anything if suppressed + } + } else { + showErrorMessageBox(m_initializationErrors); + m_initializationErrors.clear(); + } +} + +bool QDesigner::event(QEvent *ev) +{ + bool eaten; + switch (ev->type()) { + case QEvent::FileOpen: + // Set it true first since, if it's a Qt 3 form, the messagebox from convert will fire the timer. + m_suppressNewFormShow = true; + if (!m_workbench->readInForm(static_cast(ev)->file())) { + m_suppressNewFormShow = false; + } + eaten = true; + break; + case QEvent::Close: { + QCloseEvent *closeEvent = static_cast(ev); + closeEvent->setAccepted(m_workbench->handleClose()); + if (closeEvent->isAccepted()) { + // We're going down, make sure that we don't get our settings saved twice. + if (m_mainWindow) { + m_mainWindow->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents); + } + eaten = QApplication::event(ev); + } + eaten = true; + break; + } + default: + eaten = QApplication::event(ev); + break; + } + return eaten; +} + +void QDesigner::setMainWindow(MainWindowBase *tw) +{ + m_mainWindow = tw; +} + +MainWindowBase *QDesigner::mainWindow() const +{ + return m_mainWindow; +} + +void QDesigner::callCreateForm() +{ + if (!m_suppressNewFormShow) { + QFile file("quc.ui"); + if (file.exists()) { + m_workbench->actionManager()->readInForm("quc.ui"); + } else { + m_workbench->actionManager()->createForm(); + } + } +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner.h b/designer/designer/qdesigner.h new file mode 100644 index 0000000..fa2e160 --- /dev/null +++ b/designer/designer/qdesigner.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_H +#define QDESIGNER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +#define qDesigner \ + (static_cast(QCoreApplication::instance())) + +class QDesignerWorkbench; +class QDesignerToolWindow; +class MainWindowBase; +class QDesignerServer; +class QDesignerClient; +class QErrorMessage; + +class QDesigner: public QApplication +{ + Q_OBJECT +public: + QDesigner(int &argc, char **argv); + virtual ~QDesigner(); + + QDesignerWorkbench *workbench() const; + QDesignerServer *server() const; + MainWindowBase *mainWindow() const; + void setMainWindow(MainWindowBase *tw); + +protected: + bool event(QEvent *ev); + +signals: + void initialized(); + +public slots: + void showErrorMessage(const char *message); + +private slots: + void initialize(); + void callCreateForm(); + +private: + bool parseCommandLineArgs(QStringList &fileNames, QString &resourceDir); + void showErrorMessageBox(const QString &); + + QDesignerServer *m_server; + QDesignerClient *m_client; + QDesignerWorkbench *m_workbench; + QPointer m_mainWindow; + QPointer m_errorMessageDialog; + + QString m_initializationErrors; + QString m_lastErrorMessage; + bool m_suppressNewFormShow; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_H diff --git a/designer/designer/qdesigner_actions.cpp b/designer/designer/qdesigner_actions.cpp new file mode 100644 index 0000000..9208894 --- /dev/null +++ b/designer/designer/qdesigner_actions.cpp @@ -0,0 +1,1437 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_actions.h" +#include "designer_enums.h" +#include "qdesigner.h" +#include "qdesigner_workbench.h" +#include "qdesigner_formwindow.h" +#include "newform.h" +#include "versiondialog.h" +#include "saveformastemplate.h" +#include "qdesigner_toolwindow.h" +#include "preferencesdialog.h" +#include "appfontdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdesigner_integration_p.h" + +// sdk +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +const char *QDesignerActions::defaultToolbarPropertyName = "__qt_defaultToolBarAction"; + +//#ifdef Q_WS_MAC +# define NONMODAL_PREVIEW +//#endif + +static QAction *createSeparator(QObject *parent) { + QAction * rc = new QAction(parent); + rc->setSeparator(true); + return rc; +} + +static QActionGroup *createActionGroup(QObject *parent, bool exclusive = false) { + QActionGroup * rc = new QActionGroup(parent); + rc->setExclusive(exclusive); + return rc; +} + +static inline QString savedMessage(const QString &fileName) +{ + return QDesignerActions::tr("Saved %1.").arg(fileName); +} + +// Prompt for a file and make sure an extension is added +// unless the user explicitly specifies another one. + +static QString getSaveFileNameWithExtension(QWidget *parent, const QString &title, QString dir, const QString &filter, const QString &extension) +{ + const QChar dot = QLatin1Char('.'); + + QString saveFile; + while (true) { + saveFile = QFileDialog::getSaveFileName(parent, title, dir, filter, 0, QFileDialog::DontConfirmOverwrite); + if (saveFile.isEmpty()) + return saveFile; + + const QFileInfo fInfo(saveFile); + if (fInfo.suffix().isEmpty() && !fInfo.fileName().endsWith(dot)) { + saveFile += dot; + saveFile += extension; + } + + const QFileInfo fi(saveFile); + if (!fi.exists()) + break; + + const QString prompt = QDesignerActions::tr("%1 already exists.\nDo you want to replace it?").arg(fi.fileName()); + if (QMessageBox::warning(parent, title, prompt, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + break; + + dir = saveFile; + } + return saveFile; +} + +QDesignerActions::QDesignerActions(QDesignerWorkbench *workbench) + : QObject(workbench), + m_workbench(workbench), + m_core(workbench->core()), + m_settings(workbench->core()), + m_backupTimer(new QTimer(this)), + m_fileActions(createActionGroup(this)), + m_recentFilesActions(createActionGroup(this)), + m_editActions(createActionGroup(this)), + m_formActions(createActionGroup(this)), + m_settingsActions(createActionGroup(this)), + m_windowActions(createActionGroup(this)), + m_toolActions(createActionGroup(this, true)), + m_helpActions(0), + m_styleActions(0), + m_editWidgetsAction(new QAction(tr("Edit Widgets"), this)), + m_newFormAction(new QAction(qdesigner_internal::createIconSet(QLatin1String("filenew.png")), tr("&New..."), this)), + m_openFormAction(new QAction(qdesigner_internal::createIconSet(QLatin1String("fileopen.png")), tr("&Open..."), this)), + m_saveFormAction(new QAction(qdesigner_internal::createIconSet(QLatin1String("filesave.png")), tr("&Save"), this)), + m_saveFormAsAction(new QAction(tr("Save &As..."), this)), + m_saveAllFormsAction(new QAction(tr("Save A&ll"), this)), + m_saveFormAsTemplateAction(new QAction(tr("Save As &Template..."), this)), + m_closeFormAction(new QAction(tr("&Close"), this)), + m_savePreviewImageAction(new QAction(tr("Save &Image..."), this)), + m_printPreviewAction(new QAction(tr("&Print..."), this)), + m_quitAction(new QAction(tr("&Quit"), this)), + m_previewFormAction(0), + m_viewCodeAction(new QAction(tr("View &Code..."), this)), + m_minimizeAction(new QAction(tr("&Minimize"), this)), + m_bringAllToFrontSeparator(createSeparator(this)), + m_bringAllToFrontAction(new QAction(tr("Bring All to Front"), this)), + m_windowListSeparatorAction(createSeparator(this)), + m_preferencesAction(new QAction(tr("Preferences..."), this)), + m_appFontAction(new QAction(tr("Additional Fonts..."), this)), + m_appFontDialog(0), +#ifndef QT_NO_PRINTER + m_printer(0), +#endif + m_previewManager(0) +{ +#ifdef Q_WS_X11 + m_newFormAction->setIcon(QIcon::fromTheme("document-new", m_newFormAction->icon())); + m_openFormAction->setIcon(QIcon::fromTheme("document-open", m_openFormAction->icon())); + m_saveFormAction->setIcon(QIcon::fromTheme("document-save", m_saveFormAction->icon())); + m_saveFormAsAction->setIcon(QIcon::fromTheme("document-save-as", m_saveFormAsAction->icon())); + m_printPreviewAction->setIcon(QIcon::fromTheme("document-print", m_printPreviewAction->icon())); + m_closeFormAction->setIcon(QIcon::fromTheme("window-close", m_closeFormAction->icon())); + m_quitAction->setIcon(QIcon::fromTheme("application-exit", m_quitAction->icon())); +#endif + + Q_ASSERT(m_core != 0); + qdesigner_internal::QDesignerFormWindowManager *ifwm = qobject_cast(m_core->formWindowManager()); + Q_ASSERT(ifwm); + m_previewManager = ifwm->previewManager(); + m_previewFormAction = ifwm->actionDefaultPreview(); + m_styleActions = ifwm->actionGroupPreviewInStyle(); + connect(ifwm, SIGNAL(formWindowSettingsChanged(QDesignerFormWindowInterface*)), + this, SLOT(formWindowSettingsChanged(QDesignerFormWindowInterface*))); + + m_editWidgetsAction->setObjectName(QLatin1String("__qt_edit_widgets_action")); + m_newFormAction->setObjectName(QLatin1String("__qt_new_form_action")); + m_openFormAction->setObjectName(QLatin1String("__qt_open_form_action")); + m_saveFormAction->setObjectName(QLatin1String("__qt_save_form_action")); + m_saveFormAsAction->setObjectName(QLatin1String("__qt_save_form_as_action")); + m_saveAllFormsAction->setObjectName(QLatin1String("__qt_save_all_forms_action")); + m_saveFormAsTemplateAction->setObjectName(QLatin1String("__qt_save_form_as_template_action")); + m_closeFormAction->setObjectName(QLatin1String("__qt_close_form_action")); + m_quitAction->setObjectName(QLatin1String("__qt_quit_action")); + m_previewFormAction->setObjectName(QLatin1String("__qt_preview_form_action")); + m_viewCodeAction->setObjectName(QLatin1String("__qt_preview_code_action")); + m_minimizeAction->setObjectName(QLatin1String("__qt_minimize_action")); + m_bringAllToFrontAction->setObjectName(QLatin1String("__qt_bring_all_to_front_action")); + m_preferencesAction->setObjectName(QLatin1String("__qt_preferences_action")); + + m_helpActions = createHelpActions(); + + m_newFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + m_openFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + m_saveFormAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + + QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager(); + Q_ASSERT(formWindowManager != 0); + +// +// file actions +// + m_newFormAction->setShortcut(QKeySequence::New); + connect(m_newFormAction, SIGNAL(triggered()), this, SLOT(createForm())); + m_fileActions->addAction(m_newFormAction); + + m_openFormAction->setShortcut(QKeySequence::Open); + connect(m_openFormAction, SIGNAL(triggered()), this, SLOT(slotOpenForm())); + m_fileActions->addAction(m_openFormAction); + + m_fileActions->addAction(createRecentFilesMenu()); + m_fileActions->addAction(createSeparator(this)); + + m_saveFormAction->setShortcut(QKeySequence::Save); + connect(m_saveFormAction, SIGNAL(triggered()), this, SLOT(saveForm())); + m_fileActions->addAction(m_saveFormAction); + + connect(m_saveFormAsAction, SIGNAL(triggered()), this, SLOT(saveFormAs())); + m_fileActions->addAction(m_saveFormAsAction); + +#ifdef Q_OS_MAC + m_saveAllFormsAction->setShortcut(tr("ALT+CTRL+S")); +#else + m_saveAllFormsAction->setShortcut(tr("CTRL+SHIFT+S")); // Commonly "Save As" on Mac +#endif + connect(m_saveAllFormsAction, SIGNAL(triggered()), this, SLOT(saveAllForms())); + m_fileActions->addAction(m_saveAllFormsAction); + + connect(m_saveFormAsTemplateAction, SIGNAL(triggered()), this, SLOT(saveFormAsTemplate())); + m_fileActions->addAction(m_saveFormAsTemplateAction); + + m_fileActions->addAction(createSeparator(this)); + + m_printPreviewAction->setShortcut(QKeySequence::Print); + connect(m_printPreviewAction, SIGNAL(triggered()), this, SLOT(printPreviewImage())); + m_fileActions->addAction(m_printPreviewAction); + m_printPreviewAction->setObjectName(QLatin1String("__qt_print_action")); + + connect(m_savePreviewImageAction, SIGNAL(triggered()), this, SLOT(savePreviewImage())); + m_savePreviewImageAction->setObjectName(QLatin1String("__qt_saveimage_action")); + m_fileActions->addAction(m_savePreviewImageAction); + m_fileActions->addAction(createSeparator(this)); + + m_closeFormAction->setShortcut(QKeySequence::Close); + connect(m_closeFormAction, SIGNAL(triggered()), this, SLOT(closeForm())); + m_fileActions->addAction(m_closeFormAction); + updateCloseAction(); + + m_fileActions->addAction(createSeparator(this)); + + m_quitAction->setShortcuts(QKeySequence::Quit); + m_quitAction->setMenuRole(QAction::QuitRole); + connect(m_quitAction, SIGNAL(triggered()), this, SLOT(shutdown())); + m_fileActions->addAction(m_quitAction); + +// +// edit actions +// + QAction *undoAction = formWindowManager->actionUndo(); + undoAction->setObjectName(QLatin1String("__qt_undo_action")); + undoAction->setShortcut(QKeySequence::Undo); + m_editActions->addAction(undoAction); + + QAction *redoAction = formWindowManager->actionRedo(); + redoAction->setObjectName(QLatin1String("__qt_redo_action")); + redoAction->setShortcut(QKeySequence::Redo); + m_editActions->addAction(redoAction); + + m_editActions->addAction(createSeparator(this)); + + m_editActions->addAction(formWindowManager->actionCut()); + m_editActions->addAction(formWindowManager->actionCopy()); + m_editActions->addAction(formWindowManager->actionPaste()); + m_editActions->addAction(formWindowManager->actionDelete()); + + m_editActions->addAction(formWindowManager->actionSelectAll()); + + m_editActions->addAction(createSeparator(this)); + + m_editActions->addAction(formWindowManager->actionLower()); + m_editActions->addAction(formWindowManager->actionRaise()); + + formWindowManager->actionLower()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionRaise()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + +// +// edit mode actions +// + + m_editWidgetsAction->setCheckable(true); + QList shortcuts; + shortcuts.append(QKeySequence(Qt::Key_F3)); +#if QT_VERSION >= 0x040900 // "ESC" switching to edit mode: Activate once item delegates handle shortcut overrides for ESC. + shortcuts.append(QKeySequence(Qt::Key_Escape)); +#endif + m_editWidgetsAction->setShortcuts(shortcuts); + QIcon fallback(m_core->resourceLocation() + QLatin1String("/widgettool.png")); + m_editWidgetsAction->setIcon(QIcon::fromTheme("designer-edit-widget", fallback)); + connect(m_editWidgetsAction, SIGNAL(triggered()), this, SLOT(editWidgetsSlot())); + m_editWidgetsAction->setChecked(true); + m_editWidgetsAction->setEnabled(false); + m_editWidgetsAction->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + m_toolActions->addAction(m_editWidgetsAction); + + connect(formWindowManager, SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface*))); + + QList builtinPlugins = QPluginLoader::staticInstances(); + builtinPlugins += m_core->pluginManager()->instances(); + foreach (QObject *plugin, builtinPlugins) { + if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast(plugin)) { + if (QAction *action = formEditorPlugin->action()) { + m_toolActions->addAction(action); + action->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + action->setCheckable(true); + } + } + } + + connect(m_preferencesAction, SIGNAL(triggered()), this, SLOT(showPreferencesDialog())); + m_preferencesAction->setMenuRole(QAction::PreferencesRole); + m_settingsActions->addAction(m_preferencesAction); + + connect(m_appFontAction, SIGNAL(triggered()), this, SLOT(showAppFontDialog())); + m_appFontAction->setMenuRole(QAction::PreferencesRole); + m_settingsActions->addAction(m_appFontAction); +// +// form actions +// + + m_formActions->addAction(formWindowManager->actionHorizontalLayout()); + m_formActions->addAction(formWindowManager->actionVerticalLayout()); + m_formActions->addAction(formWindowManager->actionSplitHorizontal()); + m_formActions->addAction(formWindowManager->actionSplitVertical()); + m_formActions->addAction(formWindowManager->actionGridLayout()); + m_formActions->addAction(formWindowManager->actionFormLayout()); + m_formActions->addAction(formWindowManager->actionBreakLayout()); + m_formActions->addAction(formWindowManager->actionAdjustSize()); + m_formActions->addAction(formWindowManager->actionSimplifyLayout()); + m_formActions->addAction(createSeparator(this)); + + formWindowManager->actionHorizontalLayout()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionVerticalLayout()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionSplitHorizontal()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionSplitVertical()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionGridLayout()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionFormLayout()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionBreakLayout()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + formWindowManager->actionAdjustSize()->setProperty(QDesignerActions::defaultToolbarPropertyName, true); + + m_previewFormAction->setShortcut(tr("CTRL+R")); + m_formActions->addAction(m_previewFormAction); + connect(m_previewManager, SIGNAL(firstPreviewOpened()), this, SLOT(updateCloseAction())); + connect(m_previewManager, SIGNAL(lastPreviewClosed()), this, SLOT(updateCloseAction())); + + connect(m_viewCodeAction, SIGNAL(triggered()), this, SLOT(viewCode())); + // Preview code only in Cpp + if (qt_extension(m_core->extensionManager(), m_core) == 0) + m_formActions->addAction(m_viewCodeAction); + + m_formActions->addAction(createSeparator(this)); + + m_formActions->addAction(ifwm->actionShowFormWindowSettingsDialog()); +// +// window actions +// + m_minimizeAction->setEnabled(false); + m_minimizeAction->setCheckable(true); + m_minimizeAction->setShortcut(tr("CTRL+M")); + connect(m_minimizeAction, SIGNAL(triggered()), m_workbench, SLOT(toggleFormMinimizationState())); + m_windowActions->addAction(m_minimizeAction); + + m_windowActions->addAction(m_bringAllToFrontSeparator); + connect(m_bringAllToFrontAction, SIGNAL(triggered()), m_workbench, SLOT(bringAllToFront())); + m_windowActions->addAction(m_bringAllToFrontAction); + m_windowActions->addAction(m_windowListSeparatorAction); + + setWindowListSeparatorVisible(false); + +// +// connections +// + fixActionContext(); + activeFormWindowChanged(core()->formWindowManager()->activeFormWindow()); + + m_backupTimer->start(180000); // 3min + connect(m_backupTimer, SIGNAL(timeout()), this, SLOT(backupForms())); + + // Enable application font action + connect(formWindowManager, SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), this, SLOT(formWindowCountChanged())); + connect(formWindowManager, SIGNAL(formWindowRemoved(QDesignerFormWindowInterface*)), this, SLOT(formWindowCountChanged())); + formWindowCountChanged(); +} + +QActionGroup *QDesignerActions::createHelpActions() +{ + QActionGroup *helpActions = createActionGroup(this); + +#ifndef QT_JAMBI_BUILD + QAction *mainHelpAction = new QAction(tr("Qt Designer &Help"), this); + mainHelpAction->setObjectName(QLatin1String("__qt_designer_help_action")); + connect(mainHelpAction, SIGNAL(triggered()), this, SLOT(showDesignerHelp())); + mainHelpAction->setShortcut(Qt::CTRL + Qt::Key_Question); + helpActions->addAction(mainHelpAction); + + helpActions->addAction(createSeparator(this)); + QAction *widgetHelp = new QAction(tr("Current Widget Help"), this); + widgetHelp->setObjectName(QLatin1String("__qt_current_widget_help_action")); + widgetHelp->setShortcut(Qt::Key_F1); + connect(widgetHelp, SIGNAL(triggered()), this, SLOT(showWidgetSpecificHelp())); + helpActions->addAction(widgetHelp); + + helpActions->addAction(createSeparator(this)); + QAction *whatsNewAction = new QAction(tr("What's New in Qt Designer?"), this); + whatsNewAction->setObjectName(QLatin1String("__qt_whats_new_in_qt_designer_action")); + connect(whatsNewAction, SIGNAL(triggered()), this, SLOT(showWhatsNew())); + helpActions->addAction(whatsNewAction); +#endif + + helpActions->addAction(createSeparator(this)); + QAction *aboutPluginsAction = new QAction(tr("About Plugins"), this); + aboutPluginsAction->setObjectName(QLatin1String("__qt_about_plugins_action")); + aboutPluginsAction->setMenuRole(QAction::ApplicationSpecificRole); + connect(aboutPluginsAction, SIGNAL(triggered()), m_core->formWindowManager(), SLOT(aboutPlugins())); + helpActions->addAction(aboutPluginsAction); + + QAction *aboutDesignerAction = new QAction(tr("About Qt Designer"), this); + aboutDesignerAction->setMenuRole(QAction::AboutRole); + aboutDesignerAction->setObjectName(QLatin1String("__qt_about_designer_action")); + connect(aboutDesignerAction, SIGNAL(triggered()), this, SLOT(aboutDesigner())); + helpActions->addAction(aboutDesignerAction); + + QAction *aboutQtAction = new QAction(tr("About Qt"), this); + aboutQtAction->setMenuRole(QAction::AboutQtRole); + aboutQtAction->setObjectName(QLatin1String("__qt_about_qt_action")); + connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); + helpActions->addAction(aboutQtAction); + return helpActions; +} + +QDesignerActions::~QDesignerActions() +{ +#ifndef QT_NO_PRINTER + delete m_printer; +#endif +} + +QString QDesignerActions::uiExtension() const +{ + QDesignerLanguageExtension *lang + = qt_extension(m_core->extensionManager(), m_core); + if (lang) + return lang->uiExtension(); + return QLatin1String("ui"); +} + +QAction *QDesignerActions::createRecentFilesMenu() +{ + QMenu *menu = new QMenu; + QAction *act; + // Need to insert this into the QAction. + for (int i = 0; i < MaxRecentFiles; ++i) { + act = new QAction(this); + act->setVisible(false); + connect(act, SIGNAL(triggered()), this, SLOT(openRecentForm())); + m_recentFilesActions->addAction(act); + menu->addAction(act); + } + updateRecentFileActions(); + menu->addSeparator(); + act = new QAction(QIcon::fromTheme("edit-clear"), tr("Clear &Menu"), this); + act->setObjectName(QLatin1String("__qt_action_clear_menu_")); + connect(act, SIGNAL(triggered()), this, SLOT(clearRecentFiles())); + m_recentFilesActions->addAction(act); + menu->addAction(act); + + act = new QAction(QIcon::fromTheme("document-open-recent"), tr("&Recent Forms"), this); + act->setMenu(menu); + return act; +} + +QActionGroup *QDesignerActions::toolActions() const +{ return m_toolActions; } + +QDesignerWorkbench *QDesignerActions::workbench() const +{ return m_workbench; } + +QDesignerFormEditorInterface *QDesignerActions::core() const +{ return m_core; } + +QActionGroup *QDesignerActions::fileActions() const +{ return m_fileActions; } + +QActionGroup *QDesignerActions::editActions() const +{ return m_editActions; } + +QActionGroup *QDesignerActions::formActions() const +{ return m_formActions; } + +QActionGroup *QDesignerActions::settingsActions() const +{ return m_settingsActions; } + +QActionGroup *QDesignerActions::windowActions() const +{ return m_windowActions; } + +QActionGroup *QDesignerActions::helpActions() const +{ return m_helpActions; } + +QActionGroup *QDesignerActions::styleActions() const +{ return m_styleActions; } + +QAction *QDesignerActions::previewFormAction() const +{ return m_previewFormAction; } + +QAction *QDesignerActions::viewCodeAction() const +{ return m_viewCodeAction; } + + +void QDesignerActions::editWidgetsSlot() +{ + QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager(); + for (int i=0; iformWindowCount(); ++i) { + QDesignerFormWindowInterface *formWindow = formWindowManager->formWindow(i); + formWindow->editWidgets(); + } +} + +void QDesignerActions::createForm() +{ + showNewFormDialog(QString()); +} + +void QDesignerActions::showNewFormDialog(const QString &fileName) +{ + closePreview(); + NewForm *dlg = new NewForm(workbench(), workbench()->core()->topLevel(), fileName); + + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setAttribute(Qt::WA_ShowModal); + + dlg->setGeometry(fixDialogRect(dlg->rect())); + dlg->exec(); +} + +void QDesignerActions::slotOpenForm() +{ + openForm(core()->topLevel()); +} + +bool QDesignerActions::openForm(QWidget *parent) +{ + closePreview(); + const QString extension = uiExtension(); + const QStringList fileNames = QFileDialog::getOpenFileNames(parent, tr("Open Form"), + m_openDirectory, tr("Designer UI files (*.%1);;All Files (*)").arg(extension), 0, QFileDialog::DontUseSheet); + + if (fileNames.isEmpty()) + return false; + + bool atLeastOne = false; + foreach (const QString &fileName, fileNames) { + if (readInForm(fileName) && !atLeastOne) + atLeastOne = true; + } + + return atLeastOne; +} + +bool QDesignerActions::saveFormAs(QDesignerFormWindowInterface *fw) +{ + const QString extension = uiExtension(); + + QString dir = fw->fileName(); + if (dir.isEmpty()) { + do { + // Build untitled name + if (!m_saveDirectory.isEmpty()) { + dir = m_saveDirectory; + break; + } + if (!m_openDirectory.isEmpty()) { + dir = m_openDirectory; + break; + } + dir = QDir::current().absolutePath(); + } while (false); + dir += QDir::separator(); + dir += QLatin1String("untitled."); + dir += extension; + } + + const QString saveFile = getSaveFileNameWithExtension(fw, tr("Save Form As"), dir, tr("Designer UI files (*.%1);;All Files (*)").arg(extension), extension); + if (saveFile.isEmpty()) + return false; + + fw->setFileName(saveFile); + return writeOutForm(fw, saveFile); +} + +void QDesignerActions::saveForm() +{ + if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) { + if (saveForm(fw)) + showStatusBarMessage(savedMessage(QFileInfo(fw->fileName()).fileName())); + } +} + +void QDesignerActions::saveAllForms() +{ + QString fileNames; + QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager(); + if (const int totalWindows = formWindowManager->formWindowCount()) { + const QString separator = QLatin1String(", "); + for (int i = 0; i < totalWindows; ++i) { + QDesignerFormWindowInterface *fw = formWindowManager->formWindow(i); + if (fw && fw->isDirty()) { + formWindowManager->setActiveFormWindow(fw); + if (saveForm(fw)) { + if (!fileNames.isEmpty()) + fileNames += separator; + fileNames += QFileInfo(fw->fileName()).fileName(); + } else { + break; + } + } + } + } + + if (!fileNames.isEmpty()) { + showStatusBarMessage(savedMessage(fileNames)); + } +} + +bool QDesignerActions::saveForm(QDesignerFormWindowInterface *fw) +{ + bool ret; + if (fw->fileName().isEmpty()) + ret = saveFormAs(fw); + else + ret = writeOutForm(fw, fw->fileName()); + return ret; +} + +void QDesignerActions::closeForm() +{ + if (m_previewManager->previewCount()) { + closePreview(); + return; + } + + if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) + if (QWidget *parent = fw->parentWidget()) { + if (QMdiSubWindow *mdiSubWindow = qobject_cast(parent->parentWidget())) { + mdiSubWindow->close(); + } else { + parent->close(); + } + } +} + +void QDesignerActions::saveFormAs() +{ + if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) { + if (saveFormAs(fw)) + showStatusBarMessage(savedMessage(fw->fileName())); + } +} + +void QDesignerActions::saveFormAsTemplate() +{ + if (QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow()) { + SaveFormAsTemplate dlg(core(), fw, fw->window()); + dlg.exec(); + } +} + +void QDesignerActions::notImplementedYet() +{ + QMessageBox::information(core()->topLevel(), tr("Designer"), tr("Feature not implemented yet!")); +} + +void QDesignerActions::closePreview() +{ + m_previewManager->closeAllPreviews(); +} + +void QDesignerActions::viewCode() +{ + QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow(); + if (!fw) + return; + QString errorMessage; + if (!qdesigner_internal::CodeDialog::showCodeDialog(fw, fw, &errorMessage)) + QMessageBox::warning(fw, tr("Code generation failed"), errorMessage); +} + +void QDesignerActions::fixActionContext() +{ + QList actions; + actions += m_fileActions->actions(); + actions += m_editActions->actions(); + actions += m_toolActions->actions(); + actions += m_formActions->actions(); + actions += m_windowActions->actions(); + actions += m_helpActions->actions(); + + foreach (QAction *a, actions) { + a->setShortcutContext(Qt::ApplicationShortcut); + } +} + +bool QDesignerActions::readInForm(const QString &fileName) +{ + QString fn = fileName; + + // First make sure that we don't have this one open already. + QDesignerFormWindowManagerInterface *formWindowManager = core()->formWindowManager(); + const int totalWindows = formWindowManager->formWindowCount(); + for (int i = 0; i < totalWindows; ++i) { + QDesignerFormWindowInterface *w = formWindowManager->formWindow(i); + if (w->fileName() == fn) { + w->raise(); + formWindowManager->setActiveFormWindow(w); + addRecentFile(fn); + return true; + } + } + + // Otherwise load it. + do { + QString errorMessage; + if (workbench()->openForm(fn, &errorMessage)) { + addRecentFile(fn); + m_openDirectory = QFileInfo(fn).absolutePath(); + return true; + } else { + // prompt to reload + QMessageBox box(QMessageBox::Warning, tr("Read error"), + tr("%1\nDo you want to update the file location or generate a new form?").arg(errorMessage), + QMessageBox::Cancel, core()->topLevel()); + + QPushButton *updateButton = box.addButton(tr("&Update"), QMessageBox::ActionRole); + QPushButton *newButton = box.addButton(tr("&New Form"), QMessageBox::ActionRole); + box.exec(); + if (box.clickedButton() == box.button(QMessageBox::Cancel)) + return false; + + if (box.clickedButton() == updateButton) { + const QString extension = uiExtension(); + fn = QFileDialog::getOpenFileName(core()->topLevel(), + tr("Open Form"), m_openDirectory, + tr("Designer UI files (*.%1);;All Files (*)").arg(extension), 0, QFileDialog::DontUseSheet); + + if (fn.isEmpty()) + return false; + } else if (box.clickedButton() == newButton) { + // If the file does not exist, but its directory, is valid, open the template with the editor file name set to it. + // (called from command line). + QString newFormFileName; + const QFileInfo fInfo(fn); + if (!fInfo.exists()) { + // Normalize file name + const QString directory = fInfo.absolutePath(); + if (QDir(directory).exists()) { + newFormFileName = directory; + newFormFileName += QLatin1Char('/'); + newFormFileName += fInfo.fileName(); + } + } + showNewFormDialog(newFormFileName); + return false; + } + } + } while (true); + return true; +} + +static QString createBackup(const QString &fileName) +{ + const QString suffix = QLatin1String(".bak"); + QString backupFile = fileName + suffix; + QFileInfo fi(backupFile); + int i = 0; + while (fi.exists()) { + backupFile = fileName + suffix + QString::number(++i); + fi.setFile(backupFile); + } + + if (QFile::copy(fileName, backupFile)) + return backupFile; + return QString(); +} + +static void removeBackup(const QString &backupFile) +{ + if (!backupFile.isEmpty()) + QFile::remove(backupFile); +} + +bool QDesignerActions::writeOutForm(QDesignerFormWindowInterface *fw, const QString &saveFile) +{ + Q_ASSERT(fw && !saveFile.isEmpty()); + + QString backupFile; + QFileInfo fi(saveFile); + if (fi.exists()) + backupFile = createBackup(saveFile); + + QString contents = fw->contents(); + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(fw)) { + if (fwb->lineTerminatorMode() == qdesigner_internal::FormWindowBase::CRLFLineTerminator) + contents.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + } + const QByteArray utf8Array = contents.toUtf8(); + m_workbench->updateBackup(fw); + + QFile f(saveFile); + while (!f.open(QFile::WriteOnly)) { + QMessageBox box(QMessageBox::Warning, + tr("Save Form?"), + tr("Could not open file"), + QMessageBox::NoButton, fw); + + box.setWindowModality(Qt::WindowModal); + box.setInformativeText(tr("The file %1 could not be opened." + "\nReason: %2" + "\nWould you like to retry or select a different file?") + .arg(f.fileName()).arg(f.errorString())); + QPushButton *retryButton = box.addButton(QMessageBox::Retry); + retryButton->setDefault(true); + QPushButton *switchButton = box.addButton(tr("Select New File"), QMessageBox::AcceptRole); + QPushButton *cancelButton = box.addButton(QMessageBox::Cancel); + box.exec(); + + if (box.clickedButton() == cancelButton) { + removeBackup(backupFile); + return false; + } else if (box.clickedButton() == switchButton) { + QString extension = uiExtension(); + const QString fileName = QFileDialog::getSaveFileName(fw, tr("Save Form As"), + QDir::current().absolutePath(), + QLatin1String("*.") + extension); + if (fileName.isEmpty()) { + removeBackup(backupFile); + return false; + } + if (f.fileName() != fileName) { + removeBackup(backupFile); + fi.setFile(fileName); + backupFile.clear(); + if (fi.exists()) + backupFile = createBackup(fileName); + } + f.setFileName(fileName); + fw->setFileName(fileName); + } + // loop back around... + } + while (f.write(utf8Array, utf8Array.size()) != utf8Array.size()) { + QMessageBox box(QMessageBox::Warning, tr("Save Form?"), + tr("Could not write file"), + QMessageBox::Retry|QMessageBox::Cancel, fw); + box.setWindowModality(Qt::WindowModal); + box.setInformativeText(tr("It was not possible to write the entire file %1 to disk." + "\nReason:%2\nWould you like to retry?") + .arg(f.fileName()).arg(f.errorString())); + box.setDefaultButton(QMessageBox::Retry); + switch (box.exec()) { + case QMessageBox::Retry: + f.resize(0); + break; + default: + return false; + } + } + f.close(); + removeBackup(backupFile); + addRecentFile(saveFile); + m_saveDirectory = QFileInfo(f).absolutePath(); + + fw->setDirty(false); + fw->parentWidget()->setWindowModified(false); + return true; +} + +void QDesignerActions::shutdown() +{ + // Follow the idea from the Mac, i.e. send the Application a close event + // and if it's accepted, quit. + QCloseEvent ev; + QApplication::sendEvent(qDesigner, &ev); + if (ev.isAccepted()) + qDesigner->quit(); +} + +void QDesignerActions::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + const bool enable = formWindow != 0; + m_saveFormAction->setEnabled(enable); + m_saveFormAsAction->setEnabled(enable); + m_saveAllFormsAction->setEnabled(enable); + m_saveFormAsTemplateAction->setEnabled(enable); + m_closeFormAction->setEnabled(enable); + m_savePreviewImageAction->setEnabled(enable); + m_printPreviewAction->setEnabled(enable); + + m_editWidgetsAction->setEnabled(enable); + + m_previewFormAction->setEnabled(enable); + m_viewCodeAction->setEnabled(enable); + m_styleActions->setEnabled(enable); +} + +void QDesignerActions::formWindowSettingsChanged(QDesignerFormWindowInterface *fw) +{ + if (QDesignerFormWindow *window = m_workbench->findFormWindow(fw)) + window->updateChanged(); +} + +void QDesignerActions::updateRecentFileActions() +{ + QStringList files = m_settings.recentFilesList(); + const int originalSize = files.size(); + int numRecentFiles = qMin(files.size(), int(MaxRecentFiles)); + const QList recentFilesActs = m_recentFilesActions->actions(); + + for (int i = 0; i < numRecentFiles; ++i) { + const QFileInfo fi(files[i]); + // If the file doesn't exist anymore, just remove it from the list so + // people don't get confused. + if (!fi.exists()) { + files.removeAt(i); + --i; + numRecentFiles = qMin(files.size(), int(MaxRecentFiles)); + continue; + } + const QString text = fi.fileName(); + recentFilesActs[i]->setText(text); + recentFilesActs[i]->setIconText(files[i]); + recentFilesActs[i]->setVisible(true); + } + + for (int j = numRecentFiles; j < MaxRecentFiles; ++j) + recentFilesActs[j]->setVisible(false); + + // If there's been a change, right it back + if (originalSize != files.size()) + m_settings.setRecentFilesList(files); +} + +void QDesignerActions::openRecentForm() +{ + if (const QAction *action = qobject_cast(sender())) { + if (!readInForm(action->iconText())) + updateRecentFileActions(); // File doesn't exist, remove it from settings + } +} + +void QDesignerActions::clearRecentFiles() +{ + m_settings.setRecentFilesList(QStringList()); + updateRecentFileActions(); +} + +QActionGroup *QDesignerActions::recentFilesActions() const +{ + return m_recentFilesActions; +} + +void QDesignerActions::addRecentFile(const QString &fileName) +{ + QStringList files = m_settings.recentFilesList(); + files.removeAll(fileName); + files.prepend(fileName); + while (files.size() > MaxRecentFiles) + files.removeLast(); + + m_settings.setRecentFilesList(files); + updateRecentFileActions(); +} + +QAction *QDesignerActions::openFormAction() const +{ + return m_openFormAction; +} + +QAction *QDesignerActions::closeFormAction() const +{ + return m_closeFormAction; +} + +QAction *QDesignerActions::minimizeAction() const +{ + return m_minimizeAction; +} + +void QDesignerActions::showDesignerHelp() +{ + QString url = AssistantClient::designerManualUrl(); + url += QLatin1String("designer-manual.html"); + showHelp(url); +} + +void QDesignerActions::showWhatsNew() +{ + QString url = AssistantClient::qtReferenceManualUrl(); + url += QLatin1String("qt4-designer.html"); + showHelp(url); +} + +void QDesignerActions::helpRequested(const QString &manual, const QString &document) +{ + QString url = AssistantClient::documentUrl(manual); + url += document; + showHelp(url); +} + +void QDesignerActions::showHelp(const QString &url) +{ + QString errorMessage; + if (!m_assistantClient.showPage(url, &errorMessage)) + QMessageBox::warning(core()->topLevel(), tr("Assistant"), errorMessage); +} + +void QDesignerActions::aboutDesigner() +{ + VersionDialog mb(core()->topLevel()); + mb.setWindowTitle(tr("About Qt Designer")); + if (mb.exec()) { + QMessageBox messageBox(QMessageBox::Information, QLatin1String("Easter Egg"), + QLatin1String("Easter Egg"), QMessageBox::Ok, core()->topLevel()); + messageBox.setInformativeText(QLatin1String("The Easter Egg has been removed.")); + messageBox.exec(); + } +} + +QAction *QDesignerActions::editWidgets() const +{ + return m_editWidgetsAction; +} + +void QDesignerActions::showWidgetSpecificHelp() +{ + QString helpId; + if (const qdesigner_internal::QDesignerIntegration *integration = qobject_cast(core()->integration())) + helpId = integration->contextHelpId(); + + if (helpId.isEmpty()) { + showDesignerHelp(); + return; + } + + QString errorMessage; + const bool rc = m_assistantClient.activateIdentifier(helpId, &errorMessage); + if (!rc) + QMessageBox::warning(core()->topLevel(), tr("Assistant"), errorMessage); +} + +void QDesignerActions::updateCloseAction() +{ + if (m_previewManager->previewCount()) { + m_closeFormAction->setText(tr("&Close Preview")); + } else { + m_closeFormAction->setText(tr("&Close")); + } +} + +void QDesignerActions::backupForms() +{ + const int count = m_workbench->formWindowCount(); + if (!count || !ensureBackupDirectories()) + return; + + + QStringList tmpFiles; + QMap backupMap; + QDir backupDir(m_backupPath); + const bool warningsEnabled = qdesigner_internal::QSimpleResource::setWarningsEnabled(false); + for (int i = 0; i < count; ++i) { + QDesignerFormWindow *fw = m_workbench->formWindow(i); + QDesignerFormWindowInterface *fwi = fw->editor(); + + QString formBackupName; + QTextStream(&formBackupName) << m_backupPath << QDir::separator() + << QLatin1String("backup") << i << QLatin1String(".bak"); + + QString fwn = QDir::convertSeparators(fwi->fileName()); + if (fwn.isEmpty()) + fwn = fw->windowTitle(); + + backupMap.insert(fwn, formBackupName); + + QFile file(formBackupName.replace(m_backupPath, m_backupTmpPath)); + if (file.open(QFile::WriteOnly)){ + QString contents = fixResourceFileBackupPath(fwi, backupDir); + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(fwi)) { + if (fwb->lineTerminatorMode() == qdesigner_internal::FormWindowBase::CRLFLineTerminator) + contents.replace(QLatin1Char('\n'), QLatin1String("\r\n")); + } + const QByteArray utf8Array = contents.toUtf8(); + if (file.write(utf8Array, utf8Array.size()) != utf8Array.size()) { + backupMap.remove(fwn); + qdesigner_internal::designerWarning(tr("The backup file %1 could not be written.").arg(file.fileName())); + } else + tmpFiles.append(formBackupName); + + file.close(); + } + } + qdesigner_internal::QSimpleResource::setWarningsEnabled(warningsEnabled); + if(!tmpFiles.isEmpty()) { + const QStringList backupFiles = backupDir.entryList(QDir::Files); + if(!backupFiles.isEmpty()) { + QStringListIterator it(backupFiles); + while (it.hasNext()) + backupDir.remove(it.next()); + } + + QStringListIterator it(tmpFiles); + while (it.hasNext()) { + const QString tmpName = it.next(); + QString name(tmpName); + name.replace(m_backupTmpPath, m_backupPath); + QFile tmpFile(tmpName); + if (!tmpFile.copy(name)) + qdesigner_internal::designerWarning(tr("The backup file %1 could not be written.").arg(name)); + tmpFile.remove(); + } + + m_settings.setBackup(backupMap); + } +} + +QString QDesignerActions::fixResourceFileBackupPath(QDesignerFormWindowInterface *fwi, const QDir& backupDir) +{ + const QString content = fwi->contents(); + QDomDocument domDoc(QLatin1String("backup")); + if(!domDoc.setContent(content)) + return content; + + const QDomNodeList list = domDoc.elementsByTagName(QLatin1String("resources")); + if (list.isEmpty()) + return content; + + for (int i = 0; i < list.count(); i++) { + const QDomNode node = list.at(i); + if (!node.isNull()) { + const QDomElement element = node.toElement(); + if(!element.isNull() && element.tagName() == QLatin1String("resources")) { + QDomNode childNode = element.firstChild(); + while (!childNode.isNull()) { + QDomElement childElement = childNode.toElement(); + if(!childElement.isNull() && childElement.tagName() == QLatin1String("include")) { + const QString attr = childElement.attribute(QLatin1String("location")); + const QString path = fwi->absoluteDir().absoluteFilePath(attr); + childElement.setAttribute(QLatin1String("location"), backupDir.relativeFilePath(path)); + } + childNode = childNode.nextSibling(); + } + } + } + } + + + return domDoc.toString(); +} + +QRect QDesignerActions::fixDialogRect(const QRect &rect) const +{ + QRect frameGeometry; + const QRect availableGeometry = QApplication::desktop()->availableGeometry(core()->topLevel()); + + if (workbench()->mode() == DockedMode) { + frameGeometry = core()->topLevel()->frameGeometry(); + } else + frameGeometry = availableGeometry; + + QRect dlgRect = rect; + dlgRect.moveCenter(frameGeometry.center()); + + // make sure that parts of the dialog are not outside of screen + dlgRect.moveBottom(qMin(dlgRect.bottom(), availableGeometry.bottom())); + dlgRect.moveRight(qMin(dlgRect.right(), availableGeometry.right())); + dlgRect.moveLeft(qMax(dlgRect.left(), availableGeometry.left())); + dlgRect.moveTop(qMax(dlgRect.top(), availableGeometry.top())); + + return dlgRect; +} + +void QDesignerActions::showStatusBarMessage(const QString &message) const +{ + if (workbench()->mode() == DockedMode) { + QStatusBar *bar = qDesigner->mainWindow()->statusBar(); + if (bar && !bar->isHidden()) + bar->showMessage(message, 3000); + } +} + +void QDesignerActions::setBringAllToFrontVisible(bool visible) +{ + m_bringAllToFrontSeparator->setVisible(visible); + m_bringAllToFrontAction->setVisible(visible); +} + +void QDesignerActions::setWindowListSeparatorVisible(bool visible) +{ + m_windowListSeparatorAction->setVisible(visible); +} + +bool QDesignerActions::ensureBackupDirectories() { + + if (m_backupPath.isEmpty()) { + // create names + m_backupPath = QDir::homePath(); + m_backupPath += QDir::separator(); + m_backupPath += QLatin1String(".designer"); + m_backupPath += QDir::separator(); + m_backupPath += QLatin1String("backup"); + m_backupPath = QDir::convertSeparators(m_backupPath ); + + m_backupTmpPath = m_backupPath; + m_backupTmpPath += QDir::separator(); + m_backupTmpPath += QLatin1String("tmp"); + m_backupTmpPath = QDir::convertSeparators(m_backupTmpPath); + } + + // ensure directories + const QDir backupDir(m_backupPath); + const QDir backupTmpDir(m_backupTmpPath); + + if (!backupDir.exists()) { + if (!backupDir.mkpath(m_backupPath)) { + qdesigner_internal::designerWarning(tr("The backup directory %1 could not be created.").arg(m_backupPath)); + return false; + } + } + if (!backupTmpDir.exists()) { + if (!backupTmpDir.mkpath(m_backupTmpPath)) { + qdesigner_internal::designerWarning(tr("The temporary backup directory %1 could not be created.").arg(m_backupTmpPath)); + return false; + } + } + return true; +} + +void QDesignerActions::showPreferencesDialog() +{ + PreferencesDialog preferencesDialog(workbench()->core(), m_core->topLevel()); + preferencesDialog.exec(); +} + +void QDesignerActions::showAppFontDialog() +{ + if (!m_appFontDialog) // Might get deleted when switching ui modes + m_appFontDialog = new AppFontDialog(core()->topLevel()); + m_appFontDialog->show(); + m_appFontDialog->raise(); +} + +QPixmap QDesignerActions::createPreviewPixmap(QDesignerFormWindowInterface *fw) +{ + const QCursor oldCursor = core()->topLevel()->cursor(); + core()->topLevel()->setCursor(Qt::WaitCursor); + + QString errorMessage; + const QPixmap pixmap = m_previewManager->createPreviewPixmap(fw, QString(), &errorMessage); + core()->topLevel()->setCursor(oldCursor); + if (pixmap.isNull()) { + QMessageBox::warning(fw, tr("Preview failed"), errorMessage); + } + return pixmap; +} + +qdesigner_internal::PreviewConfiguration QDesignerActions::previewConfiguration() +{ + qdesigner_internal::PreviewConfiguration pc; + QDesignerSharedSettings settings(core()); + if (settings.isCustomPreviewConfigurationEnabled()) + pc = settings.customPreviewConfiguration(); + return pc; +} + +void QDesignerActions::savePreviewImage() +{ + const char *format = "png"; + + QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow(); + if (!fw) + return; + + QImage image; + const QString extension = QString::fromAscii(format); + const QString filter = tr("Image files (*.%1)").arg(extension); + + QString suggestion = fw->fileName(); + if (!suggestion.isEmpty()) { + suggestion = QFileInfo(suggestion).baseName(); + suggestion += QLatin1Char('.'); + suggestion += extension; + } + do { + const QString fileName = getSaveFileNameWithExtension(fw, tr("Save Image"), suggestion, filter, extension); + if (fileName.isEmpty()) + break; + + if (image.isNull()) { + const QPixmap pixmap = createPreviewPixmap(fw); + if (pixmap.isNull()) + break; + + image = pixmap.toImage(); + } + + if (image.save(fileName, format)) { + showStatusBarMessage(tr("Saved image %1.").arg(QFileInfo(fileName).fileName())); + break; + } + + QMessageBox box(QMessageBox::Warning, tr("Save Image"), + tr("The file %1 could not be written.").arg( fileName), + QMessageBox::Retry|QMessageBox::Cancel, fw); + if (box.exec() == QMessageBox::Cancel) + break; + } while (true); +} + +void QDesignerActions::formWindowCountChanged() +{ + const bool enabled = m_core->formWindowManager()->formWindowCount() == 0; + /* Disable the application font action if there are form windows open + * as the reordering of the fonts sets font properties to 'changed' + * and overloaded fonts are not updated. */ + static const QString disabledTip = tr("Please close all forms to enable the loading of additional fonts."); + m_appFontAction->setEnabled(enabled); + m_appFontAction->setStatusTip(enabled ? QString() : disabledTip); +} + +void QDesignerActions::printPreviewImage() +{ +#ifndef QT_NO_PRINTER + QDesignerFormWindowInterface *fw = core()->formWindowManager()->activeFormWindow(); + if (!fw) + return; + + if (!m_printer) + m_printer = new QPrinter(QPrinter::HighResolution); + + m_printer->setFullPage(false); + + // Grab the image to be able to a suggest suitable orientation + const QPixmap pixmap = createPreviewPixmap(fw); + if (pixmap.isNull()) + return; + + const QSizeF pixmapSize = pixmap.size(); + m_printer->setOrientation( pixmapSize.width() > pixmapSize.height() ? QPrinter::Landscape : QPrinter::Portrait); + + // Printer parameters + QPrintDialog dialog(m_printer, fw); + if (!dialog.exec()) + return; + + const QCursor oldCursor = core()->topLevel()->cursor(); + core()->topLevel()->setCursor(Qt::WaitCursor); + // Estimate of required scaling to make form look the same on screen and printer. + const double suggestedScaling = static_cast(m_printer->physicalDpiX()) / static_cast(fw->physicalDpiX()); + + QPainter painter(m_printer); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + + // Clamp to page + const QRectF page = painter.viewport(); + const double maxScaling = qMin(page.size().width() / pixmapSize.width(), page.size().height() / pixmapSize.height()); + const double scaling = qMin(suggestedScaling, maxScaling); + + const double xOffset = page.left() + qMax(0.0, (page.size().width() - scaling * pixmapSize.width()) / 2.0); + const double yOffset = page.top() + qMax(0.0, (page.size().height() - scaling * pixmapSize.height()) / 2.0); + + // Draw. + painter.translate(xOffset, yOffset); + painter.scale(scaling, scaling); + painter.drawPixmap(0, 0, pixmap); + core()->topLevel()->setCursor(oldCursor); + + showStatusBarMessage(tr("Printed %1.").arg(QFileInfo(fw->fileName()).fileName())); +#endif +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_actions.h b/designer/designer/qdesigner_actions.h new file mode 100644 index 0000000..a25629e --- /dev/null +++ b/designer/designer/qdesigner_actions.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_ACTIONS_H +#define QDESIGNER_ACTIONS_H + +#include "assistantclient.h" +#include "qdesigner_settings.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerWorkbench; + +class QDir; +class QTimer; +class QAction; +class QActionGroup; +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class AppFontDialog; + +class QRect; +class QWidget; +class QPixmap; +class QMenu; + +namespace qdesigner_internal { + class PreviewConfiguration; + class PreviewManager; +} + +class QDesignerActions: public QObject +{ + Q_OBJECT +public: + explicit QDesignerActions(QDesignerWorkbench *mainWindow); + virtual ~QDesignerActions(); + + QDesignerWorkbench *workbench() const; + QDesignerFormEditorInterface *core() const; + + bool saveForm(QDesignerFormWindowInterface *fw); + bool readInForm(const QString &fileName); + bool writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName); + + QActionGroup *fileActions() const; + QActionGroup *recentFilesActions() const; + QActionGroup *editActions() const; + QActionGroup *formActions() const; + QActionGroup *settingsActions() const; + QActionGroup *windowActions() const; + QActionGroup *toolActions() const; + QActionGroup *helpActions() const; + QActionGroup *uiMode() const; + QActionGroup *styleActions() const; + // file actions + QAction *openFormAction() const; + QAction *closeFormAction() const; + // window actions + QAction *minimizeAction() const; + // edit mode actions + QAction *editWidgets() const; + // form actions + QAction *previewFormAction() const; + QAction *viewCodeAction() const; + + void setBringAllToFrontVisible(bool visible); + void setWindowListSeparatorVisible(bool visible); + + bool openForm(QWidget *parent); + + QString uiExtension() const; + + // Boolean dynamic property set on actions to + // show them in the default toolbar layout + static const char *defaultToolbarPropertyName; + +public slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + void createForm(); + void slotOpenForm(); + void helpRequested(const QString &manual, const QString &document); + +signals: + void useBigIcons(bool); + +private slots: + void saveForm(); + void saveFormAs(); + void saveAllForms(); + void saveFormAsTemplate(); + void viewCode(); + void notImplementedYet(); + void shutdown(); + void editWidgetsSlot(); + void openRecentForm(); + void clearRecentFiles(); + void closeForm(); + void showDesignerHelp(); + void showWhatsNew(); + void aboutDesigner(); + void showWidgetSpecificHelp(); + void backupForms(); + void showNewFormDialog(const QString &fileName); + void showPreferencesDialog(); + void showAppFontDialog(); + void savePreviewImage(); + void printPreviewImage(); + void updateCloseAction(); + void formWindowCountChanged(); + void formWindowSettingsChanged(QDesignerFormWindowInterface *fw); + +private: + QAction *createRecentFilesMenu(); + bool saveFormAs(QDesignerFormWindowInterface *fw); + void fixActionContext(); + void updateRecentFileActions(); + void addRecentFile(const QString &fileName); + void showHelp(const QString &help); + void closePreview(); + QRect fixDialogRect(const QRect &rect) const; + QString fixResourceFileBackupPath(QDesignerFormWindowInterface *fwi, const QDir& backupDir); + void showStatusBarMessage(const QString &message) const; + QActionGroup *createHelpActions(); + bool ensureBackupDirectories(); + QPixmap createPreviewPixmap(QDesignerFormWindowInterface *fw); + qdesigner_internal::PreviewConfiguration previewConfiguration(); + + enum { MaxRecentFiles = 10 }; + QDesignerWorkbench *m_workbench; + QDesignerFormEditorInterface *m_core; + QDesignerSettings m_settings; + AssistantClient m_assistantClient; + QString m_openDirectory; + QString m_saveDirectory; + + + QString m_backupPath; + QString m_backupTmpPath; + + QTimer* m_backupTimer; + + QActionGroup *m_fileActions; + QActionGroup *m_recentFilesActions; + QActionGroup *m_editActions; + QActionGroup *m_formActions; + QActionGroup *m_settingsActions; + QActionGroup *m_windowActions; + QActionGroup *m_toolActions; + QActionGroup *m_helpActions; + QActionGroup *m_styleActions; + + QAction *m_editWidgetsAction; + + QAction *m_newFormAction; + QAction *m_openFormAction; + QAction *m_saveFormAction; + QAction *m_saveFormAsAction; + QAction *m_saveAllFormsAction; + QAction *m_saveFormAsTemplateAction; + QAction *m_closeFormAction; + QAction *m_savePreviewImageAction; + QAction *m_printPreviewAction; + + QAction *m_quitAction; + + QAction *m_previewFormAction; + QAction *m_viewCodeAction; + + QAction *m_minimizeAction; + QAction *m_bringAllToFrontSeparator; + QAction *m_bringAllToFrontAction; + QAction *m_windowListSeparatorAction; + + QAction *m_preferencesAction; + QAction *m_appFontAction; + + QPointer m_appFontDialog; + +#ifndef QT_NO_PRINTER + QPrinter *m_printer; +#endif + + qdesigner_internal::PreviewManager *m_previewManager; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_ACTIONS_H diff --git a/designer/designer/qdesigner_appearanceoptions.cpp b/designer/designer/qdesigner_appearanceoptions.cpp new file mode 100644 index 0000000..3c55d46 --- /dev/null +++ b/designer/designer/qdesigner_appearanceoptions.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_appearanceoptions.h" +#include "ui_qdesigner_appearanceoptions.h" + +#include "qdesigner_settings.h" +#include "qdesigner_toolwindow.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// ---------------- AppearanceOptions +AppearanceOptions::AppearanceOptions() : + uiMode(DockedMode) +{ +} + +bool AppearanceOptions::equals(const AppearanceOptions &rhs) const +{ + return uiMode == rhs.uiMode && toolWindowFontSettings == rhs.toolWindowFontSettings; +} + +void AppearanceOptions::toSettings(QDesignerSettings &settings) const +{ + settings.setUiMode(uiMode); + settings.setToolWindowFont(toolWindowFontSettings); +} + +void AppearanceOptions::fromSettings(const QDesignerSettings &settings) +{ + uiMode = settings.uiMode(); + toolWindowFontSettings = settings.toolWindowFont(); +} + +// ---------------- QDesignerAppearanceOptionsWidget +QDesignerAppearanceOptionsWidget::QDesignerAppearanceOptionsWidget(QWidget *parent) : + QWidget(parent), + m_ui(new Ui::AppearanceOptionsWidget), + m_initialUIMode(NeutralMode) +{ + m_ui->setupUi(this); + + m_ui->m_uiModeCombo->addItem(tr("Docked Window"), QVariant(DockedMode)); + m_ui->m_uiModeCombo->addItem(tr("Multiple Top-Level Windows"), QVariant(TopLevelMode)); + connect(m_ui->m_uiModeCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotUiModeComboChanged())); + + m_ui->m_fontPanel->setCheckable(true); + m_ui->m_fontPanel->setTitle(tr("Toolwindow Font")); + +} + +QDesignerAppearanceOptionsWidget::~QDesignerAppearanceOptionsWidget() +{ + delete m_ui; +} + +UIMode QDesignerAppearanceOptionsWidget::uiMode() const +{ + return static_cast(m_ui->m_uiModeCombo->itemData(m_ui->m_uiModeCombo->currentIndex()).toInt()); +} + +AppearanceOptions QDesignerAppearanceOptionsWidget::appearanceOptions() const +{ + AppearanceOptions rc; + rc.uiMode = uiMode(); + rc.toolWindowFontSettings.m_font = m_ui->m_fontPanel->selectedFont(); + rc.toolWindowFontSettings.m_useFont = m_ui->m_fontPanel->isChecked(); + rc.toolWindowFontSettings.m_writingSystem = m_ui->m_fontPanel->writingSystem(); + return rc; +} + +void QDesignerAppearanceOptionsWidget::setAppearanceOptions(const AppearanceOptions &ao) +{ + m_initialUIMode = ao.uiMode; + m_ui->m_uiModeCombo->setCurrentIndex(m_ui->m_uiModeCombo->findData(QVariant(ao.uiMode))); + m_ui->m_fontPanel->setWritingSystem(ao.toolWindowFontSettings.m_writingSystem); + m_ui->m_fontPanel->setSelectedFont(ao.toolWindowFontSettings.m_font); + m_ui->m_fontPanel->setChecked(ao.toolWindowFontSettings.m_useFont); +} + +void QDesignerAppearanceOptionsWidget::slotUiModeComboChanged() +{ + emit uiModeChanged(m_initialUIMode != uiMode()); +} + +// ----------- QDesignerAppearanceOptionsPage +QDesignerAppearanceOptionsPage::QDesignerAppearanceOptionsPage(QDesignerFormEditorInterface *core) : + m_core(core) +{ +} + +QString QDesignerAppearanceOptionsPage::name() const +{ + //: Tab in preferences dialog + return QCoreApplication::translate("QDesignerAppearanceOptionsPage", "Appearance"); +} + +QWidget *QDesignerAppearanceOptionsPage::createPage(QWidget *parent) +{ + m_widget = new QDesignerAppearanceOptionsWidget(parent); + m_initialOptions.fromSettings(QDesignerSettings(m_core)); + m_widget->setAppearanceOptions(m_initialOptions); + return m_widget; +} + +void QDesignerAppearanceOptionsPage::apply() +{ + if (m_widget) { + const AppearanceOptions newOptions = m_widget->appearanceOptions(); + if (newOptions != m_initialOptions) { + QDesignerSettings settings(m_core); + newOptions.toSettings(settings); + QTimer::singleShot(0, this, SIGNAL(settingsChangedDelayed())); + m_initialOptions = newOptions; + } + } +} + +void QDesignerAppearanceOptionsPage::finish() +{ +} + +QT_END_NAMESPACE + diff --git a/designer/designer/qdesigner_appearanceoptions.h b/designer/designer/qdesigner_appearanceoptions.h new file mode 100644 index 0000000..389874d --- /dev/null +++ b/designer/designer/qdesigner_appearanceoptions.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_APPEARANCEOPTIONS_H +#define QDESIGNER_APPEARANCEOPTIONS_H + +#include "designer_enums.h" +#include "qdesigner_toolwindow.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettings; + +namespace Ui { + class AppearanceOptionsWidget; +} + +/* AppearanceOptions data */ +struct AppearanceOptions { + AppearanceOptions(); + bool equals(const AppearanceOptions&) const; + void toSettings(QDesignerSettings &) const; + void fromSettings(const QDesignerSettings &); + + UIMode uiMode; + ToolWindowFontSettings toolWindowFontSettings; +}; + +inline bool operator==(const AppearanceOptions &ao1, const AppearanceOptions &ao2) +{ + return ao1.equals(ao2); +} + +inline bool operator!=(const AppearanceOptions &ao1, const AppearanceOptions &ao2) +{ + return !ao1.equals(ao2); +} + +/* QDesignerAppearanceOptionsWidget: Let the user edit AppearanceOptions */ +class QDesignerAppearanceOptionsWidget : public QWidget +{ + Q_OBJECT +public: + explicit QDesignerAppearanceOptionsWidget(QWidget *parent = 0); + ~QDesignerAppearanceOptionsWidget(); + + AppearanceOptions appearanceOptions() const; + void setAppearanceOptions(const AppearanceOptions &ao); + +signals: + void uiModeChanged(bool modified); + +private slots: + void slotUiModeComboChanged(); + +private: + UIMode uiMode() const; + + Ui::AppearanceOptionsWidget *m_ui; + UIMode m_initialUIMode; +}; + +/* The options page for appearance options. Emits a Timer-0 delayed changed + * signal to allow the preferences dialog to close (and be deleted) before a + * possible switch from docked mode to top-level mode happens. (The switch + * would delete the main window, which the preference dialog is a child of + * -> BOOM) */ + +class QDesignerAppearanceOptionsPage : public QObject, public QDesignerOptionsPageInterface +{ + Q_OBJECT + +public: + QDesignerAppearanceOptionsPage(QDesignerFormEditorInterface *core); + + QString name() const; + QWidget *createPage(QWidget *parent); + virtual void apply(); + virtual void finish(); + +signals: + void settingsChangedDelayed(); + +private: + QDesignerFormEditorInterface *m_core; + QPointer m_widget; + AppearanceOptions m_initialOptions; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_APPEARANCEOPTIONS_H diff --git a/designer/designer/qdesigner_appearanceoptions.ui b/designer/designer/qdesigner_appearanceoptions.ui new file mode 100644 index 0000000..a5582f2 --- /dev/null +++ b/designer/designer/qdesigner_appearanceoptions.ui @@ -0,0 +1,57 @@ + + + AppearanceOptionsWidget + + + + 0 + 0 + 325 + 360 + + + + Form + + + + + + User Interface Mode + + + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + FontPanel + QGroupBox +
fontpanel.h
+ 1 +
+
+ + +
diff --git a/designer/designer/qdesigner_formwindow.cpp b/designer/designer/qdesigner_formwindow.cpp new file mode 100644 index 0000000..f5a71f5 --- /dev/null +++ b/designer/designer/qdesigner_formwindow.cpp @@ -0,0 +1,289 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_formwindow.h" +#include "qdesigner_workbench.h" +#include "formwindowbase_p.h" + +// sdk +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDesignerFormWindow::QDesignerFormWindow(QDesignerFormWindowInterface *editor, QDesignerWorkbench *workbench, QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags), + m_editor(editor), + m_workbench(workbench), + m_action(new QAction(this)), + m_initialized(false), + m_windowTitleInitialized(false) +{ + Q_ASSERT(workbench); + + setMaximumSize(0xFFF, 0xFFF); + QDesignerFormEditorInterface *core = workbench->core(); + + if (m_editor) { + m_editor->setParent(this); + } else { + m_editor = core->formWindowManager()->createFormWindow(this); + } + + QVBoxLayout *l = new QVBoxLayout(this); + l->setMargin(0); + l->addWidget(m_editor); + + m_action->setCheckable(true); + + connect(m_editor->commandHistory(), SIGNAL(indexChanged(int)), this, SLOT(updateChanged())); + connect(m_editor, SIGNAL(geometryChanged()), this, SLOT(geometryChanged())); + qdesigner_internal::FormWindowBase::setupDefaultAction(m_editor); +} + +QDesignerFormWindow::~QDesignerFormWindow() +{ + if (workbench()) + workbench()->removeFormWindow(this); +} + +QAction *QDesignerFormWindow::action() const +{ + return m_action; +} + +void QDesignerFormWindow::changeEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::WindowTitleChange: + m_action->setText(windowTitle().remove(QLatin1String("[*]"))); + break; + case QEvent::WindowIconChange: + m_action->setIcon(windowIcon()); + break; + case QEvent::WindowStateChange: { + const QWindowStateChangeEvent *wsce = static_cast(e); + const bool wasMinimized = Qt::WindowMinimized & wsce->oldState(); + const bool isMinimizedNow = isMinimized(); + if (wasMinimized != isMinimizedNow ) + emit minimizationStateChanged(m_editor, isMinimizedNow); + } + break; + default: + break; + } + QWidget::changeEvent(e); +} + +QRect QDesignerFormWindow::geometryHint() const +{ + const QPoint point(0, 0); + // If we have a container, we want to be just as big. + // QMdiSubWindow attempts to resize its children to sizeHint() when switching user interface modes. + if (QWidget *mainContainer = m_editor->mainContainer()) + return QRect(point, mainContainer->size()); + + return QRect(point, sizeHint()); +} + +QDesignerFormWindowInterface *QDesignerFormWindow::editor() const +{ + return m_editor; +} + +QDesignerWorkbench *QDesignerFormWindow::workbench() const +{ + return m_workbench; +} + +void QDesignerFormWindow::firstShow() +{ + // Set up handling of file name changes and set initial title. + if (!m_windowTitleInitialized) { + m_windowTitleInitialized = true; + if (m_editor) { + connect(m_editor, SIGNAL(fileNameChanged(QString)), this, SLOT(updateWindowTitle(QString))); + updateWindowTitle(m_editor->fileName()); + } + } + show(); +} + +int QDesignerFormWindow::getNumberOfUntitledWindows() const +{ + const int totalWindows = m_workbench->formWindowCount(); + if (!totalWindows) + return 0; + + int maxUntitled = 0; + // Find the number of untitled windows excluding ourselves. + // Do not fall for 'untitled.ui', match with modified place holder. + // This will cause some problems with i18n, but for now I need the string to be "static" + QRegExp rx(QLatin1String("untitled( (\\d+))?\\[\\*\\]")); + for (int i = 0; i < totalWindows; ++i) { + QDesignerFormWindow *fw = m_workbench->formWindow(i); + if (fw != this) { + const QString title = m_workbench->formWindow(i)->windowTitle(); + if (rx.indexIn(title) != -1) { + if (maxUntitled == 0) + ++maxUntitled; + if (rx.captureCount() > 1) { + const QString numberCapture = rx.cap(2); + if (!numberCapture.isEmpty()) + maxUntitled = qMax(numberCapture.toInt(), maxUntitled); + } + } + } + } + return maxUntitled; +} + +void QDesignerFormWindow::updateWindowTitle(const QString &fileName) +{ + if (!m_windowTitleInitialized) { + m_windowTitleInitialized = true; + if (m_editor) + connect(m_editor, SIGNAL(fileNameChanged(QString)), this, SLOT(updateWindowTitle(QString))); + } + + QString fileNameTitle; + if (fileName.isEmpty()) { + fileNameTitle = QLatin1String("untitled"); + if (const int maxUntitled = getNumberOfUntitledWindows()) { + fileNameTitle += QLatin1Char(' '); + fileNameTitle += QString::number(maxUntitled + 1); + } + } else { + fileNameTitle = QFileInfo(fileName).fileName(); + } + + if (const QWidget *mc = m_editor->mainContainer()) { + setWindowIcon(mc->windowIcon()); + setWindowTitle(tr("%1 - %2[*]").arg(mc->windowTitle()).arg(fileNameTitle)); + } else { + setWindowTitle(fileNameTitle); + } +} + +void QDesignerFormWindow::closeEvent(QCloseEvent *ev) +{ + if (m_editor->isDirty()) { + raise(); + QMessageBox box(QMessageBox::Information, tr("Save Form?"), + tr("Do you want to save the changes to this document before closing?"), + QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save, m_editor); + box.setInformativeText(tr("If you don't save, your changes will be lost.")); + box.setWindowModality(Qt::WindowModal); + static_cast(box.button(QMessageBox::Save))->setDefault(true); + + switch (box.exec()) { + case QMessageBox::Save: { + bool ok = workbench()->saveForm(m_editor); + ev->setAccepted(ok); + m_editor->setDirty(!ok); + break; + } + case QMessageBox::Discard: + m_editor->setDirty(false); // Not really necessary, but stops problems if we get close again. + ev->accept(); + break; + case QMessageBox::Cancel: + ev->ignore(); + break; + } + } +} + +void QDesignerFormWindow::updateChanged() +{ + // Sometimes called after form window destruction. + if (m_editor) { + setWindowModified(m_editor->isDirty()); + updateWindowTitle(m_editor->fileName()); + } +} + +void QDesignerFormWindow::resizeEvent(QResizeEvent *rev) +{ + if(m_initialized) { + m_editor->setDirty(true); + setWindowModified(true); + } + + m_initialized = true; + QWidget::resizeEvent(rev); +} + +void QDesignerFormWindow::geometryChanged() +{ + // If the form window changes, re-update the geometry of the current widget in the property editor. + // Note that in the case of layouts, non-maincontainer widgets must also be updated, + // so, do not do it for the main container only + const QDesignerFormEditorInterface *core = m_editor->core(); + QObject *object = core->propertyEditor()->object(); + if (object == 0 || !object->isWidgetType()) + return; + static const QString geometryProperty = QLatin1String("geometry"); + const QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), object); + const int geometryIndex = sheet->indexOf(geometryProperty); + if (geometryIndex == -1) + return; + core->propertyEditor()->setPropertyValue(geometryProperty, sheet->property(geometryIndex)); +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_formwindow.h b/designer/designer/qdesigner_formwindow.h new file mode 100644 index 0000000..ef6a339 --- /dev/null +++ b/designer/designer/qdesigner_formwindow.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_FORMWINDOW_H +#define QDESIGNER_FORMWINDOW_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerWorkbench; +class QDesignerFormWindowInterface; + +class QDesignerFormWindow: public QWidget +{ + Q_OBJECT +public: + QDesignerFormWindow(QDesignerFormWindowInterface *formWindow, QDesignerWorkbench *workbench, + QWidget *parent = 0, Qt::WindowFlags flags = 0); + + void firstShow(); + + virtual ~QDesignerFormWindow(); + + QAction *action() const; + QDesignerWorkbench *workbench() const; + QDesignerFormWindowInterface *editor() const; + + QRect geometryHint() const; + +public slots: + void updateChanged(); + +private slots: + void updateWindowTitle(const QString &fileName); + void geometryChanged(); + +signals: + void minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized); + void triggerAction(); + +protected: + virtual void changeEvent(QEvent *e); + virtual void closeEvent(QCloseEvent *ev); + virtual void resizeEvent(QResizeEvent* rev); + +private: + int getNumberOfUntitledWindows() const; + QPointer m_editor; + QPointer m_workbench; + QAction *m_action; + bool m_initialized; + bool m_windowTitleInitialized; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMWINDOW_H diff --git a/designer/designer/qdesigner_pch.h b/designer/designer/qdesigner_pch.h new file mode 100644 index 0000000..1169f31 --- /dev/null +++ b/designer/designer/qdesigner_pch.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined __cplusplus +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qdesigner.h" +#include "qdesigner_formwindow.h" +#include "qdesigner_settings.h" +#include "qdesigner_toolwindow.h" +#include "qdesigner_workbench.h" +#endif diff --git a/designer/designer/qdesigner_server.cpp b/designer/designer/qdesigner_server.cpp new file mode 100644 index 0000000..02d0984 --- /dev/null +++ b/designer/designer/qdesigner_server.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 "qdesigner.h" +#include "qdesigner_server.h" + +#include + +QT_BEGIN_NAMESPACE + +// ### review + +QDesignerServer::QDesignerServer(QObject *parent) + : QObject(parent) +{ + m_socket = 0; + m_server = new QTcpServer(this); + m_server->listen(QHostAddress::LocalHost, 0); + if (m_server->isListening()) + { + connect(m_server, SIGNAL(newConnection()), + this, SLOT(handleNewConnection())); + } +} + +QDesignerServer::~QDesignerServer() +{ +} + +quint16 QDesignerServer::serverPort() const +{ + return m_server ? m_server->serverPort() : 0; +} + +void QDesignerServer::sendOpenRequest(int port, const QStringList &files) +{ + QTcpSocket *sSocket = new QTcpSocket(); + sSocket->connectToHost(QHostAddress::LocalHost, port); + if(sSocket->waitForConnected(3000)) + { + foreach(const QString &file, files) + { + QFileInfo fi(file); + sSocket->write(fi.absoluteFilePath().toUtf8() + '\n'); + } + sSocket->waitForBytesWritten(3000); + sSocket->close(); + } + delete sSocket; +} + +void QDesignerServer::readFromClient() +{ + while (m_socket->canReadLine()) { + QString file = QString::fromUtf8(m_socket->readLine()); + if (!file.isNull()) { + file.remove(QLatin1Char('\n')); + file.remove(QLatin1Char('\r')); + qDesigner->postEvent(qDesigner, new QFileOpenEvent(file)); + } + } +} + +void QDesignerServer::socketClosed() +{ + m_socket = 0; +} + +void QDesignerServer::handleNewConnection() +{ + // no need for more than one connection + if (m_socket == 0) { + m_socket = m_server->nextPendingConnection(); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readFromClient())); + connect(m_socket, SIGNAL(disconnected()), + this, SLOT(socketClosed())); + } +} + + +QDesignerClient::QDesignerClient(quint16 port, QObject *parent) +: QObject(parent) +{ + m_socket = new QTcpSocket(this); + m_socket->connectToHost(QHostAddress::LocalHost, port); + connect(m_socket, SIGNAL(readyRead()), + this, SLOT(readFromSocket())); + +} + +QDesignerClient::~QDesignerClient() +{ + m_socket->close(); + m_socket->flush(); +} + +void QDesignerClient::readFromSocket() +{ + while (m_socket->canReadLine()) { + QString file = QString::fromUtf8(m_socket->readLine()); + if (!file.isNull()) { + file.remove(QLatin1Char('\n')); + file.remove(QLatin1Char('\r')); + if (QFile::exists(file)) + qDesigner->postEvent(qDesigner, new QFileOpenEvent(file)); + } + } +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_server.h b/designer/designer/qdesigner_server.h new file mode 100644 index 0000000..139904c --- /dev/null +++ b/designer/designer/qdesigner_server.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_SERVER_H +#define QDESIGNER_SERVER_H + +#include + +QT_BEGIN_NAMESPACE + +class QTcpServer; +class QTcpSocket; + +class QDesignerServer: public QObject +{ + Q_OBJECT +public: + explicit QDesignerServer(QObject *parent = 0); + virtual ~QDesignerServer(); + + quint16 serverPort() const; + + static void sendOpenRequest(int port, const QStringList &files); + +private slots: + void handleNewConnection(); + void readFromClient(); + void socketClosed(); + +private: + QTcpServer *m_server; + QTcpSocket *m_socket; +}; + +class QDesignerClient: public QObject +{ + Q_OBJECT +public: + explicit QDesignerClient(quint16 port, QObject *parent = 0); + virtual ~QDesignerClient(); + +private slots: + void readFromSocket(); + +private: + QTcpSocket *m_socket; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_SERVER_H diff --git a/designer/designer/qdesigner_settings.cpp b/designer/designer/qdesigner_settings.cpp new file mode 100644 index 0000000..7f13821 --- /dev/null +++ b/designer/designer/qdesigner_settings.cpp @@ -0,0 +1,250 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner.h" +#include "qdesigner_settings.h" +#include "qdesigner_toolwindow.h" +#include "qdesigner_workbench.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +enum { debugSettings = 0 }; + +QT_BEGIN_NAMESPACE + +static const char *newFormShowKey = "newFormDialog/ShowOnStartup"; + +// Change the version whenever the arrangement changes significantly. +static const char *mainWindowStateKey = "MainWindowState45"; +static const char *toolBarsStateKey = "ToolBarsState45"; + +static const char *backupOrgListKey = "backup/fileListOrg"; +static const char *backupBakListKey = "backup/fileListBak"; +static const char *recentFilesListKey = "recentFilesList"; + +QDesignerSettings::QDesignerSettings(QDesignerFormEditorInterface *core) : + qdesigner_internal::QDesignerSharedSettings(core) +{ +} + +void QDesignerSettings::setValue(const QString &key, const QVariant &value) +{ + settings()->setValue(key, value); +} + +QVariant QDesignerSettings::value(const QString &key, const QVariant &defaultValue) const +{ + return settings()->value(key, defaultValue); +} + +static inline QChar modeChar(UIMode mode) +{ + return QLatin1Char(static_cast(mode) + '0'); +} + +void QDesignerSettings::saveGeometryFor(const QWidget *w) +{ + Q_ASSERT(w && !w->objectName().isEmpty()); + QDesignerSettingsInterface *s = settings(); + const bool visible = w->isVisible(); + if (debugSettings) + qDebug() << Q_FUNC_INFO << w << "visible=" << visible; + s->beginGroup(w->objectName()); + s->setValue(QLatin1String("visible"), visible); + s->setValue(QLatin1String("geometry"), w->saveGeometry()); + s->endGroup(); +} + +void QDesignerSettings::restoreGeometry(QWidget *w, QRect fallBack) const +{ + Q_ASSERT(w && !w->objectName().isEmpty()); + const QString key = w->objectName(); + const QByteArray ba(settings()->value(key + QLatin1String("/geometry")).toByteArray()); + const bool visible = settings()->value(key + QLatin1String("/visible"), true).toBool(); + + if (debugSettings) + qDebug() << Q_FUNC_INFO << w << fallBack << "visible=" << visible; + if (ba.isEmpty()) { + /// Apply default geometry, check for null and maximal size + if (fallBack.isNull()) + fallBack = QRect(QPoint(0, 0), w->sizeHint()); + if (fallBack.size() == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) { + w->setWindowState(w->windowState() | Qt::WindowMaximized); + } else { + w->move(fallBack.topLeft()); + w->resize(fallBack.size()); + } + } else { + w->restoreGeometry(ba); + } + + if (visible) + w->show(); +} + +QStringList QDesignerSettings::recentFilesList() const +{ + return settings()->value(QLatin1String(recentFilesListKey)).toStringList(); +} + +void QDesignerSettings::setRecentFilesList(const QStringList &sl) +{ + settings()->setValue(QLatin1String(recentFilesListKey), sl); +} + +void QDesignerSettings::setShowNewFormOnStartup(bool showIt) +{ + settings()->setValue(QLatin1String(newFormShowKey), showIt); +} + +bool QDesignerSettings::showNewFormOnStartup() const +{ + return settings()->value(QLatin1String(newFormShowKey), true).toBool(); +} + +QByteArray QDesignerSettings::mainWindowState(UIMode mode) const +{ + return settings()->value(QLatin1String(mainWindowStateKey) + modeChar(mode)).toByteArray(); +} + +void QDesignerSettings::setMainWindowState(UIMode mode, const QByteArray &mainWindowState) +{ + settings()->setValue(QLatin1String(mainWindowStateKey) + modeChar(mode), mainWindowState); +} + +QByteArray QDesignerSettings::toolBarsState(UIMode mode) const +{ + QString key = QLatin1String(toolBarsStateKey); + key += modeChar(mode); + return settings()->value(key).toByteArray(); +} + +void QDesignerSettings::setToolBarsState(UIMode mode, const QByteArray &toolBarsState) +{ + QString key = QLatin1String(toolBarsStateKey); + key += modeChar(mode); + settings()->setValue(key, toolBarsState); +} + +void QDesignerSettings::clearBackup() +{ + QDesignerSettingsInterface *s = settings(); + s->remove(QLatin1String(backupOrgListKey)); + s->remove(QLatin1String(backupBakListKey)); +} + +void QDesignerSettings::setBackup(const QMap &map) +{ + const QStringList org = map.keys(); + const QStringList bak = map.values(); + + QDesignerSettingsInterface *s = settings(); + s->setValue(QLatin1String(backupOrgListKey), org); + s->setValue(QLatin1String(backupBakListKey), bak); +} + +QMap QDesignerSettings::backup() const +{ + const QStringList org = settings()->value(QLatin1String(backupOrgListKey), QStringList()).toStringList(); + const QStringList bak = settings()->value(QLatin1String(backupBakListKey), QStringList()).toStringList(); + + QMap map; + const int orgCount = org.count(); + for (int i = 0; i < orgCount; ++i) + map.insert(org.at(i), bak.at(i)); + + return map; +} + +void QDesignerSettings::setUiMode(UIMode mode) +{ + QDesignerSettingsInterface *s = settings(); + s->beginGroup(QLatin1String("UI")); + s->setValue(QLatin1String("currentMode"), mode); + s->endGroup(); +} + +UIMode QDesignerSettings::uiMode() const +{ +#ifdef Q_WS_MAC + const UIMode defaultMode = TopLevelMode; +#else + const UIMode defaultMode = DockedMode; +#endif + UIMode uiMode = static_cast(value(QLatin1String("UI/currentMode"), defaultMode).toInt()); + return uiMode; +} + +void QDesignerSettings::setToolWindowFont(const ToolWindowFontSettings &fontSettings) +{ + QDesignerSettingsInterface *s = settings(); + s->beginGroup(QLatin1String("UI")); + s->setValue(QLatin1String("font"), fontSettings.m_font); + s->setValue(QLatin1String("useFont"), fontSettings.m_useFont); + s->setValue(QLatin1String("writingSystem"), fontSettings.m_writingSystem); + s->endGroup(); +} + +ToolWindowFontSettings QDesignerSettings::toolWindowFont() const +{ + ToolWindowFontSettings fontSettings; + fontSettings.m_writingSystem = + static_cast(value(QLatin1String("UI/writingSystem"), + QFontDatabase::Any).toInt()); + fontSettings.m_font = qVariantValue(value(QLatin1String("UI/font"))); + fontSettings.m_useFont = + settings()->value(QLatin1String("UI/useFont"), QVariant(false)).toBool(); + return fontSettings; +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_settings.h b/designer/designer/qdesigner_settings.h new file mode 100644 index 0000000..40035c3 --- /dev/null +++ b/designer/designer/qdesigner_settings.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_SETTINGS_H +#define QDESIGNER_SETTINGS_H + +#include "designer_enums.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettingsInterface; +struct ToolWindowFontSettings; + +class QDesignerSettings : public qdesigner_internal::QDesignerSharedSettings +{ +public: + QDesignerSettings(QDesignerFormEditorInterface *core); + + void setValue(const QString &key, const QVariant &value); + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + + void restoreGeometry(QWidget *w, QRect fallBack = QRect()) const; + void saveGeometryFor(const QWidget *w); + + QStringList recentFilesList() const; + void setRecentFilesList(const QStringList &list); + + void setShowNewFormOnStartup(bool showIt); + bool showNewFormOnStartup() const; + + void setUiMode(UIMode mode); + UIMode uiMode() const; + + void setToolWindowFont(const ToolWindowFontSettings &fontSettings); + ToolWindowFontSettings toolWindowFont() const; + + QByteArray mainWindowState(UIMode mode) const; + void setMainWindowState(UIMode mode, const QByteArray &mainWindowState); + + QByteArray toolBarsState(UIMode mode) const; + void setToolBarsState(UIMode mode, const QByteArray &mainWindowState); + + void clearBackup(); + void setBackup(const QMap &map); + QMap backup() const; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_SETTINGS_H diff --git a/designer/designer/qdesigner_toolwindow.cpp b/designer/designer/qdesigner_toolwindow.cpp new file mode 100644 index 0000000..c5ba89d --- /dev/null +++ b/designer/designer/qdesigner_toolwindow.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner.h" +#include "qdesigner_toolwindow.h" +#include "qdesigner_settings.h" +#include "qdesigner_workbench.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +enum { debugToolWindow = 0 }; + +QT_BEGIN_NAMESPACE + +// ---------------- QDesignerToolWindowFontSettings +ToolWindowFontSettings::ToolWindowFontSettings() : + m_writingSystem(QFontDatabase::Any), + m_useFont(false) +{ +} + +bool ToolWindowFontSettings::equals(const ToolWindowFontSettings &rhs) const +{ + return m_useFont == rhs.m_useFont && + m_writingSystem == rhs.m_writingSystem && + m_font == rhs.m_font; +} + +// ---------------- QDesignerToolWindow +QDesignerToolWindow::QDesignerToolWindow(QDesignerWorkbench *workbench, + QWidget *w, + const QString &objectName, + const QString &title, + const QString &actionObjectName, + Qt::DockWidgetArea dockAreaHint, + QWidget *parent, + Qt::WindowFlags flags) : + MainWindowBase(parent, flags), + m_dockAreaHint(dockAreaHint), + m_workbench(workbench), + m_action(new QAction(this)) +{ + setObjectName(objectName); + setCentralWidget(w); + + setWindowTitle(title); + + m_action->setObjectName(actionObjectName); + m_action->setShortcutContext(Qt::ApplicationShortcut); + m_action->setText(title); + m_action->setCheckable(true); + connect(m_action, SIGNAL(triggered(bool)), this, SLOT(showMe(bool))); +} + +void QDesignerToolWindow::showMe(bool v) +{ + // Access the QMdiSubWindow in MDI mode. + if (QWidget *target = m_workbench->mode() == DockedMode ? parentWidget() : this) { + if (v) + target->setWindowState(target->windowState() & ~Qt::WindowMinimized); + target->setVisible(v); + } +} + +void QDesignerToolWindow::showEvent(QShowEvent *e) +{ + Q_UNUSED(e); + + bool blocked = m_action->blockSignals(true); + m_action->setChecked(true); + m_action->blockSignals(blocked); +} + +void QDesignerToolWindow::hideEvent(QHideEvent *e) +{ + Q_UNUSED(e); + + bool blocked = m_action->blockSignals(true); + m_action->setChecked(false); + m_action->blockSignals(blocked); +} + +QAction *QDesignerToolWindow::action() const +{ + return m_action; +} + +void QDesignerToolWindow::changeEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::WindowTitleChange: + m_action->setText(windowTitle()); + break; + case QEvent::WindowIconChange: + m_action->setIcon(windowIcon()); + break; + default: + break; + } + QMainWindow::changeEvent(e); +} + +QDesignerWorkbench *QDesignerToolWindow::workbench() const +{ + return m_workbench; +} + +QRect QDesignerToolWindow::geometryHint() const +{ + return QRect(); +} + +QRect QDesignerToolWindow::availableToolWindowGeometry() const +{ + return m_workbench->availableGeometry(); +} + +// ---------------------- PropertyEditorToolWindow + +static inline QWidget *createPropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0) +{ + QDesignerPropertyEditorInterface *widget = QDesignerComponents::createPropertyEditor(core, parent); + core->setPropertyEditor(widget); + return widget; +} + +class PropertyEditorToolWindow : public QDesignerToolWindow +{ +public: + explicit PropertyEditorToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; + +protected: + virtual void showEvent(QShowEvent *event); +}; + +PropertyEditorToolWindow::PropertyEditorToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + createPropertyEditor(workbench->core()), + QLatin1String("qt_designer_propertyeditor"), + QDesignerToolWindow::tr("Property Editor"), + QLatin1String("__qt_property_editor_action"), + Qt::RightDockWidgetArea) +{ + action()->setShortcut(Qt::CTRL + Qt::Key_I); + +} + +QRect PropertyEditorToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + const int spacing = 40; + const QSize sz(g.width() * 1/4, g.height() * 4/6); + + const QRect rc = QRect((g.right() + 1 - sz.width() - margin), + (g.top() + margin + g.height() * 1/6) + spacing, + sz.width(), sz.height()); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << rc; + return rc; +} + +void PropertyEditorToolWindow::showEvent(QShowEvent *event) +{ + if (QDesignerPropertyEditorInterface *e = workbench()->core()->propertyEditor()) { + // workaround to update the propertyeditor when it is not visible! + e->setObject(e->object()); // ### remove me + } + + QDesignerToolWindow::showEvent(event); +} + +// ---------------------- ActionEditorToolWindow + +static inline QWidget *createActionEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0) +{ + QDesignerActionEditorInterface *widget = QDesignerComponents::createActionEditor(core, parent); + core->setActionEditor(widget); + return widget; +} + +class ActionEditorToolWindow: public QDesignerToolWindow +{ +public: + explicit ActionEditorToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; +}; + +ActionEditorToolWindow::ActionEditorToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + createActionEditor(workbench->core()), + QLatin1String("qt_designer_actioneditor"), + QDesignerToolWindow::tr("Action Editor"), + QLatin1String("__qt_action_editor_tool_action"), + Qt::RightDockWidgetArea) +{ +} + +QRect ActionEditorToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + + const QSize sz(g.width() * 1/4, g.height() * 1/6); + + const QRect rc = QRect((g.right() + 1 - sz.width() - margin), + g.top() + margin, + sz.width(), sz.height()); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << rc; + return rc; +} + +// ---------------------- ObjectInspectorToolWindow + +static inline QWidget *createObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent = 0) +{ + QDesignerObjectInspectorInterface *widget = QDesignerComponents::createObjectInspector(core, parent); + core->setObjectInspector(widget); + return widget; +} + +class ObjectInspectorToolWindow: public QDesignerToolWindow +{ +public: + explicit ObjectInspectorToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; +}; + +ObjectInspectorToolWindow::ObjectInspectorToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + createObjectInspector(workbench->core()), + QLatin1String("qt_designer_objectinspector"), + QDesignerToolWindow::tr("Object Inspector"), + QLatin1String("__qt_object_inspector_tool_action"), + Qt::RightDockWidgetArea) +{ +} + +QRect ObjectInspectorToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + + const QSize sz(g.width() * 1/4, g.height() * 1/6); + + const QRect rc = QRect((g.right() + 1 - sz.width() - margin), + g.top() + margin, + sz.width(), sz.height()); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << rc; + return rc; +} + +// ---------------------- ResourceEditorToolWindow + +class ResourceEditorToolWindow: public QDesignerToolWindow +{ +public: + explicit ResourceEditorToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; +}; + +ResourceEditorToolWindow::ResourceEditorToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + QDesignerComponents::createResourceEditor(workbench->core(), 0), + QLatin1String("qt_designer_resourceeditor"), + QDesignerToolWindow::tr("Resource Browser"), + QLatin1String("__qt_resource_editor_tool_action"), + Qt::RightDockWidgetArea) +{ +} + +QRect ResourceEditorToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + + const QSize sz(g.width() * 1/3, g.height() * 1/6); + QRect r(QPoint(0, 0), sz); + r.moveCenter(g.center()); + r.moveBottom(g.bottom() - margin); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << r; + return r; +} + +// ---------------------- SignalSlotEditorToolWindow + +class SignalSlotEditorToolWindow: public QDesignerToolWindow +{ +public: + explicit SignalSlotEditorToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; +}; + +SignalSlotEditorToolWindow::SignalSlotEditorToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + QDesignerComponents::createSignalSlotEditor(workbench->core(), 0), + QLatin1String("qt_designer_signalsloteditor"), + QDesignerToolWindow::tr("Signal/Slot Editor"), + QLatin1String("__qt_signal_slot_editor_tool_action"), + Qt::RightDockWidgetArea) +{ +} + +QRect SignalSlotEditorToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + + const QSize sz(g.width() * 1/3, g.height() * 1/6); + QRect r(QPoint(0, 0), sz); + r.moveCenter(g.center()); + r.moveTop(margin + g.top()); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << r; + return r; +} + +// ---------------------- WidgetBoxToolWindow + +static inline QWidget *createWidgetBox(QDesignerFormEditorInterface *core, QWidget *parent = 0) +{ + QDesignerWidgetBoxInterface *widget = QDesignerComponents::createWidgetBox(core, parent); + core->setWidgetBox(widget); + return widget; +} + +class WidgetBoxToolWindow: public QDesignerToolWindow +{ +public: + explicit WidgetBoxToolWindow(QDesignerWorkbench *workbench); + + virtual QRect geometryHint() const; +}; + +WidgetBoxToolWindow::WidgetBoxToolWindow(QDesignerWorkbench *workbench) : + QDesignerToolWindow(workbench, + createWidgetBox(workbench->core()), + QLatin1String("qt_designer_widgetbox"), + QDesignerToolWindow::tr("Widget Box"), + QLatin1String("__qt_widget_box_tool_action"), + Qt::LeftDockWidgetArea) +{ +} + +QRect WidgetBoxToolWindow::geometryHint() const +{ + const QRect g = availableToolWindowGeometry(); + const int margin = workbench()->marginHint(); + const QRect rc = QRect(g.left() + margin, + g.top() + margin, + g.width() * 1/4, g.height() * 5/6); + if (debugToolWindow) + qDebug() << Q_FUNC_INFO << rc; + return rc; +} + +// -- Factory +QDesignerToolWindow *QDesignerToolWindow::createStandardToolWindow(StandardToolWindow which, + QDesignerWorkbench *workbench) +{ + switch (which) { + case ActionEditor: + return new ActionEditorToolWindow(workbench); + case ResourceEditor: + return new ResourceEditorToolWindow(workbench); + case SignalSlotEditor: + return new SignalSlotEditorToolWindow(workbench); + case PropertyEditor: + return new PropertyEditorToolWindow(workbench); + case ObjectInspector: + return new ObjectInspectorToolWindow(workbench); + case WidgetBox: + return new WidgetBoxToolWindow(workbench); + default: + break; + } + return 0; +} + + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_toolwindow.h b/designer/designer/qdesigner_toolwindow.h new file mode 100644 index 0000000..29d1a05 --- /dev/null +++ b/designer/designer/qdesigner_toolwindow.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_TOOLWINDOW_H +#define QDESIGNER_TOOLWINDOW_H + +#include "mainwindow.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct ToolWindowFontSettings { + ToolWindowFontSettings(); + bool equals(const ToolWindowFontSettings &) const; + + QFont m_font; + QFontDatabase::WritingSystem m_writingSystem; + bool m_useFont; +}; + +inline bool operator==(const ToolWindowFontSettings &tw1, const ToolWindowFontSettings &tw2) +{ + return tw1.equals(tw2); +} + +inline bool operator!=(const ToolWindowFontSettings &tw1, const ToolWindowFontSettings &tw2) +{ + return !tw1.equals(tw2); +} + +class QDesignerWorkbench; + +/* A tool window with an action that activates it. Note that in toplevel mode, + * the Widget box is a tool window as well as the applications' main window, + * So, we need to inherit from MainWindowBase. */ + +class QDesignerToolWindow : public MainWindowBase +{ + Q_OBJECT +protected: + explicit QDesignerToolWindow(QDesignerWorkbench *workbench, + QWidget *w, + const QString &objectName, + const QString &title, + const QString &actionObjectName, + Qt::DockWidgetArea dockAreaHint, + QWidget *parent = 0, + Qt::WindowFlags flags = Qt::Window); + +public: + // Note: The order influences the dock widget position. + enum StandardToolWindow { WidgetBox, ObjectInspector, PropertyEditor, + ResourceEditor, ActionEditor, SignalSlotEditor, + StandardToolWindowCount }; + + static QDesignerToolWindow *createStandardToolWindow(StandardToolWindow which, QDesignerWorkbench *workbench); + + QDesignerWorkbench *workbench() const; + QAction *action() const; + + Qt::DockWidgetArea dockWidgetAreaHint() const { return m_dockAreaHint; } + virtual QRect geometryHint() const; + +private slots: + void showMe(bool); + +protected: + virtual void showEvent(QShowEvent *e); + virtual void hideEvent(QHideEvent *e); + virtual void changeEvent(QEvent *e); + + QRect availableToolWindowGeometry() const; + +private: + const Qt::DockWidgetArea m_dockAreaHint; + QDesignerWorkbench *m_workbench; + QAction *m_action; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_TOOLWINDOW_H diff --git a/designer/designer/qdesigner_workbench.cpp b/designer/designer/qdesigner_workbench.cpp new file mode 100644 index 0000000..25b77b0 --- /dev/null +++ b/designer/designer/qdesigner_workbench.cpp @@ -0,0 +1,1098 @@ +锘/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_workbench.h" +#include "qdesigner.h" +#include "qdesigner_actions.h" +#include "qdesigner_appearanceoptions.h" +#include "qdesigner_settings.h" +#include "qdesigner_toolwindow.h" +#include "qdesigner_formwindow.h" +#include "appfontdialog.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *appFontPrefixC = "AppFonts"; + +typedef QList ActionList; + +static QMdiSubWindow *mdiSubWindowOf(const QWidget *w) +{ + QMdiSubWindow *rc = qobject_cast(w->parentWidget()); + Q_ASSERT(rc); + return rc; +} + +static QDockWidget *dockWidgetOf(const QWidget *w) +{ + for (QWidget *parentWidget = w->parentWidget(); parentWidget ; parentWidget = parentWidget->parentWidget()) { + if (QDockWidget *dw = qobject_cast(parentWidget)) { + return dw; + } + } + Q_ASSERT("Dock widget not found"); + return 0; +} + +// ------------ QDesignerWorkbench::Position +QDesignerWorkbench::Position::Position(const QMdiSubWindow *mdiSubWindow, const QPoint &mdiAreaOffset) : + m_minimized(mdiSubWindow->isShaded()), + m_position(mdiSubWindow->pos() + mdiAreaOffset) +{ +} + +QDesignerWorkbench::Position::Position(const QDockWidget *dockWidget) : + m_minimized(dockWidget->isMinimized()), + m_position(dockWidget->pos()) +{ +} + +QDesignerWorkbench::Position::Position(const QWidget *topLevelWindow, const QPoint &desktopTopLeft) +{ + const QWidget *window =topLevelWindow->window (); + Q_ASSERT(window); + m_minimized = window->isMinimized(); + m_position = window->pos() - desktopTopLeft; +} + +void QDesignerWorkbench::Position::applyTo(QMdiSubWindow *mdiSubWindow, + const QPoint &mdiAreaOffset) const +{ + // QMdiSubWindow attempts to resize its children to sizeHint() when switching user interface modes. + // Restore old size + const QPoint mdiAreaPos = QPoint(qMax(0, m_position.x() - mdiAreaOffset.x()), + qMax(0, m_position.y() - mdiAreaOffset.y())); + mdiSubWindow->move(mdiAreaPos); + const QSize decorationSize = mdiSubWindow->size() - mdiSubWindow->contentsRect().size(); + mdiSubWindow->resize(mdiSubWindow->widget()->size() + decorationSize); + mdiSubWindow->show(); + if (m_minimized) { + mdiSubWindow->showShaded(); + } +} + +void QDesignerWorkbench::Position::applyTo(QWidget *topLevelWindow, const QPoint &desktopTopLeft) const +{ + QWidget *window = topLevelWindow->window (); + const QPoint newPos = m_position + desktopTopLeft; + window->move(newPos); + if ( m_minimized) { + topLevelWindow->showMinimized(); + } else { + topLevelWindow->show(); + } +} + +void QDesignerWorkbench::Position::applyTo(QDockWidget *dockWidget) const +{ + dockWidget->widget()->setVisible(true); + dockWidget->setVisible(!m_minimized); +} + +static inline void addActionsToMenu(QMenu *m, const ActionList &al) +{ + const ActionList::const_iterator cend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != cend; ++it) + m->addAction(*it); +} + +static inline QMenu *addMenu(QMenuBar *mb, const QString &title, const ActionList &al) +{ + QMenu *rc = mb->addMenu(title); + addActionsToMenu(rc, al); + return rc; +} + +// -------- QDesignerWorkbench + +QDesignerWorkbench::QDesignerWorkbench() : + m_core(QDesignerComponents::createFormEditor(this)), + m_windowActions(new QActionGroup(this)), + m_globalMenuBar(new QMenuBar), + m_mode(NeutralMode), + m_dockedMainWindow(0), + m_state(StateInitializing) +{ + QDesignerSettings settings(m_core); + + (void) QDesignerComponents::createTaskMenu(core(), this); + + initializeCorePlugins(); + QDesignerComponents::initializePlugins(core()); + m_actionManager = new QDesignerActions(this); // accesses plugin components + + m_windowActions->setExclusive(true); + connect(m_windowActions, SIGNAL(triggered(QAction*)), this, SLOT(formWindowActionTriggered(QAction*))); + + // Build main menu bar + addMenu(m_globalMenuBar, tr("&File"), m_actionManager->fileActions()->actions()); + + QMenu *editMenu = addMenu(m_globalMenuBar, tr("Edit"), m_actionManager->editActions()->actions()); + editMenu->addSeparator(); + addActionsToMenu(editMenu, m_actionManager->toolActions()->actions()); + + QMenu *formMenu = addMenu(m_globalMenuBar, tr("F&orm"), m_actionManager->formActions()->actions()); + QMenu *previewSubMenu = new QMenu(tr("Preview in"), formMenu); + formMenu->insertMenu(m_actionManager->previewFormAction(), previewSubMenu); + addActionsToMenu(previewSubMenu, m_actionManager->styleActions()->actions()); + + QMenu *viewMenu = m_globalMenuBar->addMenu(tr("&View")); + + addMenu(m_globalMenuBar, tr("&Settings"), m_actionManager->settingsActions()->actions()); + + m_windowMenu = addMenu(m_globalMenuBar, tr("&Window"), m_actionManager->windowActions()->actions()); + + addMenu(m_globalMenuBar, tr("&Help"), m_actionManager->helpActions()->actions()); + + // Add the tools in view menu order + QActionGroup *viewActions = new QActionGroup(this); + viewActions->setExclusive(false); + + for (int i = 0; i < QDesignerToolWindow::StandardToolWindowCount; i++) { + QDesignerToolWindow *toolWindow = QDesignerToolWindow::createStandardToolWindow(static_cast< QDesignerToolWindow::StandardToolWindow>(i), this); + m_toolWindows.push_back(toolWindow); + if (QAction *action = toolWindow->action()) { + viewMenu->addAction(action); + viewActions->addAction(action); + } + // The widget box becomes the main window in top level mode + if (i == QDesignerToolWindow::WidgetBox) + connect(toolWindow, SIGNAL(closeEventReceived(QCloseEvent*)), this, SLOT(handleCloseEvent(QCloseEvent*))); + } + // Integration + m_integration = new qdesigner_internal::QDesignerIntegration(m_core, this); + connect(m_integration, SIGNAL(helpRequested(QString,QString)), m_actionManager, SLOT(helpRequested(QString,QString))); + + // remaining view options (config toolbars) + viewMenu->addSeparator(); + m_toolbarMenu = viewMenu->addMenu(tr("Toolbars")); + + emit initialized(); + + connect(m_core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(updateWindowMenu(QDesignerFormWindowInterface*))); + + + { // Add application specific options pages + QDesignerAppearanceOptionsPage *appearanceOptions = new QDesignerAppearanceOptionsPage(m_core); + connect(appearanceOptions, SIGNAL(settingsChangedDelayed()), this, SLOT(restoreUISettings())); + QList optionsPages = m_core->optionsPages(); + optionsPages.push_front(appearanceOptions); + m_core->setOptionsPages(optionsPages); + } + + restoreUISettings(); + AppFontWidget::restore(m_core->settingsManager(), QLatin1String(appFontPrefixC)); + m_state = StateUp; +} + +QDesignerWorkbench::~QDesignerWorkbench() +{ + switch (m_mode) { + case NeutralMode: + case DockedMode: + qDeleteAll(m_toolWindows); + break; + case TopLevelMode: // Everything parented here + delete widgetBoxToolWindow(); + break; + } +} + +void QDesignerWorkbench::saveGeometriesForModeChange() +{ + m_Positions.clear(); + switch (m_mode) { + case NeutralMode: + break; + case TopLevelMode: { + const QPoint desktopOffset = QApplication::desktop()->availableGeometry().topLeft(); + foreach (QDesignerToolWindow *tw, m_toolWindows) + m_Positions.insert(tw, Position(tw, desktopOffset)); + foreach (QDesignerFormWindow *fw, m_formWindows) { + m_Positions.insert(fw, Position(fw, desktopOffset)); + } + } + break; + case DockedMode: { + const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos(); + foreach (QDesignerToolWindow *tw, m_toolWindows) { + m_Positions.insert(tw, Position(dockWidgetOf(tw))); + } + foreach (QDesignerFormWindow *fw, m_formWindows) { + m_Positions.insert(fw, Position(mdiSubWindowOf(fw), mdiAreaOffset)); + } + } + break; + } +} + +UIMode QDesignerWorkbench::mode() const +{ + return m_mode; +} + +void QDesignerWorkbench::addFormWindow(QDesignerFormWindow *formWindow) +{ + // ### Q_ASSERT(formWindow->windowTitle().isEmpty() == false); + + m_formWindows.append(formWindow); + + + m_actionManager->setWindowListSeparatorVisible(true); + + if (QAction *action = formWindow->action()) { + m_windowActions->addAction(action); + m_windowMenu->addAction(action); + action->setChecked(true); + } + + m_actionManager->minimizeAction()->setEnabled(true); + m_actionManager->minimizeAction()->setChecked(false); + connect(formWindow, SIGNAL(minimizationStateChanged(QDesignerFormWindowInterface*,bool)), + this, SLOT(minimizationStateChanged(QDesignerFormWindowInterface*,bool))); + + m_actionManager->editWidgets()->trigger(); +} + +Qt::WindowFlags QDesignerWorkbench::magicalWindowFlags(const QWidget *widgetForFlags) const +{ + switch (m_mode) { + case TopLevelMode: { +#ifdef Q_WS_MAC + if (qobject_cast(widgetForFlags)) + return Qt::Tool; +#else + Q_UNUSED(widgetForFlags); +#endif + return Qt::Window; + } + case DockedMode: + return Qt::Window | Qt::WindowShadeButtonHint | Qt::WindowSystemMenuHint | Qt::WindowTitleHint; + case NeutralMode: + return Qt::Window; + default: + Q_ASSERT(0); + return 0; + } +} + +QWidget *QDesignerWorkbench::magicalParent(const QWidget *w) const +{ + switch (m_mode) { + case TopLevelMode: { + // Use widget box as parent for all windows except self. This will + // result in having just one entry in the MS Windows task bar. + QWidget *widgetBoxWrapper = widgetBoxToolWindow(); + return w == widgetBoxWrapper ? 0 : widgetBoxWrapper; + } + case DockedMode: + return m_dockedMainWindow->mdiArea(); + case NeutralMode: + return 0; + default: + Q_ASSERT(0); + return 0; + } +} + +void QDesignerWorkbench::switchToNeutralMode() +{ + QDesignerSettings settings(m_core); + saveGeometries(settings); + saveGeometriesForModeChange(); + + if (m_mode == TopLevelMode) { + delete m_topLevelData.toolbarManager; + m_topLevelData.toolbarManager = 0; + qDeleteAll(m_topLevelData.toolbars); + m_topLevelData.toolbars.clear(); + } + + m_mode = NeutralMode; + + foreach (QDesignerToolWindow *tw, m_toolWindows) { + tw->setCloseEventPolicy(MainWindowBase::AcceptCloseEvents); + tw->setParent(0); + } + + foreach (QDesignerFormWindow *fw, m_formWindows) { + fw->setParent(0); + fw->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } + +#ifndef Q_WS_MAC + m_globalMenuBar->setParent(0); +#endif + + m_core->setTopLevel(0); + qDesigner->setMainWindow(0); + + delete m_dockedMainWindow; + m_dockedMainWindow = 0; +} + +void QDesignerWorkbench::switchToDockedMode() +{ + if (m_mode == DockedMode) + return; + + switchToNeutralMode(); + +#ifndef Q_WS_MAC + QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow(); + widgetBoxWrapper->action()->setVisible(true); + widgetBoxWrapper->setWindowTitle(tr("Widget Box")); +#endif + + m_mode = DockedMode; + const QDesignerSettings settings(m_core); + m_dockedMainWindow = new DockedMainWindow(this, m_toolbarMenu, m_toolWindows); + m_dockedMainWindow->setUnifiedTitleAndToolBarOnMac(true); + m_dockedMainWindow->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal); + connect(m_dockedMainWindow, SIGNAL(closeEventReceived(QCloseEvent*)), this, SLOT(handleCloseEvent(QCloseEvent*))); + connect(m_dockedMainWindow, SIGNAL(fileDropped(QString)), this, SLOT(slotFileDropped(QString))); + connect(m_dockedMainWindow, SIGNAL(formWindowActivated(QDesignerFormWindow*)), this, SLOT(slotFormWindowActivated(QDesignerFormWindow*))); + m_dockedMainWindow->restoreSettings(settings, m_dockedMainWindow->addToolWindows(m_toolWindows), desktopGeometry()); + + m_core->setTopLevel(m_dockedMainWindow); + +#ifndef Q_WS_MAC + m_dockedMainWindow->setMenuBar(m_globalMenuBar); + m_globalMenuBar->show(); +#endif + qDesigner->setMainWindow(m_dockedMainWindow); + + foreach (QDesignerFormWindow *fw, m_formWindows) { + QMdiSubWindow *subwin = m_dockedMainWindow->createMdiSubWindow(fw, magicalWindowFlags(fw), + m_actionManager->closeFormAction()->shortcut()); + subwin->hide(); + if (QWidget *mainContainer = fw->editor()->mainContainer()) + resizeForm(fw, mainContainer); + } + + m_actionManager->setBringAllToFrontVisible(false); + m_dockedMainWindow->show(); + // Trigger adjustMDIFormPositions() delayed as viewport size is not yet known. + + if (m_state != StateInitializing) + QMetaObject::invokeMethod(this, "adjustMDIFormPositions", Qt::QueuedConnection); +} + +void QDesignerWorkbench::adjustMDIFormPositions() +{ + const QPoint mdiAreaOffset = m_dockedMainWindow->mdiArea()->pos(); + + foreach (QDesignerFormWindow *fw, m_formWindows) { + const PositionMap::const_iterator pit = m_Positions.constFind(fw); + if (pit != m_Positions.constEnd()) + pit->applyTo(mdiSubWindowOf(fw), mdiAreaOffset); + } +} + +void QDesignerWorkbench::switchToTopLevelMode() +{ + if (m_mode == TopLevelMode) + return; + + // make sure that the widgetbox is visible if it is different from neutral. + QDesignerToolWindow *widgetBoxWrapper = widgetBoxToolWindow(); + Q_ASSERT(widgetBoxWrapper); + + switchToNeutralMode(); + const QPoint desktopOffset = desktopGeometry().topLeft(); + m_mode = TopLevelMode; + + // The widget box is special, it gets the menubar and gets to be the main widget. + + m_core->setTopLevel(widgetBoxWrapper); +#ifndef Q_WS_MAC + widgetBoxWrapper->setMenuBar(m_globalMenuBar); + widgetBoxWrapper->action()->setVisible(false); + widgetBoxWrapper->setCloseEventPolicy(MainWindowBase::EmitCloseEventSignal); + qDesigner->setMainWindow(widgetBoxWrapper); + widgetBoxWrapper->setWindowTitle(MainWindowBase::mainWindowTitle()); +#endif + + const QDesignerSettings settings(m_core); + m_topLevelData.toolbars = MainWindowBase::createToolBars(m_actionManager, false); + m_topLevelData.toolbarManager = new ToolBarManager(widgetBoxWrapper, widgetBoxWrapper, + m_toolbarMenu, m_actionManager, + m_topLevelData.toolbars, m_toolWindows); + const int toolBarCount = m_topLevelData.toolbars.size(); + for (int i = 0; i < toolBarCount; i++) { + widgetBoxWrapper->addToolBar(m_topLevelData.toolbars.at(i)); + if (i == 3) + widgetBoxWrapper->insertToolBarBreak(m_topLevelData.toolbars.at(i)); + } + m_topLevelData.toolbarManager->restoreState(settings.toolBarsState(m_mode), MainWindowBase::settingsVersion()); + widgetBoxWrapper->restoreState(settings.mainWindowState(m_mode), MainWindowBase::settingsVersion()); + + bool found_visible_window = false; + foreach (QDesignerToolWindow *tw, m_toolWindows) { + tw->setParent(magicalParent(tw), magicalWindowFlags(tw)); + settings.restoreGeometry(tw, tw->geometryHint()); + tw->action()->setChecked(tw->isVisible()); + found_visible_window |= tw->isVisible(); + } + + if (!m_toolWindows.isEmpty() && !found_visible_window) + m_toolWindows.first()->show(); + + m_actionManager->setBringAllToFrontVisible(true); + + foreach (QDesignerFormWindow *fw, m_formWindows) { + fw->setParent(magicalParent(fw), magicalWindowFlags(fw)); + fw->setAttribute(Qt::WA_DeleteOnClose, true); + const PositionMap::const_iterator pit = m_Positions.constFind(fw); + if (pit != m_Positions.constEnd()) pit->applyTo(fw, desktopOffset); + // Force an activate in order to refresh minimumSize, otherwise it will not be respected + if (QLayout *layout = fw->layout()) + layout->invalidate(); + if (QWidget *mainContainer = fw->editor()->mainContainer()) + resizeForm(fw, mainContainer); + } +} + +QDesignerFormWindowManagerInterface *QDesignerWorkbench::formWindowManager() const +{ + return m_core->formWindowManager(); +} + +QDesignerFormEditorInterface *QDesignerWorkbench::core() const +{ + return m_core; +} + +int QDesignerWorkbench::toolWindowCount() const +{ + return m_toolWindows.count(); +} + +QDesignerToolWindow *QDesignerWorkbench::toolWindow(int index) const +{ + return m_toolWindows.at(index); +} + +int QDesignerWorkbench::formWindowCount() const +{ + return m_formWindows.count(); +} + +QDesignerFormWindow *QDesignerWorkbench::formWindow(int index) const +{ + return m_formWindows.at(index); +} + +QRect QDesignerWorkbench::desktopGeometry() const +{ + // Return geometry of the desktop designer is running in. + QWidget *widget = 0; + switch (m_mode) { + case DockedMode: + widget = m_dockedMainWindow; + break; + case TopLevelMode: + widget = widgetBoxToolWindow(); + break; + case NeutralMode: + break; + } + const QDesktopWidget *desktop = qApp->desktop(); + const int screenNumber = widget ? desktop->screenNumber(widget) : 0; + return desktop->availableGeometry(screenNumber); +} + +QRect QDesignerWorkbench::availableGeometry() const +{ + if (m_mode == DockedMode) + return m_dockedMainWindow->mdiArea()->geometry(); + + const QDesktopWidget *desktop = qDesigner->desktop(); + return desktop->availableGeometry(desktop->screenNumber(widgetBoxToolWindow())); +} + +int QDesignerWorkbench::marginHint() const +{ return 20; +} + +void QDesignerWorkbench::slotFormWindowActivated(QDesignerFormWindow* fw) +{ + core()->formWindowManager()->setActiveFormWindow(fw->editor()); +} + +void QDesignerWorkbench::removeFormWindow(QDesignerFormWindow *formWindow) +{ + QDesignerFormWindowInterface *editor = formWindow->editor(); + const bool loadOk = editor->mainContainer(); + updateBackup(editor); + const int index = m_formWindows.indexOf(formWindow); + if (index != -1) { + m_formWindows.removeAt(index); + } + + if (QAction *action = formWindow->action()) { + m_windowActions->removeAction(action); + m_windowMenu->removeAction(action); + } + + if (m_formWindows.empty()) { + m_actionManager->setWindowListSeparatorVisible(false); + // Show up new form dialog unless closing + if (loadOk && m_state == StateUp + && QDesignerSettings(m_core).showNewFormOnStartup()) { + QTimer::singleShot(200, m_actionManager, SLOT(createForm())); + } + } +} + +void QDesignerWorkbench::initializeCorePlugins() +{ + QList plugins = QPluginLoader::staticInstances(); + plugins += core()->pluginManager()->instances(); + + foreach (QObject *plugin, plugins) { + if (QDesignerFormEditorPluginInterface *formEditorPlugin = qobject_cast(plugin)) { + if (!formEditorPlugin->isInitialized()) + formEditorPlugin->initialize(core()); + } + } +} + +void QDesignerWorkbench::saveSettings() const +{ + QDesignerSettings settings(m_core); + settings.clearBackup(); + saveGeometries(settings); + AppFontWidget::save(m_core->settingsManager(), QLatin1String(appFontPrefixC)); +} + +void QDesignerWorkbench::saveGeometries(QDesignerSettings &settings) const +{ + switch (m_mode) { + case DockedMode: + m_dockedMainWindow->saveSettings(settings); + break; + case TopLevelMode: + settings.setToolBarsState(m_mode, m_topLevelData.toolbarManager->saveState(MainWindowBase::settingsVersion())); + settings.setMainWindowState(m_mode, widgetBoxToolWindow()->saveState(MainWindowBase::settingsVersion())); + foreach (const QDesignerToolWindow *tw, m_toolWindows) + settings.saveGeometryFor(tw); + break; + case NeutralMode: + break; + } +} + +void QDesignerWorkbench::slotFileDropped(const QString &f) +{ + readInForm(f); +} + +bool QDesignerWorkbench::readInForm(const QString &fileName) const +{ + return m_actionManager->readInForm(fileName); +} + +bool QDesignerWorkbench::writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName) const +{ + return m_actionManager->writeOutForm(formWindow, fileName); +} + +bool QDesignerWorkbench::saveForm(QDesignerFormWindowInterface *frm) +{ + return m_actionManager->saveForm(frm); +} + +QDesignerFormWindow *QDesignerWorkbench::findFormWindow(QWidget *widget) const +{ + foreach (QDesignerFormWindow *formWindow, m_formWindows) { + if (formWindow->editor() == widget) + return formWindow; + } + + return 0; +} + +bool QDesignerWorkbench::handleClose() +{ + m_state = StateClosing; + QList dirtyForms; + foreach (QDesignerFormWindow *w, m_formWindows) { + if (w->editor()->isDirty()) + dirtyForms << w; + } + + if (dirtyForms.size()) { + if (dirtyForms.size() == 1) { + if (!dirtyForms.at(0)->close()) { + m_state = StateUp; + return false; + } + } else { + int count = dirtyForms.size(); + QMessageBox box(QMessageBox::Warning, tr("Save Forms?"), + tr("There are %n forms with unsaved changes." + " Do you want to review these changes before quitting?", "", count), + QMessageBox::Cancel | QMessageBox::Discard | QMessageBox::Save); + box.setInformativeText(tr("If you do not review your documents, all your changes will be lost.")); + box.button(QMessageBox::Discard)->setText(tr("Discard Changes")); + QPushButton *save = static_cast(box.button(QMessageBox::Save)); + save->setText(tr("Review Changes")); + box.setDefaultButton(save); + switch (box.exec()) { + case QMessageBox::Cancel: + m_state = StateUp; + return false; + case QMessageBox::Save: + foreach (QDesignerFormWindow *fw, dirtyForms) { + fw->show(); + fw->raise(); + if (!fw->close()) { + m_state = StateUp; + return false; + } + } + break; + case QMessageBox::Discard: + foreach (QDesignerFormWindow *fw, dirtyForms) { + fw->editor()->setDirty(false); + fw->setWindowModified(false); + } + break; + } + } + } + + foreach (QDesignerFormWindow *fw, m_formWindows) + fw->close(); + + saveSettings(); + return true; +} + +QDesignerActions *QDesignerWorkbench::actionManager() const +{ + return m_actionManager; +} + +void QDesignerWorkbench::updateWindowMenu(QDesignerFormWindowInterface *fwi) +{ + bool minimizeChecked = false; + bool minimizeEnabled = false; + QDesignerFormWindow *activeFormWindow = 0; + do { + if (!fwi) + break; + activeFormWindow = qobject_cast(fwi->parentWidget()); + if (!activeFormWindow) + break; + + minimizeEnabled = true; + minimizeChecked = isFormWindowMinimized(activeFormWindow); + } while (false) ; + + m_actionManager->minimizeAction()->setEnabled(minimizeEnabled); + m_actionManager->minimizeAction()->setChecked(minimizeChecked); + + if (!m_formWindows.empty()) { + const QList::const_iterator cend = m_formWindows.constEnd(); + for (QList::const_iterator it = m_formWindows.constBegin(); it != cend; ++it) + (*it)->action()->setChecked(*it == activeFormWindow); + } +} + +void QDesignerWorkbench::formWindowActionTriggered(QAction *a) +{ + QDesignerFormWindow *fw = qobject_cast(a->parentWidget()); + Q_ASSERT(fw); + + if (isFormWindowMinimized(fw)) + setFormWindowMinimized(fw, false); + + if (m_mode == DockedMode) { + if (QMdiSubWindow *subWindow = qobject_cast(fw->parent())) { + m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWindow); + } + } else { + fw->activateWindow(); + fw->raise(); + } +} + +void QDesignerWorkbench::closeAllToolWindows() +{ + foreach (QDesignerToolWindow *tw, m_toolWindows) + tw->hide(); +} + +bool QDesignerWorkbench::readInBackup() +{ + const QMap backupFileMap = QDesignerSettings(m_core).backup(); + if (backupFileMap.isEmpty()) + return false; + + const QMessageBox::StandardButton answer = + QMessageBox::question(0, tr("Backup Information"), + tr("The last session of Designer was not terminated correctly. " + "Backup files were left behind. Do you want to load them?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); + if (answer == QMessageBox::No) + return false; + + const QString modifiedPlaceHolder = QLatin1String("[*]"); + QMapIterator it(backupFileMap); + while(it.hasNext()) { + it.next(); + + QString fileName = it.key(); + fileName.remove(modifiedPlaceHolder); + + if(m_actionManager->readInForm(it.value())) + formWindowManager()->activeFormWindow()->setFileName(fileName); + + } + return true; +} + +void QDesignerWorkbench::updateBackup(QDesignerFormWindowInterface* fwi) +{ + QString fwn = QDir::convertSeparators(fwi->fileName()); + if (fwn.isEmpty()) + fwn = fwi->parentWidget()->windowTitle(); + + QDesignerSettings settings(m_core); + QMap map = settings.backup(); + map.remove(fwn); + settings.setBackup(map); +} + +namespace { + void raiseWindow(QWidget *w) { + if (w->isMinimized()) + w->setWindowState(w->windowState() & ~Qt::WindowMinimized); + w->raise(); + } +} + +void QDesignerWorkbench::bringAllToFront() +{ + if (m_mode != TopLevelMode) + return; + foreach(QDesignerToolWindow *tw, m_toolWindows) + raiseWindow(tw); + foreach(QDesignerFormWindow *dfw, m_formWindows) + raiseWindow(dfw); +} + +// Resize a form window taking MDI decorations into account +// Apply maximum size as there is no layout connection between +// the form's main container and the integration's outer +// container due to the tool widget stack. + +void QDesignerWorkbench::resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const +{ + const QSize containerSize = mainContainer->size(); + const QSize containerMinimumSize = mainContainer->minimumSize(); + const QSize containerMaximumSize = mainContainer->maximumSize(); + if (m_mode != DockedMode) { + fw->resize(containerSize); + fw->setMaximumSize(containerMaximumSize); + return; + } + // get decorations and resize MDI + QMdiSubWindow *mdiSubWindow = qobject_cast(fw->parent()); + Q_ASSERT(mdiSubWindow); + const QSize decorationSize = mdiSubWindow->geometry().size() - mdiSubWindow->contentsRect().size(); + mdiSubWindow->resize(containerSize + decorationSize); + // In Qt::RightToLeft mode, the window can grow to be partially hidden by the right border. + const int mdiAreaWidth = m_dockedMainWindow->mdiArea()->width(); + if (qApp->layoutDirection() == Qt::RightToLeft && mdiSubWindow->geometry().right() >= mdiAreaWidth) + mdiSubWindow->move(mdiAreaWidth - mdiSubWindow->width(), mdiSubWindow->pos().y()); + + if (containerMaximumSize == QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)) { + mdiSubWindow->setMaximumSize(containerMaximumSize); + } else { + mdiSubWindow->setMaximumSize(containerMaximumSize + decorationSize); + } +} + + +// Load a form or return 0 and do cleanup. file name and editor file +// name in case of loading a template file. + +QDesignerFormWindow * QDesignerWorkbench::loadForm(const QString &fileName, + bool detectLineTermiantorMode, + bool *uic3Converted, + QString *errorMessage) +{ + QFile file(fileName); + + qdesigner_internal::FormWindowBase::LineTerminatorMode mode = qdesigner_internal::FormWindowBase::NativeLineTerminator; + + if (detectLineTermiantorMode) { + if (file.open(QFile::ReadOnly)) { + const QString text = QString::fromUtf8(file.readLine()); + file.close(); + + const int lf = text.indexOf(QLatin1Char('\n')); + if (lf > 0 && text.at(lf-1) == QLatin1Char('\r')) { + mode = qdesigner_internal::FormWindowBase::CRLFLineTerminator; + } else if (lf >= 0) { + mode = qdesigner_internal::FormWindowBase::LFLineTerminator; + } + } + } + + if (!file.open(QFile::ReadOnly|QFile::Text)) { + *errorMessage = tr("The file %1 could not be opened.").arg(file.fileName()); + return 0; + } + + + // Create a form + QDesignerFormWindowManagerInterface *formWindowManager = m_core->formWindowManager(); + + QDesignerFormWindow *formWindow = new QDesignerFormWindow(/*formWindow=*/ 0, this); + addFormWindow(formWindow); + QDesignerFormWindowInterface *editor = formWindow->editor(); + Q_ASSERT(editor); + + // Temporarily set the file name. It is needed when converting a UIC 3 file. + // In this case, the file name will we be cleared on return to force a save box. + editor->setFileName(fileName); + editor->setContents(&file); + + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(editor)) + fwb->setLineTerminatorMode(mode); + + switch (m_mode) { + case DockedMode: { + // below code must be after above call to setContents(), because setContents() may popup warning dialogs which would cause + // mdi sub window activation (because of dialogs internal call to processEvent or such) + // That activation could have worse consequences, e.g. NULL resource set for active form) before the form is loaded + QMdiSubWindow *subWin = m_dockedMainWindow->createMdiSubWindow(formWindow, magicalWindowFlags(formWindow), m_actionManager->closeFormAction()->shortcut()); + m_dockedMainWindow->mdiArea()->setActiveSubWindow(subWin); + } + break; + case TopLevelMode: { + const QRect formWindowGeometryHint = formWindow->geometryHint(); + formWindow->setAttribute(Qt::WA_DeleteOnClose, true); + formWindow->setParent(magicalParent(formWindow), magicalWindowFlags(formWindow)); + formWindow->resize(formWindowGeometryHint.size()); + formWindow->move(availableGeometry().center() - formWindowGeometryHint.center()); + } + break; + case NeutralMode: + break; + } + + if (!editor->mainContainer()) { + removeFormWindow(formWindow); + formWindowManager->removeFormWindow(editor); + m_core->metaDataBase()->remove(editor); + *errorMessage = tr("The file %1 is not a valid Designer UI file.").arg(file.fileName()); + return 0; + } + *uic3Converted = editor->fileName().isEmpty(); + editor->setDirty(false); + resizeForm(formWindow, editor->mainContainer()); + formWindowManager->setActiveFormWindow(editor); + return formWindow; +} + + +QDesignerFormWindow * QDesignerWorkbench::openForm(const QString &fileName, QString *errorMessage) +{ + bool uic3Converted; + QDesignerFormWindow *rc =loadForm(fileName, true, &uic3Converted, errorMessage); + if (!rc) + return 0; + + if (!uic3Converted) + rc->editor()->setFileName(fileName); + rc->firstShow(); + return rc; +} + +QDesignerFormWindow * QDesignerWorkbench::openTemplate(const QString &templateFileName, + const QString &editorFileName, + QString *errorMessage) +{ + bool uic3Converted; + QDesignerFormWindow *rc =loadForm(templateFileName, false, &uic3Converted, errorMessage); + if (!rc) + return 0; + + if (!uic3Converted) + rc->editor()->setFileName(editorFileName); + + rc->firstShow(); + return rc; +} + +void QDesignerWorkbench::minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized) +{ + // refresh the minimize action state + if (core()->formWindowManager()->activeFormWindow() == formWindow) { + m_actionManager->minimizeAction()->setChecked(minimized); + } +} + +void QDesignerWorkbench::toggleFormMinimizationState() +{ + QDesignerFormWindowInterface *fwi = core()->formWindowManager()->activeFormWindow(); + if (!fwi || m_mode == NeutralMode) + return; + QDesignerFormWindow *fw = qobject_cast(fwi->parentWidget()); + Q_ASSERT(fw); + setFormWindowMinimized(fw, !isFormWindowMinimized(fw)); +} + +bool QDesignerWorkbench::isFormWindowMinimized(const QDesignerFormWindow *fw) +{ + switch (m_mode) { + case DockedMode: + return mdiSubWindowOf(fw)->isShaded(); + case TopLevelMode: + return fw->window()->isMinimized(); + default: + break; + } + return fw->isMinimized(); +} + +void QDesignerWorkbench::setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized) +{ + switch (m_mode) { + case DockedMode: { + QMdiSubWindow *mdiSubWindow = mdiSubWindowOf(fw); + if (minimized) { + mdiSubWindow->showShaded(); + } else { + mdiSubWindow->setWindowState(mdiSubWindow->windowState() & ~Qt::WindowMinimized); + } + } + break; + case TopLevelMode: { + QWidget *window = fw->window(); + if (window->isMinimized()) { + window->setWindowState(window->windowState() & ~Qt::WindowMinimized); + } else { + window->showMinimized(); + } + } + break; + default: + break; + } +} + +void QDesignerWorkbench::restoreUISettings() +{ + UIMode mode = QDesignerSettings(m_core).uiMode(); + switch (mode) { + case TopLevelMode: + switchToTopLevelMode(); + break; + case DockedMode: + switchToDockedMode(); + break; + + default: Q_ASSERT(0); + } + + ToolWindowFontSettings fontSettings = QDesignerSettings(m_core).toolWindowFont(); + const QFont &font = fontSettings.m_useFont ? fontSettings.m_font : qApp->font(); + + if (font == m_toolWindows.front()->font()) + return; + + foreach(QDesignerToolWindow *tw, m_toolWindows) + tw->setFont(font); +} + +void QDesignerWorkbench::handleCloseEvent(QCloseEvent *ev) +{ + ev->setAccepted(handleClose()); + if (ev->isAccepted()) + QMetaObject::invokeMethod(qDesigner, "quit", Qt::QueuedConnection); // We're going down! +} + +QDesignerToolWindow *QDesignerWorkbench::widgetBoxToolWindow() const +{ + return m_toolWindows.at(QDesignerToolWindow::WidgetBox); +} + +QT_END_NAMESPACE diff --git a/designer/designer/qdesigner_workbench.h b/designer/designer/qdesigner_workbench.h new file mode 100644 index 0000000..47a0313 --- /dev/null +++ b/designer/designer/qdesigner_workbench.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_WORKBENCH_H +#define QDESIGNER_WORKBENCH_H + +#include "designer_enums.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerActions; +class QDesignerToolWindow; +class QDesignerFormWindow; +class DockedMainWindow; +class QDesignerSettings; + +class QAction; +class QActionGroup; +class QDockWidget; +class QMenu; +class QMenuBar; +class QMainWindow; +class QToolBar; +class QMdiArea; +class QMdiSubWindow; +class QCloseEvent; +class QFont; +class QtToolBarManager; +class ToolBarManager; + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerFormWindowManagerInterface; + +namespace qdesigner_internal { +class QDesignerIntegration; +} + +class QDesignerWorkbench: public QObject +{ + Q_OBJECT + +public: + QDesignerWorkbench(); + virtual ~QDesignerWorkbench(); + + UIMode mode() const; + + QDesignerFormEditorInterface *core() const; + QDesignerFormWindow *findFormWindow(QWidget *widget) const; + + QDesignerFormWindow *openForm(const QString &fileName, QString *errorMessage); + QDesignerFormWindow *openTemplate(const QString &templateFileName, + const QString &editorFileName, + QString *errorMessage); + + int toolWindowCount() const; + QDesignerToolWindow *toolWindow(int index) const; + + int formWindowCount() const; + QDesignerFormWindow *formWindow(int index) const; + + QDesignerActions *actionManager() const; + + QActionGroup *modeActionGroup() const; + + QRect availableGeometry() const; + QRect desktopGeometry() const; + + int marginHint() const; + + bool readInForm(const QString &fileName) const; + bool writeOutForm(QDesignerFormWindowInterface *formWindow, const QString &fileName) const; + bool saveForm(QDesignerFormWindowInterface *fw); + bool handleClose(); + bool readInBackup(); + void updateBackup(QDesignerFormWindowInterface* fwi); + +signals: + void modeChanged(UIMode mode); + void initialized(); + +public slots: + void addFormWindow(QDesignerFormWindow *formWindow); + void removeFormWindow(QDesignerFormWindow *formWindow); + void bringAllToFront(); + void toggleFormMinimizationState(); + +private slots: + void switchToNeutralMode(); + void switchToDockedMode(); + void switchToTopLevelMode(); + void initializeCorePlugins(); + void handleCloseEvent(QCloseEvent *); + void slotFormWindowActivated(QDesignerFormWindow* fw); + void updateWindowMenu(QDesignerFormWindowInterface *fw); + void formWindowActionTriggered(QAction *a); + void adjustMDIFormPositions(); + void minimizationStateChanged(QDesignerFormWindowInterface *formWindow, bool minimized); + + void restoreUISettings(); + void slotFileDropped(const QString &f); + +private: + QWidget *magicalParent(const QWidget *w) const; + Qt::WindowFlags magicalWindowFlags(const QWidget *widgetForFlags) const; + QDesignerFormWindowManagerInterface *formWindowManager() const; + void closeAllToolWindows(); + QDesignerToolWindow *widgetBoxToolWindow() const; + QDesignerFormWindow *loadForm(const QString &fileName, bool detectLineTermiantorMode, bool *uic3Converted, QString *errorMessage); + void resizeForm(QDesignerFormWindow *fw, const QWidget *mainContainer) const; + void saveGeometriesForModeChange(); + void saveGeometries(QDesignerSettings &settings) const; + + bool isFormWindowMinimized(const QDesignerFormWindow *fw); + void setFormWindowMinimized(QDesignerFormWindow *fw, bool minimized); + void saveSettings() const; + + QDesignerFormEditorInterface *m_core; + qdesigner_internal::QDesignerIntegration *m_integration; + + QDesignerActions *m_actionManager; + QActionGroup *m_windowActions; + + QMenu *m_windowMenu; + + QMenuBar *m_globalMenuBar; + + struct TopLevelData { + ToolBarManager *toolbarManager; + QList toolbars; + }; + TopLevelData m_topLevelData; + + UIMode m_mode; + DockedMainWindow *m_dockedMainWindow; + + QList m_toolWindows; + QList m_formWindows; + + QMenu *m_toolbarMenu; + + // Helper class to remember the position of a window while switching user + // interface modes. + class Position { + public: + Position(const QDockWidget *dockWidget); + Position(const QMdiSubWindow *mdiSubWindow, const QPoint &mdiAreaOffset); + Position(const QWidget *topLevelWindow, const QPoint &desktopTopLeft); + + void applyTo(QMdiSubWindow *mdiSubWindow, const QPoint &mdiAreaOffset) const; + void applyTo(QWidget *topLevelWindow, const QPoint &desktopTopLeft) const; + void applyTo(QDockWidget *dockWidget) const; + + QPoint position() const { return m_position; } + private: + bool m_minimized; + // Position referring to top-left corner (desktop in top-level mode or + // main window in MDI mode) + QPoint m_position; + }; + typedef QHash PositionMap; + PositionMap m_Positions; + + enum State { StateInitializing, StateUp, StateClosing }; + State m_state; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_WORKBENCH_H diff --git a/designer/designer/qttoolbardialog.cpp b/designer/designer/qttoolbardialog.cpp new file mode 100644 index 0000000..b418d56 --- /dev/null +++ b/designer/designer/qttoolbardialog.cpp @@ -0,0 +1,1871 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qttoolbardialog.h" +#include "ui_qttoolbardialog.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QtFullToolBarManagerPrivate; + +class QtFullToolBarManager : public QObject +{ + Q_OBJECT +public: + QtFullToolBarManager(QObject *parent); + ~QtFullToolBarManager(); + + void setMainWindow(QMainWindow *mainWindow); + QMainWindow *mainWindow() const; + + void addCategory(const QString &category); + bool hasCategory(const QString &category) const; + QStringList categories() const; + QList categoryActions(const QString &category) const; + QString actionCategory(QAction *action) const; + + // only non-separator + void addAction(QAction *action, const QString &category); + + void removeAction(QAction *action); + + QSet actions() const; + bool isWidgetAction(QAction *action) const; + + /* + Adds (registers) toolBar. Adds (registers) actions that already exists in toolBar. + Remembers toolbar and its actions as a default. + */ + void addDefaultToolBar(QToolBar *toolBar, const QString &category); + + void removeDefaultToolBar(QToolBar *toolBar); + // NULL on action list means separator. + QMap > defaultToolBars() const; + bool isDefaultToolBar(QToolBar *toolBar) const; + + QToolBar *createToolBar(const QString &toolBarName); + void deleteToolBar(QToolBar *toolBar); // only those which were created, not added + + QList actions(QToolBar *toolBar) const; + + void setToolBars(const QMap > &actions); + void setToolBar(QToolBar *toolBar, const QList &actions); + + QMap > toolBarsActions() const; + QByteArray saveState(int version = 0) const; + bool restoreState(const QByteArray &state, int version = 0); + +public slots: + + void resetToolBar(QToolBar *toolBar); + void resetAllToolBars(); + +signals: + void toolBarCreated(QToolBar *toolBar); + void toolBarRemoved(QToolBar *toolBar); + + /* + If QToolBarWidgetAction was in another tool bar and is inserted into + this toolBar, toolBarChanged is first emitted for other toolbar - without + that action. (Another approach may be that user first must call setToolBar + without that action for old tool bar) + */ + void toolBarChanged(QToolBar *toolBar, const QList &actions); + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtFullToolBarManager) + Q_DISABLE_COPY(QtFullToolBarManager) +}; + +class QtFullToolBarManagerPrivate +{ + class QtFullToolBarManager *q_ptr; + Q_DECLARE_PUBLIC(QtFullToolBarManager) + +public: + + QToolBar *toolBarWidgetAction(QAction *action) const; + void removeWidgetActions(const QMap > &actions); + + enum { + VersionMarker = 0xff, + ToolBarMarker = 0xfe, + CustomToolBarMarker = 0xfd, + }; + + void saveState(QDataStream &stream) const; + bool restoreState(QDataStream &stream) const; + QToolBar *findDefaultToolBar(const QString &objectName) const; + QAction *findAction(const QString &actionName) const; + + QToolBar *toolBarByName(const QString &toolBarName) const; + + QtFullToolBarManagerPrivate(); + + QMap > categoryToActions; + QMap actionToCategory; + + QSet allActions; + QMap widgetActions; + QSet regularActions; + QMap > actionToToolBars; + + QMap > toolBars; + QMap > toolBarsWithSeparators; + QMap > defaultToolBars; + QList customToolBars; + + QMainWindow *theMainWindow; +}; + + + + +QtFullToolBarManagerPrivate::QtFullToolBarManagerPrivate() + : theMainWindow(0) +{ +} + +QToolBar *QtFullToolBarManagerPrivate::toolBarWidgetAction(QAction *action) const +{ + if (widgetActions.contains(action)) + return widgetActions.value(action); + return 0; +} + +void QtFullToolBarManagerPrivate::removeWidgetActions(const QMap > + &actions) +{ + QMap >::ConstIterator itToolBar = actions.constBegin(); + while (itToolBar != actions.constEnd()) { + QToolBar *toolBar = itToolBar.key(); + QList newActions = toolBars.value(toolBar); + QList newActionsWithSeparators = toolBarsWithSeparators.value(toolBar); + + QList removedActions; + QList actionList = itToolBar.value(); + QListIterator itAction(actionList); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + if (newActions.contains(action) && toolBarWidgetAction(action) == toolBar) { + newActions.removeAll(action); + newActionsWithSeparators.removeAll(action); + removedActions.append(action); + } + } + + //emit q_ptr->toolBarChanged(toolBar, newActions); + + toolBars.insert(toolBar, newActions); + toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators); + QListIterator itRemovedAction(removedActions); + while (itRemovedAction.hasNext()) { + QAction *oldAction = itRemovedAction.next(); + widgetActions.insert(oldAction, 0); + actionToToolBars[oldAction].removeAll(toolBar); + } + + ++itToolBar; + } +} + +void QtFullToolBarManagerPrivate::saveState(QDataStream &stream) const +{ + stream << (uchar) ToolBarMarker; + stream << defaultToolBars.size(); + QMap >::ConstIterator itToolBar = + defaultToolBars.constBegin(); + while (itToolBar != defaultToolBars.constEnd()) { + QToolBar *tb = itToolBar.key(); + if (tb->objectName().isEmpty()) { + qWarning("QtToolBarManager::saveState(): 'objectName' not set for QToolBar " + "%p '%s', using 'windowTitle' instead", + tb, tb->windowTitle().toLocal8Bit().constData()); + stream << tb->windowTitle(); + } else { + stream << tb->objectName(); + } + + stream << toolBars[tb].size(); + QListIterator itAction(toolBars[tb]); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + + if (action) { + if (action->objectName().isEmpty()) { + qWarning("QtToolBarManager::saveState(): 'objectName' not set for QAction " + "%p '%s', using 'text' instead", + action, action->text().toLocal8Bit().constData()); + stream << action->text(); + } else { + stream << action->objectName(); + } + } else { + stream << QString(); + } + } + ++itToolBar; + } + + + stream << (uchar) CustomToolBarMarker; + stream << toolBars.size() - defaultToolBars.size(); + itToolBar = toolBars.constBegin(); + while (itToolBar != toolBars.constEnd()) { + QToolBar *tb = itToolBar.key(); + if (!defaultToolBars.contains(tb)) { + stream << tb->objectName(); + stream << tb->windowTitle(); + + stream << toolBars[tb].size(); + QListIterator itAction(toolBars[tb]); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + + if (action) { + if (action->objectName().isEmpty()) { + qWarning("QtToolBarManager::saveState(): 'objectName' not set for QAction " + "%p '%s', using 'text' instead", + action, action->text().toLocal8Bit().constData()); + stream << action->text(); + } else { + stream << action->objectName(); + } + } else { + stream << QString(); + } + } + } + ++itToolBar; + } +} + +bool QtFullToolBarManagerPrivate::restoreState(QDataStream &stream) const +{ + uchar tmarker; + stream >> tmarker; + if (tmarker != ToolBarMarker) + return false; + + int toolBars; + stream >> toolBars; + for (int i = 0; i < toolBars; i++) { + QString objectName; + stream >> objectName; + int actionCount; + stream >> actionCount; + QList actions; + for (int j = 0; j < actionCount; j++) { + QString actionName; + stream >> actionName; + + if (actionName.isEmpty()) + actions.append(0); + else { + QAction *action = findAction(actionName); + if (action) + actions.append(action); + } + } + + QToolBar *toolBar = findDefaultToolBar(objectName); + if (toolBar) + q_ptr->setToolBar(toolBar, actions); + } + + + + uchar ctmarker; + stream >> ctmarker; + if (ctmarker != CustomToolBarMarker) + return false; + + QList oldCustomToolBars = customToolBars; + + stream >> toolBars; + for (int i = 0; i < toolBars; i++) { + QString objectName; + QString toolBarName; + int actionCount; + stream >> objectName; + stream >> toolBarName; + stream >> actionCount; + QList actions; + for (int j = 0; j < actionCount; j++) { + QString actionName; + stream >> actionName; + + if (actionName.isEmpty()) + actions.append(0); + else { + QAction *action = findAction(actionName); + if (action) + actions.append(action); + } + } + + QToolBar *toolBar = toolBarByName(objectName); + if (toolBar) { + toolBar->setWindowTitle(toolBarName); + oldCustomToolBars.removeAll(toolBar); + } + else + toolBar = q_ptr->createToolBar(toolBarName); + if (toolBar) { + toolBar->setObjectName(objectName); + q_ptr->setToolBar(toolBar, actions); + } + } + QListIterator itToolBar(oldCustomToolBars); + while (itToolBar.hasNext()) + q_ptr->deleteToolBar(itToolBar.next()); + return true; +} + +QToolBar *QtFullToolBarManagerPrivate::findDefaultToolBar(const QString &objectName) const +{ + QMap >::ConstIterator itToolBar = + defaultToolBars.constBegin(); + while (itToolBar != defaultToolBars.constEnd()) { + QToolBar *tb = itToolBar.key(); + if (tb->objectName() == objectName) + return tb; + + ++itToolBar; + } + + qWarning("QtToolBarManager::restoreState(): cannot find a QToolBar named " + "'%s', trying to match using 'windowTitle' instead.", + objectName.toLocal8Bit().constData()); + + itToolBar = defaultToolBars.constBegin(); + while (itToolBar != defaultToolBars.constEnd()) { + QToolBar *tb = itToolBar.key(); + if (tb->windowTitle() == objectName) + return tb; + + ++itToolBar; + } + qWarning("QtToolBarManager::restoreState(): cannot find a QToolBar with " + "matching 'windowTitle' (looking for '%s').", + objectName.toLocal8Bit().constData()); + + return 0; +} + +QAction *QtFullToolBarManagerPrivate::findAction(const QString &actionName) const +{ + QSetIterator itAction(allActions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + + if (action->objectName() == actionName) + return action; + } + qWarning("QtToolBarManager::restoreState(): cannot find a QAction named " + "'%s', trying to match using 'text' instead.", + actionName.toLocal8Bit().constData()); + + itAction.toFront(); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + + if (action->text() == actionName) + return action; + } + qWarning("QtToolBarManager::restoreState(): cannot find a QAction with " + "matching 'text' (looking for '%s').", + actionName.toLocal8Bit().constData()); + + return 0; +} + +QToolBar *QtFullToolBarManagerPrivate::toolBarByName(const QString &toolBarName) const +{ + QMap >::ConstIterator itToolBar = toolBars.constBegin(); + while (itToolBar != toolBars.constEnd()) { + QToolBar *toolBar = itToolBar.key(); + if (toolBar->objectName() == toolBarName) + return toolBar; + + ++itToolBar; + } + return 0; +} + +////////////////////////////// + +QtFullToolBarManager::QtFullToolBarManager(QObject *parent) + : QObject(parent), d_ptr(new QtFullToolBarManagerPrivate) +{ + d_ptr->q_ptr = this; +} + +QtFullToolBarManager::~QtFullToolBarManager() +{ +} + +void QtFullToolBarManager::setMainWindow(QMainWindow *mainWindow) +{ + d_ptr->theMainWindow = mainWindow; +} + +QMainWindow *QtFullToolBarManager::mainWindow() const +{ + return d_ptr->theMainWindow; +} + +void QtFullToolBarManager::addCategory(const QString &category) +{ + d_ptr->categoryToActions[category] = QList(); +} + +bool QtFullToolBarManager::hasCategory(const QString &category) const +{ + return d_ptr->categoryToActions.contains(category); +} + +QStringList QtFullToolBarManager::categories() const +{ + return d_ptr->categoryToActions.keys(); +} + +QList QtFullToolBarManager::categoryActions(const QString &category) const +{ + QMap >::ConstIterator it = + d_ptr->categoryToActions.find(category); + if (it != d_ptr->categoryToActions.constEnd()) + return it.value(); + return QList(); +} + +QString QtFullToolBarManager::actionCategory(QAction *action) const +{ + QMap::ConstIterator it = d_ptr->actionToCategory.find(action); + if (it != d_ptr->actionToCategory.constEnd()) + return it.value(); + return QString(); +} + +void QtFullToolBarManager::addAction(QAction *action, const QString &category) +{ + if (!action) + return; + if (action->isSeparator()) + return; + if (d_ptr->allActions.contains(action)) + return; + if (QLatin1String(action->metaObject()->className()) == + QLatin1String("QToolBarWidgetAction")) + d_ptr->widgetActions.insert(action, 0); + else + d_ptr->regularActions.insert(action); + d_ptr->allActions.insert(action); + d_ptr->categoryToActions[category].append(action); + d_ptr->actionToCategory[action] = category; +} + +void QtFullToolBarManager::removeAction(QAction *action) +{ + if (!d_ptr->allActions.contains(action)) + return; + + QList toolBars = d_ptr->actionToToolBars[action]; + QListIterator itToolBar(toolBars); + while (itToolBar.hasNext()) { + QToolBar *toolBar = itToolBar.next(); + + d_ptr->toolBars[toolBar].removeAll(action); + d_ptr->toolBarsWithSeparators[toolBar].removeAll(action); + + toolBar->removeAction(action); + } + + QMap >::ConstIterator itDefault = + d_ptr->defaultToolBars.constBegin(); + while (itDefault != d_ptr->defaultToolBars.constEnd()) { + if (itDefault.value().contains(action)) + d_ptr->defaultToolBars[itDefault.key()].removeAll(action); + + itDefault++; + } + + d_ptr->allActions.remove(action); + d_ptr->widgetActions.remove(action); + d_ptr->regularActions.remove(action); + d_ptr->actionToToolBars.remove(action); + + QString category = d_ptr->actionToCategory.value(action); + d_ptr->actionToCategory.remove(action); + d_ptr->categoryToActions[category].removeAll(action); + + if (d_ptr->categoryToActions[category].isEmpty()) + d_ptr->categoryToActions.remove(category); +} + +QSet QtFullToolBarManager::actions() const +{ + return d_ptr->allActions; +} + +bool QtFullToolBarManager::isWidgetAction(QAction *action) const +{ + if (d_ptr->widgetActions.contains(action)) + return true; + return false; +} + +void QtFullToolBarManager::addDefaultToolBar(QToolBar *toolBar, const QString &category) +{ + if (!toolBar) + return; + if (d_ptr->toolBars.contains(toolBar)) + return; + // could be also checked if toolBar belongs to mainwindow + + QList newActionsWithSeparators; + QList newActions; + QList actions = toolBar->actions(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + addAction(action, category); + if (d_ptr->widgetActions.contains(action)) + d_ptr->widgetActions.insert(action, toolBar); + newActionsWithSeparators.append(action); + if (action->isSeparator()) + action = 0; + else + d_ptr->actionToToolBars[action].append(toolBar); + newActions.append(action); + } + d_ptr->defaultToolBars.insert(toolBar, newActions); + //Below could be done by call setToolBar() if we want signal emission here. + d_ptr->toolBars.insert(toolBar, newActions); + d_ptr->toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators); +} + +void QtFullToolBarManager::removeDefaultToolBar(QToolBar *toolBar) +{ + if (!d_ptr->defaultToolBars.contains(toolBar)) + return; + + QList defaultActions = d_ptr->defaultToolBars[toolBar]; + setToolBar(toolBar, QList()); + QListIterator itAction(defaultActions); + while (itAction.hasNext()) + removeAction(itAction.next()); + + d_ptr->toolBars.remove(toolBar); + d_ptr->toolBarsWithSeparators.remove(toolBar); + d_ptr->defaultToolBars.remove(toolBar); + + itAction.toFront(); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + if (action) + toolBar->insertAction(0, action); + else + toolBar->insertSeparator(0); + } +} + +QMap > QtFullToolBarManager::defaultToolBars() const +{ + return d_ptr->defaultToolBars; +} + +bool QtFullToolBarManager::isDefaultToolBar(QToolBar *toolBar) const +{ + if (d_ptr->defaultToolBars.contains(toolBar)) + return true; + return false; +} + +QToolBar *QtFullToolBarManager::createToolBar(const QString &toolBarName) +{ + if (!mainWindow()) + return 0; + QToolBar *toolBar = new QToolBar(toolBarName, mainWindow()); + int i = 1; + const QString prefix = QLatin1String("_Custom_Toolbar_%1"); + QString name = prefix.arg(i); + while (d_ptr->toolBarByName(name)) + name = prefix.arg(++i); + toolBar->setObjectName(name); + mainWindow()->addToolBar(toolBar); + d_ptr->customToolBars.append(toolBar); + d_ptr->toolBars.insert(toolBar, QList()); + d_ptr->toolBarsWithSeparators.insert(toolBar, QList()); + return toolBar; +} + +void QtFullToolBarManager::deleteToolBar(QToolBar *toolBar) +{ + if (!d_ptr->toolBars.contains(toolBar)) + return; + if (d_ptr->defaultToolBars.contains(toolBar)) + return; + setToolBar(toolBar, QList()); + d_ptr->customToolBars.removeAll(toolBar); + d_ptr->toolBars.remove(toolBar); + d_ptr->toolBarsWithSeparators.remove(toolBar); + delete toolBar; +} + +QList QtFullToolBarManager::actions(QToolBar *toolBar) const +{ + if (d_ptr->toolBars.contains(toolBar)) + return d_ptr->toolBars.value(toolBar); + return QList(); +} + +void QtFullToolBarManager::setToolBars(const QMap > &actions) +{ + QMap >::ConstIterator it = actions.constBegin(); + while (it != actions.constEnd()) { + setToolBar(it.key(), it.value()); + ++it; + } +} + +void QtFullToolBarManager::setToolBar(QToolBar *toolBar, const QList &actions) +{ + if (!toolBar) + return; + if (!d_ptr->toolBars.contains(toolBar)) + return; + + if (actions == d_ptr->toolBars[toolBar]) + return; + + QMap > toRemove; + + QList newActions; + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + if (!action || (!newActions.contains(action) && d_ptr->allActions.contains(action))) + newActions.append(action); + + QToolBar *oldToolBar = d_ptr->toolBarWidgetAction(action); + if (oldToolBar && oldToolBar != toolBar) + toRemove[oldToolBar].append(action); + } + + d_ptr->removeWidgetActions(toRemove); + + QList oldActions = d_ptr->toolBarsWithSeparators.value(toolBar); + QListIterator itOldAction(oldActions); + while (itOldAction.hasNext()) { + QAction *action = itOldAction.next(); + /* + When addDefaultToolBar() separator actions could be checked if they are + inserted in other toolbars - if yes then create new one. + */ + if (d_ptr->toolBarWidgetAction(action) == toolBar) + d_ptr->widgetActions.insert(action, 0); + toolBar->removeAction(action); + if (action->isSeparator()) + delete action; + else + d_ptr->actionToToolBars[action].removeAll(toolBar); + } + + QList newActionsWithSeparators; + QListIterator itNewActions(newActions); + while (itNewActions.hasNext()) { + QAction *action = itNewActions.next(); + QAction *newAction = 0; + if (!action) + newAction = toolBar->insertSeparator(0); + if (d_ptr->allActions.contains(action)) { + toolBar->insertAction(0, action); + newAction = action; + d_ptr->actionToToolBars[action].append(toolBar); + } + newActionsWithSeparators.append(newAction); + } + d_ptr->toolBars.insert(toolBar, newActions); + d_ptr->toolBarsWithSeparators.insert(toolBar, newActionsWithSeparators); +} + +QMap > QtFullToolBarManager::toolBarsActions() const +{ + return d_ptr->toolBars; +} + +void QtFullToolBarManager::resetToolBar(QToolBar *toolBar) +{ + if (!isDefaultToolBar(toolBar)) + return; + setToolBar(toolBar, defaultToolBars().value(toolBar)); +} + +void QtFullToolBarManager::resetAllToolBars() +{ + setToolBars(defaultToolBars()); + QList oldCustomToolBars = d_ptr->customToolBars; + QListIterator itToolBar(oldCustomToolBars); + while (itToolBar.hasNext()) { + deleteToolBar(itToolBar.next()); + } +} + +QByteArray QtFullToolBarManager::saveState(int version) const +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << QtFullToolBarManagerPrivate::VersionMarker; + stream << version; + d_ptr->saveState(stream); + return data; +} + +bool QtFullToolBarManager::restoreState(const QByteArray &state, int version) +{ + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + int marker, v; + stream >> marker; + stream >> v; + if (marker != QtFullToolBarManagerPrivate::VersionMarker || v != version) + return false; + return d_ptr->restoreState(stream); +} + + +class QtToolBarManagerPrivate +{ + class QtToolBarManager *q_ptr; + Q_DECLARE_PUBLIC(QtToolBarManager) +public: + QtFullToolBarManager *manager; +}; + +////////////////////////////////////// + +/*! \class QtToolBarManager + \internal + \inmodule QtDesigner + \since 4.4 + + \brief The QtToolBarManager class provides toolbar management for + main windows. + + The QtToolBarManager is typically used with a QtToolBarDialog + which allows the user to customize the toolbars for a given main + window. The QtToolBarDialog class's functionality is controlled by + an instance of the QtToolBarManager class, and the main window is + specified using the QtToolBarManager class's setMainWindow() + function. + + The currently specified main window can be retrieved using the + mainWindow() function. + + The toolbar manager holds lists of the given main window's actions + and toolbars, and can add actions and toolbars to these + lists using the addAction() and addToolBar() functions + respectively. The actions can in addition be categorized + acccording to the user's preferences. The toolbar manager can also + remove custom actions and toolbars using the removeAction() and + removeToolBar() functions. + + Finally, the QtToolBarManager is able to save the customized state + of its toolbars using the saveState() function as well as restore + the toolbars' saved state using restoreState() function. + + \sa QtToolBarDialog +*/ + +/*! + Creates a toolbar manager with the given \a parent. +*/ +QtToolBarManager::QtToolBarManager(QObject *parent) + : QObject(parent), d_ptr(new QtToolBarManagerPrivate) +{ + d_ptr->q_ptr = this; + + d_ptr->manager = new QtFullToolBarManager(this); +} + +/*! + Destroys the toolbar manager. +*/ +QtToolBarManager::~QtToolBarManager() +{ +} + +/*! + Sets the main window upon which the toolbar manager operates, to + be the given \a mainWindow. +*/ +void QtToolBarManager::setMainWindow(QMainWindow *mainWindow) +{ + d_ptr->manager->setMainWindow(mainWindow); +} + +/*! + Returns the main window associated this toolbar manager. +*/ +QMainWindow *QtToolBarManager::mainWindow() const +{ + return d_ptr->manager->mainWindow(); +} + +/*! + Adds the given \a action to the given \a category in the manager's + list of actions. If the \a category doesn't exist it is created. + Only non separator actions can be added. If the action is already + added to the list, the function doesn't do anything. + + \sa removeAction() +*/ +void QtToolBarManager::addAction(QAction *action, const QString &category) +{ + d_ptr->manager->addAction(action, category); +} + +/*! + Removes the specified \a action from the manager's list of + actions. The action is also removed from all the registered + toolbars. If the specified \a action is the only action in its + category, that category is removed as well. + + \sa addAction() +*/ +void QtToolBarManager::removeAction(QAction *action) +{ + d_ptr->manager->removeAction(action); +} + +/*! + Adds the given \a toolBar to the manager's toolbar list. + + All the \a toolBar's actions are automatically added to the given + \a category in the manager's list of actions if they're not + already there. The manager remembers which toolbar the actions + belonged to, so, when the \a toolBar is removed, its actions will + be removed as well. + + Custom toolbars are created with the main window returned by + the mainWindow() function, as its parent. + + \sa removeToolBar() +*/ +void QtToolBarManager::addToolBar(QToolBar *toolBar, const QString &category) +{ + d_ptr->manager->addDefaultToolBar(toolBar, category); +} + +/*! + Removes the specified \a toolBar from the manager's list. All the + actions that existed in the specified \a toolBar when it was + added are removed as well. + + \sa addToolBar() +*/ +void QtToolBarManager::removeToolBar(QToolBar *toolBar) +{ + d_ptr->manager->removeDefaultToolBar(toolBar); +} + +/*! + Returns the manager's toolbar list. +*/ +QList QtToolBarManager::toolBars() const +{ + return d_ptr->manager->toolBarsActions().keys(); +} + +/* +void QtToolBarManager::resetToolBar(QToolBar *toolBar) +{ + d_ptr->manager->resetToolBar(toolBar); +} + +void QtToolBarManager::resetAllToolBars() +{ + d_ptr->manager->resetAllToolBars(); +} +*/ + +/*! + Saves the state of the toolbar manager's toolbars. The \a version + number is stored as part of the data. + + Identifies all the QToolBar and QAction objects by their object + name property. Ensure that this property is unique for each + QToolBar and QAction that you add using the QtToolBarManager. + + Returns an identifier for the state which can be passed along with + the version number to the restoreState() function to restore the + saved state. + + \sa restoreState() +*/ +QByteArray QtToolBarManager::saveState(int version) const +{ + return d_ptr->manager->saveState(version); +} + +/*! + Restores the saved state of the toolbar manager's toolbars. The + \a version number is compared with the version number of the + stored \a state. + + Returns true if the version numbers are matching and the toolbar + manager's state is restored; otherwise the toolbar manager's state + is left unchanged and the function returns false. + + Note that the state of the toolbar manager's toolbars should be + restored before restoring the state of the main window's toolbars + and dockwidgets using the QMainWindow::restoreState() function. In + that way the restoreState() function can create the custom + toolbars before the QMainWindow::restoreState() function restores + the custom toolbars' positions. + + \sa saveState() +*/ +bool QtToolBarManager::restoreState(const QByteArray &state, int version) +{ + return d_ptr->manager->restoreState(state, version); +} + +////////////////////// + +class ToolBarItem { +public: + ToolBarItem() : tb(0) {} + ToolBarItem(QToolBar *toolBar) : tb(toolBar) {} + ToolBarItem(QToolBar *toolBar, const QString &toolBarName) + : tb(toolBar), tbName(toolBarName) {} + ToolBarItem(const QString &toolBarName) : tb(0), tbName(toolBarName) {} + QToolBar *toolBar() const + { return tb; } + void setToolBar(QToolBar *toolBar) + { tb = toolBar; } + QString toolBarName() const + { return tbName; } + void setToolBarName(const QString &toolBarName) + { tbName = toolBarName; } +private: + QToolBar *tb; + QString tbName; +}; + +class QtToolBarDialogPrivate { + QtToolBarDialog *q_ptr; + Q_DECLARE_PUBLIC(QtToolBarDialog) +public: + QtToolBarDialogPrivate() + : toolBarManager(0), + currentAction(0), + currentToolBar(0) + { } + + ToolBarItem *createItem(QToolBar *toolBar); + ToolBarItem *createItem(const QString &toolBarName); + void deleteItem(ToolBarItem *item); + + void newClicked(); + void removeClicked(); + void defaultClicked(); + void okClicked(); + void applyClicked(); + void cancelClicked(); + void upClicked(); + void downClicked(); + void leftClicked(); + void rightClicked(); + void renameClicked(); + void toolBarRenamed(QListWidgetItem *item); + void currentActionChanged(QTreeWidgetItem *current); + void currentToolBarChanged(QListWidgetItem *current); + void currentToolBarActionChanged(QListWidgetItem *current); + + void removeToolBar(ToolBarItem *item); + bool isDefaultToolBar(ToolBarItem *item) const; + void setButtons(); + void clearOld(); + void fillNew(); + QtFullToolBarManager *toolBarManager; + QMap > currentState; + QMap toolBarItems; + QSet createdItems; + QSet removedItems; + + QSet allToolBarItems; + + // static + QTreeWidgetItem *currentAction; + QMap actionToItem; + QMap itemToAction; + + // dynamic + ToolBarItem *currentToolBar; + QMap toolBarToItem; + QMap itemToToolBar; + + // dynamic + QMap actionToCurrentItem; + QMap currentItemToAction; + + QMap widgetActionToToolBar; + QMap > toolBarToWidgetActions; + + QString separatorText; + Ui::QtToolBarDialog ui; +}; + +ToolBarItem *QtToolBarDialogPrivate::createItem(QToolBar *toolBar) +{ + if (!toolBar) + return 0; + ToolBarItem *item = new ToolBarItem(toolBar, toolBar->windowTitle()); + allToolBarItems.insert(item); + return item; +} + +ToolBarItem *QtToolBarDialogPrivate::createItem(const QString &toolBarName) +{ + ToolBarItem *item = new ToolBarItem(toolBarName); + allToolBarItems.insert(item); + return item; +} + +void QtToolBarDialogPrivate::deleteItem(ToolBarItem *item) +{ + if (!allToolBarItems.contains(item)) + return; + allToolBarItems.remove(item); + delete item; +} + +void QtToolBarDialogPrivate::clearOld() +{ + ui.actionTree->clear(); + ui.toolBarList->clear(); + ui.currentToolBarList->clear(); + ui.removeButton->setEnabled(false); + ui.newButton->setEnabled(false); + ui.upButton->setEnabled(false); + ui.downButton->setEnabled(false); + ui.leftButton->setEnabled(false); + ui.rightButton->setEnabled(false); + + actionToItem.clear(); + itemToAction.clear(); + toolBarToItem.clear(); + itemToToolBar.clear(); + actionToCurrentItem.clear(); + currentItemToAction.clear(); + widgetActionToToolBar.clear(); + toolBarToWidgetActions.clear(); + + toolBarItems.clear(); + currentState.clear(); + createdItems.clear(); + removedItems.clear(); + QSetIterator itItem(allToolBarItems); + while (itItem.hasNext()) + delete itItem.next(); + allToolBarItems.clear(); + + currentToolBar = 0; + currentAction = 0; +} + +void QtToolBarDialogPrivate::fillNew() +{ + if (!toolBarManager) + return; + + QTreeWidgetItem *item = new QTreeWidgetItem(ui.actionTree); + item->setText(0, separatorText); + ui.actionTree->setCurrentItem(item); + currentAction = item; + actionToItem.insert(0, item); + itemToAction.insert(item, 0); + QStringList categories = toolBarManager->categories(); + QStringListIterator itCategory(categories); + while (itCategory.hasNext()) { + QString category = itCategory.next(); + QTreeWidgetItem *categoryItem = new QTreeWidgetItem(ui.actionTree); + categoryItem->setText(0, category); + QList actions = toolBarManager->categoryActions(category); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + item = new QTreeWidgetItem(categoryItem); + item->setText(0, action->text()); + item->setIcon(0, action->icon()); + item->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic); + actionToItem.insert(action, item); + itemToAction.insert(item, action); + if (toolBarManager->isWidgetAction(action)) { + item->setData(0, Qt::TextColorRole, QColor(Qt::blue)); + widgetActionToToolBar.insert(action, 0); + } + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + } + ui.actionTree->setItemExpanded(categoryItem, true); + } + //ui.actionTree->sortItems(0, Qt::AscendingOrder); + + QMap > toolBars = toolBarManager->toolBarsActions(); + QMap >::ConstIterator it = toolBars.constBegin(); + while (it != toolBars.constEnd()) { + QToolBar *toolBar = it.key(); + ToolBarItem *tbItem = createItem(toolBar); + toolBarItems.insert(toolBar, tbItem); + QListWidgetItem *item = new QListWidgetItem(toolBar->windowTitle(), + ui.toolBarList); + toolBarToItem.insert(tbItem, item); + itemToToolBar.insert(item, tbItem); + QList actions = it.value(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + if (toolBarManager->isWidgetAction(action)) { + widgetActionToToolBar.insert(action, tbItem); + toolBarToWidgetActions[tbItem].insert(action); + } + } + currentState.insert(tbItem, actions); + if (it == toolBars.constBegin()) + ui.toolBarList->setCurrentItem(item); + if (isDefaultToolBar(tbItem)) + item->setData(Qt::TextColorRole, QColor(Qt::darkGreen)); + else + item->setFlags(item->flags() | Qt::ItemIsEditable); + + ++it; + } + ui.toolBarList->sortItems(); + setButtons(); +} + +bool QtToolBarDialogPrivate::isDefaultToolBar(ToolBarItem *item) const +{ + if (!item) + return false; + if (!item->toolBar()) + return false; + return toolBarManager->isDefaultToolBar(item->toolBar()); +} + +void QtToolBarDialogPrivate::setButtons() +{ + bool newEnabled = false; + bool removeEnabled = false; + bool renameEnabled = false; + bool upEnabled = false; + bool downEnabled = false; + bool leftEnabled = false; + bool rightEnabled = false; + + if (toolBarManager) { + newEnabled = true; + removeEnabled = !isDefaultToolBar(currentToolBar); + renameEnabled = removeEnabled; + QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem(); + if (currentToolBarAction) { + int row = ui.currentToolBarList->row(currentToolBarAction); + upEnabled = row > 0; + downEnabled = row < ui.currentToolBarList->count() - 1; + leftEnabled = true; + } + if (currentAction && currentToolBar) + rightEnabled = true; + } + ui.newButton->setEnabled(newEnabled); + ui.removeButton->setEnabled(removeEnabled); + ui.renameButton->setEnabled(renameEnabled); + ui.upButton->setEnabled(upEnabled); + ui.downButton->setEnabled(downEnabled); + ui.leftButton->setEnabled(leftEnabled); + ui.rightButton->setEnabled(rightEnabled); +} + +void QtToolBarDialogPrivate::newClicked() +{ + QString toolBarName = QtToolBarDialog::tr("Custom Toolbar"); // = QInputDialog::getString(); + // produce unique name + ToolBarItem *item = createItem(toolBarName); + currentState.insert(item, QList()); + createdItems.insert(item); + QListWidgetItem *i = new QListWidgetItem(toolBarName, ui.toolBarList); + i->setFlags(i->flags() | Qt::ItemIsEditable); + ui.toolBarList->setCurrentItem(i); + itemToToolBar.insert(i, item); + toolBarToItem.insert(item, i); + ui.toolBarList->sortItems(); + ui.toolBarList->setCurrentItem(i); + currentToolBarChanged(i); + renameClicked(); +} + +void QtToolBarDialogPrivate::removeToolBar(ToolBarItem *item) +{ + if (!item) + return; + if (item->toolBar() && toolBarManager->isDefaultToolBar(item->toolBar())) + return; + if (!toolBarToItem.contains(item)) + return; + QListWidgetItem *i = toolBarToItem.value(item); + bool wasCurrent = false; + if (i == ui.toolBarList->currentItem()) + wasCurrent = true; + int row = ui.toolBarList->row(i); + QMap >::ConstIterator itToolBar = + toolBarToWidgetActions.find(item); + if (itToolBar != toolBarToWidgetActions.constEnd()) { + QSet actions = itToolBar.value(); + QSetIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + widgetActionToToolBar.insert(action, 0); + } + toolBarToWidgetActions.remove(item); + } + + currentState.remove(item); + createdItems.remove(item); + toolBarToItem.remove(item); + itemToToolBar.remove(i); + delete i; + if (item->toolBar()) + removedItems.insert(item); + else + deleteItem(item); + if (wasCurrent) { + if (row == ui.toolBarList->count()) + row--; + if (row < 0) + ; + else + ui.toolBarList->setCurrentRow(row); + } + setButtons(); +} + +void QtToolBarDialogPrivate::removeClicked() +{ + QListWidgetItem *i = ui.toolBarList->currentItem(); + if (!i) + return; + ToolBarItem *item = itemToToolBar.value(i); + removeToolBar(item); +} + +void QtToolBarDialogPrivate::defaultClicked() +{ + QMap > defaultToolBars = toolBarManager->defaultToolBars(); + QMap >::ConstIterator itToolBar = defaultToolBars.constBegin(); + while (itToolBar != defaultToolBars.constEnd()) { + QToolBar *toolBar = itToolBar.key(); + ToolBarItem *toolBarItem = toolBarItems.value(toolBar); + + if (toolBarToWidgetActions.contains(toolBarItem)) { + QSetIterator itAction(toolBarToWidgetActions.value(toolBarItem)); + while (itAction.hasNext()) + widgetActionToToolBar.insert(itAction.next(), 0); + toolBarToWidgetActions.remove(toolBarItem); + } + + currentState.remove(toolBarItem); + + QListIterator itAction(itToolBar.value()); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + if (toolBarManager->isWidgetAction(action)) { + ToolBarItem *otherToolBar = widgetActionToToolBar.value(action); + if (otherToolBar) { + toolBarToWidgetActions[otherToolBar].remove(action); + currentState[otherToolBar].removeAll(action); + } + widgetActionToToolBar.insert(action, toolBarItem); + toolBarToWidgetActions[toolBarItem].insert(action); + } + } + currentState.insert(toolBarItem, itToolBar.value()); + + ++itToolBar; + } + currentToolBarChanged(toolBarToItem.value(currentToolBar)); + + QList toolBars = currentState.keys(); + QListIterator itTb(toolBars); + while (itTb.hasNext()) + removeToolBar(itTb.next()); +} + +void QtToolBarDialogPrivate::okClicked() +{ + applyClicked(); + q_ptr->accept(); +} + +void QtToolBarDialogPrivate::applyClicked() +{ + QMap > toolBars = currentState; + QMap >::ConstIterator itToolBar = toolBars.constBegin(); + while (itToolBar != toolBars.constEnd()) { + ToolBarItem *item = itToolBar.key(); + QToolBar *toolBar = item->toolBar(); + if (toolBar) { + toolBarManager->setToolBar(toolBar, itToolBar.value()); + toolBar->setWindowTitle(item->toolBarName()); + } + + ++itToolBar; + } + + QSet toRemove = removedItems; + QSetIterator itRemove(toRemove); + while (itRemove.hasNext()) { + ToolBarItem *item = itRemove.next(); + QToolBar *toolBar = item->toolBar(); + removedItems.remove(item); + currentState.remove(item); + deleteItem(item); + if (toolBar) + toolBarManager->deleteToolBar(toolBar); + } + + QSet toCreate = createdItems; + QSetIterator itCreate(toCreate); + while (itCreate.hasNext()) { + ToolBarItem *item = itCreate.next(); + QString toolBarName = item->toolBarName(); + createdItems.remove(item); + QList actions = currentState.value(item); + QToolBar *toolBar = toolBarManager->createToolBar(toolBarName); + item->setToolBar(toolBar); + toolBarManager->setToolBar(toolBar, actions); + } +} + +void QtToolBarDialogPrivate::upClicked() +{ + QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem(); + if (!currentToolBarAction) + return; + int row = ui.currentToolBarList->row(currentToolBarAction); + if (row == 0) + return; + ui.currentToolBarList->takeItem(row); + int newRow = row - 1; + ui.currentToolBarList->insertItem(newRow, currentToolBarAction); + QList actions = currentState.value(currentToolBar); + QAction *action = actions.at(row); + actions.removeAt(row); + actions.insert(newRow, action); + currentState.insert(currentToolBar, actions); + ui.currentToolBarList->setCurrentItem(currentToolBarAction); + setButtons(); +} + +void QtToolBarDialogPrivate::downClicked() +{ + QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem(); + if (!currentToolBarAction) + return; + int row = ui.currentToolBarList->row(currentToolBarAction); + if (row == ui.currentToolBarList->count() - 1) + return; + ui.currentToolBarList->takeItem(row); + int newRow = row + 1; + ui.currentToolBarList->insertItem(newRow, currentToolBarAction); + QList actions = currentState.value(currentToolBar); + QAction *action = actions.at(row); + actions.removeAt(row); + actions.insert(newRow, action); + currentState.insert(currentToolBar, actions); + ui.currentToolBarList->setCurrentItem(currentToolBarAction); + setButtons(); +} + +void QtToolBarDialogPrivate::leftClicked() +{ + QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem(); + if (!currentToolBarAction) + return; + int row = ui.currentToolBarList->row(currentToolBarAction); + currentState[currentToolBar].removeAt(row); + QAction *action = currentItemToAction.value(currentToolBarAction); + if (widgetActionToToolBar.contains(action)) { + ToolBarItem *item = widgetActionToToolBar.value(action); + if (item == currentToolBar) { // have to be + toolBarToWidgetActions[item].remove(action); + if (toolBarToWidgetActions[item].empty()) + toolBarToWidgetActions.remove(item); + } + widgetActionToToolBar.insert(action, 0); + } + if (action) + actionToCurrentItem.remove(action); + currentItemToAction.remove(currentToolBarAction); + delete currentToolBarAction; + if (row == ui.currentToolBarList->count()) + row--; + if (row >= 0) { + QListWidgetItem *item = ui.currentToolBarList->item(row); + ui.currentToolBarList->setCurrentItem(item); + } + setButtons(); +} + +void QtToolBarDialogPrivate::rightClicked() +{ + if (!currentAction) + return; + if (!currentToolBar) + return; + QListWidgetItem *currentToolBarAction = ui.currentToolBarList->currentItem(); + + QAction *action = itemToAction.value(currentAction); + QListWidgetItem *item = 0; + if (action) { + if (currentState[currentToolBar].contains(action)) { + item = actionToCurrentItem.value(action); + if (item == currentToolBarAction) + return; + int row = ui.currentToolBarList->row(item); + ui.currentToolBarList->takeItem(row); + currentState[currentToolBar].removeAt(row); + // only reorder here + } else { + item = new QListWidgetItem(action->text()); + item->setIcon(action->icon()); + item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic); + currentItemToAction.insert(item, action); + actionToCurrentItem.insert(action, item); + if (widgetActionToToolBar.contains(action)) { + item->setData(Qt::TextColorRole, QColor(Qt::blue)); + ToolBarItem *toolBar = widgetActionToToolBar.value(action); + if (toolBar) { + currentState[toolBar].removeAll(action); + toolBarToWidgetActions[toolBar].remove(action); + if (toolBarToWidgetActions[toolBar].empty()) + toolBarToWidgetActions.remove(toolBar); + } + widgetActionToToolBar.insert(action, currentToolBar); + toolBarToWidgetActions[currentToolBar].insert(action); + } + } + } else { + item = new QListWidgetItem(separatorText); + currentItemToAction.insert(item, 0); + } + + int row = ui.currentToolBarList->count(); + if (currentToolBarAction) { + row = ui.currentToolBarList->row(currentToolBarAction) + 1; + } + ui.currentToolBarList->insertItem(row, item); + currentState[currentToolBar].insert(row, action); + ui.currentToolBarList->setCurrentItem(item); + + setButtons(); +} + +void QtToolBarDialogPrivate::renameClicked() +{ + if (!currentToolBar) + return; + + QListWidgetItem *item = toolBarToItem.value(currentToolBar); + ui.toolBarList->editItem(item); +} + +void QtToolBarDialogPrivate::toolBarRenamed(QListWidgetItem *item) +{ + if (!currentToolBar) + return; + + ToolBarItem *tbItem = itemToToolBar.value(item); + if (!tbItem) + return; + tbItem->setToolBarName(item->text()); + //ui.toolBarList->sortItems(); +} + +void QtToolBarDialogPrivate::currentActionChanged(QTreeWidgetItem *current) +{ + if (itemToAction.contains(current)) + currentAction = current; + else + currentAction = NULL; + setButtons(); +} + +void QtToolBarDialogPrivate::currentToolBarChanged(QListWidgetItem *current) +{ + currentToolBar = itemToToolBar.value(current); + ui.currentToolBarList->clear(); + actionToCurrentItem.clear(); + currentItemToAction.clear(); + setButtons(); + if (!currentToolBar) { + return; + } + QList actions = currentState.value(currentToolBar); + QListIterator itAction(actions); + QListWidgetItem *first = 0; + while (itAction.hasNext()) { + QAction *action = itAction.next(); + QString actionName = separatorText; + if (action) + actionName = action->text(); + QListWidgetItem *item = new QListWidgetItem(actionName, ui.currentToolBarList); + if (action) { + item->setIcon(action->icon()); + item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic); + actionToCurrentItem.insert(action, item); + if (widgetActionToToolBar.contains(action)) + item->setData(Qt::TextColorRole, QColor(Qt::blue)); + } + currentItemToAction.insert(item, action); + if (!first) + first = item; + } + if (first) + ui.currentToolBarList->setCurrentItem(first); +} + +void QtToolBarDialogPrivate::currentToolBarActionChanged(QListWidgetItem *) +{ + setButtons(); +} + +void QtToolBarDialogPrivate::cancelClicked() +{ + // just nothing + q_ptr->reject(); +} + +////////////////////// +/* +class FeedbackItemDelegate : public QItemDelegate +{ + Q_OBJECT +public: + FeedbackItemDelegate(QObject *parent = 0) : QItemDelegate(parent) { } + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex & index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +void FeedbackItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if () + painter->save(); + QRect r = option.rect; + float yCentral = r.height() / 2.0; + float margin = 2.0; + float arrowWidth = 5.0; + float width = 20; + qDebug("rect: x %d, y %d, w %d, h %d", r.x(), r.y(), r.width(), r.height()); + QLineF lineBase(0.0 + margin, r.y() + yCentral, width - margin, r.y() + yCentral); + QLineF lineArrowLeft(width - margin - arrowWidth, r.y() + yCentral - arrowWidth, + width - margin, r.y() + yCentral); + QLineF lineArrowRight(width - margin - arrowWidth, r.y() + yCentral + arrowWidth, + width - margin, r.y() + yCentral); + painter->drawLine(lineBase); + painter->drawLine(lineArrowLeft); + painter->drawLine(lineArrowRight); + painter->translate(QPoint(width, 0)); + QItemDelegate::paint(painter, option, index); + painter->restore(); +} + +QSize FeedbackItemDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + //return QItemDelegate::sizeHint(option, index); + QSize s = QItemDelegate::sizeHint(option, index); + s.setWidth(s.width() - 20); + return s; +} + +class QtToolBarListWidget : public QListWidget +{ + Q_OBJECT +public: + QtToolBarListWidget(QWidget *parent) : QListWidget(parent), actionDrag(false) {} + +protected: + void startDrag(Qt::DropActions supportedActions); + + void dragEnterEvent(QDragEnterEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dragLeaveEvent(QDragLeaveEvent *); + void dropEvent(QDropEvent *event); + + void setDragAction(const QString *action) { actionName = action; } +private: + QPersistentModelIndex lastDropIndicator; + QString actionName; + bool actionDrag; +}; + +void QtToolBarListWidget::startDrag(Qt::DropActions supportedActions) +{ + QListWidgetItem *item = currentItem(); + if (item) { + actionName = QString(); + emit aboutToDrag(item); + if (!actionName.isEmpty()) { + QDrag *drag = new QDrag(this); + QMimeData *data = new QMimeData; + data->setData("action", actionName.toLocal8Bit().constData()); + drag->setMimeData(data); + drag->start(supportedActions); + } + } +} + +void QtToolBarListWidget::dragEnterEvent(QDragEnterEvent *event) +{ + const QMimeData *mime = event->mimeData(); + actionDrag = mime->hasFormat("action"); + if (actionDrag) + event->accept(); + else + event->ignore(); +} + +void QtToolBarListWidget::dragMoveEvent(QDragMoveEvent *event) +{ + event->ignore(); + if (actionDrag) { + QPoint p = event->pos(); + QListWidgetItem *item = itemAt(p); + Indicator indic = QtToolBarListWidget::None; + if (item) { + QRect rect = visualItemRect(item); + if (p.y() - rect.top() < rect.height() / 2) + indic = QtToolBarListWidget::Above; + else + indic = QtToolBarListWidget::Below; + } + setIndicator(item, indic); + event->accept(); + } +} + +void QtToolBarListWidget::dragLeaveEvent(QDragLeaveEvent *) +{ + if (actionDrag) { + actionDrag = false; + setIndicator(item, QtToolBarListWidget::None); + } +} + +void QtToolBarListWidget::dropEvent(QDropEvent *event) +{ + if (actionDrag) { + QListWidgetItem *item = indicatorItem(); + Indicator indic = indicator(); + QByteArray array = event->mimeData()->data("action"); + QDataStream stream(&array, QIODevice::ReadOnly); + QString action; + stream >> action; + emit actionDropped(action, item, ); + + actionDrag = false; + setIndicator(item, QtToolBarListWidget::None); + } +} +*/ + +/*! \class QtToolBarDialog + \internal + \inmodule QtDesigner + \since 4.4 + + \brief The QtToolBarDialog class provides a dialog for customizing + toolbars. + + QtToolBarDialog allows the user to customize the toolbars for a + given main window. + + \image qttoolbardialog.png + + The dialog lets the users add, rename and remove custom toolbars. + Note that built-in toolbars are marked with a green color, and + cannot be removed or renamed. + + The users can also add and remove actions from the toolbars. An + action can be added to many toolbars, but a toolbar can only + contain one instance of each action. Actions that contains a + widget are marked with a blue color in the list of actions, and + can only be added to one single toolbar. + + Finally, the users can add separators to the toolbars. + + The original toolbars can be restored by clicking the \gui + {Restore all} button. All custom toolbars will then be removed, + and all built-in toolbars will be restored to their original state. + + The QtToolBarDialog class's functionality is controlled by an + instance of the QtToolBarManager class, and the main window is + specified using the QtToolBarManager::setMainWindow() function. + + All you need to do to use QtToolBarDialog is to specify an + QtToolBarManager instance and call the QDialog::exec() slot: + + \snippet doc/src/snippets/code/tools_shared_qttoolbardialog_qttoolbardialog.cpp 0 + + \sa QtToolBarManager +*/ + +/*! + Creates a toolbar dialog with the given \a parent and the specified + window \a flags. +*/ +QtToolBarDialog::QtToolBarDialog(QWidget *parent, Qt::WindowFlags flags) + : QDialog(parent, flags), d_ptr(new QtToolBarDialogPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->ui.setupUi(this); + d_ptr->separatorText = tr("< S E P A R A T O R >"); + + d_ptr->ui.actionTree->setColumnCount(1); + d_ptr->ui.actionTree->setRootIsDecorated(false); + d_ptr->ui.actionTree->header()->hide(); + + d_ptr->ui.upButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/up.png"))); + d_ptr->ui.downButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/down.png"))); + d_ptr->ui.leftButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/back.png"))); + d_ptr->ui.rightButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/forward.png"))); + d_ptr->ui.newButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/plus.png"))); + d_ptr->ui.removeButton->setIcon(QIcon(QLatin1String(":/trolltech/qttoolbardialog/images/minus.png"))); + + connect(d_ptr->ui.newButton, SIGNAL(clicked()), this, SLOT(newClicked())); + connect(d_ptr->ui.removeButton, SIGNAL(clicked()), this, SLOT(removeClicked())); + connect(d_ptr->ui.renameButton, SIGNAL(clicked()), this, SLOT(renameClicked())); + connect(d_ptr->ui.upButton, SIGNAL(clicked()), this, SLOT(upClicked())); + connect(d_ptr->ui.downButton, SIGNAL(clicked()), this, SLOT(downClicked())); + connect(d_ptr->ui.leftButton, SIGNAL(clicked()), this, SLOT(leftClicked())); + connect(d_ptr->ui.rightButton, SIGNAL(clicked()), this, SLOT(rightClicked())); + + connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(defaultClicked())); + connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(okClicked())); + connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyClicked())); + connect(d_ptr->ui.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(cancelClicked())); + + connect(d_ptr->ui.actionTree, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(currentActionChanged(QTreeWidgetItem*))); + connect(d_ptr->ui.toolBarList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(currentToolBarChanged(QListWidgetItem*))); + connect(d_ptr->ui.currentToolBarList, + SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(currentToolBarActionChanged(QListWidgetItem*))); + + connect(d_ptr->ui.actionTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + this, SLOT(rightClicked())); + connect(d_ptr->ui.currentToolBarList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(leftClicked())); + connect(d_ptr->ui.toolBarList, SIGNAL(itemChanged(QListWidgetItem*)), + this, SLOT(toolBarRenamed(QListWidgetItem*))); +} + +/*! + Destroys the toolbar dialog. +*/ +QtToolBarDialog::~QtToolBarDialog() +{ + d_ptr->clearOld(); +} + +/*! + Connects the toolbar dialog to the given \a toolBarManager. Then, + when exec() is called, the toolbar dialog will operate using the + given \a toolBarManager. +*/ +void QtToolBarDialog::setToolBarManager(QtToolBarManager *toolBarManager) +{ + if (d_ptr->toolBarManager == toolBarManager->d_ptr->manager) + return; + if (isVisible()) + d_ptr->clearOld(); + d_ptr->toolBarManager = toolBarManager->d_ptr->manager; + if (isVisible()) + d_ptr->fillNew(); +} + +/*! + \reimp +*/ +void QtToolBarDialog::showEvent(QShowEvent *event) +{ + if (!event->spontaneous()) + d_ptr->fillNew(); +} + +/*! + \reimp +*/ +void QtToolBarDialog::hideEvent(QHideEvent *event) +{ + if (!event->spontaneous()) + d_ptr->clearOld(); +} + +QT_END_NAMESPACE + +#include "moc_qttoolbardialog.cpp" +#include "qttoolbardialog.moc" diff --git a/designer/designer/qttoolbardialog.h b/designer/designer/qttoolbardialog.h new file mode 100644 index 0000000..ce14777 --- /dev/null +++ b/designer/designer/qttoolbardialog.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTTOOLBARDIALOG_H +#define QTTOOLBARDIALOG_H + +#include + +QT_BEGIN_NAMESPACE + +class QMainWindow; +class QAction; +class QToolBar; + +class QtToolBarManagerPrivate; + +class QtToolBarManager : public QObject +{ + Q_OBJECT +public: + + explicit QtToolBarManager(QObject *parent = 0); + ~QtToolBarManager(); + + void setMainWindow(QMainWindow *mainWindow); + QMainWindow *mainWindow() const; + + void addAction(QAction *action, const QString &category); + void removeAction(QAction *action); + + void addToolBar(QToolBar *toolBar, const QString &category); + void removeToolBar(QToolBar *toolBar); + + QList toolBars() const; + + QByteArray saveState(int version = 0) const; + bool restoreState(const QByteArray &state, int version = 0); + +private: + + friend class QtToolBarDialog; + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtToolBarManager) + Q_DISABLE_COPY(QtToolBarManager) +}; + +class QtToolBarDialogPrivate; + +class QtToolBarDialog : public QDialog +{ + Q_OBJECT +public: + + explicit QtToolBarDialog(QWidget *parent = 0, Qt::WindowFlags flags = 0); + ~QtToolBarDialog(); + + void setToolBarManager(QtToolBarManager *toolBarManager); + +protected: + + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); + +private: + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtToolBarDialog) + Q_DISABLE_COPY(QtToolBarDialog) + + Q_PRIVATE_SLOT(d_func(), void newClicked()) + Q_PRIVATE_SLOT(d_func(), void removeClicked()) + Q_PRIVATE_SLOT(d_func(), void defaultClicked()) + Q_PRIVATE_SLOT(d_func(), void okClicked()) + Q_PRIVATE_SLOT(d_func(), void applyClicked()) + Q_PRIVATE_SLOT(d_func(), void cancelClicked()) + Q_PRIVATE_SLOT(d_func(), void upClicked()) + Q_PRIVATE_SLOT(d_func(), void downClicked()) + Q_PRIVATE_SLOT(d_func(), void leftClicked()) + Q_PRIVATE_SLOT(d_func(), void rightClicked()) + Q_PRIVATE_SLOT(d_func(), void renameClicked()) + Q_PRIVATE_SLOT(d_func(), void toolBarRenamed(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void currentActionChanged(QTreeWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void currentToolBarChanged(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void currentToolBarActionChanged(QListWidgetItem *)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/designer/designer/qttoolbardialog.ui b/designer/designer/qttoolbardialog.ui new file mode 100644 index 0000000..c4ad934 --- /dev/null +++ b/designer/designer/qttoolbardialog.ui @@ -0,0 +1,207 @@ + + QtToolBarDialog + + + + 0 + 0 + 583 + 508 + + + + Customize Toolbars + + + + 8 + + + 6 + + + + + + 1 + + + + + + + + Actions + + + + + + + 6 + + + 0 + + + + + Toolbars + + + + + + + Add new toolbar + + + New + + + + + + + Remove selected toolbar + + + Remove + + + + + + + Rename toolbar + + + Rename + + + + + + + + + 6 + + + 0 + + + + + + 0 + 0 + + + + Move action up + + + Up + + + + + + + + 0 + 0 + + + + Remove action from toolbar + + + <- + + + + + + + + 0 + 0 + + + + Add action to toolbar + + + -> + + + + + + + + 0 + 0 + + + + Move action down + + + Down + + + + + + + Qt::Vertical + + + + 29 + 16 + + + + + + + + + + + + + Current Toolbar Actions + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + + + + + newButton + removeButton + renameButton + toolBarList + upButton + leftButton + rightButton + downButton + currentToolBarList + + + + diff --git a/designer/designer/saveformastemplate.cpp b/designer/designer/saveformastemplate.cpp new file mode 100644 index 0000000..c195987 --- /dev/null +++ b/designer/designer/saveformastemplate.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "saveformastemplate.h" +#include "qdesigner_settings.h" + +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +SaveFormAsTemplate::SaveFormAsTemplate(QDesignerFormEditorInterface *core, + QDesignerFormWindowInterface *formWindow, + QWidget *parent) + : QDialog(parent, Qt::Sheet), + m_core(core), + m_formWindow(formWindow) +{ + ui.setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + ui.templateNameEdit->setText(formWindow->mainContainer()->objectName()); + ui.templateNameEdit->selectAll(); + + ui.templateNameEdit->setFocus(); + + QStringList paths = QDesignerSettings(m_core).formTemplatePaths(); + ui.categoryCombo->addItems(paths); + ui.categoryCombo->addItem(tr("Add path...")); + m_addPathIndex = ui.categoryCombo->count() - 1; + connect(ui.templateNameEdit, SIGNAL(textChanged(QString)), + this, SLOT(updateOKButton(QString))); + connect(ui.categoryCombo, SIGNAL(activated(int)), this, SLOT(checkToAddPath(int))); +} + +SaveFormAsTemplate::~SaveFormAsTemplate() +{ +} + +void SaveFormAsTemplate::accept() +{ + QString templateFileName = ui.categoryCombo->currentText(); + templateFileName += QLatin1Char('/'); + const QString name = ui.templateNameEdit->text(); + templateFileName += name; + const QString extension = QLatin1String(".ui"); + if (!templateFileName.endsWith(extension)) + templateFileName.append(extension); + QFile file(templateFileName); + + if (file.exists()) { + QMessageBox msgBox(QMessageBox::Information, tr("Template Exists"), + tr("A template with the name %1 already exists.\n" + "Do you want overwrite the template?").arg(name), QMessageBox::Cancel, m_formWindow); + msgBox.setDefaultButton(QMessageBox::Cancel); + QPushButton *overwriteButton = msgBox.addButton(tr("Overwrite Template"), QMessageBox::AcceptRole); + msgBox.exec(); + if (msgBox.clickedButton() != overwriteButton) + return; + } + + while (!file.open(QFile::WriteOnly)) { + if (QMessageBox::information(m_formWindow, tr("Open Error"), + tr("There was an error opening template %1 for writing. Reason: %2").arg(name).arg(file.errorString()), + QMessageBox::Retry|QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Cancel) { + return; + } + } + + const QString origName = m_formWindow->fileName(); + // ensure m_formWindow->contents() will convert properly resource paths to relative paths + // (relative to template location, not to the current form location) + m_formWindow->setFileName(templateFileName); + QByteArray ba = m_formWindow->contents().toUtf8(); + m_formWindow->setFileName(origName); + while (file.write(ba) != ba.size()) { + if (QMessageBox::information(m_formWindow, tr("Write Error"), + tr("There was an error writing the template %1 to disk. Reason: %2").arg(name).arg(file.errorString()), + QMessageBox::Retry|QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Cancel) { + file.close(); + file.remove(); + return; + } + file.reset(); + } + // update the list of places too... + QStringList sl; + for (int i = 0; i < m_addPathIndex; ++i) + sl << ui.categoryCombo->itemText(i); + + QDesignerSettings(m_core).setFormTemplatePaths(sl); + + QDialog::accept(); +} + +void SaveFormAsTemplate::updateOKButton(const QString &str) +{ + QPushButton *okButton = ui.buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(!str.isEmpty()); +} + +QString SaveFormAsTemplate::chooseTemplatePath(QWidget *parent) +{ + QString rc = QFileDialog::getExistingDirectory(parent, + tr("Pick a directory to save templates in")); + if (rc.isEmpty()) + return rc; + + if (rc.endsWith(QDir::separator())) + rc.remove(rc.size() - 1, 1); + return rc; +} + +void SaveFormAsTemplate::checkToAddPath(int itemIndex) +{ + if (itemIndex != m_addPathIndex) + return; + + const QString dir = chooseTemplatePath(this); + if (dir.isEmpty()) { + ui.categoryCombo->setCurrentIndex(0); + return; + } + + ui.categoryCombo->insertItem(m_addPathIndex, dir); + ui.categoryCombo->setCurrentIndex(m_addPathIndex); + ++m_addPathIndex; +} + +QT_END_NAMESPACE diff --git a/designer/designer/saveformastemplate.h b/designer/designer/saveformastemplate.h new file mode 100644 index 0000000..020d99f --- /dev/null +++ b/designer/designer/saveformastemplate.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SAVEFORMASTEMPLATE_H +#define SAVEFORMASTEMPLATE_H + +#include "ui_saveformastemplate.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class SaveFormAsTemplate: public QDialog +{ + Q_OBJECT +public: + explicit SaveFormAsTemplate(QDesignerFormEditorInterface *m_core, + QDesignerFormWindowInterface *formWindow, + QWidget *parent = 0); + virtual ~SaveFormAsTemplate(); + +private slots: + void accept(); + void updateOKButton(const QString &str); + void checkToAddPath(int itemIndex); + +private: + static QString chooseTemplatePath(QWidget *parent); + + Ui::SaveFormAsTemplate ui; + QDesignerFormEditorInterface *m_core; + QDesignerFormWindowInterface *m_formWindow; + int m_addPathIndex; +}; + +QT_END_NAMESPACE + +#endif // SAVEFORMASTEMPLATE_H diff --git a/designer/designer/saveformastemplate.ui b/designer/designer/saveformastemplate.ui new file mode 100644 index 0000000..9865feb --- /dev/null +++ b/designer/designer/saveformastemplate.ui @@ -0,0 +1,166 @@ + + ********************************************************************* +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +********************************************************************* + SaveFormAsTemplate + + + Save Form As Template + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + &Name: + + + Qt::AutoText + + + templateNameEdit + + + + + + + + 222 + 0 + + + + + + + QLineEdit::Normal + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + &Category: + + + Qt::AutoText + + + categoryCombo + + + + + + + + + + + + QFrame::HLine + + + QFrame::Sunken + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SaveFormAsTemplate + accept() + + + 256 + 124 + + + 113 + 143 + + + + + buttonBox + rejected() + SaveFormAsTemplate + reject() + + + 332 + 127 + + + 372 + 147 + + + + + diff --git a/designer/designer/versiondialog.cpp b/designer/designer/versiondialog.cpp new file mode 100644 index 0000000..29d3004 --- /dev/null +++ b/designer/designer/versiondialog.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 "versiondialog.h" + +QT_BEGIN_NAMESPACE + +class VersionLabel : public QLabel +{ + Q_OBJECT +public: + VersionLabel(QWidget *parent = 0); + +signals: + void triggered(); + +protected: + void mousePressEvent(QMouseEvent *me); + void mouseMoveEvent(QMouseEvent *me); + void mouseReleaseEvent(QMouseEvent *me); + void paintEvent(QPaintEvent *pe); +private: + QVector hitPoints; + QVector missPoints; + QPainterPath m_path; + bool secondStage; + bool m_pushed; +}; + +VersionLabel::VersionLabel(QWidget *parent) + : QLabel(parent), secondStage(false), m_pushed(false) +{ + setPixmap(QPixmap(QLatin1String(":/trolltech/designer/images/designer.png"))); + hitPoints.append(QPoint(56, 25)); + hitPoints.append(QPoint(29, 55)); + hitPoints.append(QPoint(56, 87)); + hitPoints.append(QPoint(82, 55)); + hitPoints.append(QPoint(58, 56)); + + secondStage = false; + m_pushed = false; +} + +void VersionLabel::mousePressEvent(QMouseEvent *me) +{ + if (me->button() == Qt::LeftButton) { + if (!secondStage) { + m_path = QPainterPath(me->pos()); + } else { + m_pushed = true; + update(); + } + } +} + +void VersionLabel::mouseMoveEvent(QMouseEvent *me) +{ + if (me->buttons() & Qt::LeftButton) + if (!secondStage) + m_path.lineTo(me->pos()); +} + +void VersionLabel::mouseReleaseEvent(QMouseEvent *me) +{ + if (me->button() == Qt::LeftButton) { + if (!secondStage) { + m_path.lineTo(me->pos()); + bool gotIt = true; + foreach(const QPoint &pt, hitPoints) { + if (!m_path.contains(pt)) { + gotIt = false; + break; + } + } + if (gotIt) { + foreach(const QPoint &pt, missPoints) { + if (m_path.contains(pt)) { + gotIt = false; + break; + } + } + } + if (gotIt && !secondStage) { + secondStage = true; + m_path = QPainterPath(); + update(); + } + } else { + m_pushed = false; + update(); + emit triggered(); + } + } +} + +void VersionLabel::paintEvent(QPaintEvent *pe) +{ + if (secondStage) { + QPainter p(this); + QStyleOptionButton opt; + opt.init(this); + if (!m_pushed) + opt.state |= QStyle::State_Raised; + else + opt.state |= QStyle::State_Sunken; + opt.state &= ~QStyle::State_HasFocus; + style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this); + } + QLabel::paintEvent(pe); +} + +VersionDialog::VersionDialog(QWidget *parent) + : QDialog(parent +#ifdef Q_WS_MAC + , Qt::Tool +#endif + ) +{ + setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | Qt::MSWindowsFixedSizeDialogHint); + QGridLayout *layout = new QGridLayout(this); + VersionLabel *label = new VersionLabel; + QLabel *lbl = new QLabel; + QString version = tr("

%1



Version %2"); + version = version.arg(tr("Qt Designer")).arg(QLatin1String(QT_VERSION_STR)); + version.append(tr("
Qt Designer is a graphical user interface designer for Qt applications.
")); + + lbl->setText(tr("%1" + "
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)." + ).arg(version)); + + lbl->setWordWrap(true); + lbl->setOpenExternalLinks(true); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + connect(label, SIGNAL(triggered()), this, SLOT(accept())); + layout->addWidget(label, 0, 0, 1, 1); + layout->addWidget(lbl, 0, 1, 4, 4); + layout->addWidget(buttonBox, 4, 2, 1, 1); +} + +QT_END_NAMESPACE + +#include "versiondialog.moc" diff --git a/designer/designer/versiondialog.h b/designer/designer/versiondialog.h new file mode 100644 index 0000000..ceb757c --- /dev/null +++ b/designer/designer/versiondialog.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VERSIONDIALOG_H +#define VERSIONDIALOG_H + +#include + +QT_BEGIN_NAMESPACE + +class VersionDialog : public QDialog +{ + Q_OBJECT +public: + explicit VersionDialog(QWidget *parent); +}; + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/extension/default_extensionfactory.cpp b/designer/lib/extension/default_extensionfactory.cpp new file mode 100644 index 0000000..6ae58e6 --- /dev/null +++ b/designer/lib/extension/default_extensionfactory.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qextensionmanager.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QExtensionFactory + + \brief The QExtensionFactory class allows you to create a factory + that is able to make instances of custom extensions in Qt + Designer. + + \inmodule QtDesigner + + In \QD the extensions are not created until they are required. For + that reason, when implementing a custom extension, you must also + create a QExtensionFactory, i.e. a class that is able to make an + instance of your extension, and register it using \QD's \l + {QExtensionManager}{extension manager}. + + The QExtensionManager class provides extension management + facilities for Qt Designer. When an extension is required, Qt + Designer's \l {QExtensionManager}{extension manager} will run + through all its registered factories calling + QExtensionFactory::createExtension() for each until the first one + that is able to create a requested extension for the selected + object, is found. This factory will then make an instance of the + extension. + + There are four available types of extensions in Qt Designer: + QDesignerContainerExtension , QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. Qt + Designer's behavior is the same whether the requested extension is + associated with a multi page container, a member sheet, a property + sheet or a task menu. + + You can either create a new QExtensionFactory and reimplement the + QExtensionFactory::createExtension() function. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_extension_default_extensionfactory.cpp 0 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create your extension as well. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_extension_default_extensionfactory.cpp 1 + + For a complete example using the QExtensionFactory class, see the + \l {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to Qt Designer's task menu. + + \sa QExtensionManager, QAbstractExtensionFactory +*/ + +/*! + Constructs an extension factory with the given \a parent. +*/ +QExtensionFactory::QExtensionFactory(QExtensionManager *parent) + : QObject(parent) +{ +} + +/*! + Returns the extension specified by \a iid for the given \a object. + + \sa createExtension() +*/ + +QObject *QExtensionFactory::extension(QObject *object, const QString &iid) const +{ + if (!object) + return 0; + const IdObjectKey key = qMakePair(iid, object); + + ExtensionMap::iterator it = m_extensions.find(key); + if (it == m_extensions.end()) { + if (QObject *ext = createExtension(object, iid, const_cast(this))) { + connect(ext, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + it = m_extensions.insert(key, ext); + } + } + + if (!m_extended.contains(object)) { + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + m_extended.insert(object, true); + } + + if (it == m_extensions.end()) + return 0; + + return it.value(); +} + +void QExtensionFactory::objectDestroyed(QObject *object) +{ + QMutableMapIterator< IdObjectKey, QObject*> it(m_extensions); + while (it.hasNext()) { + it.next(); + + QObject *o = it.key().second; + if (o == object || object == it.value()) { + it.remove(); + } + } + + m_extended.remove(object); +} + +/*! + Creates an extension specified by \a iid for the given \a object. + The extension object is created as a child of the specified \a + parent. + + \sa extension() +*/ +QObject *QExtensionFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const +{ + Q_UNUSED(object); + Q_UNUSED(iid); + Q_UNUSED(parent); + + return 0; +} + +/*! + Returns the extension manager for the extension factory. +*/ +QExtensionManager *QExtensionFactory::extensionManager() const +{ + return static_cast(parent()); +} + +QT_END_NAMESPACE diff --git a/designer/lib/extension/default_extensionfactory.h b/designer/lib/extension/default_extensionfactory.h new file mode 100644 index 0000000..32a81e3 --- /dev/null +++ b/designer/lib/extension/default_extensionfactory.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEFAULT_EXTENSIONFACTORY_H +#define DEFAULT_EXTENSIONFACTORY_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QExtensionManager; + +class QDESIGNER_EXTENSION_EXPORT QExtensionFactory : public QObject, public QAbstractExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) +public: + QExtensionFactory(QExtensionManager *parent = 0); + + virtual QObject *extension(QObject *object, const QString &iid) const; + QExtensionManager *extensionManager() const; + +private Q_SLOTS: + void objectDestroyed(QObject *object); + +protected: + virtual QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const; + +private: + typedef QPair IdObjectKey; + typedef QMap< IdObjectKey, QObject*> ExtensionMap; + mutable ExtensionMap m_extensions; + typedef QHash ExtendedSet; + mutable ExtendedSet m_extended; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DEFAULT_EXTENSIONFACTORY_H diff --git a/designer/lib/extension/extension.cpp b/designer/lib/extension/extension.cpp new file mode 100644 index 0000000..6818789 --- /dev/null +++ b/designer/lib/extension/extension.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractExtensionFactory + + \brief The QAbstractExtensionFactory class provides an interface + for extension factories in Qt Designer. + + \inmodule QtDesigner + + QAbstractExtensionFactory is not intended to be instantiated + directly; use the QExtensionFactory instead. + + In \QD, extension factories are used to look up and create named + extensions as they are required. For that reason, when + implementing a custom extension, you must also create a + QExtensionFactory, i.e a class that is able to make an instance of + your extension, and register it using \QD's \l + {QExtensionManager}{extension manager}. + + When an extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create the requested + extension for the selected object, is found. This factory will + then make an instance of the extension. + + \sa QExtensionFactory, QExtensionManager +*/ + +/*! + \fn QAbstractExtensionFactory::~QAbstractExtensionFactory() + + Destroys the extension factory. +*/ + +/*! + \fn QObject *QAbstractExtensionFactory::extension(QObject *object, const QString &iid) const + + Returns the extension specified by \a iid for the given \a object. +*/ + + +/*! + \class QAbstractExtensionManager + + \brief The QAbstractExtensionManager class provides an interface + for extension managers in Qt Designer. + + \inmodule QtDesigner + + QAbstractExtensionManager is not intended to be instantiated + directly; use the QExtensionManager instead. + + In \QD, extension are not created until they are required. For + that reason, when implementing a custom extension, you must also + create a QExtensionFactory, i.e a class that is able to make an + instance of your extension, and register it using \QD's \l + {QExtensionManager}{extension manager}. + + When an extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create the requested + extension for the selected object, is found. This factory will + then make an instance of the extension. + + \sa QExtensionManager, QExtensionFactory +*/ + +/*! + \fn QAbstractExtensionManager::~QAbstractExtensionManager() + + Destroys the extension manager. +*/ + +/*! + \fn void QAbstractExtensionManager::registerExtensions(QAbstractExtensionFactory *factory, const QString &iid) + + Register the given extension \a factory with the extension + specified by \a iid. +*/ + +/*! + \fn void QAbstractExtensionManager::unregisterExtensions(QAbstractExtensionFactory *factory, const QString &iid) + + Unregister the given \a factory with the extension specified by \a + iid. +*/ + +/*! + \fn QObject *QAbstractExtensionManager::extension(QObject *object, const QString &iid) const + + Returns the extension, specified by \a iid, for the given \a + object. +*/ + +/*! + \fn T qt_extension(QAbstractExtensionManager* manager, QObject *object) + + \relates QExtensionManager + + Returns the extension of the given \a object cast to type T if the + object is of type T (or of a subclass); otherwise returns 0. The + extension is retrieved using the given extension \a manager. + + \snippet doc/src/snippets/code/tools_designer_src_lib_extension_extension.cpp 0 + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor) is + provided by the QDesignerCustomWidgetInterface::initialize() + function's parameter. + + If the widget in the example above doesn't have a defined + QDesignerPropertySheetExtension, \c propertySheet will be a null + pointer. + +*/ + +/*! + \macro Q_DECLARE_EXTENSION_INTERFACE(ExtensionName, Identifier) + + \relates QExtensionManager + + Associates the given \a Identifier (a string literal) to the + extension class called \a ExtensionName. The \a Identifier must be + unique. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_extension_extension.cpp 1 + + Using the company and product names is a good way to ensure + uniqueness of the identifier. + + When implementing a custom extension class, you must use + Q_DECLARE_EXTENSION_INTERFACE() to enable usage of the + qt_extension() function. The macro is normally located right after the + class definition for \a ExtensionName, in the associated header + file. + + \sa Q_DECLARE_INTERFACE() +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/extension/extension.h b/designer/lib/extension/extension.h new file mode 100644 index 0000000..f0117d8 --- /dev/null +++ b/designer/lib/extension/extension.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXTENSION_H +#define EXTENSION_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#define Q_TYPEID(IFace) QLatin1String(IFace##_iid) + +class QAbstractExtensionFactory +{ +public: + virtual ~QAbstractExtensionFactory() {} + + virtual QObject *extension(QObject *object, const QString &iid) const = 0; +}; +Q_DECLARE_INTERFACE(QAbstractExtensionFactory, "com.trolltech.Qt.QAbstractExtensionFactory") + +class QAbstractExtensionManager +{ +public: + virtual ~QAbstractExtensionManager() {} + + virtual void registerExtensions(QAbstractExtensionFactory *factory, const QString &iid) = 0; + virtual void unregisterExtensions(QAbstractExtensionFactory *factory, const QString &iid) = 0; + + virtual QObject *extension(QObject *object, const QString &iid) const = 0; +}; +Q_DECLARE_INTERFACE(QAbstractExtensionManager, "com.trolltech.Qt.QAbstractExtensionManager") + +#if defined(Q_CC_MSVC) && (_MSC_VER < 1300) + +template +inline T qt_extension_helper(QAbstractExtensionManager *, QObject *, T) +{ return 0; } + +template +inline T qt_extension(QAbstractExtensionManager* manager, QObject *object) +{ return qt_extension_helper(manager, object, T(0)); } + +#define Q_DECLARE_EXTENSION_INTERFACE(IFace, IId) \ +const char * const IFace##_iid = IId; \ +Q_DECLARE_INTERFACE(IFace, IId) \ +template <> inline IFace *qt_extension_helper(QAbstractExtensionManager *manager, QObject *object, IFace *) \ +{ QObject *extension = manager->extension(object, Q_TYPEID(IFace)); return (IFace *)(extension ? extension->qt_metacast(IFace##_iid) : 0); } + +#else + +template +inline T qt_extension(QAbstractExtensionManager* manager, QObject *object) +{ return 0; } + +#define Q_DECLARE_EXTENSION_INTERFACE(IFace, IId) \ +const char * const IFace##_iid = IId; \ +Q_DECLARE_INTERFACE(IFace, IId) \ +template <> inline IFace *qt_extension(QAbstractExtensionManager *manager, QObject *object) \ +{ QObject *extension = manager->extension(object, Q_TYPEID(IFace)); return extension ? static_cast(extension->qt_metacast(IFace##_iid)) : static_cast(0); } + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // EXTENSION_H diff --git a/designer/lib/extension/extension.pri b/designer/lib/extension/extension.pri new file mode 100644 index 0000000..d8ef658 --- /dev/null +++ b/designer/lib/extension/extension.pri @@ -0,0 +1,12 @@ +# Input + +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/default_extensionfactory.h \ + $$PWD/extension.h \ + $$PWD/qextensionmanager.h + +SOURCES += $$PWD/default_extensionfactory.cpp \ + $$PWD/extension.cpp \ + $$PWD/qextensionmanager.cpp + diff --git a/designer/lib/extension/extension_global.h b/designer/lib/extension/extension_global.h new file mode 100644 index 0000000..2328fc2 --- /dev/null +++ b/designer/lib/extension/extension_global.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXTENSION_GLOBAL_H +#define EXTENSION_GLOBAL_H + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#define QDESIGNER_EXTENSION_EXTERN Q_DECL_EXPORT +#define QDESIGNER_EXTENSION_IMPORT Q_DECL_IMPORT + +#ifdef QT_DESIGNER_STATIC +# define QDESIGNER_EXTENSION_EXPORT +#elif defined(QDESIGNER_EXTENSION_LIBRARY) +# define QDESIGNER_EXTENSION_EXPORT QDESIGNER_EXTENSION_EXTERN +#else +# define QDESIGNER_EXTENSION_EXPORT QDESIGNER_EXTENSION_IMPORT +#endif + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // EXTENSION_GLOBAL_H diff --git a/designer/lib/extension/qextensionmanager.cpp b/designer/lib/extension/qextensionmanager.cpp new file mode 100644 index 0000000..c7ea7fc --- /dev/null +++ b/designer/lib/extension/qextensionmanager.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qextensionmanager.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QExtensionManager + + \brief The QExtensionManager class provides extension management + facilities for Qt Designer. + + \inmodule QtDesigner + + In \QD the extensions are not created until they are required. For + that reason, when implementing an extension, you must also create + a QExtensionFactory, i.e a class that is able to make an instance + of your extension, and register it using \QD's extension manager. + + The registration of an extension factory is typically made in the + QDesignerCustomWidgetInterface::initialize() function: + + \snippet doc/src/snippets/code/tools_designer_src_lib_extension_qextensionmanager.cpp 0 + + The QExtensionManager is not intended to be instantiated + directly. You can retrieve an interface to \QD's extension manager + using the QDesignerFormEditorInterface::extensionManager() + function. A pointer to \QD's current QDesignerFormEditorInterface + object (\c formEditor in the example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface to expose your plugin + to \QD. + + Then, when an extension is required, \QD's extension manager will + run through all its registered factories calling + QExtensionFactory::createExtension() for each until the first one + that is able to create the requested extension for the selected + object, is found. This factory will then make an instance of the + extension. + + There are four available types of extensions in \QD: + QDesignerContainerExtension , QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and + QDesignerTaskMenuExtension. \QD's behavior is the same whether the + requested extension is associated with a container, a member + sheet, a property sheet or a task menu. + + For a complete example using the QExtensionManager class, see the + \l {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to \QD's task menu. + + \sa QExtensionFactory, QAbstractExtensionManager +*/ + +/*! + Constructs an extension manager with the given \a parent. +*/ +QExtensionManager::QExtensionManager(QObject *parent) + : QObject(parent) +{ +} + + +/*! + Destroys the extension manager +*/ +QExtensionManager::~QExtensionManager() +{ +} + +/*! + Register the extension specified by the given \a factory and + extension identifier \a iid. +*/ +void QExtensionManager::registerExtensions(QAbstractExtensionFactory *factory, const QString &iid) +{ + if (iid.isEmpty()) { + m_globalExtension.prepend(factory); + return; + } + + FactoryMap::iterator it = m_extensions.find(iid); + if (it == m_extensions.end()) + it = m_extensions.insert(iid, FactoryList()); + + it.value().prepend(factory); +} + +/*! + Unregister the extension specified by the given \a factory and + extension identifier \a iid. +*/ +void QExtensionManager::unregisterExtensions(QAbstractExtensionFactory *factory, const QString &iid) +{ + if (iid.isEmpty()) { + m_globalExtension.removeAll(factory); + return; + } + + const FactoryMap::iterator it = m_extensions.find(iid); + if (it == m_extensions.end()) + return; + + FactoryList &factories = it.value(); + factories.removeAll(factory); + + if (factories.isEmpty()) + m_extensions.erase(it); +} + +/*! + Returns the extension specified by \a iid, for the given \a + object. +*/ +QObject *QExtensionManager::extension(QObject *object, const QString &iid) const +{ + const FactoryMap::const_iterator it = m_extensions.constFind(iid); + if (it != m_extensions.constEnd()) { + const FactoryList::const_iterator fcend = it.value().constEnd(); + for (FactoryList::const_iterator fit = it.value().constBegin(); fit != fcend; ++fit) + if (QObject *ext = (*fit)->extension(object, iid)) + return ext; + } + const FactoryList::const_iterator gfcend = m_globalExtension.constEnd(); + for (FactoryList::const_iterator git = m_globalExtension.constBegin(); git != gfcend; ++git) + if (QObject *ext = (*git)->extension(object, iid)) + return ext; + + return 0; +} + +QT_END_NAMESPACE diff --git a/designer/lib/extension/qextensionmanager.h b/designer/lib/extension/qextensionmanager.h new file mode 100644 index 0000000..8761387 --- /dev/null +++ b/designer/lib/extension/qextensionmanager.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEXTENSIONMANAGER_H +#define QEXTENSIONMANAGER_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QObject; // Fool syncqt + +class QDESIGNER_EXTENSION_EXPORT QExtensionManager: public QObject, public QAbstractExtensionManager +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionManager) +public: + QExtensionManager(QObject *parent = 0); + ~QExtensionManager(); + + virtual void registerExtensions(QAbstractExtensionFactory *factory, const QString &iid = QString()); + virtual void unregisterExtensions(QAbstractExtensionFactory *factory, const QString &iid = QString()); + + virtual QObject *extension(QObject *object, const QString &iid) const; + +private: + typedef QList FactoryList; + typedef QHash FactoryMap; + FactoryMap m_extensions; + FactoryList m_globalExtension; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QEXTENSIONMANAGER_H diff --git a/designer/lib/lib.pro b/designer/lib/lib.pro new file mode 100644 index 0000000..495976d --- /dev/null +++ b/designer/lib/lib.pro @@ -0,0 +1,78 @@ +TEMPLATE=lib +TARGET=QtDesigner +QT += xml +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols +CONFIG += qt +win32|mac: CONFIG += debug_and_release +DESTDIR = ../../../../lib +!wince*:DLLDESTDIR = ../../../../bin + +isEmpty(QT_MAJOR_VERSION) { + VERSION=4.3.0 +} else { + VERSION=$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} +} + +unix:QMAKE_PKGCONFIG_REQUIRES += QtXml + +include(../../../../src/qt_targets.pri) +QMAKE_TARGET_PRODUCT = Designer +QMAKE_TARGET_DESCRIPTION = Graphical user interface designer. + +!contains(CONFIG, static) { + CONFIG += dll + + DEFINES += \ + QDESIGNER_SDK_LIBRARY \ + QDESIGNER_EXTENSION_LIBRARY \ + QDESIGNER_UILIB_LIBRARY \ + QDESIGNER_SHARED_LIBRARY +} else { + DEFINES += QT_DESIGNER_STATIC +} + +#load up the headers info +CONFIG += qt_install_headers +HEADERS_PRI = $$QT_BUILD_TREE/include/QtDesigner/headers.pri +include($$HEADERS_PRI, "", true)|clear(HEADERS_PRI) + +#mac frameworks +mac:CONFIG += explicitlib +mac:!static:contains(QT_CONFIG, qt_framework) { + QMAKE_FRAMEWORK_BUNDLE_NAME = $$TARGET + CONFIG += lib_bundle qt_no_framework_direct_includes qt_framework + CONFIG(debug, debug|release) { + !build_pass:CONFIG += build_all + } else { #release + !debug_and_release|build_pass { + CONFIG -= qt_install_headers #no need to install these as well + FRAMEWORK_HEADERS.version = Versions + FRAMEWORK_HEADERS.files = $$SYNCQT.HEADER_FILES $$SYNCQT.HEADER_CLASSES + FRAMEWORK_HEADERS.path = Headers + } + QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS + } +} + +include(extension/extension.pri) +include(sdk/sdk.pri) +include(uilib/uilib.pri) +include(shared/shared.pri) +PRECOMPILED_HEADER=lib_pch.h + +include(../sharedcomponents.pri) +include(../components/component.pri) + +target.path=$$[QT_INSTALL_LIBS] +INSTALLS += target +win32 { + dlltarget.path=$$[QT_INSTALL_BINS] + INSTALLS += dlltarget +} + + +qt_install_headers { + designer_headers.files = $$SYNCQT.HEADER_FILES $$SYNCQT.HEADER_CLASSES + designer_headers.path = $$[QT_INSTALL_HEADERS]/QtDesigner + INSTALLS += designer_headers +} diff --git a/designer/lib/lib_pch.h b/designer/lib/lib_pch.h new file mode 100644 index 0000000..40d480b --- /dev/null +++ b/designer/lib/lib_pch.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef __cplusplus +#include "shared_global_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qdesigner_widget_p.h" +#include +#include +#include +#include +#include "layout_p.h" +#endif diff --git a/designer/lib/sdk/abstractactioneditor.cpp b/designer/lib/sdk/abstractactioneditor.cpp new file mode 100644 index 0000000..063e99d --- /dev/null +++ b/designer/lib/sdk/abstractactioneditor.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractactioneditor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerActionEditorInterface + + \brief The QDesignerActionEditorInterface class allows you to + change the focus of Qt Designer's action editor. + + \inmodule QtDesigner + + The QDesignerActionEditorInterface class is not intended to be + instantiated directly. You can retrieve an interface to \QD's + action editor using the + QDesignerFormEditorInterface::actionEditor() function. + + You can control which actions that are available in the action + editor's window using the manageAction() and unmanageAction() + functions. An action that is managed by \QD is available in the + action editor while an unmanaged action is ignored. + + QDesignerActionEditorInterface also provides the core() function + that you can use to retrieve a pointer to \QD's current + QDesignerFormEditorInterface object, and the setFormWindow() + function that enables you to change the currently selected form + window. + + \sa QDesignerFormEditorInterface, QDesignerFormWindowInterface +*/ + +/*! + Constructs an action editor interface with the given \a parent and + the specified window \a flags. +*/ +QDesignerActionEditorInterface::QDesignerActionEditorInterface(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags) +{ +} + +/*! + Destroys the action editor interface. +*/ +QDesignerActionEditorInterface::~QDesignerActionEditorInterface() +{ +} + +/*! + Returns a pointer to \QD's current QDesignerFormEditorInterface + object. +*/ +QDesignerFormEditorInterface *QDesignerActionEditorInterface::core() const +{ + return 0; +} + +/*! + \fn void QDesignerActionEditorInterface::setFormWindow(QDesignerFormWindowInterface *formWindow) + + Sets the currently selected form window to \a formWindow. + +*/ + +/*! + \fn void QDesignerActionEditorInterface::manageAction(QAction *action) + + Instructs \QD to manage the specified \a action. An action that is + managed by \QD is available in the action editor. + + \sa unmanageAction() +*/ + +/*! + \fn void QDesignerActionEditorInterface::unmanageAction(QAction *action) + + Instructs \QD to ignore the specified \a action. An unmanaged + action is not available in the action editor. + + \sa manageAction() +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractactioneditor.h b/designer/lib/sdk/abstractactioneditor.h new file mode 100644 index 0000000..6a1d8fa --- /dev/null +++ b/designer/lib/sdk/abstractactioneditor.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTACTIONEDITOR_H +#define ABSTRACTACTIONEDITOR_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QDESIGNER_SDK_EXPORT QDesignerActionEditorInterface: public QWidget +{ + Q_OBJECT +public: + QDesignerActionEditorInterface(QWidget *parent, Qt::WindowFlags flags = 0); + virtual ~QDesignerActionEditorInterface(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual void manageAction(QAction *action) = 0; + virtual void unmanageAction(QAction *action) = 0; + +public Q_SLOTS: + virtual void setFormWindow(QDesignerFormWindowInterface *formWindow) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTACTIONEDITOR_H diff --git a/designer/lib/sdk/abstractbrushmanager.h b/designer/lib/sdk/abstractbrushmanager.h new file mode 100644 index 0000000..7a09999 --- /dev/null +++ b/designer/lib/sdk/abstractbrushmanager.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTBRUSHMANAGER_H +#define ABSTRACTBRUSHMANAGER_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QObject; + +class QDESIGNER_SDK_EXPORT QDesignerBrushManagerInterface : public QObject +{ + Q_OBJECT +public: + QDesignerBrushManagerInterface(QObject *parentObject = 0) : QObject(parentObject) {} + + virtual QBrush brush(const QString &name) const = 0; + virtual QMap brushes() const = 0; + virtual QString currentBrush() const = 0; + + virtual QString addBrush(const QString &name, const QBrush &brush) = 0; + virtual void removeBrush(const QString &name) = 0; + virtual void setCurrentBrush(const QString &name) = 0; + + virtual QPixmap brushPixmap(const QBrush &brush) const = 0; +Q_SIGNALS: + void brushAdded(const QString &name, const QBrush &brush); + void brushRemoved(const QString &name); + void currentBrushChanged(const QString &name, const QBrush &brush); + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/designer/lib/sdk/abstractdialoggui.cpp b/designer/lib/sdk/abstractdialoggui.cpp new file mode 100644 index 0000000..4d5111f --- /dev/null +++ b/designer/lib/sdk/abstractdialoggui.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractdialoggui_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerDialogGuiInterface + \since 4.4 + \internal + + \brief The QDesignerDialogGuiInterface allows integrations of \QD to replace the + message boxes displayed by \QD by custom dialogs. + + \inmodule QtDesigner + + QDesignerDialogGuiInterface provides virtual functions that can be overwritten + to display message boxes and file dialogs. + \sa QMessageBox, QFileDialog +*/ + +/*! + \enum QDesignerDialogGuiInterface::Message + + This enum specifies the context from within the message box is called. + + \value FormLoadFailureMessage Loading of a form failed + \value UiVersionMismatchMessage Attempt to load a file created with an old version of Designer + \value ResourceLoadFailureMessage Resources specified in a file could not be found + \value TopLevelSpacerMessage Spacer items detected on a container without layout + \value PropertyEditorMessage Messages of the propert yeditor + \value SignalSlotEditorMessage Messages of the signal / slot editor + \value FormEditorMessage Messages of the form editor + \value PreviewFailureMessage A preview could not be created + \value PromotionErrorMessage Messages related to promotion of a widget + \value ResourceEditorMessage Messages of the resource editor + \value ScriptDialogMessage Messages of the script dialog + \value SignalSlotDialogMessage Messages of the signal slot dialog + \value OtherMessage Unspecified context +*/ + +/*! + Constructs a QDesignerDialogGuiInterface object. +*/ + +QDesignerDialogGuiInterface::QDesignerDialogGuiInterface() +{ +} + +/*! + Destroys the QDesignerDialogGuiInterface object. +*/ +QDesignerDialogGuiInterface::~QDesignerDialogGuiInterface() +{ +} + +/*! + \fn QMessageBox::StandardButton QDesignerDialogGuiInterface::message(QWidget *parent, Message context, QMessageBox::Icon icon, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) + + Opens a message box as child of \a parent within the context \a context, using \a icon, \a title, \a text, \a buttons and \a defaultButton + and returns the button chosen by the user. +*/ + +/*! + \fn QString QDesignerDialogGuiInterface::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) + + Opens a file dialog as child of \a parent using the parameters \a caption, \a dir and \a options that prompts the + user for an existing directory. Returns a directory selected by the user. +*/ + +/*! + \fn QString QDesignerDialogGuiInterface::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options) + + Opens a file dialog as child of \a parent using the parameters \a caption, \a dir, \a filter, \a selectedFilter and \a options + that prompts the user for an existing file. Returns a file selected by the user. +*/ + +/*! + \fn QStringList QDesignerDialogGuiInterface::getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options) + + Opens a file dialog as child of \a parent using the parameters \a caption, \a dir, \a filter, \a selectedFilter and \a options + that prompts the user for a set of existing files. Returns one or more existing files selected by the user. +*/ + +/*! + Opens a file dialog with image browsing capabilities as child of \a parent using the parameters \a caption, \a dir, \a filter, \a selectedFilter and \a options + that prompts the user for an existing file. Returns a file selected by the user. + + The default implementation simply calls getOpenFileName(). On platforms that do not support an image preview in the QFileDialog, + the function can be reimplemented to provide an image browser. + + \since 4.5 +*/ + +QString QDesignerDialogGuiInterface::getOpenImageFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +} + +/*! + Opens a file dialog with image browsing capabilities as child of \a parent using the parameters \a caption, \a dir, \a filter, \a selectedFilter and \a options + that prompts the user for a set of existing files. Returns one or more existing files selected by the user. + + The default implementation simply calls getOpenFileNames(). On platforms that do not support an image preview in the QFileDialog, + the function can be reimplemented to provide an image browser. + + \since 4.5 +*/ + +QStringList QDesignerDialogGuiInterface::getOpenImageFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return getOpenImageFileNames(parent, caption, dir, filter, selectedFilter, options); +} + +/*! + \fn QString QDesignerDialogGuiInterface::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options) + + Opens a file dialog as child of \a parent using the parameters \a caption, \a dir, \a filter, \a selectedFilter and \a options + that prompts the user for a file. Returns a file selected by the user. The file does not have to exist. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractdialoggui_p.h b/designer/lib/sdk/abstractdialoggui_p.h new file mode 100644 index 0000000..fa960d3 --- /dev/null +++ b/designer/lib/sdk/abstractdialoggui_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ABSTRACTDIALOGGUI_H +#define ABSTRACTDIALOGGUI_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWidget; + +class QDESIGNER_SDK_EXPORT QDesignerDialogGuiInterface +{ + Q_DISABLE_COPY(QDesignerDialogGuiInterface) +public: + QDesignerDialogGuiInterface(); + virtual ~QDesignerDialogGuiInterface(); + + enum Message { FormLoadFailureMessage, UiVersionMismatchMessage, ResourceLoadFailureMessage, + TopLevelSpacerMessage, PropertyEditorMessage, SignalSlotEditorMessage, FormEditorMessage, + PreviewFailureMessage, PromotionErrorMessage, ResourceEditorMessage, + ScriptDialogMessage, SignalSlotDialogMessage, OtherMessage, FileChangedMessage }; + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) = 0; + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) = 0; + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, const QString &detailedText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) = 0; + + virtual QString getExistingDirectory(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), QFileDialog::Options options = QFileDialog::ShowDirsOnly)= 0; + virtual QString getOpenFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0)= 0; + virtual QString getOpenImageFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QStringList getOpenFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0)= 0; + virtual QStringList getOpenImageFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QString getSaveFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0)= 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTDIALOGGUI_H diff --git a/designer/lib/sdk/abstractdnditem.h b/designer/lib/sdk/abstractdnditem.h new file mode 100644 index 0000000..83dc02d --- /dev/null +++ b/designer/lib/sdk/abstractdnditem.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTDNDITEM_H +#define ABSTRACTDNDITEM_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DomUI; +class QWidget; +class QPoint; + +class QDESIGNER_SDK_EXPORT QDesignerDnDItemInterface +{ +public: + enum DropType { MoveDrop, CopyDrop }; + + QDesignerDnDItemInterface() {} + virtual ~QDesignerDnDItemInterface() {} + + virtual DomUI *domUi() const = 0; + virtual QWidget *widget() const = 0; + virtual QWidget *decoration() const = 0; + virtual QPoint hotSpot() const = 0; + virtual DropType type() const = 0; + virtual QWidget *source() const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTDNDITEM_H diff --git a/designer/lib/sdk/abstractdnditem.qdoc b/designer/lib/sdk/abstractdnditem.qdoc new file mode 100644 index 0000000..bdae59c --- /dev/null +++ b/designer/lib/sdk/abstractdnditem.qdoc @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerDnDItemInterface + \brief The QDesignerDnDItemInterface class provides an interface that is used to manage items + during a drag and drop operation. + \inmodule QtDesigner + \internal +*/ + +/*! + \enum QDesignerDnDItemInterface::DropType + + This enum describes the result of a drag and drop operation. + + \value MoveDrop The item was moved. + \value CopyDrop The item was copied. +*/ + +/*! + \fn QDesignerDnDItemInterface::QDesignerDnDItemInterface() + + Constructs a new interface to a drag and drop item. +*/ + +/*! + \fn QDesignerDnDItemInterface::~QDesignerDnDItemInterface() + + Destroys the interface to the item. +*/ + +/*! + \fn DomUI *QDesignerDnDItemInterface::domUi() const + + Returns a user interface object for the item. +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::widget() const + + Returns the widget being copied or moved in the drag and drop operation. + + \sa source() +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::decoration() const + + Returns the widget used to represent the item. +*/ + +/*! + \fn QPoint QDesignerDnDItemInterface::hotSpot() const + + Returns the cursor's hotspot. + + \sa QDrag::hotSpot() +*/ + +/*! + \fn DropType QDesignerDnDItemInterface::type() const + + Returns the type of drag and drop operation in progress. +*/ + +/*! + \fn QWidget *QDesignerDnDItemInterface::source() const + + Returns the widget that is the source of the drag and drop operation; i.e. the original + container of the widget being dragged. + + \sa widget() +*/ diff --git a/designer/lib/sdk/abstractformeditor.cpp b/designer/lib/sdk/abstractformeditor.cpp new file mode 100644 index 0000000..9b2662a --- /dev/null +++ b/designer/lib/sdk/abstractformeditor.cpp @@ -0,0 +1,630 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformeditor.h" +#include "abstractdialoggui_p.h" +#include "abstractintrospection_p.h" +#include "abstractsettings_p.h" +#include "abstractoptionspage_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Must be done outside of the Qt namespace +static void initResources() +{ + Q_INIT_RESOURCE(shared); + Q_INIT_RESOURCE(ClamshellPhone); + Q_INIT_RESOURCE(PortableMedia); + Q_INIT_RESOURCE(S60_nHD_Touchscreen); + Q_INIT_RESOURCE(S60_QVGA_Candybar); + Q_INIT_RESOURCE(SmartPhone2); + Q_INIT_RESOURCE(SmartPhone); + Q_INIT_RESOURCE(SmartPhoneWithButtons); + Q_INIT_RESOURCE(TouchscreenPhone); +} + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterfacePrivate { +public: + QDesignerFormEditorInterfacePrivate(); + ~QDesignerFormEditorInterfacePrivate(); + + + QPointer m_topLevel; + QPointer m_widgetBox; + QPointer m_propertyEditor; + QPointer m_formWindowManager; + QPointer m_extensionManager; + QPointer m_metaDataBase; + QPointer m_widgetDataBase; + QPointer m_widgetFactory; + QPointer m_objectInspector; + QPointer m_brushManager; + QPointer m_integration; + QPointer m_iconCache; + QPointer m_actionEditor; + QDesignerSettingsInterface *m_settingsManager; + QDesignerPluginManager *m_pluginManager; + QDesignerPromotionInterface *m_promotion; + QDesignerIntrospectionInterface *m_introspection; + QDesignerDialogGuiInterface *m_dialogGui; + QPointer m_resourceModel; + QPointer m_gradientManager; // instantiated and deleted by designer_integration + QList m_optionsPages; +}; + +QDesignerFormEditorInterfacePrivate::QDesignerFormEditorInterfacePrivate() : + m_settingsManager(0), + m_pluginManager(0), + m_promotion(0), + m_introspection(0), + m_dialogGui(0), + m_resourceModel(0), + m_gradientManager(0) +{ +} + +QDesignerFormEditorInterfacePrivate::~QDesignerFormEditorInterfacePrivate() +{ + delete m_settingsManager; + delete m_formWindowManager; + delete m_promotion; + delete m_introspection; + delete m_dialogGui; + delete m_resourceModel; + qDeleteAll(m_optionsPages); +} + +/*! + \class QDesignerFormEditorInterface + + \brief The QDesignerFormEditorInterface class allows you to access + Qt Designer's various components. + + \inmodule QtDesigner + + \QD's current QDesignerFormEditorInterface object holds + information about all \QD's components: The action editor, the + object inspector, the property editor, the widget box, and the + extension and form window managers. QDesignerFormEditorInterface + contains a collection of functions that provides interfaces to all + these components. They are typically used to query (and + manipulate) the respective component. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformeditor.cpp 0 + + QDesignerFormEditorInterface is not intended to be instantiated + directly. A pointer to \QD's current QDesignerFormEditorInterface + object (\c formEditor in the example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface to expose your plugin + to \QD. + + QDesignerFormEditorInterface also provides functions that can set + the action editor, property editor, object inspector and widget + box. These are only useful if you want to provide your own custom + components. + + If designer is embedded in another program, one could to provide its + own settings manager. The manager is used by the components of \QD + to store/retrieve persistent configuration settings. The default + manager uses QSettings as the backend. + + Finally, QDesignerFormEditorInterface provides the topLevel() + function that returns \QD's top-level widget. + + \sa QDesignerCustomWidgetInterface +*/ + +/*! + Constructs a QDesignerFormEditorInterface object with the given \a + parent. +*/ + +QDesignerFormEditorInterface::QDesignerFormEditorInterface(QObject *parent) + : QObject(parent), + d(new QDesignerFormEditorInterfacePrivate()) +{ + initResources(); +} + +/*! + Destroys the QDesignerFormEditorInterface object. +*/ +QDesignerFormEditorInterface::~QDesignerFormEditorInterface() +{ + delete d; +} + +/*! + Returns an interface to \QD's widget box. + + \sa setWidgetBox() +*/ +QDesignerWidgetBoxInterface *QDesignerFormEditorInterface::widgetBox() const +{ + return d->m_widgetBox; +} + +/*! + Sets \QD's widget box to be the specified \a widgetBox. + + \sa widgetBox() +*/ +void QDesignerFormEditorInterface::setWidgetBox(QDesignerWidgetBoxInterface *widgetBox) +{ + d->m_widgetBox = widgetBox; +} + +/*! + Returns an interface to \QD's property editor. + + \sa setPropertyEditor() +*/ +QDesignerPropertyEditorInterface *QDesignerFormEditorInterface::propertyEditor() const +{ + return d->m_propertyEditor; +} + +/*! + Sets \QD's property editor to be the specified \a propertyEditor. + + \sa propertyEditor() +*/ +void QDesignerFormEditorInterface::setPropertyEditor(QDesignerPropertyEditorInterface *propertyEditor) +{ + d->m_propertyEditor = propertyEditor; +} + +/*! + Returns an interface to \QD's action editor. + + \sa setActionEditor() +*/ +QDesignerActionEditorInterface *QDesignerFormEditorInterface::actionEditor() const +{ + return d->m_actionEditor; +} + +/*! + Sets \QD's action editor to be the specified \a actionEditor. + + \sa actionEditor() +*/ +void QDesignerFormEditorInterface::setActionEditor(QDesignerActionEditorInterface *actionEditor) +{ + d->m_actionEditor = actionEditor; +} + +/*! + Returns \QD's top-level widget. +*/ +QWidget *QDesignerFormEditorInterface::topLevel() const +{ + return d->m_topLevel; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setTopLevel(QWidget *topLevel) +{ + d->m_topLevel = topLevel; +} + +/*! + Returns an interface to \QD's form window manager. +*/ +QDesignerFormWindowManagerInterface *QDesignerFormEditorInterface::formWindowManager() const +{ + return d->m_formWindowManager; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setFormManager(QDesignerFormWindowManagerInterface *formWindowManager) +{ + d->m_formWindowManager = formWindowManager; +} + +/*! + Returns an interface to \QD's extension manager. +*/ +QExtensionManager *QDesignerFormEditorInterface::extensionManager() const +{ + return d->m_extensionManager; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setExtensionManager(QExtensionManager *extensionManager) +{ + d->m_extensionManager = extensionManager; +} + +/*! + \internal + + Returns an interface to the meta database used by the form editor. +*/ +QDesignerMetaDataBaseInterface *QDesignerFormEditorInterface::metaDataBase() const +{ + return d->m_metaDataBase; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setMetaDataBase(QDesignerMetaDataBaseInterface *metaDataBase) +{ + d->m_metaDataBase = metaDataBase; +} + +/*! + \internal + + Returns an interface to the widget database used by the form editor. +*/ +QDesignerWidgetDataBaseInterface *QDesignerFormEditorInterface::widgetDataBase() const +{ + return d->m_widgetDataBase; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setWidgetDataBase(QDesignerWidgetDataBaseInterface *widgetDataBase) +{ + d->m_widgetDataBase = widgetDataBase; +} + +/*! + \internal + + Returns an interface to the designer promotion handler. +*/ + +QDesignerPromotionInterface *QDesignerFormEditorInterface::promotion() const +{ + return d->m_promotion; +} + +/*! + \internal + + Sets the designer promotion handler. +*/ + +void QDesignerFormEditorInterface::setPromotion(QDesignerPromotionInterface *promotion) +{ + if (d->m_promotion) + delete d->m_promotion; + d->m_promotion = promotion; +} + +/*! + \internal + + Returns an interface to the widget factory used by the form editor + to create widgets for the form. +*/ +QDesignerWidgetFactoryInterface *QDesignerFormEditorInterface::widgetFactory() const +{ + return d->m_widgetFactory; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setWidgetFactory(QDesignerWidgetFactoryInterface *widgetFactory) +{ + d->m_widgetFactory = widgetFactory; +} + +/*! + Returns an interface to \QD's object inspector. +*/ +QDesignerObjectInspectorInterface *QDesignerFormEditorInterface::objectInspector() const +{ + return d->m_objectInspector; +} + +/*! + Sets \QD's object inspector to be the specified \a + objectInspector. + + \sa objectInspector() +*/ +void QDesignerFormEditorInterface::setObjectInspector(QDesignerObjectInspectorInterface *objectInspector) +{ + d->m_objectInspector = objectInspector; +} + +/*! + \internal + + Returns an interface to the brush manager used by the palette editor. +*/ +QDesignerBrushManagerInterface *QDesignerFormEditorInterface::brushManager() const +{ + return d->m_brushManager; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setBrushManager(QDesignerBrushManagerInterface *brushManager) +{ + d->m_brushManager = brushManager; +} + +/*! + \internal + + Returns an interface to the integration. +*/ +QDesignerIntegrationInterface *QDesignerFormEditorInterface::integration() const +{ + return d->m_integration; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setIntegration(QDesignerIntegrationInterface *integration) +{ + d->m_integration = integration; +} + +/*! + \internal + + Returns an interface to the icon cache used by the form editor to + manage icons. +*/ +QDesignerIconCacheInterface *QDesignerFormEditorInterface::iconCache() const +{ + return d->m_iconCache; +} + +/*! + \internal +*/ +void QDesignerFormEditorInterface::setIconCache(QDesignerIconCacheInterface *cache) +{ + d->m_iconCache = cache; +} + +/*! + \internal + \since 4.5 + Returns the list of options pages that allow the user to configure \QD components. +*/ +QList QDesignerFormEditorInterface::optionsPages() const +{ + return d->m_optionsPages; +} + +/*! + \internal + \since 4.5 + Sets the list of options pages that allow the user to configure \QD components. +*/ +void QDesignerFormEditorInterface::setOptionsPages(const QList &optionsPages) +{ + d->m_optionsPages = optionsPages; +} + + +/*! + \internal + + Returns the plugin manager used by the form editor. +*/ +QDesignerPluginManager *QDesignerFormEditorInterface::pluginManager() const +{ + return d->m_pluginManager; +} + +/*! + \internal + + Sets the plugin manager used by the form editor to the specified + \a pluginManager. +*/ +void QDesignerFormEditorInterface::setPluginManager(QDesignerPluginManager *pluginManager) +{ + d->m_pluginManager = pluginManager; +} + +/*! + \internal + \since 4.4 + Returns the resource model used by the form editor. +*/ +QtResourceModel *QDesignerFormEditorInterface::resourceModel() const +{ + return d->m_resourceModel; +} + +/*! + \internal + + Sets the resource model used by the form editor to the specified + \a resourceModel. +*/ +void QDesignerFormEditorInterface::setResourceModel(QtResourceModel *resourceModel) +{ + d->m_resourceModel = resourceModel; +} + +/*! + \internal + \since 4.4 + Returns the gradient manager used by the style sheet editor. +*/ +QtGradientManager *QDesignerFormEditorInterface::gradientManager() const +{ + return d->m_gradientManager; +} + +/*! + \internal + + Sets the gradient manager used by the style sheet editor to the specified + \a gradientManager. +*/ +void QDesignerFormEditorInterface::setGradientManager(QtGradientManager *gradientManager) +{ + d->m_gradientManager = gradientManager; +} + +/*! + \internal + \since 4.5 + Returns the settings manager used by the components to store persistent settings. +*/ +QDesignerSettingsInterface *QDesignerFormEditorInterface::settingsManager() const +{ + return d->m_settingsManager; +} + +/*! + \internal + \since 4.5 + Sets the settings manager used to store/retrieve the persistent settings of the components. +*/ +void QDesignerFormEditorInterface::setSettingsManager(QDesignerSettingsInterface *settingsManager) +{ + if (d->m_settingsManager) + delete d->m_settingsManager; + d->m_settingsManager = settingsManager; + + // This is a (hopefully) safe place to perform settings-dependent + // initializations. + const qdesigner_internal::QDesignerSharedSettings settings(this); + qdesigner_internal::FormWindowBase::setDefaultDesignerGrid(settings.defaultGrid()); +} + +/*! + \internal + \since 4.4 + Returns the introspection used by the form editor. +*/ +QDesignerIntrospectionInterface *QDesignerFormEditorInterface::introspection() const +{ + return d->m_introspection; +} + +/*! + \internal + \since 4.4 + + Sets the introspection used by the form editor to the specified \a introspection. +*/ +void QDesignerFormEditorInterface::setIntrospection(QDesignerIntrospectionInterface *introspection) +{ + if (d->m_introspection) + delete d->m_introspection; + d->m_introspection = introspection; +} + +/*! + \internal + + Returns the path to the resources used by the form editor. +*/ +QString QDesignerFormEditorInterface::resourceLocation() const +{ +#ifdef Q_WS_MAC + return QLatin1String(":/trolltech/formeditor/images/mac"); +#else + return QLatin1String(":/trolltech/formeditor/images/win"); +#endif +} + +/*! + \internal + + Returns the dialog GUI used by the form editor. +*/ + +QDesignerDialogGuiInterface *QDesignerFormEditorInterface::dialogGui() const +{ + return d->m_dialogGui; +} + +/*! + \internal + + Sets the dialog GUI used by the form editor to the specified \a dialogGui. +*/ + +void QDesignerFormEditorInterface::setDialogGui(QDesignerDialogGuiInterface *dialogGui) +{ + delete d->m_dialogGui; + d->m_dialogGui = dialogGui; +} + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractformeditor.h b/designer/lib/sdk/abstractformeditor.h new file mode 100644 index 0000000..4b521b0 --- /dev/null +++ b/designer/lib/sdk/abstractformeditor.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMEDITOR_H +#define ABSTRACTFORMEDITOR_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerWidgetBoxInterface; +class QDesignerPropertyEditorInterface; +class QDesignerFormWindowManagerInterface; +class QDesignerWidgetDataBaseInterface; +class QDesignerMetaDataBaseInterface; +class QDesignerWidgetFactoryInterface; +class QDesignerObjectInspectorInterface; +class QDesignerPromotionInterface; +class QDesignerBrushManagerInterface; +class QDesignerIconCacheInterface; +class QDesignerActionEditorInterface; +class QDesignerIntegrationInterface; +class QDesignerPluginManager; +class QDesignerIntrospectionInterface; +class QDesignerDialogGuiInterface; +class QDesignerSettingsInterface; +class QDesignerOptionsPageInterface; +class QtResourceModel; +class QtGradientManager; + +class QWidget; + +class QExtensionManager; + +class QDesignerFormEditorInterfacePrivate; + +class QDESIGNER_SDK_EXPORT QDesignerFormEditorInterface : public QObject +{ + Q_OBJECT +public: + QDesignerFormEditorInterface(QObject *parent = 0); + virtual ~QDesignerFormEditorInterface(); + + QExtensionManager *extensionManager() const; + + QWidget *topLevel() const; + QDesignerWidgetBoxInterface *widgetBox() const; + QDesignerPropertyEditorInterface *propertyEditor() const; + QDesignerObjectInspectorInterface *objectInspector() const; + QDesignerFormWindowManagerInterface *formWindowManager() const; + QDesignerWidgetDataBaseInterface *widgetDataBase() const; + QDesignerMetaDataBaseInterface *metaDataBase() const; + QDesignerPromotionInterface *promotion() const; + QDesignerWidgetFactoryInterface *widgetFactory() const; + QDesignerBrushManagerInterface *brushManager() const; + QDesignerIconCacheInterface *iconCache() const; + QDesignerActionEditorInterface *actionEditor() const; + QDesignerIntegrationInterface *integration() const; + QDesignerPluginManager *pluginManager() const; + QDesignerIntrospectionInterface *introspection() const; + QDesignerDialogGuiInterface *dialogGui() const; + QDesignerSettingsInterface *settingsManager() const; + QString resourceLocation() const; + QtResourceModel *resourceModel() const; + QtGradientManager *gradientManager() const; + QList optionsPages() const; + + void setTopLevel(QWidget *topLevel); + void setWidgetBox(QDesignerWidgetBoxInterface *widgetBox); + void setPropertyEditor(QDesignerPropertyEditorInterface *propertyEditor); + void setObjectInspector(QDesignerObjectInspectorInterface *objectInspector); + void setPluginManager(QDesignerPluginManager *pluginManager); + void setActionEditor(QDesignerActionEditorInterface *actionEditor); + void setIntegration(QDesignerIntegrationInterface *integration); + void setIntrospection(QDesignerIntrospectionInterface *introspection); + void setDialogGui(QDesignerDialogGuiInterface *dialogGui); + void setSettingsManager(QDesignerSettingsInterface *settingsManager); + void setResourceModel(QtResourceModel *model); + void setGradientManager(QtGradientManager *manager); + void setOptionsPages(const QList &optionsPages); + +protected: + void setFormManager(QDesignerFormWindowManagerInterface *formWindowManager); + void setMetaDataBase(QDesignerMetaDataBaseInterface *metaDataBase); + void setWidgetDataBase(QDesignerWidgetDataBaseInterface *widgetDataBase); + void setPromotion(QDesignerPromotionInterface *promotion); + void setWidgetFactory(QDesignerWidgetFactoryInterface *widgetFactory); + void setExtensionManager(QExtensionManager *extensionManager); + void setBrushManager(QDesignerBrushManagerInterface *brushManager); + void setIconCache(QDesignerIconCacheInterface *cache); + +private: + QPointer m_pad1; + QPointer m_pad2; + QPointer m_pad3; + QPointer m_pad4; + QPointer m_pad5; + QPointer m_pad6; + QPointer m_pad7; + QPointer m_pad8; + QPointer m_pad9; + QPointer m_pad10; + QPointer m_pad11; + QPointer m_pad12; + QDesignerFormEditorInterfacePrivate *d; + +private: + QDesignerFormEditorInterface(const QDesignerFormEditorInterface &other); + void operator = (const QDesignerFormEditorInterface &other); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMEDITOR_H diff --git a/designer/lib/sdk/abstractformeditorplugin.cpp b/designer/lib/sdk/abstractformeditorplugin.cpp new file mode 100644 index 0000000..c017aab --- /dev/null +++ b/designer/lib/sdk/abstractformeditorplugin.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QDesignerFormEditorPluginInterface + \brief The QDesignerFormEditorPluginInterface class provides an interface that is used to + manage plugins for Qt Designer's form editor component. + \inmodule QtDesigner + + \sa QDesignerFormEditorInterface +*/ + +/*! + \fn virtual QDesignerFormEditorPluginInterface::~QDesignerFormEditorPluginInterface() + + Destroys the plugin interface. +*/ + +/*! + \fn virtual bool QDesignerFormEditorPluginInterface::isInitialized() const = 0 + + Returns true if the plugin interface is initialized; otherwise returns false. +*/ + +/*! + \fn virtual void QDesignerFormEditorPluginInterface::initialize(QDesignerFormEditorInterface *core) = 0 + + Initializes the plugin interface for the specified \a core interface. +*/ + +/*! + \fn virtual QAction *QDesignerFormEditorPluginInterface::action() const = 0 + + Returns the action associated with this interface. +*/ + +/*! + \fn virtual QDesignerFormEditorInterface *QDesignerFormEditorPluginInterface::core() const = 0 + + Returns the core form editor interface associated with this component. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractformeditorplugin.h b/designer/lib/sdk/abstractformeditorplugin.h new file mode 100644 index 0000000..1b6a8f0 --- /dev/null +++ b/designer/lib/sdk/abstractformeditorplugin.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMEDITORPLUGIN_H +#define ABSTRACTFORMEDITORPLUGIN_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QAction; + +class QDESIGNER_SDK_EXPORT QDesignerFormEditorPluginInterface +{ +public: + virtual ~QDesignerFormEditorPluginInterface() {} + + virtual bool isInitialized() const = 0; + virtual void initialize(QDesignerFormEditorInterface *core) = 0; + virtual QAction *action() const = 0; + + virtual QDesignerFormEditorInterface *core() const = 0; +}; +Q_DECLARE_INTERFACE(QDesignerFormEditorPluginInterface, "com.trolltech.Qt.Designer.QDesignerFormEditorPluginInterface") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMEDITORPLUGIN_H diff --git a/designer/lib/sdk/abstractformwindow.cpp b/designer/lib/sdk/abstractformwindow.cpp new file mode 100644 index 0000000..b362fa2 --- /dev/null +++ b/designer/lib/sdk/abstractformwindow.cpp @@ -0,0 +1,814 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerFormWindowInterface + + \brief The QDesignerFormWindowInterface class allows you to query + and manipulate form windows appearing in Qt Designer's workspace. + + \inmodule QtDesigner + + QDesignerFormWindowInterface provides information about + the associated form window as well as allowing its properties to be + altered. The interface is not intended to be instantiated + directly, but to provide access to \QD's current form windows + controlled by \QD's \l {QDesignerFormWindowManagerInterface}{form + window manager}. + + If you are looking for the form window containing a specific + widget, you can use the static + QDesignerFormWindowInterface::findFormWindow() function: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformwindow.cpp 0 + + But in addition, you can access any of the current form windows + through \QD's form window manager: Use the + QDesignerFormEditorInterface::formWindowManager() function to + retrieve an interface to the manager. Once you have this + interface, you have access to all of \QD's current form windows + through the QDesignerFormWindowManagerInterface::formWindow() + function. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformwindow.cpp 1 + + The pointer to \QD's current QDesignerFormEditorInterface object + (\c formEditor in the example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface class to expose your + plugin to \QD. + + Once you have the form window, you can query its properties. For + example, a plain custom widget plugin is managed by \QD only at + its top level, i.e. none of its child widgets can be resized in + \QD's workspace. But QDesignerFormWindowInterface provides you + with functions that enables you to control whether a widget should + be managed by \QD, or not: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformwindow.cpp 2 + + The complete list of functions concerning widget management is: + isManaged(), manageWidget() and unmanageWidget(). There is also + several associated signals: widgetManaged(), widgetRemoved(), + aboutToUnmanageWidget() and widgetUnmanaged(). + + In addition to controlling the management of widgets, you can + control the current selection in the form window using the + selectWidget(), clearSelection() and emitSelectionChanged() + functions, and the selectionChanged() signal. + + You can also retrieve information about where the form is stored + using absoluteDir(), its include files using includeHints(), and + its layout and pixmap functions using layoutDefault(), + layoutFunction() and pixmapFunction(). You can find out whether + the form window has been modified (but not saved) or not, using + the isDirty() function. You can retrieve its author(), its + contents(), its fileName(), associated comment() and + exportMacro(), its mainContainer(), its features(), its grid() and + its resourceFiles(). + + The interface provides you with functions and slots allowing you + to alter most of this information as well. The exception is the + directory storing the form window. Finally, there is several + signals associated with changes to the information mentioned above + and to the form window in general. + + \sa QDesignerFormWindowCursorInterface, + QDesignerFormEditorInterface, QDesignerFormWindowManagerInterface +*/ + +/*! + \enum QDesignerFormWindowInterface::FeatureFlag + + This enum describes the features that are available and can be + controlled by the form window interface. These values are used + when querying the form window to determine which features it + supports: + + \value EditFeature Form editing + \value GridFeature Grid display and snap-to-grid facilities for editing + \value TabOrderFeature Tab order management + \value DefaultFeature Support for default features (form editing and grid) + + \sa hasFeature(), features() +*/ + +/*! + Constructs a form window interface with the given \a parent and + the specified window \a flags. +*/ +QDesignerFormWindowInterface::QDesignerFormWindowInterface(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags) +{ +} + +/*! + Destroys the form window interface. +*/ +QDesignerFormWindowInterface::~QDesignerFormWindowInterface() +{ +} + +/*! + Returns a pointer to \QD's current QDesignerFormEditorInterface + object. +*/ +QDesignerFormEditorInterface *QDesignerFormWindowInterface::core() const +{ + return 0; +} + +/*! + \fn QDesignerFormWindowInterface *QDesignerFormWindowInterface::findFormWindow(QWidget *widget) + + Returns the form window interface for the given \a widget. +*/ + +static inline bool stopFindAtTopLevel(const QObject *w, bool stopAtMenu) +{ + // Do we need to go beyond top levels when looking for the form window? + // 1) A dialog has a window attribute at the moment it is created + // before it is properly embedded into a form window. The property + // sheet queries the layout attributes precisely at this moment. + // 2) In the case of floating docks and toolbars, we also need to go beyond the top level window. + // 3) In the case of menu editing, we don't want to block events from the + // Designer menu, so, we say stop. + // Note that there must be no false positives for dialogs parented on + // the form (for example, the "change object name" dialog), else, its + // events will be blocked. + + if (stopAtMenu && w->inherits("QDesignerMenu")) + return true; + return !qdesigner_internal::WidgetFactory::isFormEditorObject(w); +} + +QDesignerFormWindowInterface *QDesignerFormWindowInterface::findFormWindow(QWidget *w) +{ + while (w != 0) { + if (QDesignerFormWindowInterface *fw = qobject_cast(w)) { + return fw; + } else { + if (w->isWindow() && stopFindAtTopLevel(w, true)) + break; + } + + w = w->parentWidget(); + } + + return 0; +} + +/*! + \fn QDesignerFormWindowInterface *QDesignerFormWindowInterface::findFormWindow(QObject *object) + + Returns the form window interface for the given \a object. + + \since 4.4 +*/ + +QDesignerFormWindowInterface *QDesignerFormWindowInterface::findFormWindow(QObject *object) +{ + while (object != 0) { + if (QDesignerFormWindowInterface *fw = qobject_cast(object)) { + return fw; + } else { + QWidget *w = qobject_cast(object); + // QDesignerMenu is a window, so stopFindAtTopLevel(w) returns 0. + // However, we want to find the form window for QActions of a menu. + // If this check is inside stopFindAtTopLevel(w), it will break designer + // menu editing (e.g. when clicking on items inside an opened menu) + if (w && w->isWindow() && stopFindAtTopLevel(w, false)) + break; + + } + + object = object->parent(); + } + + return 0; +} + +/*! + \fn virtual QString QDesignerFormWindowInterface::fileName() const + + Returns the file name of the UI file that describes the form + currently being shown. + + \sa setFileName() +*/ + +/*! + \fn virtual QDir QDesignerFormWindowInterface::absoluteDir() const + + Returns the absolute location of the directory containing the form + shown in the form window. +*/ + +/*! + \fn virtual QString QDesignerFormWindowInterface::contents() const + + Returns details of the contents of the form currently being + displayed in the window. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setContents(QIODevice *device) + + Sets the form's contents using data obtained from the given \a device. + + Data can be read from QFile objects or any other subclass of QIODevice. +*/ + +/*! + \fn virtual Feature QDesignerFormWindowInterface::features() const + + Returns a combination of the features provided by the form window + associated with the interface. The value returned can be tested + against the \l Feature enum values to determine which features are + supported by the window. + + \sa setFeatures(), hasFeature() +*/ + +/*! + \fn virtual bool QDesignerFormWindowInterface::hasFeature(Feature feature) const + + Returns true if the form window offers the specified \a feature; + otherwise returns false. + + \sa features() +*/ + +/*! + \fn virtual QString QDesignerFormWindowInterface::author() const + + Returns details of the author or creator of the form currently + being displayed in the window. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setAuthor(const QString &author) + + Sets the details for the author or creator of the form to the \a + author specified. +*/ + +/*! + \fn virtual QString QDesignerFormWindowInterface::comment() const + + Returns comments about the form currently being displayed in the window. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setComment(const QString &comment) + + Sets the information about the form to the \a comment + specified. This information should be a human-readable comment + about the form. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::layoutDefault(int *margin, int *spacing) + + Fills in the default margin and spacing for the form's default + layout in the \a margin and \a spacing variables specified. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setLayoutDefault(int margin, int spacing) + + Sets the default \a margin and \a spacing for the form's layout. + + \sa layoutDefault() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::layoutFunction(QString *margin, QString *spacing) + + Fills in the current margin and spacing for the form's layout in + the \a margin and \a spacing variables specified. +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setLayoutFunction(const QString &margin, const QString &spacing) + + Sets the \a margin and \a spacing for the form's layout. + + The default layout properties will be replaced by the + corresponding layout functions when \c uic generates code for the + form, that is, if the functions are specified. This is useful when + different environments requires different layouts for the same + form. + + \sa layoutFunction() +*/ + +/*! + \fn virtual QString QDesignerFormWindowInterface::pixmapFunction() const + + Returns the name of the function used to load pixmaps into the + form window. + + \sa setPixmapFunction() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setPixmapFunction(const QString &pixmapFunction) + + Sets the function used to load pixmaps into the form window + to the given \a pixmapFunction. + + \sa pixmapFunction() +*/ + +/*! + \fn virtual QString QDesignerFormWindowInterface::exportMacro() const + + Returns the export macro associated with the form currently being + displayed in the window. The export macro is used when the form + is compiled to create a widget plugin. + + \sa {Creating Custom Widgets for Qt Designer} +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setExportMacro(const QString &exportMacro) + + Sets the form window's export macro to \a exportMacro. The export + macro is used when building a widget plugin to export the form's + interface to other components. +*/ + +/*! + \fn virtual QStringList QDesignerFormWindowInterface::includeHints() const + + Returns a list of the header files that will be included in the + form window's associated UI file. + + Header files may be local, i.e. relative to the project's + directory, \c "mywidget.h", or global, i.e. part of Qt or the + compilers standard libraries: \c . + + \sa setIncludeHints() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setIncludeHints(const QStringList &includeHints) + + Sets the header files that will be included in the form window's + associated UI file to the specified \a includeHints. + + Header files may be local, i.e. relative to the project's + directory, \c "mywidget.h", or global, i.e. part of Qt or the + compilers standard libraries: \c . + + \sa includeHints() +*/ + +/*! + \fn virtual QDesignerFormWindowCursorInterface *QDesignerFormWindowInterface::cursor() const + + Returns the cursor interface used by the form window. +*/ + +/*! + \fn virtual int QDesignerFormWindowInterface::toolCount() const + + Returns the number of tools available. + + \internal +*/ + +/*! + \fn virtual int QDesignerFormWindowInterface::currentTool() const + + Returns the index of the current tool in use. + + \sa setCurrentTool() + + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setCurrentTool(int index) + + Sets the current tool to be the one with the given \a index. + + \sa currentTool() + + \internal +*/ + +/*! + \fn virtual QDesignerFormWindowToolInterface *QDesignerFormWindowInterface::tool(int index) const + + Returns an interface to the tool with the given \a index. + + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::registerTool(QDesignerFormWindowToolInterface *tool) + + Registers the given \a tool with the form window. + + \internal +*/ + +/*! + \fn virtual QPoint QDesignerFormWindowInterface::grid() const = 0 + + Returns the grid spacing used by the form window. + + \sa setGrid() +*/ + +/*! + \fn virtual QWidget *QDesignerFormWindowInterface::mainContainer() const + + Returns the main container widget for the form window. + + \sa setMainContainer() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setMainContainer(QWidget *mainContainer) + + Sets the main container widget on the form to the specified \a + mainContainer. + + \sa mainContainer(), mainContainerChanged() +*/ + +/*! + \fn virtual bool QDesignerFormWindowInterface::isManaged(QWidget *widget) const + + Returns true if the specified \a widget is managed by the form + window; otherwise returns false. + + \sa manageWidget() +*/ + +/*! + \fn virtual bool QDesignerFormWindowInterface::isDirty() const + + Returns true if the form window is "dirty" (modified but not + saved); otherwise returns false. + + \sa setDirty() +*/ + +/*! + \fn virtual QUndoStack *QDesignerFormWindowInterface::commandHistory() const + + Returns an object that can be used to obtain the commands used so + far in the construction of the form. + + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::beginCommand(const QString &description) + + Begins execution of a command with the given \a + description. Commands are executed between beginCommand() and + endCommand() function calls to ensure that they are recorded on + the undo stack. + + \sa endCommand() + + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::endCommand() + + Ends execution of the current command. + + \sa beginCommand() + + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::simplifySelection(QList *widgets) const + + Simplifies the selection of widgets specified by \a widgets. + + \sa selectionChanged() + \internal +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::emitSelectionChanged() + + Emits the selectionChanged() signal. + + \sa selectWidget(), clearSelection() +*/ + +/*! + \fn virtual QStringList QDesignerFormWindowInterface::resourceFiles() const + + Returns a list of paths to resource files that are currently being + used by the form window. + + \sa addResourceFile(), removeResourceFile() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::addResourceFile(const QString &path) + + Adds the resource file at the given \a path to those used by the form. + + \sa resourceFiles(), resourceFilesChanged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::removeResourceFile(const QString &path) + + Removes the resource file at the specified \a path from the list + of those used by the form. + + \sa resourceFiles(), resourceFilesChanged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::ensureUniqueObjectName(QObject *object) + + Ensures that the specified \a object has a unique name amongst the + other objects on the form. + + \internal +*/ + +// Slots + +/*! + \fn virtual void QDesignerFormWindowInterface::manageWidget(QWidget *widget) + + Instructs the form window to manage the specified \a widget. + + \sa isManaged(), unmanageWidget(), widgetManaged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::unmanageWidget(QWidget *widget) + + Instructs the form window not to manage the specified \a widget. + + \sa aboutToUnmanageWidget(), widgetUnmanaged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setFeatures(Feature features) + + Enables the specified \a features for the form window. + + \sa features(), featureChanged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setDirty(bool dirty) + + If \a dirty is true, the form window is marked as dirty, meaning + that it is modified but not saved. If \a dirty is false, the form + window is considered to be unmodified. + + \sa isDirty() +*/ + +/*! +\fn virtual void QDesignerFormWindowInterface::clearSelection(bool update) + + Clears the current selection in the form window. If \a update is + true, the emitSelectionChanged() function is called, emitting the + selectionChanged() signal. + + \sa selectWidget() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::selectWidget(QWidget *widget, bool select) + + If \a select is true, the given \a widget is selected; otherwise + the \a widget is deselected. + + \sa clearSelection(), selectionChanged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setGrid(const QPoint &grid) + + Sets the grid size for the form window to the point specified by + \a grid. In this function, the coordinates in the QPoint are used + to specify the dimensions of a rectangle in the grid. + + \sa grid() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setFileName(const QString &fileName) + + Sets the file name for the form to the given \a fileName. + + \sa fileName(), fileNameChanged() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::setContents(const QString &contents) + + Sets the contents of the form using data read from the specified + \a contents string. + + \sa contents() +*/ + +/*! + \fn virtual void QDesignerFormWindowInterface::editWidgets() + + Switches the form window into editing mode. + + \sa \l {Qt Designer's Form Editing Mode} + + \internal +*/ + +// Signals + +/*! + \fn void QDesignerFormWindowInterface::mainContainerChanged(QWidget *mainContainer) + + This signal is emitted whenever the main container changes. + The new container is specified by \a mainContainer. + + \sa setMainContainer() +*/ + +/*! + \fn void QDesignerFormWindowInterface::toolChanged(int toolIndex) + + This signal is emitted whenever the current tool changes. + The specified \a toolIndex is the index of the new tool in the list of + tools in the widget box. + + \internal +*/ + +/*! + \fn void QDesignerFormWindowInterface::fileNameChanged(const QString &fileName) + + This signal is emitted whenever the file name of the form changes. + The new file name is specified by \a fileName. + + \sa setFileName() +*/ + +/*! + \fn void QDesignerFormWindowInterface::featureChanged(Feature feature) + + This signal is emitted whenever a feature changes in the form. + The new feature is specified by \a feature. + + \sa setFeatures() +*/ + +/*! + \fn void QDesignerFormWindowInterface::selectionChanged() + + This signal is emitted whenever the selection in the form changes. + + \sa selectWidget(), clearSelection() +*/ + +/*! + \fn void QDesignerFormWindowInterface::geometryChanged() + + This signal is emitted whenever the form's geometry changes. +*/ + +/*! + \fn void QDesignerFormWindowInterface::resourceFilesChanged() + + This signal is emitted whenever the list of resource files used by the + form changes. + + \sa resourceFiles() +*/ + +/*! + \fn void QDesignerFormWindowInterface::widgetManaged(QWidget *widget) + + This signal is emitted whenever a widget on the form becomes managed. + The newly managed widget is specified by \a widget. + + \sa manageWidget() +*/ + +/*! + \fn void QDesignerFormWindowInterface::widgetUnmanaged(QWidget *widget) + + This signal is emitted whenever a widget on the form becomes unmanaged. + The newly released widget is specified by \a widget. + + \sa unmanageWidget(), aboutToUnmanageWidget() +*/ + +/*! + \fn void QDesignerFormWindowInterface::aboutToUnmanageWidget(QWidget *widget) + + This signal is emitted whenever a widget on the form is about to + become unmanaged. When this signal is emitted, the specified \a + widget is still managed, and a widgetUnmanaged() signal will + follow, indicating when it is no longer managed. + + \sa unmanageWidget(), isManaged() +*/ + +/*! + \fn void QDesignerFormWindowInterface::activated(QWidget *widget) + + This signal is emitted whenever a widget is activated on the form. + The activated widget is specified by \a widget. +*/ + +/*! + \fn void QDesignerFormWindowInterface::changed() + + This signal is emitted whenever a form is changed. +*/ + +/*! + \fn void QDesignerFormWindowInterface::widgetRemoved(QWidget *widget) + + This signal is emitted whenever a widget is removed from the form. + The widget that was removed is specified by \a widget. +*/ + +/*! + \fn void QDesignerFormWindowInterface::objectRemoved(QObject *object) + + This signal is emitted whenever an object (such as + an action or a QButtonGroup) is removed from the form. + The object that was removed is specified by \a object. + + \since 4.5 +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractformwindow.h b/designer/lib/sdk/abstractformwindow.h new file mode 100644 index 0000000..357788d --- /dev/null +++ b/designer/lib/sdk/abstractformwindow.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMWINDOW_H +#define ABSTRACTFORMWINDOW_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowCursorInterface; +class QDesignerFormWindowToolInterface; +class DomUI; +class QUndoStack; +class QDir; + +class QDESIGNER_SDK_EXPORT QDesignerFormWindowInterface: public QWidget +{ + Q_OBJECT +public: + enum FeatureFlag + { + EditFeature = 0x01, + GridFeature = 0x02, + TabOrderFeature = 0x04, + DefaultFeature = EditFeature | GridFeature + }; + Q_DECLARE_FLAGS(Feature, FeatureFlag) + +public: + QDesignerFormWindowInterface(QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~QDesignerFormWindowInterface(); + + virtual QString fileName() const = 0; + virtual QDir absoluteDir() const = 0; + + virtual QString contents() const = 0; + virtual void setContents(QIODevice *dev) = 0; + + virtual Feature features() const = 0; + virtual bool hasFeature(Feature f) const = 0; + + virtual QString author() const = 0; + virtual void setAuthor(const QString &author) = 0; + + virtual QString comment() const = 0; + virtual void setComment(const QString &comment) = 0; + + virtual void layoutDefault(int *margin, int *spacing) = 0; + virtual void setLayoutDefault(int margin, int spacing) = 0; + + virtual void layoutFunction(QString *margin, QString *spacing) = 0; + virtual void setLayoutFunction(const QString &margin, const QString &spacing) = 0; + + virtual QString pixmapFunction() const = 0; + virtual void setPixmapFunction(const QString &pixmapFunction) = 0; + + virtual QString exportMacro() const = 0; + virtual void setExportMacro(const QString &exportMacro) = 0; + + virtual QStringList includeHints() const = 0; + virtual void setIncludeHints(const QStringList &includeHints) = 0; + + virtual QDesignerFormEditorInterface *core() const; + virtual QDesignerFormWindowCursorInterface *cursor() const = 0; + + virtual int toolCount() const = 0; + + virtual int currentTool() const = 0; + virtual void setCurrentTool(int index) = 0; + + virtual QDesignerFormWindowToolInterface *tool(int index) const = 0; + virtual void registerTool(QDesignerFormWindowToolInterface *tool) = 0; + + virtual QPoint grid() const = 0; + + virtual QWidget *mainContainer() const = 0; + virtual void setMainContainer(QWidget *mainContainer) = 0; + + virtual bool isManaged(QWidget *widget) const = 0; + + virtual bool isDirty() const = 0; + + static QDesignerFormWindowInterface *findFormWindow(QWidget *w); + static QDesignerFormWindowInterface *findFormWindow(QObject *obj); + + virtual QUndoStack *commandHistory() const = 0; + virtual void beginCommand(const QString &description) = 0; + virtual void endCommand() = 0; + + virtual void simplifySelection(QList *widgets) const = 0; + + // notifications + virtual void emitSelectionChanged() = 0; + + virtual QStringList resourceFiles() const = 0; + virtual void addResourceFile(const QString &path) = 0; + virtual void removeResourceFile(const QString &path) = 0; + + virtual void ensureUniqueObjectName(QObject *object) = 0; + +public Q_SLOTS: + virtual void manageWidget(QWidget *widget) = 0; + virtual void unmanageWidget(QWidget *widget) = 0; + + virtual void setFeatures(Feature f) = 0; + virtual void setDirty(bool dirty) = 0; + virtual void clearSelection(bool changePropertyDisplay = true) = 0; + virtual void selectWidget(QWidget *w, bool select = true) = 0; + virtual void setGrid(const QPoint &grid) = 0; + virtual void setFileName(const QString &fileName) = 0; + virtual void setContents(const QString &contents) = 0; + + virtual void editWidgets() = 0; + +Q_SIGNALS: + void mainContainerChanged(QWidget *mainContainer); + void toolChanged(int toolIndex); + void fileNameChanged(const QString &fileName); + void featureChanged(Feature f); + void selectionChanged(); + void geometryChanged(); + + void resourceFilesChanged(); + + void widgetManaged(QWidget *widget); + void widgetUnmanaged(QWidget *widget); + void aboutToUnmanageWidget(QWidget *widget); + void activated(QWidget *widget); + + void changed(); + void widgetRemoved(QWidget *w); + void objectRemoved(QObject *o); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMWINDOW_H diff --git a/designer/lib/sdk/abstractformwindowcursor.cpp b/designer/lib/sdk/abstractformwindowcursor.cpp new file mode 100644 index 0000000..576e927 --- /dev/null +++ b/designer/lib/sdk/abstractformwindowcursor.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindowcursor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerFormWindowCursorInterface + + \brief The QDesignerFormWindowCursorInterface class allows you to + query and modify a form window's widget selection, and in addition + modify the properties of all the form's widgets. + + \inmodule QtDesigner + + QDesignerFormWindowCursorInterface is a convenience class that + provides an interface to the associated form window's text cursor; + it provides a collection of functions that enables you to query a + given form window's selection and change the selection's focus + according to defined modes (MoveMode) and movements + (MoveOperation). You can also use the interface to query the + form's widgets and change their properties. + + The interface is not intended to be instantiated directly, but to + provide access to the selections and widgets of \QD's current form + windows. QDesignerFormWindowInterface always provides an + associated cursor interface. The form window for a given widget + can be retrieved using the static + QDesignerFormWindowInterface::findFormWindow() functions. For + example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformwindowcursor.cpp 0 + + You can retrieve any of \QD's current form windows through + \QD's \l {QDesignerFormWindowManagerInterface}{form window + manager}. + + Once you have a form window's cursor interface, you can check if + the form window has a selection at all using the hasSelection() + function. You can query the form window for its total + widgetCount() and selectedWidgetCount(). You can retrieve the + currently selected widget (or widgets) using the current() or + selectedWidget() functions. + + You can retrieve any of the form window's widgets using the + widget() function, and check if a widget is selected using the + isWidgetSelected() function. You can use the setProperty() + function to set the selected widget's properties, and the + setWidgetProperty() or resetWidgetProperty() functions to modify + the properties of any given widget. + + Finally, you can change the selection by changing the text + cursor's position() using the setPosition() and movePosition() + functions. + + \sa QDesignerFormWindowInterface, QDesignerFormWindowManagerInterface +*/ + +/*! + \enum QDesignerFormWindowCursorInterface::MoveOperation + + This enum describes the types of text cursor operation that can occur in a form window. + + \value NoMove The cursor does not move. + \value Start Moves the cursor to the start of the focus chain. + \value End Moves the cursor to the end of the focus chain. + \value Next Moves the cursor to the next widget in the focus chain. + \value Prev Moves the cursor to the previous widget in the focus chain. + \value Left The cursor moves to the left. + \value Right The cursor moves to the right. + \value Up The cursor moves upwards. + \value Down The cursor moves downwards. +*/ + +/*! + \enum QDesignerFormWindowCursorInterface::MoveMode + + This enum describes the different modes that are used when the text cursor moves. + + \value MoveAnchor The anchor moves with the cursor to its new location. + \value KeepAnchor The anchor remains at the cursor's old location. +*/ + +/*! + Returns true if the specified \a widget is selected; otherwise + returns false. +*/ +bool QDesignerFormWindowCursorInterface::isWidgetSelected(QWidget *widget) const +{ + for (int index=0; index + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QWidget; +class QVariant; +class QString; + +class QDESIGNER_SDK_EXPORT QDesignerFormWindowCursorInterface +{ +public: + enum MoveOperation + { + NoMove, + + Start, + End, + Next, + Prev, + Left, + Right, + Up, + Down + }; + + enum MoveMode + { + MoveAnchor, + KeepAnchor + }; + +public: + virtual ~QDesignerFormWindowCursorInterface() {} + + virtual QDesignerFormWindowInterface *formWindow() const = 0; + + virtual bool movePosition(MoveOperation op, MoveMode mode = MoveAnchor) = 0; + + virtual int position() const = 0; + virtual void setPosition(int pos, MoveMode mode = MoveAnchor) = 0; + + virtual QWidget *current() const = 0; + + virtual int widgetCount() const = 0; + virtual QWidget *widget(int index) const = 0; + + virtual bool hasSelection() const = 0; + virtual int selectedWidgetCount() const = 0; + virtual QWidget *selectedWidget(int index) const = 0; + + virtual void setProperty(const QString &name, const QVariant &value) = 0; + virtual void setWidgetProperty(QWidget *widget, const QString &name, const QVariant &value) = 0; + virtual void resetWidgetProperty(QWidget *widget, const QString &name) = 0; + + bool isWidgetSelected(QWidget *widget) const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMWINDOWCURSOR_H diff --git a/designer/lib/sdk/abstractformwindowmanager.cpp b/designer/lib/sdk/abstractformwindowmanager.cpp new file mode 100644 index 0000000..15f283a --- /dev/null +++ b/designer/lib/sdk/abstractformwindowmanager.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindowmanager.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerFormWindowManagerInterface + + \brief The QDesignerFormWindowManagerInterface class allows you to + manipulate the collection of form windows in Qt Designer, and + control Qt Designer's form editing actions. + + \inmodule QtDesigner + + QDesignerFormWindowManagerInterface is not intended to be + instantiated directly. \QD uses the form window manager to + control the various form windows in its workspace. You can + retrieve an interface to \QD's form window manager using + the QDesignerFormEditorInterface::formWindowManager() + function. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractformwindowmanager.cpp 0 + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor in the + example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's parameter. + You must subclass the QDesignerCustomWidgetInterface to expose + your plugin to Qt Designer. + + The form window manager interface provides the createFormWindow() + function that enables you to create a new form window which you + can add to the collection of form windows that the manager + maintains, using the addFormWindow() slot. It also provides the + formWindowCount() function returning the number of form windows + currently under the manager's control, the formWindow() function + returning the form window associated with a given index, and the + activeFormWindow() function returning the currently selected form + window. The removeFormWindow() slot allows you to reduce the + number of form windows the manager must maintain, and the + setActiveFormWindow() slot allows you to change the form window + focus in \QD's workspace. + + In addition, QDesignerFormWindowManagerInterface contains a + collection of functions that enables you to intervene and control + \QD's form editing actions. All these functions return the + original action, making it possible to propagate the function + further after intervention. + + Finally, the interface provides three signals which are emitted + when a form window is added, when the currently selected form + window changes, or when a form window is removed, respectively. All + the signals carry the form window in question as their parameter. + + \sa QDesignerFormEditorInterface, QDesignerFormWindowInterface +*/ + +// ------------- QDesignerFormWindowManagerInterfacePrivate + +struct QDesignerFormWindowManagerInterfacePrivate { + QDesignerFormWindowManagerInterfacePrivate(); + QAction *m_simplifyLayoutAction; + QAction *m_formLayoutAction; +}; + +QDesignerFormWindowManagerInterfacePrivate::QDesignerFormWindowManagerInterfacePrivate() : + m_simplifyLayoutAction(0), + m_formLayoutAction(0) +{ +} + +typedef QMap FormWindowManagerPrivateMap; + +Q_GLOBAL_STATIC(FormWindowManagerPrivateMap, g_FormWindowManagerPrivateMap) + +/*! + Constructs an interface with the given \a parent for the form window + manager. +*/ +QDesignerFormWindowManagerInterface::QDesignerFormWindowManagerInterface(QObject *parent) + : QObject(parent) +{ + g_FormWindowManagerPrivateMap()->insert(this, new QDesignerFormWindowManagerInterfacePrivate); +} + +/*! + Destroys the interface for the form window manager. +*/ +QDesignerFormWindowManagerInterface::~QDesignerFormWindowManagerInterface() +{ + FormWindowManagerPrivateMap *fwmpm = g_FormWindowManagerPrivateMap(); + const FormWindowManagerPrivateMap::iterator it = fwmpm->find(this); + Q_ASSERT(it != fwmpm->end()); + delete it.value(); + fwmpm->erase(it); +} + +/*! + Allows you to intervene and control \QD's "cut" action. The function + returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionCut() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "copy" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionCopy() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "paste" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionPaste() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "delete" action. The function + returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionDelete() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "select all" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionSelectAll() const +{ + return 0; +} + +/*! + Allows you to intervene and control the action of lowering a form + window in \QD's workspace. The function returns the original + action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionLower() const +{ + return 0; +} + +/*! + Allows you to intervene and control the action of raising of a + form window in \QD's workspace. The function returns the original + action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionRaise() const +{ + return 0; +} + +/*! + Allows you to intervene and control a request for horizontal + layout for a form window in \QD's workspace. The function returns + the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionHorizontalLayout() const +{ + return 0; +} + +/*! + Allows you to intervene and control a request for vertical layout + for a form window in \QD's workspace. The function returns the + original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionVerticalLayout() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "split horizontal" + action. The function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionSplitHorizontal() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "split vertical" + action. The function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionSplitVertical() const +{ + return 0; +} + +/*! + Allows you to intervene and control a request for grid layout for + a form window in \QD's workspace. The function returns the + original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionGridLayout() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "form layout" action. The + function returns the original action. + +FormWindowManagerPrivateMap *fwmpm = g_FormWindowManagerPrivateMap(); \sa QAction + \since 4.4 +*/ + +QAction *QDesignerFormWindowManagerInterface::actionFormLayout() const +{ + const QDesignerFormWindowManagerInterfacePrivate *d = g_FormWindowManagerPrivateMap()->value(this); + Q_ASSERT(d); + return d->m_formLayoutAction; +} + +/*! + Sets the "form layout" action to \a action. + + \internal + \since 4.4 +*/ + +void QDesignerFormWindowManagerInterface::setActionFormLayout(QAction *action) +{ + QDesignerFormWindowManagerInterfacePrivate *d = g_FormWindowManagerPrivateMap()->value(this); + Q_ASSERT(d); + d->m_formLayoutAction = action; +} + +/*! + Allows you to intervene and control \QD's "break layout" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionBreakLayout() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "adjust size" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionAdjustSize() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "simplify layout" action. The + function returns the original action. + + \sa QAction + \since 4.4 +*/ + +QAction *QDesignerFormWindowManagerInterface::actionSimplifyLayout() const +{ + const QDesignerFormWindowManagerInterfacePrivate *d = g_FormWindowManagerPrivateMap()->value(this); + Q_ASSERT(d); + return d->m_simplifyLayoutAction; +} + +/*! + Sets the "simplify layout" action to \a action. + + \internal + \since 4.4 +*/ + +void QDesignerFormWindowManagerInterface::setActionSimplifyLayout(QAction *action) +{ + QDesignerFormWindowManagerInterfacePrivate *d = g_FormWindowManagerPrivateMap()->value(this); + Q_ASSERT(d); + d->m_simplifyLayoutAction = action; +} + +/*! + Returns the currently active form window in \QD's workspace. + + \sa setActiveFormWindow(), removeFormWindow() +*/ +QDesignerFormWindowInterface *QDesignerFormWindowManagerInterface::activeFormWindow() const +{ + return 0; +} + +/*! + Returns a pointer to \QD's current QDesignerFormEditorInterface + object. +*/ +QDesignerFormEditorInterface *QDesignerFormWindowManagerInterface::core() const +{ + return 0; +} + +/*! + Adds the given \a formWindow to the collection of windows that + \QD's form window manager maintains. + + \sa formWindowAdded() +*/ +void QDesignerFormWindowManagerInterface::addFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_UNUSED(formWindow); +} + +/*! + Removes the given \a formWindow from the collection of windows that + \QD's form window manager maintains. + + \sa formWindow(), formWindowRemoved() +*/ +void QDesignerFormWindowManagerInterface::removeFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_UNUSED(formWindow); +} + +/*! + Sets the given \a formWindow to be the currently active form window in + \QD's workspace. + + \sa activeFormWindow(), activeFormWindowChanged() +*/ +void QDesignerFormWindowManagerInterface::setActiveFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_UNUSED(formWindow); +} + +/*! + Returns the number of form windows maintained by \QD's form window + manager. +*/ +int QDesignerFormWindowManagerInterface::formWindowCount() const +{ + return 0; +} + +/*! + Returns the form window at the given \a index. + + \sa setActiveFormWindow(), removeFormWindow() +*/ +QDesignerFormWindowInterface *QDesignerFormWindowManagerInterface::formWindow(int index) const +{ + Q_UNUSED(index); + return 0; +} + +/*! + \fn QDesignerFormWindowInterface *QDesignerFormWindowManagerInterface::createFormWindow(QWidget *parent, Qt::WindowFlags flags) + + Creates a form window with the given \a parent and the given window + \a flags. + + \sa addFormWindow() +*/ +QDesignerFormWindowInterface *QDesignerFormWindowManagerInterface::createFormWindow(QWidget *parentWidget, Qt::WindowFlags flags) +{ + Q_UNUSED(parentWidget); + Q_UNUSED(flags); + return 0; +} + +/*! + Allows you to intervene and control \QD's "undo" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionUndo() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's "redo" action. The + function returns the original action. + + \sa QAction +*/ +QAction *QDesignerFormWindowManagerInterface::actionRedo() const +{ + return 0; +} + +/*! + \fn void QDesignerFormWindowManagerInterface::formWindowAdded(QDesignerFormWindowInterface *formWindow) + + This signal is emitted when a new form window is added to the + collection of windows that \QD's form window manager maintains. A + pointer to the new \a formWindow is passed as an argument. + + \sa addFormWindow(), setActiveFormWindow() +*/ + +/*! + \fn void QDesignerFormWindowManagerInterface::formWindowRemoved(QDesignerFormWindowInterface *formWindow) + + This signal is emitted when a form window is removed from the + collection of windows that \QD's form window manager maintains. A + pointer to the removed \a formWindow is passed as an argument. + + \sa removeFormWindow() +*/ + +/*! + \fn void QDesignerFormWindowManagerInterface::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) + + This signal is emitted when the contents of the currently active + form window in \QD's workspace changed. A pointer to the currently + active \a formWindow is passed as an argument. + + \sa activeFormWindow() +*/ + +/*! + \fn void QDesignerFormWindowManagerInterface::dragItems(const QList &item_list) + + \internal +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractformwindowmanager.h b/designer/lib/sdk/abstractformwindowmanager.h new file mode 100644 index 0000000..9fc667a --- /dev/null +++ b/designer/lib/sdk/abstractformwindowmanager.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMWINDOWMANAGER_H +#define ABSTRACTFORMWINDOWMANAGER_H + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAction; +class QActionGroup; +class QDesignerFormEditorInterface; +class DomUI; +class QWidget; +class QDesignerDnDItemInterface; + +class QDESIGNER_SDK_EXPORT QDesignerFormWindowManagerInterface: public QObject +{ + Q_OBJECT +public: + QDesignerFormWindowManagerInterface(QObject *parent = 0); + virtual ~QDesignerFormWindowManagerInterface(); + + virtual QAction *actionCut() const; + virtual QAction *actionCopy() const; + virtual QAction *actionPaste() const; + virtual QAction *actionDelete() const; + virtual QAction *actionSelectAll() const; + virtual QAction *actionLower() const; + virtual QAction *actionRaise() const; + virtual QAction *actionUndo() const; + virtual QAction *actionRedo() const; + + virtual QAction *actionHorizontalLayout() const; + virtual QAction *actionVerticalLayout() const; + virtual QAction *actionSplitHorizontal() const; + virtual QAction *actionSplitVertical() const; + virtual QAction *actionGridLayout() const; + QAction *actionFormLayout() const; + virtual QAction *actionBreakLayout() const; + virtual QAction *actionAdjustSize() const; + QAction *actionSimplifyLayout() const; + + virtual QDesignerFormWindowInterface *activeFormWindow() const; + + virtual int formWindowCount() const; + virtual QDesignerFormWindowInterface *formWindow(int index) const; + + virtual QDesignerFormWindowInterface *createFormWindow(QWidget *parentWidget = 0, Qt::WindowFlags flags = 0); + + virtual QDesignerFormEditorInterface *core() const; + + virtual void dragItems(const QList &item_list) = 0; + +Q_SIGNALS: + void formWindowAdded(QDesignerFormWindowInterface *formWindow); + void formWindowRemoved(QDesignerFormWindowInterface *formWindow); + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + +public Q_SLOTS: + virtual void addFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void removeFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void setActiveFormWindow(QDesignerFormWindowInterface *formWindow); + +protected: + void setActionFormLayout(QAction *action); + void setActionSimplifyLayout(QAction *action); + +private: + QDesignerFormWindowManagerInterface(const QDesignerFormWindowManagerInterface &other); + QDesignerFormWindowManagerInterface &operator = (const QDesignerFormWindowManagerInterface &other); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMWINDOWMANAGER_H diff --git a/designer/lib/sdk/abstractformwindowtool.cpp b/designer/lib/sdk/abstractformwindowtool.cpp new file mode 100644 index 0000000..ab8c25a --- /dev/null +++ b/designer/lib/sdk/abstractformwindowtool.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindowtool.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerFormWindowToolInterface + + \brief The QDesignerFormWindowToolInterface class provides an + interface that enables tools to be used on items in a form window. + + \inmodule QtDesigner + + \internal +*/ + +/*! +*/ +QDesignerFormWindowToolInterface::QDesignerFormWindowToolInterface(QObject *parent) + : QObject(parent) +{ +} + +/*! +*/ +QDesignerFormWindowToolInterface::~QDesignerFormWindowToolInterface() +{ +} + +/*! + \fn virtual QDesignerFormEditorInterface *QDesignerFormWindowToolInterface::core() const = 0 +*/ + +/*! + \fn virtual QDesignerFormWindowInterface *QDesignerFormWindowToolInterface::formWindow() const = 0 +*/ + +/*! + \fn virtual QWidget *QDesignerFormWindowToolInterface::editor() const = 0 +*/ + +/*! + \fn virtual QAction *QDesignerFormWindowToolInterface::action() const = 0 +*/ + +/*! + \fn virtual void QDesignerFormWindowToolInterface::activated() = 0 +*/ + +/*! + \fn virtual void QDesignerFormWindowToolInterface::deactivated() = 0 +*/ + +/*! + \fn virtual void QDesignerFormWindowToolInterface::saveToDom(DomUI*, QWidget*) { +*/ + +/*! + \fn virtual void QDesignerFormWindowToolInterface::loadFromDom(DomUI*, QWidget*) { +*/ + +/*! + \fn virtual bool QDesignerFormWindowToolInterface::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) = 0 +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractformwindowtool.h b/designer/lib/sdk/abstractformwindowtool.h new file mode 100644 index 0000000..9a9de82 --- /dev/null +++ b/designer/lib/sdk/abstractformwindowtool.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMWINDOWTOOL_H +#define ABSTRACTFORMWINDOWTOOL_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QWidget; +class QAction; +class DomUI; + +class QDESIGNER_SDK_EXPORT QDesignerFormWindowToolInterface: public QObject +{ + Q_OBJECT +public: + QDesignerFormWindowToolInterface(QObject *parent = 0); + virtual ~QDesignerFormWindowToolInterface(); + + virtual QDesignerFormEditorInterface *core() const = 0; + virtual QDesignerFormWindowInterface *formWindow() const = 0; + virtual QWidget *editor() const = 0; + + virtual QAction *action() const = 0; + + virtual void activated() = 0; + virtual void deactivated() = 0; + + virtual void saveToDom(DomUI*, QWidget*) {} + virtual void loadFromDom(DomUI*, QWidget*) {} + + virtual bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMWINDOWTOOL_H diff --git a/designer/lib/sdk/abstracticoncache.h b/designer/lib/sdk/abstracticoncache.h new file mode 100644 index 0000000..e0d1cfe --- /dev/null +++ b/designer/lib/sdk/abstracticoncache.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTICONCACHE_H +#define ABSTRACTICONCACHE_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIcon; +class QPixmap; +class QString; + +class QDESIGNER_SDK_EXPORT QDesignerIconCacheInterface : public QObject +{ + Q_OBJECT +public: + QDesignerIconCacheInterface(QObject *parent_) + : QObject(parent_) {} + + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath = QString()) = 0; + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath = QString()) = 0; + + virtual QString iconToFilePath(const QIcon &pm) const = 0; + virtual QString iconToQrcPath(const QIcon &pm) const = 0; + + virtual QString pixmapToFilePath(const QPixmap &pm) const = 0; + virtual QString pixmapToQrcPath(const QPixmap &pm) const = 0; + + virtual QList pixmapList() const = 0; + virtual QList iconList() const = 0; + + virtual QString resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &workingDirectory = QString()) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTICONCACHE_H diff --git a/designer/lib/sdk/abstracticoncache.qdoc b/designer/lib/sdk/abstracticoncache.qdoc new file mode 100644 index 0000000..6eb6b13 --- /dev/null +++ b/designer/lib/sdk/abstracticoncache.qdoc @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerIconCacheInterface + \brief The QDesignerIconCacheInterface class provides an interface to \QD's icon cache. + \inmodule QtDesigner + \internal +*/ + +/*! + \fn QDesignerIconCacheInterface::QDesignerIconCacheInterface(QObject *parent) + + Constructs a new interface with the given \a parent. +*/ + +/*! + \fn QIcon QDesignerIconCacheInterface::nameToIcon(const QString &filePath, const QString &qrcPath) + + Returns the icon associated with the name specified by \a filePath in the resource + file specified by \a qrcPath. + + If \a qrcPath refers to a valid resource file, the name used for the file path is a path + within those resources; otherwise the file path refers to a local file. + + \sa {The Qt Resource System}, nameToPixmap() +*/ + +/*! + \fn QPixmap QDesignerIconCacheInterface::nameToPixmap(const QString &filePath, const QString &qrcPath) + + Returns the pixmap associated with the name specified by \a filePath in the resource + file specified by \a qrcPath. + + If \a qrcPath refers to a valid resource file, the name used for the file path is a path + within those resources; otherwise the file path refers to a local file. + + \sa {The Qt Resource System}, nameToIcon() +*/ + +/*! + \fn QString QDesignerIconCacheInterface::iconToFilePath(const QIcon &icon) const + + Returns the file path associated with the given \a icon. The file path is a path within + an application resources. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::iconToQrcPath(const QIcon &icon) const + + Returns the path to the resource file that refers to the specified \a icon. The resource + path refers to a local file. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::pixmapToFilePath(const QPixmap &pixmap) const + + Returns the file path associated with the given \a pixmap. The file path is a path within + an application resources. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::pixmapToQrcPath(const QPixmap &pixmap) const + + Returns the path to the resource file that refers to the specified \a pixmap. The resource + path refers to a local file. +*/ + +/*! + \fn QList QDesignerIconCacheInterface::pixmapList() const + + Returns a list of pixmaps for the icons provided by the icon cache. +*/ + +/*! + \fn QList QDesignerIconCacheInterface::iconList() const + + Returns a list of icons provided by the icon cache. +*/ + +/*! + \fn QString QDesignerIconCacheInterface::resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &workingDirectory) const + + Returns a path to a resource specified by the \a filePath within + the resource file located at \a qrcPath. If \a workingDirectory is + a valid path to a directory, the path returned will be relative to + that directory; otherwise an absolute path is returned. + + \omit + ### Needs checking + \endomit +*/ diff --git a/designer/lib/sdk/abstractintegration.cpp b/designer/lib/sdk/abstractintegration.cpp new file mode 100644 index 0000000..acd28e2 --- /dev/null +++ b/designer/lib/sdk/abstractintegration.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractintegration.h" +#include "abstractformeditor.h" + +QT_BEGIN_NAMESPACE + +QDesignerIntegrationInterface::QDesignerIntegrationInterface(QDesignerFormEditorInterface *core, QObject *parent) + : QObject(parent), + m_core(core) +{ + core->setIntegration(this); +} + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractintegration.h b/designer/lib/sdk/abstractintegration.h new file mode 100644 index 0000000..2b8be09 --- /dev/null +++ b/designer/lib/sdk/abstractintegration.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTINTEGRATION_H +#define ABSTRACTINTEGRATION_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +class QDESIGNER_SDK_EXPORT QDesignerIntegrationInterface: public QObject +{ + Q_OBJECT +public: + QDesignerIntegrationInterface(QDesignerFormEditorInterface *core, QObject *parent = 0); + + inline QDesignerFormEditorInterface *core() const; + + virtual QWidget *containerWindow(QWidget *widget) const = 0; + +private: + QDesignerFormEditorInterface *m_core; +}; + +inline QDesignerFormEditorInterface *QDesignerIntegrationInterface::core() const +{ return m_core; } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTINTEGRATION_H diff --git a/designer/lib/sdk/abstractintrospection.cpp b/designer/lib/sdk/abstractintrospection.cpp new file mode 100644 index 0000000..b5dd795 --- /dev/null +++ b/designer/lib/sdk/abstractintrospection.cpp @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractintrospection_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerMetaEnumInterface + \internal + \since 4.4 + + \brief QDesignerMetaEnumInterface is part of \QD's introspection interface and represents an enumeration. + + \inmodule QtDesigner + + The QDesignerMetaEnumInterface class provides meta-data about an enumerator. + + \sa QDesignerMetaObjectInterface +*/ + +/*! + Constructs a QDesignerMetaEnumInterface object. +*/ + +QDesignerMetaEnumInterface::QDesignerMetaEnumInterface() +{ +} + +/*! + Destroys the QDesignerMetaEnumInterface object. +*/ +QDesignerMetaEnumInterface::~QDesignerMetaEnumInterface() +{ +} + +/*! + \fn bool QDesignerMetaEnumInterface::isFlag() const + + Returns true if this enumerator is used as a flag. +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::key(int index) const + + Returns the key with the given \a index. +*/ + +/*! + \fn int QDesignerMetaEnumInterface::keyCount() const + + Returns the number of keys. +*/ + +/*! + \fn int QDesignerMetaEnumInterface::keyToValue(const QString &key) const + + Returns the integer value of the given enumeration \a key, or -1 if \a key is not defined. +*/ + +/*! + \fn int QDesignerMetaEnumInterface::keysToValue(const QString &keys) const + + Returns the value derived from combining together the values of the \a keys using the OR operator, or -1 if keys is not defined. Note that the strings in \a keys must be '|'-separated. +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::name() const + + Returns the name of the enumerator (without the scope). +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::scope() const + + Returns the scope this enumerator was declared in. +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::separator() const + + Returns the separator to be used when building enumeration names. +*/ + +/*! + \fn int QDesignerMetaEnumInterface::value(int index) const + + Returns the value with the given \a index; or returns -1 if there is no such value. +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::valueToKey(int value) const + + Returns the string that is used as the name of the given enumeration \a value, or QString::null if value is not defined. +*/ + +/*! + \fn QString QDesignerMetaEnumInterface::valueToKeys(int value) const + + Returns a byte array of '|'-separated keys that represents the given \a value. +*/ + +/*! + \class QDesignerMetaPropertyInterface + \internal + \since 4.4 + + \brief QDesignerMetaPropertyInterface is part of \QD's introspection interface and represents a property. + + \inmodule QtDesigner + + The QDesignerMetaPropertyInterface class provides meta-data about a property. + + \sa QDesignerMetaObjectInterface +*/ + +/*! + Constructs a QDesignerMetaPropertyInterface object. +*/ + +QDesignerMetaPropertyInterface::QDesignerMetaPropertyInterface() +{ +} + +/*! + Destroys the QDesignerMetaPropertyInterface object. +*/ + +QDesignerMetaPropertyInterface::~QDesignerMetaPropertyInterface() +{ +} + +/*! + \enum QDesignerMetaPropertyInterface::Kind + + This enum indicates whether the property is of a special type. + + \value EnumKind The property is of an enumeration type + \value FlagKind The property is of an flag type + \value OtherKind The property is of another type + */ + +/*! + \enum QDesignerMetaPropertyInterface::AccessFlag + + These flags specify the access the property provides. + + \value ReadAccess Property can be read + \value WriteAccess Property can be written + \value ResetAccess Property can be reset to a default value + */ + +/*! + \enum QDesignerMetaPropertyInterface::Attribute + + Various attributes of the property. + + \value DesignableAttribute Property is designable (visible in \QD) + \value ScriptableAttribute Property is scriptable + \value StoredAttribute Property is stored, that is, not calculated + \value UserAttribute Property is the property that the user can edit for the QObject + */ + +/*! + \fn const QDesignerMetaEnumInterface *QDesignerMetaPropertyInterface::enumerator() const + + Returns the enumerator if this property's type is an enumerator type; +*/ + +/*! + \fn Kind QDesignerMetaPropertyInterface::kind() const + + Returns the type of the property. +*/ + +/*! + \fn AccessFlags QDesignerMetaPropertyInterface::accessFlags() const + + Returns a combination of access flags. +*/ + +/*! + \fn Attributes QDesignerMetaPropertyInterface::attributes(const QObject *object) const + + Returns the attributes of the property for the gives \a object. +*/ + +/*! + \fn QVariant::Type QDesignerMetaPropertyInterface::type() const + + Returns the type of the property. +*/ + +/*! + \fn QString QDesignerMetaPropertyInterface::name() const + + Returns the name of the property. +*/ + +/*! + \fn QString QDesignerMetaPropertyInterface::typeName() const + + Returns the name of this property's type. +*/ + + +/*! + \fn int QDesignerMetaPropertyInterface::userType() const + + Returns this property's user type. +*/ + +/*! + \fn bool QDesignerMetaPropertyInterface::hasSetter() const + + Returns whether getter and setter methods exist for this property. +*/ + +/*! + \fn QVariant QDesignerMetaPropertyInterface::read(const QObject *object) const + + Reads the property's value from the given \a object. Returns the value if it was able to read it; otherwise returns an invalid variant. +*/ + +/*! + \fn bool QDesignerMetaPropertyInterface::reset(QObject *object) const + + Resets the property for the given \a object with a reset method. Returns true if the reset worked; otherwise returns false. +*/ + +/*! + \fn bool QDesignerMetaPropertyInterface::write(QObject *object, const QVariant &value) const + + Writes \a value as the property's value to the given \a object. Returns true if the write succeeded; otherwise returns false. +*/ + +/*! + \class QDesignerMetaMethodInterface + \internal + \since 4.4 + + \brief QDesignerMetaMethodInterface is part of \QD's introspection interface and represents a member function. + + \inmodule QtDesigner + + The QDesignerMetaMethodInterface class provides meta-data about a member function. + + \sa QDesignerMetaObjectInterface +*/ + +/*! + Constructs a QDesignerMetaMethodInterface object. +*/ + +QDesignerMetaMethodInterface::QDesignerMetaMethodInterface() +{ +} + +/*! + Destroys the QDesignerMetaMethodInterface object. +*/ + +QDesignerMetaMethodInterface::~QDesignerMetaMethodInterface() +{ +} + +/*! + \enum QDesignerMetaMethodInterface::MethodType + + This enum specifies the type of the method + + \value Method The function is a plain member function. + \value Signal The function is a signal. + \value Slot The function is a slot. + \value Constructor The function is a constructor. + +*/ + +/*! + \enum QDesignerMetaMethodInterface::Access + + This enum represents the access specification of the method + + \value Private A private member function + \value Protected A protected member function + \value Public A public member function +*/ + +/*! + \fn QDesignerMetaMethodInterface::Access QDesignerMetaMethodInterface::access() const + + Returns the access specification of this method. +*/ + + +/*! + \fn QDesignerMetaMethodInterface::MethodType QDesignerMetaMethodInterface::methodType() const + + Returns the type of this method. +*/ + +/*! + \fn QStringList QDesignerMetaMethodInterface::parameterNames() const + + Returns a list of parameter names. +*/ + +/*! + \fn QStringList QDesignerMetaMethodInterface::parameterTypes() const + + Returns a list of parameter types. +*/ + +/*! + \fn QString QDesignerMetaMethodInterface::signature() const + + Returns the signature of this method. +*/ + +/*! + \fn QString QDesignerMetaMethodInterface::normalizedSignature() const + + Returns the normalized signature of this method (suitable as signal/slot specification). +*/ + + +/*! + \fn QString QDesignerMetaMethodInterface::tag() const + + Returns the tag associated with this method. +*/ + +/*! + \fn QString QDesignerMetaMethodInterface::typeName() const + + Returns the return type of this method, or an empty string if the return type is void. +*/ + +/*! + \class QDesignerMetaObjectInterface + \internal + \since 4.4 + + \brief QDesignerMetaObjectInterface is part of \QD's introspection interface and provides meta-information about Qt objects + + \inmodule QtDesigner + + The QDesignerMetaObjectInterface class provides meta-data about Qt objects. For a given object, it can be obtained + by querying QDesignerIntrospectionInterface. + + \sa QDesignerIntrospectionInterface +*/ + +/*! + Constructs a QDesignerMetaObjectInterface object. +*/ + +QDesignerMetaObjectInterface::QDesignerMetaObjectInterface() +{ +} + +/*! + Destroys the QDesignerMetaObjectInterface object. +*/ + +QDesignerMetaObjectInterface::~QDesignerMetaObjectInterface() +{ +} + +/*! + \fn QString QDesignerMetaObjectInterface::className() const + + Returns the class name. +*/ + +/*! + \fn const QDesignerMetaEnumInterface *QDesignerMetaObjectInterface::enumerator(int index) const + + Returns the meta-data for the enumerator with the given \a index. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::enumeratorCount() const + + Returns the number of enumerators in this class. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::enumeratorOffset() const + + Returns the enumerator offset for this class; i.e. the index position of this class's first enumerator. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::indexOfEnumerator(const QString &name) const + + Finds enumerator \a name and returns its index; otherwise returns -1. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::indexOfMethod(const QString &method) const + + Finds \a method and returns its index; otherwise returns -1. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::indexOfProperty(const QString &name) const + + Finds property \a name and returns its index; otherwise returns -1. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::indexOfSignal(const QString &signal) const + + Finds \a signal and returns its index; otherwise returns -1. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::indexOfSlot(const QString &slot) const + + Finds \a slot and returns its index; otherwise returns -1. +*/ + +/*! + \fn const QDesignerMetaMethodInterface *QDesignerMetaObjectInterface::method(int index) const + + Returns the meta-data for the method with the given \a index. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::methodCount() const + + Returns the number of methods in this class. These include ordinary methods, signals, and slots. +*/ + +/*! + \fn int QDesignerMetaObjectInterface::methodOffset() const + + Returns the method offset for this class; i.e. the index position of this class's first member function. +*/ + +/*! + \fn const QDesignerMetaPropertyInterface *QDesignerMetaObjectInterface::property(int index) const + + Returns the meta-data for the property with the given \a index. +*/ +/*! + \fn int QDesignerMetaObjectInterface::propertyCount() const + + Returns the number of properties in this class. +*/ +/*! + \fn int QDesignerMetaObjectInterface::propertyOffset() const + + Returns the property offset for this class; i.e. the index position of this class's first property. +*/ + +/*! + \fn const QDesignerMetaObjectInterface *QDesignerMetaObjectInterface::superClass() const + + Returns the meta-object of the superclass, or 0 if there is no such object. +*/ + +/*! + \fn const QDesignerMetaPropertyInterface *QDesignerMetaObjectInterface::userProperty() const + + Returns the property that has the USER flag set to true. +*/ + +/*! + \class QDesignerIntrospectionInterface + \internal + \since 4.4 + + \brief QDesignerIntrospectionInterface provides access to a QDesignerMetaObjectInterface for a given Qt object. + + \inmodule QtDesigner + + QDesignerIntrospectionInterface is the main class of \QD's introspection interface. These + interfaces provide a layer of abstraction around QMetaObject and related classes to allow for the integration + of other programming languages. + + An instance of QDesignerIntrospectionInterface can be obtained from the core. + + \sa QDesignerMetaObjectInterface +*/ + +/*! + Constructs a QDesignerIntrospectionInterface object. +*/ + +QDesignerIntrospectionInterface::QDesignerIntrospectionInterface() +{ +} + +/*! + Destroys the QDesignerIntrospectionInterface object. +*/ + +QDesignerIntrospectionInterface::~QDesignerIntrospectionInterface() +{ +} + +/*! + \fn const QDesignerMetaObjectInterface* QDesignerIntrospectionInterface::metaObject(const QObject *object) const + + Returns the meta object of this \a object. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractintrospection_p.h b/designer/lib/sdk/abstractintrospection_p.h new file mode 100644 index 0000000..a5c99b5 --- /dev/null +++ b/designer/lib/sdk/abstractintrospection_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ABSTRACTMETAOBJECT_H +#define ABSTRACTMETAOBJECT_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDESIGNER_SDK_EXPORT QDesignerMetaEnumInterface +{ +public: + QDesignerMetaEnumInterface(); + virtual ~QDesignerMetaEnumInterface(); + virtual bool isFlag() const = 0; + virtual QString key(int index) const = 0; + virtual int keyCount() const = 0; + virtual int keyToValue(const QString &key) const = 0; + virtual int keysToValue(const QString &keys) const = 0; + virtual QString name() const = 0; + virtual QString scope() const = 0; + virtual QString separator() const = 0; + virtual int value(int index) const = 0; + virtual QString valueToKey(int value) const = 0; + virtual QString valueToKeys(int value) const = 0; +}; + +class QDESIGNER_SDK_EXPORT QDesignerMetaPropertyInterface +{ +public: + enum Kind { EnumKind, FlagKind, OtherKind }; + enum AccessFlag { ReadAccess = 0x0001, WriteAccess = 0x0002, ResetAccess = 0x0004 }; + enum Attribute { DesignableAttribute = 0x0001, ScriptableAttribute = 0x0002, StoredAttribute = 0x0004, UserAttribute = 0x0008}; + Q_DECLARE_FLAGS(Attributes, Attribute) + Q_DECLARE_FLAGS(AccessFlags, AccessFlag) + + QDesignerMetaPropertyInterface(); + virtual ~QDesignerMetaPropertyInterface(); + + virtual const QDesignerMetaEnumInterface *enumerator() const = 0; + + virtual Kind kind() const = 0; + virtual AccessFlags accessFlags() const = 0; + virtual Attributes attributes(const QObject *object = 0) const = 0; + + virtual QVariant::Type type() const = 0; + virtual QString name() const = 0; + virtual QString typeName() const = 0; + virtual int userType() const = 0; + virtual bool hasSetter() const = 0; + + virtual QVariant read(const QObject *object) const = 0; + virtual bool reset(QObject *object) const = 0; + virtual bool write(QObject *object, const QVariant &value) const = 0; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDesignerMetaPropertyInterface::AccessFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QDesignerMetaPropertyInterface::Attributes) + +class QDESIGNER_SDK_EXPORT QDesignerMetaMethodInterface +{ +public: + QDesignerMetaMethodInterface(); + virtual ~QDesignerMetaMethodInterface(); + + enum MethodType { Method, Signal, Slot, Constructor }; + enum Access { Private, Protected, Public }; + + virtual Access access() const = 0; + virtual MethodType methodType() const = 0; + virtual QStringList parameterNames() const = 0; + virtual QStringList parameterTypes() const = 0; + virtual QString signature() const = 0; + virtual QString normalizedSignature() const = 0; + virtual QString tag() const = 0; + virtual QString typeName() const = 0; +}; + +class QDESIGNER_SDK_EXPORT QDesignerMetaObjectInterface { +public: + QDesignerMetaObjectInterface(); + virtual ~QDesignerMetaObjectInterface(); + + virtual QString className() const = 0; + virtual const QDesignerMetaEnumInterface *enumerator(int index) const = 0; + virtual int enumeratorCount() const = 0; + virtual int enumeratorOffset() const = 0; + + virtual int indexOfEnumerator(const QString &name) const = 0; + virtual int indexOfMethod(const QString &method) const = 0; + virtual int indexOfProperty(const QString &name) const = 0; + virtual int indexOfSignal(const QString &signal) const = 0; + virtual int indexOfSlot(const QString &slot) const = 0; + + virtual const QDesignerMetaMethodInterface *method(int index) const = 0; + virtual int methodCount() const = 0; + virtual int methodOffset() const = 0; + + virtual const QDesignerMetaPropertyInterface *property(int index) const = 0; + virtual int propertyCount() const = 0; + virtual int propertyOffset() const = 0; + + virtual const QDesignerMetaObjectInterface *superClass() const = 0; + virtual const QDesignerMetaPropertyInterface *userProperty() const = 0; +}; + +// To be obtained from core +class QDESIGNER_SDK_EXPORT QDesignerIntrospectionInterface { +public: + QDesignerIntrospectionInterface(); + virtual ~QDesignerIntrospectionInterface(); + + virtual const QDesignerMetaObjectInterface* metaObject(const QObject *object) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTMETAOBJECT_H diff --git a/designer/lib/sdk/abstractlanguage.h b/designer/lib/sdk/abstractlanguage.h new file mode 100644 index 0000000..097aa89 --- /dev/null +++ b/designer/lib/sdk/abstractlanguage.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_ABTRACT_LANGUAGE_H +#define QDESIGNER_ABTRACT_LANGUAGE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDialog; +class QWidget; +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class QDesignerResourceBrowserInterface; + +class QDesignerLanguageExtension +{ +public: + virtual ~QDesignerLanguageExtension() {} + + virtual QDialog *createFormWindowSettingsDialog(QDesignerFormWindowInterface *formWindow, QWidget *parentWidget) = 0; + virtual QDesignerResourceBrowserInterface *createResourceBrowser(QWidget *parentWidget) = 0; + + virtual QDialog *createPromotionDialog(QDesignerFormEditorInterface *formEditor, QWidget *parentWidget = 0) = 0; + + virtual QDialog *createPromotionDialog(QDesignerFormEditorInterface *formEditor, + const QString &promotableWidgetClassName, + QString *promoteToClassName, + QWidget *parentWidget = 0) = 0; + + virtual bool isLanguageResource(const QString &path) const = 0; + + virtual QString classNameOf(QObject *object) const = 0; + + virtual bool signalMatchesSlot(const QString &signal, const QString &slot) const = 0; + + virtual QString widgetBoxContents() const = 0; + + virtual QString uiExtension() const = 0; +}; + +Q_DECLARE_EXTENSION_INTERFACE(QDesignerLanguageExtension, "com.trolltech.Qt.Designer.Language.3") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDESIGNER_ABTRACT_LANGUAGE_H diff --git a/designer/lib/sdk/abstractmetadatabase.cpp b/designer/lib/sdk/abstractmetadatabase.cpp new file mode 100644 index 0000000..e32adc0 --- /dev/null +++ b/designer/lib/sdk/abstractmetadatabase.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// sdk +#include "abstractmetadatabase.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerMetaDataBaseInterface + \brief The QDesignerMetaDataBaseInterface class provides an interface to Qt Designer's + object meta database. + \inmodule QtDesigner + \internal +*/ + +/*! + Constructs an interface to the meta database with the given \a parent. +*/ +QDesignerMetaDataBaseInterface::QDesignerMetaDataBaseInterface(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the interface to the meta database. +*/ +QDesignerMetaDataBaseInterface::~QDesignerMetaDataBaseInterface() +{ +} + +/*! + \fn QDesignerMetaDataBaseItemInterface *QDesignerMetaDataBaseInterface::item(QObject *object) const + + Returns the item in the meta database associated with the given \a object. +*/ + +/*! + \fn void QDesignerMetaDataBaseInterface::add(QObject *object) + + Adds the specified \a object to the meta database. +*/ + +/*! + \fn void QDesignerMetaDataBaseInterface::remove(QObject *object) + + Removes the specified \a object from the meta database. +*/ + +/*! + \fn QList QDesignerMetaDataBaseInterface::objects() const + + Returns the list of objects that have corresponding items in the meta database. +*/ + +/*! + \fn QDesignerFormEditorInterface *QDesignerMetaDataBaseInterface::core() const + + Returns the core interface that is associated with the meta database. +*/ + + +// Doc: Interface only + +/*! + \class QDesignerMetaDataBaseItemInterface + \brief The QDesignerMetaDataBaseItemInterface class provides an interface to individual + items in Qt Designer's meta database. + \inmodule QtDesigner + \internal + + This class allows individual items in \QD's meta-data database to be accessed and modified. + Use the QDesignerMetaDataBaseInterface class to change the properties of the database itself. +*/ + +/*! + \fn QDesignerMetaDataBaseItemInterface::~QDesignerMetaDataBaseItemInterface() + + Destroys the item interface to the meta-data database. +*/ + +/*! + \fn QString QDesignerMetaDataBaseItemInterface::name() const + + Returns the name of the item in the database. + + \sa setName() +*/ + +/*! + \fn void QDesignerMetaDataBaseItemInterface::setName(const QString &name) + + Sets the name of the item to the given \a name. + + \sa name() +*/ + +/*! + \fn QList QDesignerMetaDataBaseItemInterface::tabOrder() const + + Returns a list of widgets in the order defined by the form's tab order. + + \sa setTabOrder() +*/ + + +/*! + \fn void QDesignerMetaDataBaseItemInterface::setTabOrder(const QList &tabOrder) + + Sets the tab order in the form using the list of widgets defined by \a tabOrder. + + \sa tabOrder() +*/ + + +/*! + \fn bool QDesignerMetaDataBaseItemInterface::enabled() const + + Returns whether the item is enabled. + + \sa setEnabled() +*/ + +/*! + \fn void QDesignerMetaDataBaseItemInterface::setEnabled(bool enabled) + + If \a enabled is true, the item is enabled; otherwise it is disabled. + + \sa enabled() +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractmetadatabase.h b/designer/lib/sdk/abstractmetadatabase.h new file mode 100644 index 0000000..1b6967d --- /dev/null +++ b/designer/lib/sdk/abstractmetadatabase.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTMETADATABASE_H +#define ABSTRACTMETADATABASE_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QCursor; +class QWidget; + +class QDesignerFormEditorInterface; + +class QDesignerMetaDataBaseItemInterface +{ +public: + virtual ~QDesignerMetaDataBaseItemInterface() {} + + virtual QString name() const = 0; + virtual void setName(const QString &name) = 0; + + virtual QList tabOrder() const = 0; + virtual void setTabOrder(const QList &tabOrder) = 0; + + virtual bool enabled() const = 0; + virtual void setEnabled(bool b) = 0; +}; + + +class QDESIGNER_SDK_EXPORT QDesignerMetaDataBaseInterface: public QObject +{ + Q_OBJECT +public: + QDesignerMetaDataBaseInterface(QObject *parent = 0); + virtual ~QDesignerMetaDataBaseInterface(); + + virtual QDesignerMetaDataBaseItemInterface *item(QObject *object) const = 0; + virtual void add(QObject *object) = 0; + virtual void remove(QObject *object) = 0; + + virtual QList objects() const = 0; + + virtual QDesignerFormEditorInterface *core() const = 0; + +Q_SIGNALS: + void changed(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTMETADATABASE_H diff --git a/designer/lib/sdk/abstractnewformwidget.cpp b/designer/lib/sdk/abstractnewformwidget.cpp new file mode 100644 index 0000000..b513a8c --- /dev/null +++ b/designer/lib/sdk/abstractnewformwidget.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractnewformwidget_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerNewFormWidgetInterface + \since 4.5 + \internal + + \brief QDesignerNewFormWidgetInterface provides an interface for chooser + widgets that can be used within "New Form" dialogs and wizards. + It presents the user with a list of choices taken from built-in + templates, pre-defined template paths and suitable custom widgets. + It provides a static creation function that returns \QD's + implementation. + + \inmodule QtDesigner +*/ + +/*! + Constructs a QDesignerNewFormWidgetInterface object. +*/ + +QDesignerNewFormWidgetInterface::QDesignerNewFormWidgetInterface(QWidget *parent) : + QWidget(parent) +{ +} + +/*! + Destroys the QDesignerNewFormWidgetInterface object. +*/ + +QDesignerNewFormWidgetInterface::~QDesignerNewFormWidgetInterface() +{ +} + +/*! + Creates an instance of the QDesignerNewFormWidgetInterface as a child + of \a parent using \a core. +*/ + +QDesignerNewFormWidgetInterface *QDesignerNewFormWidgetInterface::createNewFormWidget(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new qdesigner_internal::NewFormWidget(core, parent); +} + +/*! + \fn bool QDesignerNewFormWidgetInterface::hasCurrentTemplate() const + + Returns whether a form template is currently selected. +*/ + +/*! + \fn QString QDesignerNewFormWidgetInterface::currentTemplate(QString *errorMessage = 0) + + Returns the contents of the currently selected template. If the method fails, + an empty string is returned and \a errorMessage receives an error message. +*/ + +// Signals + +/*! + \fn void QDesignerNewFormWidgetInterface::templateActivated() + + This signal is emitted whenever the user activates a template by double-clicking. +*/ + +/*! + \fn void QDesignerNewFormWidgetInterface::currentTemplateChanged(bool templateSelected) + + This signal is emitted whenever the user changes the current template. + \a templateSelected indicates whether a template is currently selected. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractnewformwidget_p.h b/designer/lib/sdk/abstractnewformwidget_p.h new file mode 100644 index 0000000..bccb2a1 --- /dev/null +++ b/designer/lib/sdk/abstractnewformwidget_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ABSTRACTNEWFORMWIDGET_H +#define ABSTRACTNEWFORMWIDGET_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +class QDESIGNER_SDK_EXPORT QDesignerNewFormWidgetInterface : public QWidget +{ + Q_DISABLE_COPY(QDesignerNewFormWidgetInterface) + Q_OBJECT +public: + explicit QDesignerNewFormWidgetInterface(QWidget *parent = 0); + virtual ~QDesignerNewFormWidgetInterface(); + + virtual bool hasCurrentTemplate() const = 0; + virtual QString currentTemplate(QString *errorMessage = 0) = 0; + + static QDesignerNewFormWidgetInterface *createNewFormWidget(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +Q_SIGNALS: + void templateActivated(); + void currentTemplateChanged(bool templateSelected); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTNEWFORMWIDGET_H diff --git a/designer/lib/sdk/abstractobjectinspector.cpp b/designer/lib/sdk/abstractobjectinspector.cpp new file mode 100644 index 0000000..2d1097f --- /dev/null +++ b/designer/lib/sdk/abstractobjectinspector.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractobjectinspector.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerObjectInspectorInterface + + \brief The QDesignerObjectInspectorInterface class allows you to + change the focus of Qt Designer's object inspector. + + \inmodule QtDesigner + + You can use the QDesignerObjectInspectorInterface to change the + current form window selection. For example, when implementing a + custom widget plugin: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractobjectinspector.cpp 0 + + The QDesignerObjectInspectorInterface class is not intended to be + instantiated directly. You can retrieve an interface to \QD's + object inspector using the + QDesignerFormEditorInterface::objectInspector() function. A + pointer to \QD's current QDesignerFormEditorInterface object (\c + formEditor in the example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface to expose your plugin + to \QD. + + The interface provides the core() function that you can use to + retrieve a pointer to \QD's current QDesignerFormEditorInterface + object, and the setFormWindow() function that enables you to + change the current form window selection. + + \sa QDesignerFormEditorInterface, QDesignerFormWindowInterface +*/ + +/*! + Constructs an object inspector interface with the given \a parent + and the specified window \a flags. +*/ +QDesignerObjectInspectorInterface::QDesignerObjectInspectorInterface(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags) +{ +} + +/*! + Destroys the object inspector interface. +*/ +QDesignerObjectInspectorInterface::~QDesignerObjectInspectorInterface() +{ +} + +/*! + Returns a pointer to \QD's current QDesignerFormEditorInterface + object. +*/ +QDesignerFormEditorInterface *QDesignerObjectInspectorInterface::core() const +{ + return 0; +} + +/*! + \fn void QDesignerObjectInspectorInterface::setFormWindow(QDesignerFormWindowInterface *formWindow) + + Sets the currently selected form window to \a formWindow. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractobjectinspector.h b/designer/lib/sdk/abstractobjectinspector.h new file mode 100644 index 0000000..d490143 --- /dev/null +++ b/designer/lib/sdk/abstractobjectinspector.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTOBJECTINSPECTOR_H +#define ABSTRACTOBJECTINSPECTOR_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QDESIGNER_SDK_EXPORT QDesignerObjectInspectorInterface: public QWidget +{ + Q_OBJECT +public: + QDesignerObjectInspectorInterface(QWidget *parent, Qt::WindowFlags flags = 0); + virtual ~QDesignerObjectInspectorInterface(); + + virtual QDesignerFormEditorInterface *core() const; + +public Q_SLOTS: + virtual void setFormWindow(QDesignerFormWindowInterface *formWindow) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTOBJECTINSPECTOR_H diff --git a/designer/lib/sdk/abstractoptionspage_p.h b/designer/lib/sdk/abstractoptionspage_p.h new file mode 100644 index 0000000..e43e7f6 --- /dev/null +++ b/designer/lib/sdk/abstractoptionspage_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ABSTRACTOPTIONSPAGE_P_H +#define ABSTRACTOPTIONSPAGE_P_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QString; +class QWidget; + +class QDESIGNER_SDK_EXPORT QDesignerOptionsPageInterface +{ +public: + virtual ~QDesignerOptionsPageInterface() {} + virtual QString name() const = 0; + virtual QWidget *createPage(QWidget *parent) = 0; + virtual void apply() = 0; + virtual void finish() = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTOPTIONSPAGE_P_H diff --git a/designer/lib/sdk/abstractpromotioninterface.cpp b/designer/lib/sdk/abstractpromotioninterface.cpp new file mode 100644 index 0000000..0033c6e --- /dev/null +++ b/designer/lib/sdk/abstractpromotioninterface.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractpromotioninterface.h" + +QT_BEGIN_NAMESPACE + +QDesignerPromotionInterface::~QDesignerPromotionInterface() +{ +} + +/*! + \class QDesignerPromotionInterface + + \brief The QDesignerPromotionInterface provides functions for modifying + the promoted classes in Designer. + \inmodule QtDesigner + \internal + \since 4.3 +*/ + +/*! + \class QDesignerPromotionInterface::PromotedClass + A pair of database items containing the base class and the promoted class. + + \typedef QDesignerPromotionInterface::PromotedClasses + A list of PromotedClass items. + + virtual QDesignerPromotionInterface::PromotedClasses promotedClasses() const + + Returns a list of promoted classes along with their base classes in alphabetical order. + It can be used to populate tree models for editing promoted widgets. + +*/ + +/*! + \fn virtual QSet QDesignerPromotionInterface::referencedPromotedClassNames() const + + Returns a set of promoted classed that are referenced by the currently opened forms. +*/ + +/*! + \fn virtual bool QDesignerPromotionInterface::addPromotedClass(const QString &baseClass, const QString &className, const QString &includeFile, QString *errorMessage) + + Add a promoted class named \a with the base class \a and include file \a includeFile. Returns \c true on success or \c false along + with an error message in \a errorMessage on failure. +*/ + +/*! + \fn virtual bool QDesignerPromotionInterface::removePromotedClass(const QString &className, QString *errorMessage) + + Remove the promoted class named \a className unless it is referenced by a form. Returns \c true on success or \c false along + with an error message in \a errorMessage on failure. +*/ + +/*! + \fn virtual bool QDesignerPromotionInterface::changePromotedClassName(const QString &oldClassName, const QString &newClassName, QString *errorMessage) + + Change the class name of a promoted class from \a oldClassName to \a newClassName. Returns \c true on success or \c false along + with an error message in \a errorMessage on failure. +*/ + +/*! + \fn virtual bool QDesignerPromotionInterface::setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage) + + Change the include file of a promoted class named \a className to be \a includeFile. Returns \c true on success or \c false along + with an error message in \a errorMessage on failure. +*/ + +/*! \fn virtual QList QDesignerPromotionInterface::promotionBaseClasses() const + + Return a list of base classes that are suitable for promotion. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractpromotioninterface.h b/designer/lib/sdk/abstractpromotioninterface.h new file mode 100644 index 0000000..4d65a34 --- /dev/null +++ b/designer/lib/sdk/abstractpromotioninterface.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTPROMOTIONINTERFACE_H +#define ABSTRACTPROMOTIONINTERFACE_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerWidgetDataBaseItemInterface; + +class QDESIGNER_SDK_EXPORT QDesignerPromotionInterface +{ +public: + virtual ~QDesignerPromotionInterface(); + + struct PromotedClass { + QDesignerWidgetDataBaseItemInterface *baseItem; + QDesignerWidgetDataBaseItemInterface *promotedItem; + }; + + typedef QList PromotedClasses; + + virtual PromotedClasses promotedClasses() const = 0; + + virtual QSet referencedPromotedClassNames() const = 0; + + virtual bool addPromotedClass(const QString &baseClass, + const QString &className, + const QString &includeFile, + QString *errorMessage) = 0; + + virtual bool removePromotedClass(const QString &className, QString *errorMessage) = 0; + + virtual bool changePromotedClassName(const QString &oldClassName, const QString &newClassName, QString *errorMessage) = 0; + + virtual bool setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage) = 0; + + virtual QList promotionBaseClasses() const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTPROMOTIONINTERFACE_H diff --git a/designer/lib/sdk/abstractpropertyeditor.cpp b/designer/lib/sdk/abstractpropertyeditor.cpp new file mode 100644 index 0000000..5c3b326 --- /dev/null +++ b/designer/lib/sdk/abstractpropertyeditor.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractpropertyeditor.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerPropertyEditorInterface + + \brief The QDesignerPropertyEditorInterface class allows you to + query and manipulate the current state of Qt Designer's property + editor. + + \inmodule QtDesigner + + QDesignerPropertyEditorInterface contains a collection of + functions that is typically used to query the property editor for + its current state, and several slots manipulating it's state. The + interface also provide a signal, propertyChanged(), which is + emitted whenever a property changes in the property editor. The + signal's arguments are the property that changed and its new + value. + + For example, when implementing a custom widget plugin, you can + connect the signal to a custom slot: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractpropertyeditor.cpp 0 + + Then the custom slot can check if the new value is within the + range we want when a specified property, belonging to a particular + widget, changes: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractpropertyeditor.cpp 1 + + The QDesignerPropertyEditorInterface class is not intended to be + instantiated directly. You can retrieve an interface to \QD's + property editor using the + QDesignerFormEditorInterface::propertyEditor() function. A pointer + to \QD's current QDesignerFormEditorInterface object (\c + formEditor in the examples above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface to expose your plugin + to \QD. + + The functions accessing the property editor are the core() + function that you can use to retrieve an interface to the form + editor, the currentPropertyName() function that returns the name + of the currently selected property in the property editor, the + object() function that returns the currently selected object in + \QD's workspace, and the isReadOnly() function that returns true + if the property editor is write proteced (otherwise false). + + The slots manipulating the property editor's state are the + setObject() slot that you can use to change the currently selected + object in \QD's workspace, the setPropertyValue() slot that + changes the value of a given property and the setReadOnly() slot + that control the write protection of the property editor. + + \sa QDesignerFormEditorInterface +*/ + +/*! + Constructs a property editor interface with the given \a parent and + the specified window \a flags. +*/ +QDesignerPropertyEditorInterface::QDesignerPropertyEditorInterface(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags) +{ +} + +/*! + Destroys the property editor interface. +*/ +QDesignerPropertyEditorInterface::~QDesignerPropertyEditorInterface() +{ +} + +/*! + Returns a pointer to \QD's current QDesignerFormEditorInterface + object. +*/ +QDesignerFormEditorInterface *QDesignerPropertyEditorInterface::core() const +{ + return 0; +} + +/*! + \fn bool QDesignerPropertyEditorInterface::isReadOnly() const + + Returns true if the property editor is write protected; otherwise + false. + + \sa setReadOnly() +*/ + +/*! + \fn QObject *QDesignerPropertyEditorInterface::object() const + + Returns the currently selected object in \QD's workspace. + + \sa setObject() +*/ + +/*! + \fn QString QDesignerPropertyEditorInterface::currentPropertyName() const + + Returns the name of the currently selected property in the + property editor. + + \sa setPropertyValue() +*/ + +/*! + \fn void QDesignerPropertyEditorInterface::propertyChanged(const QString &name, const QVariant &value) + + This signal is emitted whenever a property changes in the property + editor. The property that changed and its new value are specified + by \a name and \a value respectively. + + \sa setPropertyValue() +*/ + +/*! + \fn void QDesignerPropertyEditorInterface::setObject(QObject *object) + + Changes the currently selected object in \QD's workspace, to \a + object. + + \sa object() +*/ + +/*! + \fn void QDesignerPropertyEditorInterface::setPropertyValue(const QString &name, const QVariant &value, bool changed = true) + + Sets the value of the property specified by \a name to \a + value. + + In addition, the property is marked as \a changed in the property + editor, i.e. its value is different from the default value. + + \sa currentPropertyName(), propertyChanged() +*/ + +/*! + \fn void QDesignerPropertyEditorInterface::setReadOnly(bool readOnly) + + If \a readOnly is true, the property editor is made write + protected; otherwise the write protection is removed. + + \sa isReadOnly() +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractpropertyeditor.h b/designer/lib/sdk/abstractpropertyeditor.h new file mode 100644 index 0000000..5e0a624 --- /dev/null +++ b/designer/lib/sdk/abstractpropertyeditor.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTPROPERTYEDITOR_H +#define ABSTRACTPROPERTYEDITOR_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QString; +class QVariant; + +class QDESIGNER_SDK_EXPORT QDesignerPropertyEditorInterface: public QWidget +{ + Q_OBJECT +public: + QDesignerPropertyEditorInterface(QWidget *parent, Qt::WindowFlags flags = 0); + virtual ~QDesignerPropertyEditorInterface(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual bool isReadOnly() const = 0; + virtual QObject *object() const = 0; + + virtual QString currentPropertyName() const = 0; + +Q_SIGNALS: + void propertyChanged(const QString &name, const QVariant &value); + +public Q_SLOTS: + virtual void setObject(QObject *object) = 0; + virtual void setPropertyValue(const QString &name, const QVariant &value, bool changed = true) = 0; + virtual void setReadOnly(bool readOnly) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTPROPERTYEDITOR_H diff --git a/designer/lib/sdk/abstractresourcebrowser.cpp b/designer/lib/sdk/abstractresourcebrowser.cpp new file mode 100644 index 0000000..354fcba --- /dev/null +++ b/designer/lib/sdk/abstractresourcebrowser.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractresourcebrowser.h" + +QT_BEGIN_NAMESPACE + +QDesignerResourceBrowserInterface::QDesignerResourceBrowserInterface(QWidget *parent) + : QWidget(parent) +{ + +} + +QDesignerResourceBrowserInterface::~QDesignerResourceBrowserInterface() +{ + +} + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractresourcebrowser.h b/designer/lib/sdk/abstractresourcebrowser.h new file mode 100644 index 0000000..c578ab9 --- /dev/null +++ b/designer/lib/sdk/abstractresourcebrowser.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTRESOURCEBROWSER_H +#define ABSTRACTRESOURCEBROWSER_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWidget; // FIXME: fool syncqt + +class QDESIGNER_SDK_EXPORT QDesignerResourceBrowserInterface: public QWidget +{ + Q_OBJECT +public: + QDesignerResourceBrowserInterface(QWidget *parent = 0); + virtual ~QDesignerResourceBrowserInterface(); + + virtual void setCurrentPath(const QString &filePath) = 0; + virtual QString currentPath() const = 0; + +Q_SIGNALS: + void currentPathChanged(const QString &filePath); + void pathActivated(const QString &filePath); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMEDITOR_H + diff --git a/designer/lib/sdk/abstractsettings_p.h b/designer/lib/sdk/abstractsettings_p.h new file mode 100644 index 0000000..4bb5f78 --- /dev/null +++ b/designer/lib/sdk/abstractsettings_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ABSTRACTSETTINGS_P_H +#define ABSTRACTSETTINGS_P_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QString; + +/*! + To be implemented by IDEs that want to control the way designer retrieves/stores its settings. + */ +class QDESIGNER_SDK_EXPORT QDesignerSettingsInterface +{ +public: + virtual ~QDesignerSettingsInterface() {} + + virtual void beginGroup(const QString &prefix) = 0; + virtual void endGroup() = 0; + + virtual bool contains(const QString &key) const = 0; + virtual void setValue(const QString &key, const QVariant &value) = 0; + virtual QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const = 0; + virtual void remove(const QString &key) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTSETTINGS_P_H diff --git a/designer/lib/sdk/abstractwidgetbox.cpp b/designer/lib/sdk/abstractwidgetbox.cpp new file mode 100644 index 0000000..3f1134e --- /dev/null +++ b/designer/lib/sdk/abstractwidgetbox.cpp @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractwidgetbox.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerWidgetBoxInterface + + \brief The QDesignerWidgetBoxInterface class allows you to control + the contents of Qt Designer's widget box. + + \inmodule QtDesigner + + QDesignerWidgetBoxInterface contains a collection of functions + that is typically used to manipulate the contents of \QD's widget + box. + + \QD uses an XML file to populate its widget box. The name of that + file is one of the widget box's properties, and you can retrieve + it using the fileName() function. + + QDesignerWidgetBoxInterface also provides the save() function that + saves the contents of the widget box in the file specified by the + widget box's file name property. If you have made changes to the + widget box, for example by dropping a widget into the widget box, + without calling the save() function, the original content can be + restored by a simple invocation of the load() function: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractwidgetbox.cpp 0 + + The QDesignerWidgetBoxInterface class is not intended to be + instantiated directly. You can retrieve an interface to Qt + Designer's widget box using the + QDesignerFormEditorInterface::widgetBox() function. A pointer to + \QD's current QDesignerFormEditorInterface object (\c formEditor + in the example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's + parameter. When implementing a custom widget plugin, you must + subclass the QDesignerCustomWidgetInterface to expose your plugin + to \QD. + + If you want to save your changes, and at the same time preserve + the original contents, you can use the save() function combined + with the setFileName() function to save your changes into another + file. Remember to store the name of the original file first: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractwidgetbox.cpp 1 + + Then you can restore the original contents of the widget box by + resetting the file name to the original file and calling load(): + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractwidgetbox.cpp 2 + + In a similar way, you can later use your customized XML file: + + \snippet doc/src/snippets/code/tools_designer_src_lib_sdk_abstractwidgetbox.cpp 3 + + + \sa QDesignerFormEditorInterface +*/ + +/*! + Constructs a widget box interface with the given \a parent and + the specified window \a flags. +*/ +QDesignerWidgetBoxInterface::QDesignerWidgetBoxInterface(QWidget *parent, Qt::WindowFlags flags) + : QWidget(parent, flags) +{ +} + +/*! + Destroys the widget box interface. +*/ +QDesignerWidgetBoxInterface::~QDesignerWidgetBoxInterface() +{ +} + +/*! + \internal +*/ +int QDesignerWidgetBoxInterface::findOrInsertCategory(const QString &categoryName) +{ + int count = categoryCount(); + for (int index=0; index &item_list, const QPoint &global_mouse_pos) + +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::setFileName(const QString &fileName) + + Sets the XML file that \QD will use to populate its widget box, to + \a fileName. You must call load() to update the widget box with + the new XML file. + + \sa fileName(), load() +*/ + +/*! + \fn QString QDesignerWidgetBoxInterface::fileName() const + + Returns the name of the XML file \QD is currently using to + populate its widget box. + + \sa setFileName() +*/ + +/*! + \fn bool QDesignerWidgetBoxInterface::load() + + Populates \QD's widget box by loading (or reloading) the currently + specified XML file. Returns true if the file is successfully + loaded; otherwise false. + + \sa setFileName() +*/ + +/*! + \fn bool QDesignerWidgetBoxInterface::save() + + Saves the contents of \QD's widget box in the file specified by + the fileName() function. Returns true if the content is + successfully saved; otherwise false. + + \sa fileName(), setFileName() +*/ + + +/*! + \internal + + \class QDesignerWidgetBoxInterface::Widget + + \brief The Widget class specified a widget in Qt Designer's widget + box component. +*/ + +/*! + \enum QDesignerWidgetBoxInterface::Widget::Type + + \value Default + \value Custom +*/ + +/*! + \fn QDesignerWidgetBoxInterface::Widget::Widget(const QString &aname, const QString &xml, const QString &icon_name, Type atype) +*/ + +/*! + \fn QString QDesignerWidgetBoxInterface::Widget::name() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Widget::setName(const QString &aname) +*/ + +/*! + \fn QString QDesignerWidgetBoxInterface::Widget::domXml() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Widget::setDomXml(const QString &xml) +*/ + +/*! + \fn QString QDesignerWidgetBoxInterface::Widget::iconName() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Widget::setIconName(const QString &icon_name) +*/ + +/*! + \fn Type QDesignerWidgetBoxInterface::Widget::type() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Widget::setType(Type atype) +*/ + +/*! + \fn bool QDesignerWidgetBoxInterface::Widget::isNull() const +*/ + + +/*! + \class QDesignerWidgetBoxInterface::Category + \brief The Category class specifies a category in Qt Designer's widget box component. + \internal +*/ + +/*! + \enum QDesignerWidgetBoxInterface::Category::Type + + \value Default + \value Scratchpad +*/ + +/*! + \fn QDesignerWidgetBoxInterface::Category::Category(const QString &aname, Type atype) +*/ + +/*! + \fn QString QDesignerWidgetBoxInterface::Category::name() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Category::setName(const QString &aname) +*/ + +/*! + \fn int QDesignerWidgetBoxInterface::Category::widgetCount() const +*/ + +/*! + \fn Widget QDesignerWidgetBoxInterface::Category::widget(int idx) const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Category::removeWidget(int idx) +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Category::addWidget(const Widget &awidget) +*/ + +/*! + \fn Type QDesignerWidgetBoxInterface::Category::type() const +*/ + +/*! + \fn void QDesignerWidgetBoxInterface::Category::setType(Type atype) +*/ + +/*! + \fn bool QDesignerWidgetBoxInterface::Category::isNull() const +*/ + +/*! + \typedef QDesignerWidgetBoxInterface::CategoryList + \internal +*/ + +/*! + \typedef QDesignerWidgetBoxInterface::WidgetList + \internal +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractwidgetbox.h b/designer/lib/sdk/abstractwidgetbox.h new file mode 100644 index 0000000..c1a17e7 --- /dev/null +++ b/designer/lib/sdk/abstractwidgetbox.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTWIDGETBOX_H +#define ABSTRACTWIDGETBOX_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DomUI; +class QDesignerDnDItemInterface; + +class QDESIGNER_SDK_EXPORT QDesignerWidgetBoxInterface : public QWidget +{ + Q_OBJECT +public: + class Widget { + public: + enum Type { Default, Custom }; + Widget(const QString &aname = QString(), const QString &xml = QString(), + const QString &icon_name = QString(), Type atype = Default) + : m_name(aname), m_xml(xml), m_icon_name(icon_name), m_type(atype) {} + QString name() const { return m_name; } + void setName(const QString &aname) { m_name = aname; } + QString domXml() const { return m_xml; } + void setDomXml(const QString &xml) { m_xml = xml; } + QString iconName() const { return m_icon_name; } + void setIconName(const QString &icon_name) { m_icon_name = icon_name; } + Type type() const { return m_type; } + void setType(Type atype) { m_type = atype; } + + bool isNull() const { return m_name.isEmpty(); } + + private: + QString m_name; + QString m_xml; + QString m_icon_name; + Type m_type; + }; + typedef QList WidgetList; + + class Category { + public: + enum Type { Default, Scratchpad }; + + Category(const QString &aname = QString(), Type atype = Default) + : m_name(aname), m_type(atype) {} + + QString name() const { return m_name; } + void setName(const QString &aname) { m_name = aname; } + int widgetCount() const { return m_widget_list.size(); } + Widget widget(int idx) const { return m_widget_list.at(idx); } + void removeWidget(int idx) { m_widget_list.removeAt(idx); } + void addWidget(const Widget &awidget) { m_widget_list.append(awidget); } + Type type() const { return m_type; } + void setType(Type atype) { m_type = atype; } + + bool isNull() const { return m_name.isEmpty(); } + + private: + QString m_name; + Type m_type; + QList m_widget_list; + }; + typedef QList CategoryList; + + QDesignerWidgetBoxInterface(QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~QDesignerWidgetBoxInterface(); + + virtual int categoryCount() const = 0; + virtual Category category(int cat_idx) const = 0; + virtual void addCategory(const Category &cat) = 0; + virtual void removeCategory(int cat_idx) = 0; + + virtual int widgetCount(int cat_idx) const = 0; + virtual Widget widget(int cat_idx, int wgt_idx) const = 0; + virtual void addWidget(int cat_idx, const Widget &wgt) = 0; + virtual void removeWidget(int cat_idx, int wgt_idx) = 0; + + int findOrInsertCategory(const QString &categoryName); + + virtual void dropWidgets(const QList &item_list, + const QPoint &global_mouse_pos) = 0; + + virtual void setFileName(const QString &file_name) = 0; + virtual QString fileName() const = 0; + virtual bool load() = 0; + virtual bool save() = 0; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QT_PREPEND_NAMESPACE(QDesignerWidgetBoxInterface::Widget)) + +QT_END_HEADER + +#endif // ABSTRACTWIDGETBOX_H diff --git a/designer/lib/sdk/abstractwidgetdatabase.cpp b/designer/lib/sdk/abstractwidgetdatabase.cpp new file mode 100644 index 0000000..c2716b3 --- /dev/null +++ b/designer/lib/sdk/abstractwidgetdatabase.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractwidgetdatabase.h" +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + enum { debugWidgetDataBase = 0 }; +} + +/*! + \class QDesignerWidgetDataBaseInterface + \brief The QDesignerWidgetDataBaseInterface class provides an interface that is used to + access and modify Qt Designer's widget database. + \inmodule QtDesigner + \internal +*/ + +/*! + Constructs an interface to the widget database with the given \a parent. +*/ +QDesignerWidgetDataBaseInterface::QDesignerWidgetDataBaseInterface(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the interface to the widget database. +*/ +QDesignerWidgetDataBaseInterface::~QDesignerWidgetDataBaseInterface() +{ + qDeleteAll(m_items); +} + +/*! + +*/ +int QDesignerWidgetDataBaseInterface::count() const +{ + return m_items.count(); +} + +/*! +*/ +QDesignerWidgetDataBaseItemInterface *QDesignerWidgetDataBaseInterface::item(int index) const +{ + return index != -1 ? m_items.at(index) : 0; +} + +/*! +*/ +int QDesignerWidgetDataBaseInterface::indexOf(QDesignerWidgetDataBaseItemInterface *item) const +{ + return m_items.indexOf(item); +} + +/*! +*/ +void QDesignerWidgetDataBaseInterface::insert(int index, QDesignerWidgetDataBaseItemInterface *item) +{ + if (debugWidgetDataBase) + qDebug() << "insert at " << index << ' ' << item->name() << " derived from " << item->extends(); + + m_items.insert(index, item); +} + +/*! +*/ +void QDesignerWidgetDataBaseInterface::append(QDesignerWidgetDataBaseItemInterface *item) +{ + if (debugWidgetDataBase) + qDebug() << "append " << item->name() << " derived from " << item->extends(); + m_items.append(item); +} + +/*! +*/ +QDesignerFormEditorInterface *QDesignerWidgetDataBaseInterface::core() const +{ + return 0; +} + +/*! +*/ +int QDesignerWidgetDataBaseInterface::indexOfClassName(const QString &name, bool) const +{ + const int itemCount = count(); + for (int i=0; iname() == name) + return i; + } + + return -1; +} + +/*! +*/ +int QDesignerWidgetDataBaseInterface::indexOfObject(QObject *object, bool) const +{ + if (!object) + return -1; + + const QString className = QString::fromUtf8(object->metaObject()->className()); + return indexOfClassName(className); +} + +/*! +*/ +bool QDesignerWidgetDataBaseInterface::isContainer(QObject *object, bool resolveName) const +{ + if (const QDesignerWidgetDataBaseItemInterface *i = item(indexOfObject(object, resolveName))) + return i->isContainer(); + return false; +} + +/*! +*/ +bool QDesignerWidgetDataBaseInterface::isCustom(QObject *object, bool resolveName) const +{ + if (const QDesignerWidgetDataBaseItemInterface *i = item(indexOfObject(object, resolveName))) + return i->isCustom(); + return false; +} + +/*! + \fn void QDesignerWidgetDataBaseInterface::changed() + + This signal is emitted ... +*/ + + +// Doc: No implementation - an abstract class + +/*! + \class QDesignerWidgetDataBaseItemInterface + \brief The QDesignerWidgetDataBaseItemInterface class provides an interface that is used to + access individual items in Qt Designer's widget database. + \inmodule QtDesigner + \internal + + This class enables individual items in the widget database to be accessed and modified. + Changes to the widget database itself are made through the QDesignerWidgetDataBaseInterface + class. +*/ + +/*! + \fn virtual QDesignerWidgetDataBaseItemInterface::~QDesignerWidgetDataBaseItemInterface() + + Destroys the interface. +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::name() const = 0 + + Returns the name of the widget. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setName(const QString &name) = 0 +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::group() const = 0 + + Returns the name of the group that the widget belongs to. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setGroup(const QString &group) = 0 +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::toolTip() const = 0 + + Returns the tool tip to be used by the widget. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setToolTip(const QString &toolTip) = 0 +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::whatsThis() const = 0 + + Returns the "What's This?" help for the widget. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setWhatsThis(const QString &whatsThis) = 0 +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::includeFile() const = 0 + + Returns the name of the include file that the widget needs when being built from source. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setIncludeFile(const QString &includeFile) = 0 +*/ + +/*! + \fn virtual QIcon QDesignerWidgetDataBaseItemInterface::icon() const = 0 + + Returns the icon used to represent the item. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setIcon(const QIcon &icon) = 0 +*/ + +/*! + \fn virtual bool QDesignerWidgetDataBaseItemInterface::isCompat() const = 0 + + Returns true if this type of widget is provided for compatibility purposes (e.g. Qt3Support + widgets); otherwise returns false. + + \sa setCompat() +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setCompat(bool compat) = 0 + + If \a compat is true, the widget is handled as a compatibility widget; otherwise it is + handled normally by \QD. + + \sa isCompat() +*/ + +/*! + \fn virtual bool QDesignerWidgetDataBaseItemInterface::isContainer() const = 0 + + Returns true if this widget is intended to be used to hold other widgets; otherwise returns + false. + + \sa setContainer() +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setContainer(bool container) = 0 + + If \a container is true, the widget can be used to hold other widgets in \QD; otherwise + \QD will refuse to let the user place other widgets inside it. + + \sa isContainer() +*/ + +/*! + \fn virtual bool QDesignerWidgetDataBaseItemInterface::isCustom() const = 0 + + Returns true if the widget is a custom widget; otherwise return false if it is a standard + Qt widget. + + \sa setCustom() +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setCustom(bool custom) = 0 + + If \a custom is true, the widget is handled specially by \QD; otherwise it is handled as + a standard Qt widget. + + \sa isCustom() +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::pluginPath() const = 0 + + Returns the path to use for the widget plugin. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setPluginPath(const QString &path) = 0 +*/ + +/*! + \fn virtual bool QDesignerWidgetDataBaseItemInterface::isPromoted() const = 0 + + Returns true if the widget is promoted; otherwise returns false. + + Promoted widgets are those that represent custom widgets, but which are represented in + \QD by either standard Qt widgets or readily-available custom widgets. + + \sa setPromoted() +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setPromoted(bool promoted) = 0 + + If \a promoted is true, the widget is handled as a promoted widget by \QD and will use + a placeholder widget to represent it; otherwise it is handled as a standard widget. + + \sa isPromoted() +*/ + +/*! + \fn virtual QString QDesignerWidgetDataBaseItemInterface::extends() const = 0 + + Returns the name of the widget that the item extends. +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setExtends(const QString &s) = 0 +*/ + +/*! + \fn virtual void QDesignerWidgetDataBaseItemInterface::setDefaultPropertyValues(const QList &list) = 0 + + Sets the default property values for the widget to the given \a list. +*/ + +/*! + \fn virtual QList QDesignerWidgetDataBaseItemInterface::defaultPropertyValues() const = 0 + + Returns a list of default values to be used as properties for the item. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractwidgetdatabase.h b/designer/lib/sdk/abstractwidgetdatabase.h new file mode 100644 index 0000000..5c0a750 --- /dev/null +++ b/designer/lib/sdk/abstractwidgetdatabase.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTWIDGETDATABASE_H +#define ABSTRACTWIDGETDATABASE_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIcon; +class QString; +class QDesignerFormEditorInterface; +class QDebug; + +class QDesignerWidgetDataBaseItemInterface +{ +public: + virtual ~QDesignerWidgetDataBaseItemInterface() {} + + virtual QString name() const = 0; + virtual void setName(const QString &name) = 0; + + virtual QString group() const = 0; + virtual void setGroup(const QString &group) = 0; + + virtual QString toolTip() const = 0; + virtual void setToolTip(const QString &toolTip) = 0; + + virtual QString whatsThis() const = 0; + virtual void setWhatsThis(const QString &whatsThis) = 0; + + virtual QString includeFile() const = 0; + virtual void setIncludeFile(const QString &includeFile) = 0; + + virtual QIcon icon() const = 0; + virtual void setIcon(const QIcon &icon) = 0; + + virtual bool isCompat() const = 0; + virtual void setCompat(bool compat) = 0; + + virtual bool isContainer() const = 0; + virtual void setContainer(bool container) = 0; + + virtual bool isCustom() const = 0; + virtual void setCustom(bool custom) = 0; + + virtual QString pluginPath() const = 0; + virtual void setPluginPath(const QString &path) = 0; + + virtual bool isPromoted() const = 0; + virtual void setPromoted(bool b) = 0; + + virtual QString extends() const = 0; + virtual void setExtends(const QString &s) = 0; + + virtual void setDefaultPropertyValues(const QList &list) = 0; + virtual QList defaultPropertyValues() const = 0; +}; + +class QDESIGNER_SDK_EXPORT QDesignerWidgetDataBaseInterface: public QObject +{ + Q_OBJECT +public: + QDesignerWidgetDataBaseInterface(QObject *parent = 0); + virtual ~QDesignerWidgetDataBaseInterface(); + + virtual int count() const; + virtual QDesignerWidgetDataBaseItemInterface *item(int index) const; + + virtual int indexOf(QDesignerWidgetDataBaseItemInterface *item) const; + virtual void insert(int index, QDesignerWidgetDataBaseItemInterface *item); + virtual void append(QDesignerWidgetDataBaseItemInterface *item); + + virtual int indexOfObject(QObject *object, bool resolveName = true) const; + virtual int indexOfClassName(const QString &className, bool resolveName = true) const; + + virtual QDesignerFormEditorInterface *core() const; + + bool isContainer(QObject *object, bool resolveName = true) const; + bool isCustom(QObject *object, bool resolveName = true) const; + +Q_SIGNALS: + void changed(); + +protected: + QList m_items; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTWIDGETDATABASE_H diff --git a/designer/lib/sdk/abstractwidgetfactory.cpp b/designer/lib/sdk/abstractwidgetfactory.cpp new file mode 100644 index 0000000..7c1769a --- /dev/null +++ b/designer/lib/sdk/abstractwidgetfactory.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "abstractformeditor.h" +#include "abstractwidgetdatabase.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerWidgetFactoryInterface + \brief The QDesignerWidgetFactoryInterface class provides an interface that is used to control + the widget factory used by Qt Designer. + \inmodule QtDesigner + \internal +*/ + +/*! + \fn QDesignerWidgetFactoryInterface::QDesignerWidgetFactoryInterface(QObject *parent) + + Constructs an interface to a widget factory with the given \a parent. +*/ +QDesignerWidgetFactoryInterface::QDesignerWidgetFactoryInterface(QObject *parent) + : QObject(parent) +{ +} + +/*! + \fn virtual QDesignerWidgetFactoryInterface::~QDesignerWidgetFactoryInterface() +*/ +QDesignerWidgetFactoryInterface::~QDesignerWidgetFactoryInterface() +{ +} + +/*! + \fn virtual QDesignerFormEditorInterface *QDesignerWidgetFactoryInterface::core() const = 0 + + Returns the core form editor interface associated with this interface. +*/ + +/*! + \fn virtual QWidget* QDesignerWidgetFactoryInterface::containerOfWidget(QWidget *child) const = 0 + + Returns the widget that contains the specified \a child widget. +*/ + +/*! + \fn virtual QWidget* QDesignerWidgetFactoryInterface::widgetOfContainer(QWidget *container) const = 0 + + +*/ + +/*! + \fn virtual QWidget *QDesignerWidgetFactoryInterface::createWidget(const QString &name, QWidget *parent) const = 0 + + Returns a new widget with the given \a name and \a parent widget. If no parent is specified, + the widget created will be a top-level widget. +*/ + +/*! + \fn virtual QLayout *QDesignerWidgetFactoryInterface::createLayout(QWidget *widget, QLayout *layout, int type) const = 0 + + Returns a new layout of the specified \a type for the given \a widget or \a layout. +*/ + +/*! + \fn virtual bool QDesignerWidgetFactoryInterface::isPassiveInteractor(QWidget *widget) = 0 +*/ + +/*! + \fn virtual void QDesignerWidgetFactoryInterface::initialize(QObject *object) const = 0 +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/abstractwidgetfactory.h b/designer/lib/sdk/abstractwidgetfactory.h new file mode 100644 index 0000000..4f21b80 --- /dev/null +++ b/designer/lib/sdk/abstractwidgetfactory.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTWIDGETFACTORY_H +#define ABSTRACTWIDGETFACTORY_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QWidget; +class QLayout; + +class QDESIGNER_SDK_EXPORT QDesignerWidgetFactoryInterface: public QObject +{ + Q_OBJECT +public: + QDesignerWidgetFactoryInterface(QObject *parent = 0); + virtual ~QDesignerWidgetFactoryInterface(); + + virtual QDesignerFormEditorInterface *core() const = 0; + + virtual QWidget* containerOfWidget(QWidget *w) const = 0; + virtual QWidget* widgetOfContainer(QWidget *w) const = 0; + + virtual QWidget *createWidget(const QString &name, QWidget *parentWidget = 0) const = 0; + virtual QLayout *createLayout(QWidget *widget, QLayout *layout, int type) const = 0; + + virtual bool isPassiveInteractor(QWidget *widget) = 0; + virtual void initialize(QObject *object) const = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTWIDGETFACTORY_H diff --git a/designer/lib/sdk/dynamicpropertysheet.h b/designer/lib/sdk/dynamicpropertysheet.h new file mode 100644 index 0000000..470449d --- /dev/null +++ b/designer/lib/sdk/dynamicpropertysheet.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DYNAMICPROPERTYSHEET_H +#define DYNAMICPROPERTYSHEET_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QString; // FIXME: fool syncqt + +class QDesignerDynamicPropertySheetExtension +{ +public: + virtual ~QDesignerDynamicPropertySheetExtension() {} + + virtual bool dynamicPropertiesAllowed() const = 0; + virtual int addDynamicProperty(const QString &propertyName, const QVariant &value) = 0; + virtual bool removeDynamicProperty(int index) = 0; + virtual bool isDynamicProperty(int index) const = 0; + virtual bool canAddDynamicProperty(const QString &propertyName) const = 0; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerDynamicPropertySheetExtension, "com.trolltech.Qt.Designer.DynamicPropertySheet") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DYNAMICPROPERTYSHEET_H diff --git a/designer/lib/sdk/dynamicpropertysheet.qdoc b/designer/lib/sdk/dynamicpropertysheet.qdoc new file mode 100644 index 0000000..b813c16 --- /dev/null +++ b/designer/lib/sdk/dynamicpropertysheet.qdoc @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerDynamicPropertySheetExtension + + \brief The QDesignerDynamicPropertySheetExtension class allows you to + manipulate a widget's dynamic properties in Qt Designer's property editor. + + \sa QDesignerPropertySheetExtension, {QObject#Dynamic Properties}{Dynamic Properties} + + \inmodule QtDesigner + \since 4.3 +*/ + +/*! + \fn QDesignerDynamicPropertySheetExtension::~QDesignerDynamicPropertySheetExtension() + + Destroys the dynamic property sheet extension. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::dynamicPropertiesAllowed() const + + Returns true if the widget supports dynamic properties; otherwise returns false. +*/ + +/*! + \fn int QDesignerDynamicPropertySheetExtension::addDynamicProperty(const QString &propertyName, const QVariant &value) + + Adds a dynamic property named \a propertyName and sets its value to \a value. + Returns the index of the property if it was added successfully; otherwise returns -1 to + indicate failure. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::removeDynamicProperty(int index) + + Removes the dynamic property at the given \a index. + Returns true if the operation succeeds; otherwise returns false. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::isDynamicProperty(int index) const + + Returns true if the property at the given \a index is a dynamic property; otherwise + returns false. +*/ + +/*! + \fn bool QDesignerDynamicPropertySheetExtension::canAddDynamicProperty(const QString &propertyName) const + + Returns true if \a propertyName is a valid, unique name for a dynamic + property; otherwise returns false. + +*/ diff --git a/designer/lib/sdk/extrainfo.cpp b/designer/lib/sdk/extrainfo.cpp new file mode 100644 index 0000000..32d35a6 --- /dev/null +++ b/designer/lib/sdk/extrainfo.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "extrainfo.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerExtraInfoExtension + \brief The QDesignerExtraInfoExtension class provides extra information about a widget in + Qt Designer. + \inmodule QtDesigner + \internal +*/ + +/*! + Returns the path to the working directory used by this extension.*/ +QString QDesignerExtraInfoExtension::workingDirectory() const +{ + return m_workingDirectory; +} + +/*! + Sets the path to the working directory used by the extension to \a workingDirectory.*/ +void QDesignerExtraInfoExtension::setWorkingDirectory(const QString &workingDirectory) +{ + m_workingDirectory = workingDirectory; +} + +/*! + \fn virtual QDesignerExtraInfoExtension::~QDesignerExtraInfoExtension() + + Destroys the extension. +*/ + +/*! + \fn virtual QDesignerFormEditorInterface *QDesignerExtraInfoExtension::core() const = 0 + + \omit + ### Description required + \endomit +*/ + +/*! + \fn virtual QWidget *QDesignerExtraInfoExtension::widget() const = 0 + + Returns the widget described by this extension. +*/ + +/*! + \fn virtual bool QDesignerExtraInfoExtension::saveUiExtraInfo(DomUI *ui) = 0 + + Saves the information about the user interface specified by \a ui, and returns true if + successful; otherwise returns false. +*/ + +/*! + \fn virtual bool QDesignerExtraInfoExtension::loadUiExtraInfo(DomUI *ui) = 0 + + Loads extra information about the user interface specified by \a ui, and returns true if + successful; otherwise returns false. +*/ + +/*! + \fn virtual bool QDesignerExtraInfoExtension::saveWidgetExtraInfo(DomWidget *widget) = 0 + + Saves the information about the specified \a widget, and returns true if successful; + otherwise returns false. +*/ + +/*! + \fn virtual bool QDesignerExtraInfoExtension::loadWidgetExtraInfo(DomWidget *widget) = 0 + + Loads extra information about the specified \a widget, and returns true if successful; + otherwise returns false. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/extrainfo.h b/designer/lib/sdk/extrainfo.h new file mode 100644 index 0000000..0fefbec --- /dev/null +++ b/designer/lib/sdk/extrainfo.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EXTRAINFO_H +#define EXTRAINFO_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DomWidget; +class DomUI; +class QWidget; + +class QDesignerFormEditorInterface; + +class QDESIGNER_SDK_EXPORT QDesignerExtraInfoExtension +{ +public: + virtual ~QDesignerExtraInfoExtension() {} + + virtual QDesignerFormEditorInterface *core() const = 0; + virtual QWidget *widget() const = 0; + + virtual bool saveUiExtraInfo(DomUI *ui) = 0; + virtual bool loadUiExtraInfo(DomUI *ui) = 0; + + virtual bool saveWidgetExtraInfo(DomWidget *ui_widget) = 0; + virtual bool loadWidgetExtraInfo(DomWidget *ui_widget) = 0; + + QString workingDirectory() const; + void setWorkingDirectory(const QString &workingDirectory); + +private: + QString m_workingDirectory; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerExtraInfoExtension, "com.trolltech.Qt.Designer.ExtraInfo.2") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // EXTRAINFO_H diff --git a/designer/lib/sdk/layoutdecoration.h b/designer/lib/sdk/layoutdecoration.h new file mode 100644 index 0000000..f2ca116 --- /dev/null +++ b/designer/lib/sdk/layoutdecoration.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LAYOUTDECORATION_H +#define LAYOUTDECORATION_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QPoint; +class QLayoutItem; +class QWidget; +class QRect; +class QLayout; + +class QDesignerLayoutDecorationExtension +{ +public: + enum InsertMode + { + InsertWidgetMode, + InsertRowMode, + InsertColumnMode + }; + + virtual ~QDesignerLayoutDecorationExtension() {} + + virtual QList widgets(QLayout *layout) const = 0; + + virtual QRect itemInfo(int index) const = 0; + virtual int indexOf(QWidget *widget) const = 0; + virtual int indexOf(QLayoutItem *item) const = 0; + + virtual InsertMode currentInsertMode() const = 0; + virtual int currentIndex() const = 0; + virtual QPair currentCell() const = 0; + virtual void insertWidget(QWidget *widget, const QPair &cell) = 0; + virtual void removeWidget(QWidget *widget) = 0; + + virtual void insertRow(int row) = 0; + virtual void insertColumn(int column) = 0; + virtual void simplify() = 0; + + virtual int findItemAt(const QPoint &pos) const = 0; + virtual int findItemAt(int row, int column) const = 0; // atm only for grid. + + virtual void adjustIndicator(const QPoint &pos, int index) = 0; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerLayoutDecorationExtension, "com.trolltech.Qt.Designer.LayoutDecoration") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // LAYOUTDECORATION_H diff --git a/designer/lib/sdk/layoutdecoration.qdoc b/designer/lib/sdk/layoutdecoration.qdoc new file mode 100644 index 0000000..1b38975 --- /dev/null +++ b/designer/lib/sdk/layoutdecoration.qdoc @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerLayoutDecorationExtension + \brief The QDesignerLayoutDecorationExtension class provides an extension to a layout in \QD. + \inmodule QtDesigner + \internal +*/ + +/*! + \enum QDesignerLayoutDecorationExtension::InsertMode + + This enum describes the modes that are used to insert items into a layout. + + \value InsertWidgetMode Widgets are inserted into empty cells in a layout. + \value InsertRowMode Whole rows are inserted into a vertical or grid layout. + \value InsertColumnMode Whole columns are inserted into a horizontal or grid layout. +*/ + +/*! + \fn virtual QDesignerLayoutDecorationExtension::~QDesignerLayoutDecorationExtension() + + Destroys the extension. +*/ + +/*! + \fn virtual QList QDesignerLayoutDecorationExtension::widgets(QLayout *layout) const + + Returns the widgets that are managed by the given \a layout. + + \sa insertWidget(), removeWidget() +*/ + +/*! + \fn QRect QDesignerLayoutDecorationExtension::itemInfo(int index) const + + Returns the rectangle covered by the item at the given \a index in the layout. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::indexOf(QWidget *widget) const + + Returns the index of the specified \a widget in the layout. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::indexOf(QLayoutItem *item) const + + Returns the index of the specified layout \a item. +*/ + +/*! + \fn QDesignerLayoutDecorationExtension::InsertMode QDesignerLayoutDecorationExtension::currentInsertMode() const + + Returns the current insertion mode. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::currentIndex() const + + Returns the current index in the layout. +*/ + +/*! + \fn QPair QDesignerLayoutDecorationExtension::currentCell() const + + Returns a pair containing the row and column of the current cell in the layout. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertWidget(QWidget *widget, const QPair &cell) + + Inserts the given \a widget into the specified \a cell in the layout. + + \sa removeWidget() +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::removeWidget(QWidget *widget) + + Removes the specified \a widget from the layout. + + \sa insertWidget() +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertRow(int row) + + Inserts a new row into the form at the position specified by \a row. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::insertColumn(int column) + + Inserts a new column into the form at the position specified by \a column. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::simplify() + + Simplifies the layout by removing unnecessary empty rows and columns, and by changing the + number of rows or columns spanned by widgets. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::findItemAt(const QPoint &position) const + + Returns the index of the item in the layout that covers the given \a position. +*/ + +/*! + \fn int QDesignerLayoutDecorationExtension::findItemAt(int row, int column) const + + Returns the item in the layout that occupies the specified \a row and \a column in the layout. + + Currently, this only applies to grid layouts. +*/ + +/*! + \fn void QDesignerLayoutDecorationExtension::adjustIndicator(const QPoint &position, int index) + + Adjusts the indicator for the item specified by \a index so that + it lies at the given \a position on the form. +*/ diff --git a/designer/lib/sdk/membersheet.h b/designer/lib/sdk/membersheet.h new file mode 100644 index 0000000..6c49aab --- /dev/null +++ b/designer/lib/sdk/membersheet.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MEMBERSHEET_H +#define MEMBERSHEET_H + +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QString; // FIXME: fool syncqt + +class QDesignerMemberSheetExtension +{ +public: + virtual ~QDesignerMemberSheetExtension() {} + + virtual int count() const = 0; + + virtual int indexOf(const QString &name) const = 0; + + virtual QString memberName(int index) const = 0; + virtual QString memberGroup(int index) const = 0; + virtual void setMemberGroup(int index, const QString &group) = 0; + + virtual bool isVisible(int index) const = 0; + virtual void setVisible(int index, bool b) = 0; + + virtual bool isSignal(int index) const = 0; + virtual bool isSlot(int index) const = 0; + + virtual bool inheritedFromWidget(int index) const = 0; + + virtual QString declaredInClass(int index) const = 0; + + virtual QString signature(int index) const = 0; + virtual QList parameterTypes(int index) const = 0; + virtual QList parameterNames(int index) const = 0; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerMemberSheetExtension, "com.trolltech.Qt.Designer.MemberSheet") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // MEMBERSHEET_H diff --git a/designer/lib/sdk/membersheet.qdoc b/designer/lib/sdk/membersheet.qdoc new file mode 100644 index 0000000..7185579 --- /dev/null +++ b/designer/lib/sdk/membersheet.qdoc @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerMemberSheetExtension + + \brief The QDesignerMemberSheetExtension class allows you to + manipulate a widget's member functions which is displayed when + configuring connections using Qt Designer's mode for editing + signals and slots. + + \inmodule QtDesigner + + QDesignerMemberSheetExtension is a collection of functions that is + typically used to query a widget's member functions, and to + manipulate the member functions' appearance in \QD's signals and + slots editing mode. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 2 + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor in the + example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's parameter. + + The member sheet (and any other extension), can be retrieved by + querying \QD's extension manager using the qt_extension() + function. When you want to release the extension, you only need to + delete the pointer. + + All widgets have a default member sheet used in \QD's signals and + slots editing mode with the widget's member functions. But + QDesignerMemberSheetExtension also provides an interface for + creating custom member sheet extensions. + + \warning \QD uses the QDesignerMemberSheetExtension to facilitate + the signal and slot editing mode. Whenever a connection between + two widgets is requested, \QD will query for the widgets' member + sheet extensions. If a widget has an implemented member sheet + extension, this extension will override the default member sheet. + + To create a member sheet extension, your extension class must + inherit from both QObject and QDesignerMemberSheetExtension. Then, + since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 3 + + This enables \QD to use qobject_cast() to query for + supported interfaces using nothing but a QObject pointer. + + In \QD the extensions are not created until they are + required. For that reason, when implementing a member sheet + extension, you must also create a QExtensionFactory, i.e a class + that is able to make an instance of your extension, and register + it using \QD's \l {QExtensionManager}{extension manager}. + + When a widget's member sheet extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a member sheet + extension for that widget, is found. This factory will then make + an instance of the extension. If no such factory is found, \QD + will use the default member sheet. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and + QDesignerTaskMenuExtension. \QD's behavior is the same whether the + requested extension is associated with a multi page container, a + member sheet, a property sheet or a task menu. + + The QExtensionFactory class provides a standard extension + factory, and can also be used as an interface for custom + extension factories. You can either create a new + QExtensionFactory and reimplement the + QExtensionFactory::createExtension() function. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 4 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a member sheet extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 5 + + For a complete example using an extension class, see \l + {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerMemberSheetExtension::~QDesignerMemberSheetExtension() + + Destroys the member sheet extension. +*/ + +/*! + \fn int QDesignerMemberSheetExtension::count() const + + Returns the extension's number of member functions. +*/ + +/*! + \fn int QDesignerMemberSheetExtension::indexOf(const QString &name) const + + Returns the index of the member function specified by the given \a + name. + + \sa memberName() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::memberName(int index) const + + Returns the name of the member function with the given \a index. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::memberGroup(int index) const + + Returns the name of the member group specified for the function + with the given \a index. + + \sa indexOf(), setMemberGroup() +*/ + +/*! + \fn void QDesignerMemberSheetExtension::setMemberGroup(int index, const QString &group) + + Sets the member group of the member function with the given \a + index, to \a group. + + \sa indexOf(), memberGroup() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::isVisible(int index) const + + Returns true if the member function with the given \a index is + visible in \QD's signal and slot editor, otherwise false. + + \sa indexOf(), setVisible() +*/ + +/*! + \fn void QDesignerMemberSheetExtension::setVisible(int index, bool visible) + + If \a visible is true, the member function with the given \a index + is visible in \QD's signals and slots editing mode; otherwise the + member function is hidden. + + \sa indexOf(), isVisible() +*/ + +/*! + \fn virtual bool QDesignerMemberSheetExtension::isSignal(int index) const + + Returns true if the member function with the given \a index is a + signal, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::isSlot(int index) const + + Returns true if the member function with the given \a index is a + slot, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn bool QDesignerMemberSheetExtension::inheritedFromWidget(int index) const + + Returns true if the member function with the given \a index is + inherited from QWidget, otherwise false. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::declaredInClass(int index) const + + Returns the name of the class in which the member function with + the given \a index is declared. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerMemberSheetExtension::signature(int index) const + + Returns the signature of the member function with the given \a + index. + + \sa indexOf() +*/ + +/*! + \fn QList QDesignerMemberSheetExtension::parameterTypes(int index) const + + Returns the parameter types of the member function with the given + \a index, as a QByteArray list. + + \sa indexOf(), parameterNames() +*/ + +/*! + \fn QList QDesignerMemberSheetExtension::parameterNames(int index) const + + Returns the parameter names of the member function with the given + \a index, as a QByteArray list. + + \sa indexOf(), parameterTypes() +*/ diff --git a/designer/lib/sdk/propertysheet.h b/designer/lib/sdk/propertysheet.h new file mode 100644 index 0000000..4ed7053 --- /dev/null +++ b/designer/lib/sdk/propertysheet.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPERTYSHEET_H +#define PROPERTYSHEET_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QVariant; + +class QDesignerPropertySheetExtension +{ +public: + virtual ~QDesignerPropertySheetExtension() {} + + virtual int count() const = 0; + + virtual int indexOf(const QString &name) const = 0; + + virtual QString propertyName(int index) const = 0; + virtual QString propertyGroup(int index) const = 0; + virtual void setPropertyGroup(int index, const QString &group) = 0; + + virtual bool hasReset(int index) const = 0; + virtual bool reset(int index) = 0; + + virtual bool isVisible(int index) const = 0; + virtual void setVisible(int index, bool b) = 0; + + virtual bool isAttribute(int index) const = 0; + virtual void setAttribute(int index, bool b) = 0; + + virtual QVariant property(int index) const = 0; + virtual void setProperty(int index, const QVariant &value) = 0; + + virtual bool isChanged(int index) const = 0; + virtual void setChanged(int index, bool changed) = 0; + +}; + +Q_DECLARE_EXTENSION_INTERFACE(QDesignerPropertySheetExtension, + "com.trolltech.Qt.Designer.PropertySheet") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // PROPERTYSHEET_H diff --git a/designer/lib/sdk/propertysheet.qdoc b/designer/lib/sdk/propertysheet.qdoc new file mode 100644 index 0000000..8c49087 --- /dev/null +++ b/designer/lib/sdk/propertysheet.qdoc @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerPropertySheetExtension + + \brief The QDesignerPropertySheetExtension class allows you to + manipulate a widget's properties which is displayed in Qt + Designer's property editor. + + \sa QDesignerDynamicPropertySheetExtension + + \inmodule QtDesigner + + QDesignerPropertySheetExtension provides a collection of functions that + are typically used to query a widget's properties, and to + manipulate the properties' appearance in the property editor. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 15 + + Note that if you change the value of a property using the + QDesignerPropertySheetExtension::setProperty() function, the undo + stack is not updated. To ensure that a property's value can be + reverted using the undo stack, you must use the + QDesignerFormWindowCursorInterface::setProperty() function, or its + buddy \l + {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, + instead. + + When implementing a custom widget plugin, a pointer to \QD's + current QDesignerFormEditorInterface object (\c formEditor in the + example above) is provided by the + QDesignerCustomWidgetInterface::initialize() function's parameter. + + The property sheet, or any other extension, can be retrieved by + querying \QD's extension manager using the qt_extension() + function. When you want to release the extension, you only need to + delete the pointer. + + All widgets have a default property sheet which populates \QD's + property editor with the widget's properties (i.e the ones defined + with the Q_PROPERTY() macro). But QDesignerPropertySheetExtension + also provides an interface for creating custom property sheet + extensions. + + \warning \QD uses the QDesignerPropertySheetExtension to feed its + property editor. Whenever a widget is selected in its workspace, + \QD will query for the widget's property sheet extension. If the + selected widget has an implemented property sheet extension, this + extension will override the default property sheet. + + To create a property sheet extension, your extension class must + inherit from both QObject and + QDesignerPropertySheetExtension. Then, since we are implementing + an interface, we must ensure that it's made known to the meta + object system using the Q_INTERFACES() macro: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 16 + + This enables \QD to use qobject_cast() to query for supported + interfaces using nothing but a QObject pointer. + + In \QD the extensions are not created until they are + required. For that reason, when implementing a property sheet + extension, you must also create a QExtensionFactory, i.e a class + that is able to make an instance of your extension, and register + it using \QD's \l {QExtensionManager}{extension manager}. + + When a property sheet extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a property + sheet extension for the selected widget, is found. This factory + will then make an instance of the extension. If no such factory + can be found, \QD will use the default property sheet. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. Qt + Designer's behavior is the same whether the requested extension is + associated with a multi page container, a member sheet, a property + sheet or a task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 17 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a property sheet extension extension as well. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 18 + + For a complete example using an extension class, see the \l + {designer/taskmenuextension}{Task Menu Extension example}. The + example shows how to create a custom widget plugin for Qt + Designer, and how to to use the QDesignerTaskMenuExtension class + to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerPropertySheetExtension::~QDesignerPropertySheetExtension() + + Destroys the property sheet extension. +*/ + +/*! + \fn int QDesignerPropertySheetExtension::count() const + + Returns the selected widget's number of properties. +*/ + +/*! + \fn int QDesignerPropertySheetExtension::indexOf(const QString &name) const + + Returns the index for a given property \a name. + + \sa propertyName() +*/ + +/*! + \fn QString QDesignerPropertySheetExtension::propertyName(int index) const + + Returns the name of the property at the given \a index. + + \sa indexOf() +*/ + +/*! + \fn QString QDesignerPropertySheetExtension::propertyGroup(int index) const + + Returns the property group for the property at the given \a index. + + \QD's property editor supports property groups, i.e. sections of + related properties. A property can be related to a group using the + setPropertyGroup() function. The default group of any property is + the name of the class that defines it. For example, the + QObject::objectName property appears within the QObject property + group. + + \sa indexOf(), setPropertyGroup() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setPropertyGroup(int index, const QString &group) + + Sets the property group for the property at the given \a index to + \a group. + + Relating a property to a group makes it appear within that group's + section in the property editor. The default property group of any + property is the name of the class that defines it. For example, + the QObject::objectName property appears within the QObject + property group. + + \sa indexOf(), property(), propertyGroup() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::hasReset(int index) const + + Returns true if the property at the given \a index has a reset + button in \QD's property editor, otherwise false. + + \sa indexOf(), reset() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::reset(int index) + + Resets the value of the property at the given \a index, to the + default value. Returns true if a default value could be found, otherwise false. + + \sa indexOf(), hasReset(), isChanged() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isVisible(int index) const + + Returns true if the property at the given \a index is visible in + \QD's property editor, otherwise false. + + \sa indexOf(), setVisible() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setVisible(int index, bool visible) + + If \a visible is true, the property at the given \a index is + visible in \QD's property editor; otherwise the property is + hidden. + + \sa indexOf(), isVisible() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isAttribute(int index) const + + Returns true if the property at the given \a index is an attribute, + which will be \e excluded from the UI file, otherwise false. + + \sa indexOf(), setAttribute() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setAttribute(int index, bool attribute) + + If \a attribute is true, the property at the given \a index is + made an attribute which will be \e excluded from the UI file; + otherwise it will be included. + + \sa indexOf(), isAttribute() +*/ + +/*! + \fn QVariant QDesignerPropertySheetExtension::property(int index) const + + Returns the value of the property at the given \a index. + + \sa indexOf(), setProperty(), propertyGroup() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setProperty(int index, const QVariant &value) + + Sets the \a value of the property at the given \a index. + + \warning If you change the value of a property using this + function, the undo stack is not updated. To ensure that a + property's value can be reverted using the undo stack, you must + use the QDesignerFormWindowCursorInterface::setProperty() + function, or its buddy \l + {QDesignerFormWindowCursorInterface::setWidgetProperty()}{setWidgetProperty()}, + instead. + + \sa indexOf(), property(), propertyGroup() +*/ + +/*! + \fn bool QDesignerPropertySheetExtension::isChanged(int index) const + + Returns true if the value of the property at the given \a index + differs from the property's default value, otherwise false. + + \sa indexOf(), setChanged(), reset() +*/ + +/*! + \fn void QDesignerPropertySheetExtension::setChanged(int index, bool changed) + + Sets whether the property at the given \a index is different from + its default value, or not, depending on the \a changed parameter. + + \sa indexOf(), isChanged() +*/ diff --git a/designer/lib/sdk/script.cpp b/designer/lib/sdk/script.cpp new file mode 100644 index 0000000..a8a2a9c --- /dev/null +++ b/designer/lib/sdk/script.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "script_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerScriptExtension + \brief The QDesignerScriptExtension class allows you to generate a + per-widget \l{QtScript} {Qt Script} snippet to be executed while + building the form. + + \internal + \inmodule QtDesigner + \since 4.3 + + On saving the form, the extension is queried for a script snippet + to be associated with the widget while saving the UI file. + This script is then run after creating the widget by \l uic or + QUiLoader. + + As opposed to \l QDesignerCustomWidgetInterface::codeTemplate(), + it allows for applying an internal state of the widget + that can be manipulated using \QD. + + Such a state might for example be the contents of a custom item view widget, + for which an editor is provided by the QDesignerTaskMenuExtension. + + While saving the form, the state is serialized as a QVariantMap of + \QD-supported properties, which is stored in the UI file. This is + handled by data() and setData(). + + For item view contents, there might be for example a key that determines + the number of items and other keys that contain the actual items following + a naming scheme (\c numItems, \c item1, \c item2, ...). + + On saving, script() is invoked, which should return a script snippet that + applies the state to the widget while building the form. + + \sa {Creating Custom Widgets for Qt Designer#Using Qt Script to Aid in Building Forms}{Creating Custom Widgets for Qt Designer}, QtScript +*/ + +/*! + Destroys the extension. +*/ + +QDesignerScriptExtension::~QDesignerScriptExtension() +{ +} + +/*! + \fn virtual QString QDesignerScriptExtension::script() const + + Returns a script snippet to be associated with the widget. +*/ + +/*! + \fn virtual QVariantMap QDesignerScriptExtension::data() const + + Returns a map of variants describing the internal state to be + stored in the UI file. +*/ + +/*! + \fn virtual void QDesignerScriptExtension::setData(const QVariantMap &data) + + Applies the internal state stored in \a data to the widget while loading a form. +*/ + +QT_END_NAMESPACE diff --git a/designer/lib/sdk/script_p.h b/designer/lib/sdk/script_p.h new file mode 100644 index 0000000..89308f8 --- /dev/null +++ b/designer/lib/sdk/script_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SCRIPT_H +#define SCRIPT_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QString; // FIXME: fool syncqt + +class QDESIGNER_SDK_EXPORT QDesignerScriptExtension +{ +public: + virtual ~QDesignerScriptExtension(); + + virtual QVariantMap data() const = 0; + virtual void setData(const QVariantMap &data) = 0; + + virtual QString script() const = 0; + +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerScriptExtension, "com.trolltech.Qt.Designer.Script") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // SCRIPT_H diff --git a/designer/lib/sdk/sdk.pri b/designer/lib/sdk/sdk.pri new file mode 100644 index 0000000..bc46a1e --- /dev/null +++ b/designer/lib/sdk/sdk.pri @@ -0,0 +1,58 @@ +# Input + +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/abstractformeditor.h \ + $$PWD/abstractintrospection_p.h \ + $$PWD/abstractsettings_p.h \ + $$PWD/abstractformeditorplugin.h \ + $$PWD/abstractresourcebrowser.h \ + $$PWD/abstractintegration.h \ + $$PWD/abstractpropertyeditor.h \ + $$PWD/abstractformwindow.h \ + $$PWD/abstractformwindowtool.h \ + $$PWD/abstractformwindowcursor.h \ + $$PWD/abstractformwindowmanager.h \ + $$PWD/abstractwidgetdatabase.h \ + $$PWD/abstractmetadatabase.h \ + $$PWD/abstractwidgetfactory.h \ + $$PWD/abstractobjectinspector.h \ + $$PWD/abstractactioneditor.h \ + $$PWD/abstractbrushmanager.h \ + $$PWD/abstracticoncache.h \ + $$PWD/abstractlanguage.h \ + $$PWD/abstractoptionspage_p.h \ + $$PWD/propertysheet.h \ + $$PWD/dynamicpropertysheet.h \ + $$PWD/membersheet.h \ + $$PWD/taskmenu.h \ + $$PWD/extrainfo.h \ + $$PWD/abstractwidgetbox.h \ + $$PWD/layoutdecoration.h \ + $$PWD/abstractdnditem.h \ + $$PWD/abstractpromotioninterface.h \ + $$PWD/abstractdialoggui_p.h \ + $$PWD/script_p.h \ + $$PWD/abstractnewformwidget_p.h + +SOURCES += $$PWD/abstractformeditor.cpp \ + $$PWD/abstractintrospection.cpp \ + $$PWD/abstractformeditorplugin.cpp \ + $$PWD/abstractresourcebrowser.cpp \ + $$PWD/abstractintegration.cpp \ + $$PWD/abstractpropertyeditor.cpp \ + $$PWD/abstractformwindow.cpp \ + $$PWD/abstractformwindowtool.cpp \ + $$PWD/abstractformwindowcursor.cpp \ + $$PWD/abstractformwindowmanager.cpp \ + $$PWD/abstractwidgetdatabase.cpp \ + $$PWD/abstractmetadatabase.cpp \ + $$PWD/abstractwidgetfactory.cpp \ + $$PWD/abstractobjectinspector.cpp \ + $$PWD/abstractactioneditor.cpp \ + $$PWD/abstractwidgetbox.cpp \ + $$PWD/extrainfo.cpp \ + $$PWD/abstractpromotioninterface.cpp \ + $$PWD/abstractdialoggui.cpp \ + $$PWD/script.cpp \ + $$PWD/abstractnewformwidget.cpp diff --git a/designer/lib/sdk/sdk_global.h b/designer/lib/sdk/sdk_global.h new file mode 100644 index 0000000..0c7560d --- /dev/null +++ b/designer/lib/sdk/sdk_global.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SDK_GLOBAL_H +#define SDK_GLOBAL_H + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#define QDESIGNER_SDK_EXTERN Q_DECL_EXPORT +#define QDESIGNER_SDK_IMPORT Q_DECL_IMPORT + +#ifdef QT_DESIGNER_STATIC +# define QDESIGNER_SDK_EXPORT +#elif defined(QDESIGNER_SDK_LIBRARY) +# define QDESIGNER_SDK_EXPORT QDESIGNER_SDK_EXTERN +#else +# define QDESIGNER_SDK_EXPORT QDESIGNER_SDK_IMPORT +#endif + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // SDK_GLOBAL_H diff --git a/designer/lib/sdk/taskmenu.h b/designer/lib/sdk/taskmenu.h new file mode 100644 index 0000000..9a1ea05 --- /dev/null +++ b/designer/lib/sdk/taskmenu.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TASKMENU_H +#define TASKMENU_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAction; + +class QDesignerTaskMenuExtension +{ +public: + virtual ~QDesignerTaskMenuExtension() {} + + virtual QAction *preferredEditAction() const; + + virtual QList taskActions() const = 0; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerTaskMenuExtension, "com.trolltech.Qt.Designer.TaskMenu") + + +inline QAction *QDesignerTaskMenuExtension::preferredEditAction() const +{ return 0; } + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // TASKMENU_H diff --git a/designer/lib/sdk/taskmenu.qdoc b/designer/lib/sdk/taskmenu.qdoc new file mode 100644 index 0000000..23ea1b2 --- /dev/null +++ b/designer/lib/sdk/taskmenu.qdoc @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerTaskMenuExtension + \brief The QDesignerTaskMenuExtension class allows you to add custom + menu entries to Qt Designer's task menu. + \inmodule QtDesigner + + QDesignerTaskMenuExtension provides an interface for creating + custom task menu extensions. It is typically used to create task + menu entries that are specific to a plugin in \QD. + + \QD uses the QDesignerTaskMenuExtension to feed its task + menu. Whenever a task menu is requested, \QD will query + for the selected widget's task menu extension. + + \image taskmenuextension-example-faded.png + + A task menu extension is a collection of QActions. The actions + appear as entries in the task menu when the plugin with the + specified extension is selected. The image above shows the custom + \gui {Edit State...} action which appears in addition to \QD's + default task menu entries: \gui Cut, \gui Copy, \gui Paste etc. + + To create a custom task menu extension, your extension class must + inherit from both QObject and QDesignerTaskMenuExtension. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 9 + + Since we are implementing an interface, we must ensure that it + is made known to the meta-object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + You must reimplement the taskActions() function to return a list + of actions that will be included in \QD task menu. Optionally, you + can reimplement the preferredEditAction() function to set the + action that is invoked when selecting your plugin and pressing + \key F2. The preferred edit action must be one of the actions + returned by taskActions() and, if it's not defined, pressing the + \key F2 key will simply be ignored. + + In \QD, extensions are not created until they are required. A + task menu extension, for example, is created when you click the + right mouse button over a widget in \QD's workspace. For that + reason you must also construct an extension factory, using either + QExtensionFactory or a subclass, and register it using \QD's + \l {QExtensionManager}{extension manager}. + + When a task menu extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until it finds one that is able to create a task menu + extension for the selected widget. This factory will then make an + instance of the extension. + + There are four available types of extensions in \QD: + QDesignerContainerExtension, QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension, and QDesignerTaskMenuExtension. + \QD's behavior is the same whether the requested extension is + associated with a container, a member sheet, a property sheet or a + task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 10 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a task menu extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 11 + + For a complete example using the QDesignerTaskMenuExtension class, + see the \l {designer/taskmenuextension}{Task Menu Extension + example}. The example shows how to create a custom widget plugin + for \QD, and how to to use the QDesignerTaskMenuExtension + class to add custom items to \QD's task menu. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerTaskMenuExtension::~QDesignerTaskMenuExtension() + + Destroys the task menu extension. +*/ + +/*! + \fn QAction *QDesignerTaskMenuExtension::preferredEditAction() const + + Returns the action that is invoked when selecting a plugin with + the specified extension and pressing \key F2. + + The action must be one of the actions returned by taskActions(). +*/ + +/*! + \fn QList QDesignerTaskMenuExtension::taskActions() const + + Returns the task menu extension as a list of actions which will be + included in \QD's task menu when a plugin with the specified + extension is selected. + + The function must be reimplemented to add actions to the list. +*/ diff --git a/designer/lib/shared/actioneditor.cpp b/designer/lib/shared/actioneditor.cpp new file mode 100644 index 0000000..e5860fd --- /dev/null +++ b/designer/lib/shared/actioneditor.cpp @@ -0,0 +1,824 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "actioneditor_p.h" +#include "filterwidget_p.h" +#include "actionrepository_p.h" +#include "iconloader_p.h" +#include "newactiondialog_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_objectinspector_p.h" +#include "qdesigner_utils_p.h" +#include "qsimpleresource_p.h" +#include "formwindowbase_p.h" +#include "qdesigner_taskmenu_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +static const char *actionEditorViewModeKey = "ActionEditorViewMode"; + +static const char *iconPropertyC = "icon"; +static const char *shortcutPropertyC = "shortcut"; +static const char *toolTipPropertyC = "toolTip"; +static const char *checkablePropertyC = "checkable"; +static const char *objectNamePropertyC = "objectName"; +static const char *textPropertyC = "text"; + +namespace qdesigner_internal { +//-------- ActionGroupDelegate +class ActionGroupDelegate: public QItemDelegate +{ +public: + ActionGroupDelegate(QObject *parent) + : QItemDelegate(parent) {} + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + QItemDelegate::paint(painter, option, index); + } + + virtual void drawFocus(QPainter * /*painter*/, const QStyleOptionViewItem &/*option*/, const QRect &/*rect*/) const {} +}; + +//-------- ActionEditor +ActionEditor::ActionEditor(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerActionEditorInterface(parent, flags), + m_core(core), + m_actionGroups(0), + m_actionView(new ActionView), + m_actionNew(new QAction(tr("New..."), this)), + m_actionEdit(new QAction(tr("Edit..."), this)), + m_actionNavigateToSlot(new QAction(tr("Go to slot..."), this)), + m_actionCopy(new QAction(tr("Copy"), this)), + m_actionCut(new QAction(tr("Cut"), this)), + m_actionPaste(new QAction(tr("Paste"), this)), + m_actionSelectAll(new QAction(tr("Select all"), this)), + m_actionDelete(new QAction(tr("Delete"), this)), + m_viewModeGroup(new QActionGroup(this)), + m_iconViewAction(0), + m_listViewAction(0), + m_filterWidget(0), + m_selectAssociatedWidgetsMapper(0) +{ + m_actionView->initialize(m_core); + m_actionView->setSelectionMode(QAbstractItemView::ExtendedSelection); + setWindowTitle(tr("Actions")); + + QVBoxLayout *l = new QVBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + + QToolBar *toolbar = new QToolBar; + toolbar->setIconSize(QSize(22, 22)); + toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + l->addWidget(toolbar); + // edit actions + QIcon documentNewIcon = QIcon::fromTheme("document-new", createIconSet(QLatin1String("filenew.png"))); + m_actionNew->setIcon(documentNewIcon); + m_actionNew->setEnabled(false); + connect(m_actionNew, SIGNAL(triggered()), this, SLOT(slotNewAction())); + toolbar->addAction(m_actionNew); + + connect(m_actionSelectAll, SIGNAL(triggered()), m_actionView, SLOT(selectAll())); + + m_actionCut->setEnabled(false); + connect(m_actionCut, SIGNAL(triggered()), this, SLOT(slotCut())); + QIcon editCutIcon = QIcon::fromTheme("edit-cut", createIconSet(QLatin1String("editcut.png"))); + m_actionCut->setIcon(editCutIcon); + + m_actionCopy->setEnabled(false); + connect(m_actionCopy, SIGNAL(triggered()), this, SLOT(slotCopy())); + QIcon editCopyIcon = QIcon::fromTheme("edit-copy", createIconSet(QLatin1String("editcopy.png"))); + m_actionCopy->setIcon(editCopyIcon); + toolbar->addAction(m_actionCopy); + + connect(m_actionPaste, SIGNAL(triggered()), this, SLOT(slotPaste())); + QIcon editPasteIcon = QIcon::fromTheme("edit-paste", createIconSet(QLatin1String("editpaste.png"))); + m_actionPaste->setIcon(editPasteIcon); + toolbar->addAction(m_actionPaste); + + m_actionEdit->setEnabled(false); + connect(m_actionEdit, SIGNAL(triggered()), this, SLOT(editCurrentAction())); + + connect(m_actionNavigateToSlot, SIGNAL(triggered()), this, SLOT(navigateToSlotCurrentAction())); + + QIcon editDeleteIcon = QIcon::fromTheme("edit-delete", createIconSet(QLatin1String("editdelete.png"))); + m_actionDelete->setIcon(editDeleteIcon); + m_actionDelete->setEnabled(false); + connect(m_actionDelete, SIGNAL(triggered()), this, SLOT(slotDelete())); + toolbar->addAction(m_actionDelete); + + // Toolbutton with menu containing action group for detailed/icon view. Steal the icons from the file dialog. + // + QMenu *configureMenu; + toolbar->addWidget(createConfigureMenuButton(tr("Configure Action Editor"), &configureMenu)); + + connect(m_viewModeGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotViewMode(QAction*))); + m_iconViewAction = m_viewModeGroup->addAction(tr("Icon View")); + m_iconViewAction->setData(QVariant(ActionView::IconView)); + m_iconViewAction->setCheckable(true); + m_iconViewAction->setIcon(style()->standardIcon (QStyle::SP_FileDialogListView)); + configureMenu->addAction(m_iconViewAction); + + m_listViewAction = m_viewModeGroup->addAction(tr("Detailed View")); + m_listViewAction->setData(QVariant(ActionView::DetailedView)); + m_listViewAction->setCheckable(true); + m_listViewAction->setIcon(style()->standardIcon (QStyle::SP_FileDialogDetailedView)); + configureMenu->addAction(m_listViewAction); + // filter + m_filterWidget = new FilterWidget(toolbar); + connect(m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(setFilter(QString))); + m_filterWidget->setEnabled(false); + toolbar->addWidget(m_filterWidget); + + // main layout + QSplitter *splitter = new QSplitter(Qt::Horizontal); + splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + splitter->addWidget(m_actionView); + l->addWidget(splitter); + +#if 0 // ### implement me + m_actionGroups = new QListWidget(splitter); + splitter->addWidget(m_actionGroups); + m_actionGroups->setItemDelegate(new ActionGroupDelegate(m_actionGroups)); + m_actionGroups->setMovement(QListWidget::Static); + m_actionGroups->setResizeMode(QListWidget::Fixed); + m_actionGroups->setIconSize(QSize(48, 48)); + m_actionGroups->setFlow(QListWidget::TopToBottom); + m_actionGroups->setViewMode(QListWidget::IconMode); + m_actionGroups->setWrapping(false); +#endif + + connect(m_actionView, SIGNAL(resourceImageDropped(QString,QAction*)), + this, SLOT(resourceImageDropped(QString,QAction*))); + + connect(m_actionView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentItemChanged(QAction*))); + // make it possible for vs integration to reimplement edit action dialog + connect(m_actionView, SIGNAL(activated(QAction*)), this, SIGNAL(itemActivated(QAction*))); + + connect(m_actionView,SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + + connect(m_actionView, SIGNAL(contextMenuRequested(QContextMenuEvent*,QAction*)), + this, SLOT(slotContextMenuRequested(QContextMenuEvent*,QAction*))); + + connect(this, SIGNAL(itemActivated(QAction*)), this, SLOT(editAction(QAction*))); + + restoreSettings(); + updateViewModeActions(); +} + +// Utility to create a configure button with menu for usage on toolbars +QToolButton *ActionEditor::createConfigureMenuButton(const QString &t, QMenu **ptrToMenu) +{ + QToolButton *configureButton = new QToolButton; + QAction *configureAction = new QAction(t, configureButton); + QIcon configureIcon = QIcon::fromTheme("document-properties", createIconSet(QLatin1String("configure.png"))); + configureAction->setIcon(configureIcon); + QMenu *configureMenu = new QMenu; + configureAction->setMenu(configureMenu); + configureButton->setDefaultAction(configureAction); + configureButton->setPopupMode(QToolButton::InstantPopup); + *ptrToMenu = configureMenu; + return configureButton; +} + +ActionEditor::~ActionEditor() +{ + saveSettings(); +} + +QAction *ActionEditor::actionNew() const +{ + return m_actionNew; +} + +QAction *ActionEditor::actionDelete() const +{ + return m_actionDelete; +} + +QDesignerFormWindowInterface *ActionEditor::formWindow() const +{ + return m_formWindow; +} + +void ActionEditor::setFormWindow(QDesignerFormWindowInterface *formWindow) +{ + if (formWindow != 0 && formWindow->mainContainer() == 0) + formWindow = 0; + + // we do NOT rely on this function to update the action editor + if (m_formWindow == formWindow) + return; + + if (m_formWindow != 0) { + const ActionList actionList = qFindChildren(m_formWindow->mainContainer()); + foreach (QAction *action, actionList) + disconnect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + } + + m_formWindow = formWindow; + + m_actionView->model()->clearActions(); + + m_actionEdit->setEnabled(false); + m_actionCopy->setEnabled(false); + m_actionCut->setEnabled(false); + m_actionDelete->setEnabled(false); + + if (!formWindow || !formWindow->mainContainer()) { + m_actionNew->setEnabled(false); + m_filterWidget->setEnabled(false); + return; + } + + m_actionNew->setEnabled(true); + m_filterWidget->setEnabled(true); + + const ActionList actionList = qFindChildren(formWindow->mainContainer()); + foreach (QAction *action, actionList) + if (!action->isSeparator() && core()->metaDataBase()->item(action) != 0) { + // Show unless it has a menu. However, listen for change on menu actions also as it might be removed + if (!action->menu()) + m_actionView->model()->addAction(action); + connect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + } + + setFilter(m_filter); +} + +void ActionEditor::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) +{ + const bool hasSelection = !selected.indexes().empty(); + m_actionCopy->setEnabled(hasSelection); + m_actionCut->setEnabled(hasSelection); + m_actionDelete->setEnabled(hasSelection); +} + +void ActionEditor::slotCurrentItemChanged(QAction *action) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const bool hasCurrentAction = action != 0; + m_actionEdit->setEnabled(hasCurrentAction); + + if (!action) { + fw->clearSelection(); + return; + } + + QDesignerObjectInspector *oi = qobject_cast(core()->objectInspector()); + + if (action->associatedWidgets().empty()) { + // Special case: action not in object tree. Deselect all and set in property editor + fw->clearSelection(false); + if (oi) + oi->clearSelection(); + core()->propertyEditor()->setObject(action); + } else { + if (oi) + oi->selectObject(action); + } +} + +void ActionEditor::slotActionChanged() +{ + QAction *action = qobject_cast(sender()); + Q_ASSERT(action != 0); + + ActionModel *model = m_actionView->model(); + const int row = model->findAction(action); + if (row == -1) { + if (action->menu() == 0) // action got its menu deleted, create item + model->addAction(action); + } else if (action->menu() != 0) { // action got its menu created, remove item + model->removeRow(row); + } else { + // action text or icon changed, update item + model->update(row); + } +} + +QDesignerFormEditorInterface *ActionEditor::core() const +{ + return m_core; +} + +QString ActionEditor::filter() const +{ + return m_filter; +} + +void ActionEditor::setFilter(const QString &f) +{ + m_filter = f; + m_actionView->filter(m_filter); +} + +// Set changed state of icon property, reset when icon is cleared +static void refreshIconPropertyChanged(const QAction *action, QDesignerPropertySheetExtension *sheet) +{ + sheet->setChanged(sheet->indexOf(QLatin1String(iconPropertyC)), !action->icon().isNull()); +} + +void ActionEditor::manageAction(QAction *action) +{ + action->setParent(formWindow()->mainContainer()); + core()->metaDataBase()->add(action); + + if (action->isSeparator() || action->menu() != 0) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), action); + sheet->setChanged(sheet->indexOf(QLatin1String(objectNamePropertyC)), true); + sheet->setChanged(sheet->indexOf(QLatin1String(textPropertyC)), true); + refreshIconPropertyChanged(action, sheet); + + m_actionView->setCurrentIndex(m_actionView->model()->addAction(action)); + connect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); +} + +void ActionEditor::unmanageAction(QAction *action) +{ + core()->metaDataBase()->remove(action); + action->setParent(0); + + disconnect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + + const int row = m_actionView->model()->findAction(action); + if (row != -1) + m_actionView->model()->remove(row); +} + +// Set an initial property and mark it as changed in the sheet +static void setInitialProperty(QDesignerPropertySheetExtension *sheet, const QString &name, const QVariant &value) +{ + const int index = sheet->indexOf(name); + Q_ASSERT(index != -1); + sheet->setProperty(index, value); + sheet->setChanged(index, true); +} + +void ActionEditor::slotNewAction() +{ + NewActionDialog dlg(this); + dlg.setWindowTitle(tr("New action")); + + if (dlg.exec() == QDialog::Accepted) { + const ActionData actionData = dlg.actionData(); + m_actionView->clearSelection(); + + QAction *action = new QAction(formWindow()); + action->setObjectName(actionData.name); + formWindow()->ensureUniqueObjectName(action); + action->setText(actionData.text); + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), action); + if (!actionData.toolTip.isEmpty()) + setInitialProperty(sheet, QLatin1String(toolTipPropertyC), actionData.toolTip); + + if (actionData.checkable) + setInitialProperty(sheet, QLatin1String(checkablePropertyC), QVariant(true)); + + if (!actionData.keysequence.value().isEmpty()) + setInitialProperty(sheet, QLatin1String(shortcutPropertyC), qVariantFromValue(actionData.keysequence)); + + sheet->setProperty(sheet->indexOf(QLatin1String(iconPropertyC)), qVariantFromValue(actionData.icon)); + + AddActionCommand *cmd = new AddActionCommand(formWindow()); + cmd->init(action); + formWindow()->commandHistory()->push(cmd); + } +} + +static inline bool isSameIcon(const QIcon &i1, const QIcon &i2) +{ + return i1.serialNumber() == i2.serialNumber(); +} + +// return a FormWindow command to apply an icon or a reset command in case it +// is empty. + +static QDesignerFormWindowCommand *setIconPropertyCommand(const PropertySheetIconValue &newIcon, QAction *action, QDesignerFormWindowInterface *fw) +{ + const QString iconProperty = QLatin1String(iconPropertyC); + if (newIcon.paths().isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(action, iconProperty); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, iconProperty, qVariantFromValue(newIcon)); + return cmd; +} + +// return a FormWindow command to apply a QKeySequence or a reset command +// in case it is empty. + +static QDesignerFormWindowCommand *setKeySequencePropertyCommand(const PropertySheetKeySequenceValue &ks, QAction *action, QDesignerFormWindowInterface *fw) +{ + const QString shortcutProperty = QLatin1String(shortcutPropertyC); + if (ks.value().isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(action, shortcutProperty); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, shortcutProperty, qVariantFromValue(ks)); + return cmd; +} + +// return a FormWindow command to apply a POD value or reset command in case +// it is equal to the default value. + +template +QDesignerFormWindowCommand *setPropertyCommand(const QString &name, T value, T defaultValue, + QObject *o, QDesignerFormWindowInterface *fw) +{ + if (value == defaultValue) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(o, name); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(o, name, QVariant(value)); + return cmd; +} + +// Return the text value of a string property via PropertySheetStringValue +static inline QString textPropertyValue(const QDesignerPropertySheetExtension *sheet, const QString &name) +{ + const int index = sheet->indexOf(name); + Q_ASSERT(index != -1); + const PropertySheetStringValue ps = qVariantValue(sheet->property(index)); + return ps.value(); +} + +void ActionEditor::editAction(QAction *action) +{ + if (!action) + return; + + NewActionDialog dlg(this); + dlg.setWindowTitle(tr("Edit action")); + + ActionData oldActionData; + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), action); + oldActionData.name = action->objectName(); + oldActionData.text = action->text(); + oldActionData.toolTip = textPropertyValue(sheet, QLatin1String(toolTipPropertyC)); + oldActionData.icon = qVariantValue(sheet->property(sheet->indexOf(QLatin1String(iconPropertyC)))); + oldActionData.keysequence = ActionModel::actionShortCut(sheet); + oldActionData.checkable = action->isCheckable(); + dlg.setActionData(oldActionData); + + if (!dlg.exec()) + return; + + // figure out changes and whether to start a macro + const ActionData newActionData = dlg.actionData(); + const unsigned changeMask = newActionData.compare(oldActionData); + if (changeMask == 0u) + return; + + const bool severalChanges = (changeMask != ActionData::TextChanged) && (changeMask != ActionData::NameChanged) + && (changeMask != ActionData::ToolTipChanged) && (changeMask != ActionData::IconChanged) + && (changeMask != ActionData::CheckableChanged) && (changeMask != ActionData::KeysequenceChanged); + + QDesignerFormWindowInterface *fw = formWindow(); + QUndoStack *undoStack = fw->commandHistory(); + if (severalChanges) + fw->beginCommand(QLatin1String("Edit action")); + + if (changeMask & ActionData::NameChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(objectNamePropertyC), newActionData.name, action, fw)); + + if (changeMask & ActionData::TextChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(textPropertyC), newActionData.text, action, fw)); + + if (changeMask & ActionData::ToolTipChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(toolTipPropertyC), newActionData.toolTip, action, fw)); + + if (changeMask & ActionData::IconChanged) + undoStack->push(setIconPropertyCommand(newActionData.icon, action, fw)); + + if (changeMask & ActionData::CheckableChanged) + undoStack->push(setPropertyCommand(QLatin1String(checkablePropertyC), newActionData.checkable, false, action, fw)); + + if (changeMask & ActionData::KeysequenceChanged) + undoStack->push(setKeySequencePropertyCommand(newActionData.keysequence, action, fw)); + + if (severalChanges) + fw->endCommand(); +} + +void ActionEditor::editCurrentAction() +{ + if (QAction *a = m_actionView->currentAction()) + editAction(a); +} + +void ActionEditor::navigateToSlotCurrentAction() +{ + if (QAction *a = m_actionView->currentAction()) + QDesignerTaskMenu::navigateToSlot(m_core, a, QLatin1String("triggered()")); +} + +void ActionEditor::deleteActions(QDesignerFormWindowInterface *fw, const ActionList &actions) +{ + // We need a macro even in the case of single action because the commands might cause the + // scheduling of other commands (signal slots connections) + const QString description = actions.size() == 1 ? + tr("Remove action '%1'").arg(actions.front()->objectName()) : tr("Remove actions"); + fw->beginCommand(description); + foreach(QAction *action, actions) { + RemoveActionCommand *cmd = new RemoveActionCommand(fw); + cmd->init(action); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void ActionEditor::copyActions(QDesignerFormWindowInterface *fwi, const ActionList &actions) +{ + FormWindowBase *fw = qobject_cast(fwi); + if (!fw ) + return; + + FormBuilderClipboard clipboard; + clipboard.m_actions = actions; + + if (clipboard.empty()) + return; + + QEditorFormBuilder *formBuilder = fw->createFormBuilder(); + Q_ASSERT(formBuilder); + + QBuffer buffer; + if (buffer.open(QIODevice::WriteOnly)) + if (formBuilder->copy(&buffer, clipboard)) + qApp->clipboard()->setText(QString::fromUtf8(buffer.buffer()), QClipboard::Clipboard); + delete formBuilder; +} + +void ActionEditor::slotDelete() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + deleteActions(fw, selection); +} + +QString ActionEditor::actionTextToName(const QString &text, const QString &prefix) +{ + QString name = text; + if (name.isEmpty()) + return QString(); + + name[0] = name.at(0).toUpper(); + name.prepend(prefix); + const QString underscore = QString(QLatin1Char('_')); + name.replace(QRegExp(QString(QLatin1String("[^a-zA-Z_0-9]"))), underscore); + name.replace(QRegExp(QLatin1String("__*")), underscore); + if (name.endsWith(underscore.at(0))) + name.truncate(name.size() - 1); + + return name; +} + +void ActionEditor::resourceImageDropped(const QString &path, QAction *action) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), action); + const PropertySheetIconValue oldIcon = + qVariantValue(sheet->property(sheet->indexOf(QLatin1String(iconPropertyC)))); + PropertySheetIconValue newIcon; + newIcon.setPixmap(QIcon::Normal, QIcon::Off, PropertySheetPixmapValue(path)); + if (newIcon.paths().isEmpty() || newIcon.paths() == oldIcon.paths()) + return; + + fw->commandHistory()->push(setIconPropertyCommand(newIcon , action, fw)); +} + +void ActionEditor::mainContainerChanged() +{ + // Invalidate references to objects kept in model + if (sender() == formWindow()) + setFormWindow(0); +} + +void ActionEditor::slotViewMode(QAction *a) +{ + m_actionView->setViewMode(a->data().toInt()); + updateViewModeActions(); +} + +void ActionEditor::slotSelectAssociatedWidget(QWidget *w) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + QDesignerObjectInspector *oi = qobject_cast(core()->objectInspector()); + if (!oi) + return; + + fw->clearSelection(); // Actually, there are no widgets selected due to focus in event handling. Just to be sure. + oi->selectObject(w); +} + +void ActionEditor::restoreSettings() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + m_actionView->setViewMode(settings->value(QLatin1String(actionEditorViewModeKey), 0).toInt()); + updateViewModeActions(); +} + +void ActionEditor::saveSettings() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->setValue(QLatin1String(actionEditorViewModeKey), m_actionView->viewMode()); +} + +void ActionEditor::updateViewModeActions() +{ + switch (m_actionView->viewMode()) { + case ActionView::IconView: + m_iconViewAction->setChecked(true); + break; + case ActionView::DetailedView: + m_listViewAction->setChecked(true); + break; + } +} + +void ActionEditor::slotCopy() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + copyActions(fw, selection); +} + +void ActionEditor::slotCut() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + copyActions(fw, selection); + deleteActions(fw, selection); +} + +void ActionEditor::slotPaste() +{ + FormWindowBase *fw = qobject_cast(formWindow()); + if (!fw) + return; + m_actionView->clearSelection(); + fw->paste(FormWindowBase::PasteActionsOnly); +} + +void ActionEditor::slotContextMenuRequested(QContextMenuEvent *e, QAction *item) +{ + // set up signal mapper + if (!m_selectAssociatedWidgetsMapper) { + m_selectAssociatedWidgetsMapper = new QSignalMapper(this); + connect(m_selectAssociatedWidgetsMapper, SIGNAL(mapped(QWidget*)), this, SLOT(slotSelectAssociatedWidget(QWidget*))); + } + + QMenu menu(this); + menu.addAction(m_actionNew); + menu.addSeparator(); + menu.addAction(m_actionEdit); + if (QDesignerTaskMenu::isSlotNavigationEnabled(m_core)) + menu.addAction(m_actionNavigateToSlot); + + // Associated Widgets + if (QAction *action = m_actionView->currentAction()) { + const QWidgetList associatedWidgets = ActionModel::associatedWidgets(action); + if (!associatedWidgets.empty()) { + QMenu *associatedWidgetsSubMenu = menu.addMenu(tr("Used In")); + foreach (QWidget *w, associatedWidgets) { + QAction *action = associatedWidgetsSubMenu->addAction(w->objectName()); + m_selectAssociatedWidgetsMapper->setMapping(action, w); + connect(action, SIGNAL(triggered()), m_selectAssociatedWidgetsMapper, SLOT(map())); + } + } + } + + menu.addSeparator(); + menu.addAction(m_actionCut); + menu.addAction(m_actionCopy); + menu.addAction(m_actionPaste); + menu.addAction(m_actionSelectAll); + menu.addAction(m_actionDelete); + menu.addSeparator(); + menu.addAction(m_iconViewAction); + menu.addAction(m_listViewAction); + + emit contextMenuRequested(&menu, item); + + menu.exec(e->globalPos()); + e->accept(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + diff --git a/designer/lib/shared/actioneditor_p.h b/designer/lib/shared/actioneditor_p.h new file mode 100644 index 0000000..4a5a862 --- /dev/null +++ b/designer/lib/shared/actioneditor_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ACTIONEDITOR_H +#define ACTIONEDITOR_H + +#include "shared_global_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerPropertyEditorInterface; +class QDesignerSettingsInterface; +class QMenu; +class QActionGroup; +class QSignalMapper; +class QItemSelection; +class QListWidget; +class QPushButton; +class QLineEdit; +class QToolButton; + +namespace qdesigner_internal { + +class ActionView; +class ResourceMimeData; + +class QDESIGNER_SHARED_EXPORT ActionEditor: public QDesignerActionEditorInterface +{ + Q_OBJECT +public: + explicit ActionEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~ActionEditor(); + + QDesignerFormWindowInterface *formWindow() const; + virtual void setFormWindow(QDesignerFormWindowInterface *formWindow); + + virtual QDesignerFormEditorInterface *core() const; + + QAction *actionNew() const; + QAction *actionDelete() const; + + QString filter() const; + + virtual void manageAction(QAction *action); + virtual void unmanageAction(QAction *action); + + static QString actionTextToName(const QString &text, const QString &prefix = QLatin1String("action")); + + // Utility to create a configure button with menu for usage on toolbars + static QToolButton *createConfigureMenuButton(const QString &t, QMenu **ptrToMenu); + +public slots: + void setFilter(const QString &filter); + void mainContainerChanged(); + +private slots: + void slotCurrentItemChanged(QAction *item); + void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void editAction(QAction *item); + void editCurrentAction(); + void navigateToSlotCurrentAction(); + void slotActionChanged(); + void slotNewAction(); + void slotDelete(); + void resourceImageDropped(const QString &path, QAction *action); + void slotContextMenuRequested(QContextMenuEvent *, QAction *); + void slotViewMode(QAction *a); + void slotSelectAssociatedWidget(QWidget *w); + void slotCopy(); + void slotCut(); + void slotPaste(); + +signals: + void itemActivated(QAction *item); + // Context menu for item or global menu if item == 0. + void contextMenuRequested(QMenu *menu, QAction *item); + +private: + typedef QList ActionList; + void deleteActions(QDesignerFormWindowInterface *formWindow, const ActionList &); + void copyActions(QDesignerFormWindowInterface *formWindow, const ActionList &); + + void restoreSettings(); + void saveSettings(); + + void updateViewModeActions(); + + QDesignerFormEditorInterface *m_core; + QPointer m_formWindow; + QListWidget *m_actionGroups; + + ActionView *m_actionView; + + QAction *m_actionNew; + QAction *m_actionEdit; + QAction *m_actionNavigateToSlot; + QAction *m_actionCopy; + QAction *m_actionCut; + QAction *m_actionPaste; + QAction *m_actionSelectAll; + QAction *m_actionDelete; + + QActionGroup *m_viewModeGroup; + QAction *m_iconViewAction; + QAction *m_listViewAction; + + QString m_filter; + QWidget *m_filterWidget; + QSignalMapper *m_selectAssociatedWidgetsMapper; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ACTIONEDITOR_H diff --git a/designer/lib/shared/actionprovider_p.h b/designer/lib/shared/actionprovider_p.h new file mode 100644 index 0000000..e450ad7 --- /dev/null +++ b/designer/lib/shared/actionprovider_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ACTIONPROVIDER_H +#define ACTIONPROVIDER_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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QAction; + +class QDesignerActionProviderExtension +{ +public: + virtual ~QDesignerActionProviderExtension() {} + + virtual QRect actionGeometry(QAction *action) const = 0; + virtual QAction *actionAt(const QPoint &pos) const = 0; + + virtual void adjustIndicator(const QPoint &pos) = 0; +}; + +// Find action at the given position for a widget that has actionGeometry() (QToolBar, +// QMenuBar, QMenu). They usually have actionAt(), but that fails since Designer usually sets +// WA_TransparentForMouseEvents on the widgets. +template + int actionIndexAt(const Widget *w, const QPoint &pos, Qt::Orientation orientation) +{ + const QList actions = w->actions(); + const int actionCount = actions.count(); + if (actionCount == 0) + return -1; + // actionGeometry() can be wrong sometimes; it returns a geometry that + // stretches to the end of the toolbar/menu bar. So, check from the beginning + // in the case of a horizontal right-to-left orientation. + const bool checkTopRight = orientation == Qt::Horizontal && w->layoutDirection() == Qt::RightToLeft; + const QPoint topRight = QPoint(w->rect().width(), 0); + for (int index = 0; index < actionCount; ++index) { + QRect g = w->actionGeometry(actions.at(index)); + if (checkTopRight) + g.setTopRight(topRight); + else + g.setTopLeft(QPoint(0, 0)); + + if (g.contains(pos)) + return index; + } + return -1; +} + +Q_DECLARE_EXTENSION_INTERFACE(QDesignerActionProviderExtension, "com.trolltech.Qt.Designer.ActionProvider") + +QT_END_NAMESPACE + +#endif // ACTIONPROVIDER_H diff --git a/designer/lib/shared/actionrepository.cpp b/designer/lib/shared/actionrepository.cpp new file mode 100644 index 0000000..4578ec3 --- /dev/null +++ b/designer/lib/shared/actionrepository.cpp @@ -0,0 +1,663 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "actionrepository_p.h" +#include "qtresourceview_p.h" +#include "iconloader_p.h" +#include "qdesigner_utils_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +namespace { + enum { listModeIconSize = 16, iconModeIconSize = 24 }; +} + +static const char *actionMimeType = "action-repository/actions"; +static const char *plainTextMimeType = "text/plain"; + +static inline QAction *actionOfItem(const QStandardItem* item) +{ + return qvariant_cast(item->data(qdesigner_internal::ActionModel::ActionRole)); +} + +namespace qdesigner_internal { + +// ----------- ActionModel +ActionModel::ActionModel(QWidget *parent ) : + QStandardItemModel(parent), + m_emptyIcon(emptyIcon()), + m_core(0) +{ + QStringList headers; + headers += tr("Name"); + headers += tr("Used"); + headers += tr("Text"); + headers += tr("Shortcut"); + headers += tr("Checkable"); + headers += tr("ToolTip"); + Q_ASSERT(NumColumns == headers.size()); + setHorizontalHeaderLabels(headers); +} + +void ActionModel::clearActions() +{ + removeRows(0, rowCount()); +} + +int ActionModel::findAction(QAction *action) const +{ + const int rows = rowCount(); + for (int i = 0; i < rows; i++) + if (action == actionOfItem(item(i))) + return i; + return -1; +} + +void ActionModel::update(int row) +{ + Q_ASSERT(m_core); + // need to create the row list ... grrr.. + if (row >= rowCount()) + return; + + QStandardItemList list; + for (int i = 0; i < NumColumns; i++) + list += item(row, i); + + setItems(m_core, actionOfItem(list.front()), m_emptyIcon, list); +} + +void ActionModel::remove(int row) +{ + qDeleteAll(takeRow(row)); +} + +QModelIndex ActionModel::addAction(QAction *action) +{ + Q_ASSERT(m_core); + QStandardItemList items; + const Qt::ItemFlags flags = Qt::ItemIsSelectable|Qt::ItemIsDropEnabled|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled; + + QVariant itemData; + qVariantSetValue(itemData, action); + + for (int i = 0; i < NumColumns; i++) { + QStandardItem *item = new QStandardItem; + item->setData(itemData, ActionRole); + item->setFlags(flags); + items.push_back(item); + } + setItems(m_core, action, m_emptyIcon, items); + appendRow(items); + return indexFromItem(items.front()); +} + +// Find the associated menus and toolbars, ignore toolbuttons +QWidgetList ActionModel::associatedWidgets(const QAction *action) +{ + QWidgetList rc = action->associatedWidgets(); + for (QWidgetList::iterator it = rc.begin(); it != rc.end(); ) + if (qobject_cast(*it) || qobject_cast(*it)) { + ++it; + } else { + it = rc.erase(it); + } + return rc; +} + +// shortcut is a fake property, need to retrieve it via property sheet. +PropertySheetKeySequenceValue ActionModel::actionShortCut(QDesignerFormEditorInterface *core, QAction *action) +{ + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), action); + if (!sheet) + return PropertySheetKeySequenceValue(); + return actionShortCut(sheet); +} + +PropertySheetKeySequenceValue ActionModel::actionShortCut(const QDesignerPropertySheetExtension *sheet) +{ + const int index = sheet->indexOf(QLatin1String("shortcut")); + if (index == -1) + return PropertySheetKeySequenceValue(); + return qvariant_cast(sheet->property(index)); +} + +void ActionModel::setItems(QDesignerFormEditorInterface *core, QAction *action, + const QIcon &defaultIcon, + QStandardItemList &sl) +{ + + // Tooltip, mostly for icon view mode + QString firstTooltip = action->objectName(); + const QString text = action->text(); + if (!text.isEmpty()) { + firstTooltip += QLatin1Char('\n'); + firstTooltip += text; + } + + Q_ASSERT(sl.size() == NumColumns); + + QStandardItem *item = sl[NameColumn]; + item->setText(action->objectName()); + QIcon icon = action->icon(); + if (icon.isNull()) + icon = defaultIcon; + item->setIcon(icon); + item->setToolTip(firstTooltip); + item->setWhatsThis(firstTooltip); + // Used + const QWidgetList associatedDesignerWidgets = associatedWidgets(action); + const bool used = !associatedDesignerWidgets.empty(); + item = sl[UsedColumn]; + item->setCheckState(used ? Qt::Checked : Qt::Unchecked); + if (used) { + QString usedToolTip; + const QString separator = QLatin1String(", "); + const int count = associatedDesignerWidgets.size(); + for (int i = 0; i < count; i++) { + if (i) + usedToolTip += separator; + usedToolTip += associatedDesignerWidgets.at(i)->objectName(); + } + item->setToolTip(usedToolTip); + } else { + item->setToolTip(QString()); + } + // text + item = sl[TextColumn]; + item->setText(action->text()); + item->setToolTip(action->text()); + // shortcut + const QString shortcut = actionShortCut(core, action).value().toString(QKeySequence::NativeText); + item = sl[ShortCutColumn]; + item->setText(shortcut); + item->setToolTip(shortcut); + // checkable + sl[CheckedColumn]->setCheckState(action->isCheckable() ? Qt::Checked : Qt::Unchecked); + // ToolTip. This might be multi-line, rich text + QString toolTip = action->toolTip(); + item = sl[ToolTipColumn]; + item->setToolTip(toolTip); + item->setText(toolTip.replace(QLatin1Char('\n'), QLatin1Char(' '))); +} + +QMimeData *ActionModel::mimeData(const QModelIndexList &indexes ) const +{ + ActionRepositoryMimeData::ActionList actionList; + + QSet actions; + foreach (const QModelIndex &index, indexes) + if (QStandardItem *item = itemFromIndex(index)) + if (QAction *action = actionOfItem(item)) + actions.insert(action); + return new ActionRepositoryMimeData(actions.toList(), Qt::CopyAction); +} + +// Resource images are plain text. The drag needs to be restricted, however. +QStringList ActionModel::mimeTypes() const +{ + return QStringList(QLatin1String(plainTextMimeType)); +} + +QString ActionModel::actionName(int row) const +{ + return item(row, NameColumn)->text(); +} + +bool ActionModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &) +{ + if (action != Qt::CopyAction) + return false; + + QStandardItem *droppedItem = item(row, column); + if (!droppedItem) + return false; + + + QtResourceView::ResourceType type; + QString path; + if (!QtResourceView::decodeMimeData(data, &type, &path) || type != QtResourceView::ResourceImage) + return false; + + emit resourceImageDropped(path, actionOfItem(droppedItem)); + return true; +} + +QAction *ActionModel::actionAt(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + QStandardItem *i = itemFromIndex(index); + if (!i) + return 0; + return actionOfItem(i); +} + +// helpers + +static bool handleImageDragEnterMoveEvent(QDropEvent *event) +{ + QtResourceView::ResourceType type; + const bool rc = QtResourceView::decodeMimeData(event->mimeData(), &type) && type == QtResourceView::ResourceImage; + if (rc) + event->acceptProposedAction(); + else + event->ignore(); + return rc; +} + +static void handleImageDropEvent(const QAbstractItemView *iv, QDropEvent *event, ActionModel *am) +{ + const QModelIndex index = iv->indexAt(event->pos()); + if (!index.isValid()) { + event->ignore(); + return; + } + + if (!handleImageDragEnterMoveEvent(event)) + return; + + am->dropMimeData(event->mimeData(), event->proposedAction(), index.row(), 0, iv->rootIndex()); +} + +// Basically mimic QAbstractItemView's startDrag routine, except that +// another pixmap is used, we don't want the whole row. + +void startActionDrag(QWidget *dragParent, ActionModel *model, const QModelIndexList &indexes, Qt::DropActions supportedActions) +{ + if (indexes.empty()) + return; + + QDrag *drag = new QDrag(dragParent); + QMimeData *data = model->mimeData(indexes); + drag->setMimeData(data); + if (ActionRepositoryMimeData *actionMimeData = qobject_cast(data)) + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(actionMimeData->actionList().front())); + + drag->start(supportedActions); +} + +// ---------------- ActionTreeView: +ActionTreeView::ActionTreeView(ActionModel *model, QWidget *parent) : + QTreeView(parent), + m_model(model) +{ + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(DragDrop); + setModel(model); + setRootIsDecorated(false); + setTextElideMode(Qt::ElideMiddle); + + setModel(model); + connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(slotActivated(QModelIndex))); + connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int))); + + setIconSize(QSize(listModeIconSize, listModeIconSize)); + +} + +QAction *ActionTreeView::currentAction() const +{ + return m_model->actionAt(currentIndex()); +} + +void ActionTreeView::filter(const QString &text) +{ + const int rowCount = m_model->rowCount(); + const bool empty = text.isEmpty(); + const QModelIndex parent = rootIndex(); + for (int i = 0; i < rowCount; i++) + setRowHidden(i, parent, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive)); +} + +void ActionTreeView::dragEnterEvent(QDragEnterEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionTreeView::dragMoveEvent(QDragMoveEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionTreeView::dropEvent(QDropEvent *event) +{ + handleImageDropEvent(this, event, m_model); +} + +void ActionTreeView::focusInEvent(QFocusEvent *event) +{ + QTreeView::focusInEvent(event); + // Make property editor display current action + if (QAction *a = currentAction()) + emit currentChanged(a); +} + +void ActionTreeView::contextMenuEvent(QContextMenuEvent *event) +{ + emit contextMenuRequested(event, m_model->actionAt(indexAt(event->pos()))); +} + +void ActionTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + emit currentChanged(m_model->actionAt(current)); +} + +void ActionTreeView::slotActivated(const QModelIndex &index) +{ + emit activated(m_model->actionAt(index)); +} + +void ActionTreeView::startDrag(Qt::DropActions supportedActions) +{ + startActionDrag(this, m_model, selectedIndexes(), supportedActions); +} + +// ---------------- ActionListView: +ActionListView::ActionListView(ActionModel *model, QWidget *parent) : + QListView(parent), + m_model(model) +{ + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(DragDrop); + setModel(model); + setTextElideMode(Qt::ElideMiddle); + connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(slotActivated(QModelIndex))); + + // We actually want 'Static' as the user should be able to + // drag away actions only (not to rearrange icons). + // We emulate that by not accepting our own + // drag data. 'Static' causes the list view to disable drag and drop + // on the viewport. + setMovement(Snap); + setViewMode(IconMode); + setIconSize(QSize(iconModeIconSize, iconModeIconSize)); + setGridSize(QSize(4 * iconModeIconSize, 2 * iconModeIconSize)); + setSpacing(iconModeIconSize / 3); +} + +QAction *ActionListView::currentAction() const +{ + return m_model->actionAt(currentIndex()); +} + +void ActionListView::filter(const QString &text) +{ + const int rowCount = m_model->rowCount(); + const bool empty = text.isEmpty(); + for (int i = 0; i < rowCount; i++) + setRowHidden(i, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive)); +} + +void ActionListView::dragEnterEvent(QDragEnterEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionListView::dragMoveEvent(QDragMoveEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionListView::dropEvent(QDropEvent *event) +{ + handleImageDropEvent(this, event, m_model); +} + +void ActionListView::focusInEvent(QFocusEvent *event) +{ + QListView::focusInEvent(event); + // Make property editor display current action + if (QAction *a = currentAction()) + emit currentChanged(a); +} + +void ActionListView::contextMenuEvent(QContextMenuEvent *event) +{ + emit contextMenuRequested(event, m_model->actionAt(indexAt(event->pos()))); +} + +void ActionListView::currentChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) +{ + emit currentChanged(m_model->actionAt(current)); +} + +void ActionListView::slotActivated(const QModelIndex &index) +{ + emit activated(m_model->actionAt(index)); +} + +void ActionListView::startDrag(Qt::DropActions supportedActions) +{ + startActionDrag(this, m_model, selectedIndexes(), supportedActions); +} + +// ActionView +ActionView::ActionView(QWidget *parent) : + QStackedWidget(parent), + m_model(new ActionModel(this)), + m_actionTreeView(new ActionTreeView(m_model)), + m_actionListView(new ActionListView(m_model)) +{ + addWidget(m_actionListView); + addWidget(m_actionTreeView); + // Wire signals + connect(m_actionTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*,QAction*)), + this, SIGNAL(contextMenuRequested(QContextMenuEvent*,QAction*))); + connect(m_actionListView, SIGNAL(contextMenuRequested(QContextMenuEvent*,QAction*)), + this, SIGNAL(contextMenuRequested(QContextMenuEvent*,QAction*))); + + // make it possible for vs integration to reimplement edit action dialog + // [which it shouldn't do actually] + connect(m_actionListView, SIGNAL(activated(QAction*)), this, SIGNAL(activated(QAction*))); + connect(m_actionTreeView, SIGNAL(activated(QAction*)), this, SIGNAL(activated(QAction*))); + + connect(m_actionListView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentChanged(QAction*))); + connect(m_actionTreeView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentChanged(QAction*))); + + connect(m_model, SIGNAL(resourceImageDropped(QString,QAction*)), + this, SIGNAL(resourceImageDropped(QString,QAction*))); + + // sync selection models + QItemSelectionModel *selectionModel = m_actionTreeView->selectionModel(); + m_actionListView->setSelectionModel(selectionModel); + connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); +} + +int ActionView::viewMode() const +{ + return currentWidget() == m_actionListView ? IconView : DetailedView; +} + +void ActionView::setViewMode(int lm) +{ + if (viewMode() == lm) + return; + + switch (lm) { + case IconView: + setCurrentWidget(m_actionListView); + break; + case DetailedView: + setCurrentWidget(m_actionTreeView); + break; + default: + break; + } +} + +void ActionView::slotCurrentChanged(QAction *action) +{ + // emit only for currently visible + if (sender() == currentWidget()) + emit currentChanged(action); +} + +void ActionView::filter(const QString &text) +{ + m_actionTreeView->filter(text); + m_actionListView->filter(text); +} + +void ActionView::selectAll() +{ + m_actionTreeView->selectAll(); +} + +void ActionView::clearSelection() +{ + m_actionTreeView->selectionModel()->clearSelection(); +} + +void ActionView::setCurrentIndex(const QModelIndex &index) +{ + m_actionTreeView->setCurrentIndex(index); +} + +QAction *ActionView::currentAction() const +{ + return m_actionListView->currentAction(); +} + +void ActionView::setSelectionMode(QAbstractItemView::SelectionMode sm) +{ + m_actionTreeView->setSelectionMode(sm); + m_actionListView->setSelectionMode(sm); +} + +QAbstractItemView::SelectionMode ActionView::selectionMode() const +{ + return m_actionListView->selectionMode(); +} + +QItemSelection ActionView::selection() const +{ + return m_actionListView->selectionModel()->selection(); +} + +ActionView::ActionList ActionView::selectedActions() const +{ + ActionList rc; + foreach (const QModelIndex &index, selection().indexes()) + if (index.column() == 0) + rc += actionOfItem(m_model->itemFromIndex(index)); + return rc; +} +// ---------- ActionRepositoryMimeData +ActionRepositoryMimeData::ActionRepositoryMimeData(QAction *a, Qt::DropAction dropAction) : + m_dropAction(dropAction) +{ + m_actionList += a; +} + +ActionRepositoryMimeData::ActionRepositoryMimeData(const ActionList &al, Qt::DropAction dropAction) : + m_dropAction(dropAction), + m_actionList(al) +{ +} + +QStringList ActionRepositoryMimeData::formats() const +{ + return QStringList(QLatin1String(actionMimeType)); +} + +QPixmap ActionRepositoryMimeData::actionDragPixmap(const QAction *action) +{ + + // Try to find a suitable pixmap. Grab either widget or icon. + const QIcon icon = action->icon(); + if (!icon.isNull()) + return icon.pixmap(QSize(22, 22)); + + foreach (QWidget *w, action->associatedWidgets()) + if (QToolButton *tb = qobject_cast(w)) + return QPixmap::grabWidget(tb); + + // Create a QToolButton + QToolButton *tb = new QToolButton; + tb->setText(action->text()); + tb->setToolButtonStyle(Qt::ToolButtonTextOnly); +#ifdef Q_WS_WIN // Force alien off to make adjustSize() take the system minimumsize into account. + tb->createWinId(); +#endif + tb->adjustSize(); + const QPixmap rc = QPixmap::grabWidget(tb); + tb->deleteLater(); + return rc; +} + +void ActionRepositoryMimeData::accept(QDragMoveEvent *event) const +{ + if (event->proposedAction() == m_dropAction) { + event->acceptProposedAction(); + } else { + event->setDropAction(m_dropAction); + event->accept(); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/actionrepository_p.h b/designer/lib/shared/actionrepository_p.h new file mode 100644 index 0000000..d58e2e1 --- /dev/null +++ b/designer/lib/shared/actionrepository_p.h @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ACTIONREPOSITORY_H +#define ACTIONREPOSITORY_H + +#include "shared_global_p.h" +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPixmap; + +class QDesignerFormEditorInterface; +class QDesignerPropertySheetExtension; + +namespace qdesigner_internal { + +class PropertySheetKeySequenceValue; + +// Shared model of actions, to be used for several views (detailed/icon view). +class QDESIGNER_SHARED_EXPORT ActionModel: public QStandardItemModel +{ + Q_OBJECT +public: + enum Columns { NameColumn, UsedColumn, TextColumn, ShortCutColumn, CheckedColumn, ToolTipColumn, NumColumns }; + enum { ActionRole = Qt::UserRole + 1000 }; + + explicit ActionModel(QWidget *parent = 0); + void initialize(QDesignerFormEditorInterface *core) { m_core = core; } + + void clearActions(); + QModelIndex addAction(QAction *a); + // remove row + void remove(int row); + // update the row from the underlying action + void update(int row); + + // return row of action or -1. + int findAction(QAction *) const; + + QString actionName(int row) const; + QAction *actionAt(const QModelIndex &index) const; + + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QStringList mimeTypes() const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + // Find the associated menus and toolbars, ignore toolbuttons + static QWidgetList associatedWidgets(const QAction *action); + + // Retrieve shortcut via property sheet as it is a fake property + static PropertySheetKeySequenceValue actionShortCut(QDesignerFormEditorInterface *core, QAction *action); + static PropertySheetKeySequenceValue actionShortCut(const QDesignerPropertySheetExtension *ps); + +signals: + void resourceImageDropped(const QString &path, QAction *action); + +private: + typedef QList QStandardItemList; + + void initializeHeaders(); + static void setItems(QDesignerFormEditorInterface *core, QAction *a, + const QIcon &defaultIcon, + QStandardItemList &sl); + + const QIcon m_emptyIcon; + + QDesignerFormEditorInterface *m_core; +}; + +// Internal class that provides the detailed view of actions. +class ActionTreeView: public QTreeView +{ + Q_OBJECT +public: + explicit ActionTreeView(ActionModel *model, QWidget *parent = 0); + QAction *currentAction() const; + +public slots: + void filter(const QString &text); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + +protected slots: + virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +protected: + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void startDrag(Qt::DropActions supportedActions); + +private slots: + void slotActivated(const QModelIndex &); + +private: + ActionModel *m_model; +}; + +// Internal class that provides the icon view of actions. +class ActionListView: public QListView +{ + Q_OBJECT +public: + explicit ActionListView(ActionModel *model, QWidget *parent = 0); + QAction *currentAction() const; + +public slots: + void filter(const QString &text); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + +protected slots: + virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +protected: + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void startDrag(Qt::DropActions supportedActions); + +private slots: + void slotActivated(const QModelIndex &); + +private: + ActionModel *m_model; +}; + +// Action View that can be switched between detailed and icon view +// using a QStackedWidget of ActionListView / ActionTreeView +// that share the item model and the selection model. + +class ActionView : public QStackedWidget { + Q_OBJECT +public: + // Separate initialize() function takes core argument to make this + // thing usable as promoted widget. + explicit ActionView(QWidget *parent = 0); + void initialize(QDesignerFormEditorInterface *core) { m_model->initialize(core); } + + // View mode + enum { DetailedView, IconView }; + int viewMode() const; + void setViewMode(int lm); + + void setSelectionMode(QAbstractItemView::SelectionMode sm); + QAbstractItemView::SelectionMode selectionMode() const; + + ActionModel *model() const { return m_model; } + + QAction *currentAction() const; + void setCurrentIndex(const QModelIndex &index); + + typedef QList ActionList; + ActionList selectedActions() const; + QItemSelection selection() const; + +public slots: + void filter(const QString &text); + void selectAll(); + void clearSelection(); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void resourceImageDropped(const QString &data, QAction *action); + +private slots: + void slotCurrentChanged(QAction *action); + +private: + ActionModel *m_model; + ActionTreeView *m_actionTreeView; + ActionListView *m_actionListView; +}; + +class QDESIGNER_SHARED_EXPORT ActionRepositoryMimeData: public QMimeData +{ + Q_OBJECT +public: + typedef QList ActionList; + + ActionRepositoryMimeData(const ActionList &, Qt::DropAction dropAction); + ActionRepositoryMimeData(QAction *, Qt::DropAction dropAction); + + const ActionList &actionList() const { return m_actionList; } + virtual QStringList formats() const; + + static QPixmap actionDragPixmap(const QAction *action); + + // Utility to accept with right action + void accept(QDragMoveEvent *event) const; +private: + const Qt::DropAction m_dropAction; + ActionList m_actionList; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ACTIONREPOSITORY_H diff --git a/designer/lib/shared/addlinkdialog.ui b/designer/lib/shared/addlinkdialog.ui new file mode 100644 index 0000000..3171159 --- /dev/null +++ b/designer/lib/shared/addlinkdialog.ui @@ -0,0 +1,112 @@ + + AddLinkDialog + + + Insert Link + + + false + + + true + + + + + + + + Title: + + + + + + + + 337 + 0 + + + + + + + + URL: + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddLinkDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddLinkDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/codedialog.cpp b/designer/lib/shared/codedialog.cpp new file mode 100644 index 0000000..506b6c4 --- /dev/null +++ b/designer/lib/shared/codedialog.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "codedialog_p.h" +#include "qdesigner_utils_p.h" +#include "iconloader_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// ----------------- CodeDialogPrivate +struct CodeDialog::CodeDialogPrivate { + CodeDialogPrivate(); + + QTextEdit *m_textEdit; + TextEditFindWidget *m_findWidget; + QString m_formFileName; +}; + +CodeDialog::CodeDialogPrivate::CodeDialogPrivate() + : m_textEdit(new QTextEdit) + , m_findWidget(new TextEditFindWidget) +{ +} + +// ----------------- CodeDialog +CodeDialog::CodeDialog(QWidget *parent) : + QDialog(parent), + m_impl(new CodeDialogPrivate) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QVBoxLayout *vBoxLayout = new QVBoxLayout; + + // Edit tool bar + QToolBar *toolBar = new QToolBar; + + const QIcon saveIcon = createIconSet(QLatin1String("filesave.png")); + QAction *saveAction = toolBar->addAction(saveIcon, tr("Save...")); + connect(saveAction, SIGNAL(triggered()), this, SLOT(slotSaveAs())); + + const QIcon copyIcon = createIconSet(QLatin1String("editcopy.png")); + QAction *copyAction = toolBar->addAction(copyIcon, tr("Copy All")); + connect(copyAction, SIGNAL(triggered()), this, SLOT(copyAll())); + + QAction *findAction = toolBar->addAction( + TextEditFindWidget::findIconSet(), + tr("&Find in Text..."), + m_impl->m_findWidget, SLOT(activate())); + findAction->setShortcut(QKeySequence::Find); + + vBoxLayout->addWidget(toolBar); + + // Edit + m_impl->m_textEdit->setReadOnly(true); + m_impl->m_textEdit->setMinimumSize(QSize( + m_impl->m_findWidget->minimumSize().width(), + 500)); + vBoxLayout->addWidget(m_impl->m_textEdit); + + // Find + m_impl->m_findWidget->setTextEdit(m_impl->m_textEdit); + vBoxLayout->addWidget(m_impl->m_findWidget); + + // Button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + // Disable auto default + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + closeButton->setAutoDefault(false); + vBoxLayout->addWidget(buttonBox); + + setLayout(vBoxLayout); +} + +CodeDialog::~CodeDialog() +{ + delete m_impl; +} + +void CodeDialog::setCode(const QString &code) +{ + m_impl->m_textEdit->setPlainText(code); +} + +QString CodeDialog::code() const +{ + return m_impl->m_textEdit->toPlainText(); +} + +void CodeDialog::setFormFileName(const QString &f) +{ + m_impl->m_formFileName = f; +} + +QString CodeDialog::formFileName() const +{ + return m_impl->m_formFileName; +} + +bool CodeDialog::generateCode(const QDesignerFormWindowInterface *fw, + QString *code, + QString *errorMessage) +{ + // Generate temporary file name similar to form file name + // (for header guards) + QString tempPattern = QDir::tempPath(); + if (!tempPattern.endsWith(QDir::separator())) // platform-dependant + tempPattern += QDir::separator(); + const QString fileName = fw->fileName(); + if (fileName.isEmpty()) { + tempPattern += QLatin1String("designer"); + } else { + tempPattern += QFileInfo(fileName).baseName(); + } + tempPattern += QLatin1String("XXXXXX.ui"); + // Write to temp file + QTemporaryFile tempFormFile(tempPattern); + + tempFormFile.setAutoRemove(true); + if (!tempFormFile.open()) { + *errorMessage = tr("A temporary form file could not be created in %1.").arg(QDir::tempPath()); + return false; + } + const QString tempFormFileName = tempFormFile.fileName(); + tempFormFile.write(fw->contents().toUtf8()); + if (!tempFormFile.flush()) { + *errorMessage = tr("The temporary form file %1 could not be written.").arg(tempFormFileName); + return false; + } + tempFormFile.close(); + // Run uic + QByteArray rc; + if (!runUIC(tempFormFileName, UIC_GenerateCode, rc, *errorMessage)) + return false; + *code = QString::fromUtf8(rc); + return true; +} + +bool CodeDialog::showCodeDialog(const QDesignerFormWindowInterface *fw, + QWidget *parent, + QString *errorMessage) +{ + QString code; + if (!generateCode(fw, &code, errorMessage)) + return false; + + CodeDialog dialog(parent); + dialog.setWindowTitle(tr("%1 - [Code]").arg(fw->mainContainer()->windowTitle())); + dialog.setCode(code); + dialog.setFormFileName(fw->fileName()); + dialog.exec(); + return true; +} + +void CodeDialog::slotSaveAs() +{ + // build the default relative name 'ui_sth.h' + const QString headerSuffix = QString(QLatin1Char('h')); + QString filter; + const QString uiFile = formFileName(); + + if (!uiFile.isEmpty()) { + filter = QLatin1String("ui_"); + filter += QFileInfo(uiFile).baseName(); + filter += QLatin1Char('.'); + filter += headerSuffix; + } + // file dialog + while (true) { + const QString fileName = + QFileDialog::getSaveFileName (this, tr("Save Code"), filter, tr("Header Files (*.%1)").arg(headerSuffix)); + if (fileName.isEmpty()) + break; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) { + warning(tr("The file %1 could not be opened: %2").arg(fileName).arg(file.errorString())); + continue; + } + file.write(code().toUtf8()); + if (!file.flush()) { + warning(tr("The file %1 could not be written: %2").arg(fileName).arg(file.errorString())); + continue; + } + file.close(); + break; + } +} + +void CodeDialog::warning(const QString &msg) +{ + QMessageBox::warning( + this, tr("%1 - Error").arg(windowTitle()), + msg, QMessageBox::Close); +} + +void CodeDialog::copyAll() +{ + QApplication::clipboard()->setText(code()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/codedialog_p.h b/designer/lib/shared/codedialog_p.h new file mode 100644 index 0000000..fd9303a --- /dev/null +++ b/designer/lib/shared/codedialog_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef CODEPREVIEWDIALOG_H +#define CODEPREVIEWDIALOG_H + +#include "shared_global_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +// Dialog for viewing code. +class QDESIGNER_SHARED_EXPORT CodeDialog : public QDialog +{ + Q_OBJECT + explicit CodeDialog(QWidget *parent = 0); +public: + virtual ~CodeDialog(); + + static bool generateCode(const QDesignerFormWindowInterface *fw, + QString *code, + QString *errorMessage); + + static bool showCodeDialog(const QDesignerFormWindowInterface *fw, + QWidget *parent, + QString *errorMessage); + +private slots: + void slotSaveAs(); + void copyAll(); + +private: + void setCode(const QString &code); + QString code() const; + void setFormFileName(const QString &f); + QString formFileName() const; + + void warning(const QString &msg); + + struct CodeDialogPrivate; + CodeDialogPrivate *m_impl; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CODEPREVIEWDIALOG_H diff --git a/designer/lib/shared/connectionedit.cpp b/designer/lib/shared/connectionedit.cpp new file mode 100644 index 0000000..006b0a1 --- /dev/null +++ b/designer/lib/shared/connectionedit.cpp @@ -0,0 +1,1612 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "connectionedit_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static const int BG_ALPHA = 32; +static const int LINE_PROXIMITY_RADIUS = 3; +static const int LOOP_MARGIN = 20; +static const int VLABEL_MARGIN = 1; +static const int HLABEL_MARGIN = 3; +static const int GROUND_W = 20; +static const int GROUND_H = 25; + +/******************************************************************************* +** Tools +*/ + +static QRect fixRect(const QRect &r) +{ + return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1); +} + +static QRect expand(const QRect &r, int i) +{ + return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i); +} + +static QRect endPointRectHelper(const QPoint &pos) +{ + const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), + QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); + return r; +} + +static void paintGround(QPainter *p, QRect r) +{ + const QPoint mid = r.center(); + p->drawLine(mid.x(), r.top(), mid.x(), mid.y()); + p->drawLine(r.left(), mid.y(), r.right(), mid.y()); + int y = r.top() + 4*r.height()/6; + int x = GROUND_W/6; + p->drawLine(r.left() + x, y, r.right() - x, y); + y = r.top() + 5*r.height()/6; + x = 2*GROUND_W/6; + p->drawLine(r.left() + x, y, r.right() - x, y); + p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom()); +} + +static void paintEndPoint(QPainter *p, const QPoint &pos) +{ + const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), + QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); + p->fillRect(fixRect(r), p->pen().color()); +} + +static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2) +{ + if (p1.x() == p2.x()) + return p1.y() < p2.y() ? qdesigner_internal::CETypes::DownDir : qdesigner_internal::CETypes::UpDir; + Q_ASSERT(p1.y() == p2.y()); + return p1.x() < p2.x() ? qdesigner_internal::CETypes::RightDir : qdesigner_internal::CETypes::LeftDir; +} + +static QPoint pointInsideRect(const QRect &r, QPoint p) +{ + if (p.x() < r.left()) + p.setX(r.left()); + else if (p.x() > r.right()) + p.setX(r.right()); + + if (p.y() < r.top()) + p.setY(r.top()); + else if (p.y() > r.bottom()) + p.setY(r.bottom()); + + return p; +} + +namespace qdesigner_internal { + +/******************************************************************************* +** Commands +*/ + +AddConnectionCommand::AddConnectionCommand(ConnectionEdit *edit, Connection *con) + : CECommand(edit), m_con(con) +{ + setText(QApplication::translate("Command", "Add connection")); +} + +void AddConnectionCommand::redo() +{ + edit()->selectNone(); + emit edit()->aboutToAddConnection(edit()->m_con_list.size()); + edit()->m_con_list.append(m_con); + m_con->inserted(); + edit()->setSelected(m_con, true); + emit edit()->connectionAdded(m_con); +} + +void AddConnectionCommand::undo() +{ + const int idx = edit()->indexOfConnection(m_con); + emit edit()->aboutToRemoveConnection(m_con); + edit()->setSelected(m_con, false); + m_con->update(); + m_con->removed(); + edit()->m_con_list.removeAll(m_con); + emit edit()->connectionRemoved(idx); +} + +class AdjustConnectionCommand : public CECommand +{ +public: + AdjustConnectionCommand(ConnectionEdit *edit, Connection *con, + const QPoint &old_source_pos, + const QPoint &old_target_pos, + const QPoint &new_source_pos, + const QPoint &new_target_pos); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; + const QPoint m_old_source_pos; + const QPoint m_old_target_pos; + const QPoint m_new_source_pos; + const QPoint m_new_target_pos; +}; + +AdjustConnectionCommand::AdjustConnectionCommand(ConnectionEdit *edit, Connection *con, + const QPoint &old_source_pos, + const QPoint &old_target_pos, + const QPoint &new_source_pos, + const QPoint &new_target_pos) : + CECommand(edit), + m_con(con), + m_old_source_pos(old_source_pos), + m_old_target_pos(old_target_pos), + m_new_source_pos(new_source_pos), + m_new_target_pos(new_target_pos) +{ + setText(QApplication::translate("Command", "Adjust connection")); +} + +void AdjustConnectionCommand::undo() +{ + m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos); + m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos); +} + +void AdjustConnectionCommand::redo() +{ + m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos); + m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos); +} + +DeleteConnectionsCommand::DeleteConnectionsCommand(ConnectionEdit *edit, + const ConnectionList &con_list) + : CECommand(edit), m_con_list(con_list) +{ + setText(QApplication::translate("Command", "Delete connections")); +} + +void DeleteConnectionsCommand::redo() +{ + foreach (Connection *con, m_con_list) { + const int idx = edit()->indexOfConnection(con); + emit edit()->aboutToRemoveConnection(con); + Q_ASSERT(edit()->m_con_list.contains(con)); + edit()->setSelected(con, false); + con->update(); + con->removed(); + edit()->m_con_list.removeAll(con); + emit edit()->connectionRemoved(idx); + } +} + +void DeleteConnectionsCommand::undo() +{ + foreach (Connection *con, m_con_list) { + Q_ASSERT(!edit()->m_con_list.contains(con)); + emit edit()->aboutToAddConnection(edit()->m_con_list.size()); + edit()->m_con_list.append(con); + edit()->setSelected(con, true); + con->update(); + con->inserted(); + emit edit()->connectionAdded(con); + } +} + +class SetEndPointCommand : public CECommand +{ +public: + SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; + const EndPoint::Type m_type; + QObject *m_old_widget, *m_new_widget; + const QPoint m_old_pos; + QPoint m_new_pos; +}; + +SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con, + EndPoint::Type type, QObject *object) : + CECommand(edit), + m_con(con), + m_type(type), + m_old_widget(con->object(type)), + m_new_widget(object), + m_old_pos(con->endPointPos(type)) +{ + if (QWidget *widget = qobject_cast(object)) { + m_new_pos = edit->widgetRect(widget).center(); + } + + if (m_type == EndPoint::Source) + setText(QApplication::translate("Command", "Change source")); + else + setText(QApplication::translate("Command", "Change target")); +} + +void SetEndPointCommand::redo() +{ + m_con->setEndPoint(m_type, m_new_widget, m_new_pos); + emit edit()->connectionChanged(m_con); +} + +void SetEndPointCommand::undo() +{ + m_con->setEndPoint(m_type, m_old_widget, m_old_pos); + emit edit()->connectionChanged(m_con); +} + +/******************************************************************************* +** Connection +*/ + +Connection::Connection(ConnectionEdit *edit) : + m_source_pos(QPoint(-1, -1)), + m_target_pos(QPoint(-1, -1)), + m_source(0), + m_target(0), + m_edit(edit), + m_visible(true) +{ + +} + +Connection::Connection(ConnectionEdit *edit, QObject *source, QObject *target) : + m_source_pos(QPoint(-1, -1)), + m_target_pos(QPoint(-1, -1)), + m_source(source), + m_target(target), + m_edit(edit), + m_visible(true) +{ +} + +void Connection::setVisible(bool b) +{ + m_visible = b; +} + +void Connection::updateVisibility() +{ + QWidget *source = widget(EndPoint::Source); + QWidget *target = widget(EndPoint::Target); + + if (source == 0 || target == 0) { + setVisible(false); + return; + } + + QWidget *w = source; + while (w && w->parentWidget()) { + if (!w->isVisibleTo(w->parentWidget())) { + setVisible(false); + return; + } + w = w->parentWidget(); + } + + w = target; + while (w && w->parentWidget()) { + if (!w->isVisibleTo(w->parentWidget())) { + setVisible(false); + return; + } + w = w->parentWidget(); + } + + setVisible(true); +} + +bool Connection::isVisible() const +{ + return m_visible; +} + +bool Connection::ground() const +{ + return m_target != 0 && m_target == m_edit->m_bg_widget; +} + +QPoint Connection::endPointPos(EndPoint::Type type) const +{ + if (type == EndPoint::Source) + return m_source_pos; + else + return m_target_pos; +} + +static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect) +{ + QPoint result; + + switch (classifyLine(p1, p2)) { + case CETypes::UpDir: + result = QPoint(p1.x(), rect.bottom()); + break; + case CETypes::DownDir: + result = QPoint(p1.x(), rect.top()); + break; + case CETypes::LeftDir: + result = QPoint(rect.right(), p1.y()); + break; + case CETypes::RightDir: + result = QPoint(rect.left(), p1.y()); + break; + } + + return result; +} + +static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2) +{ + QPolygonF result; + + switch (classifyLine(p1, p2)) { + case CETypes::UpDir: + result.append(p2 + QPoint(0, 1)); + result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); + result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); + break; + case CETypes::DownDir: + result.append(p2); + result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); + result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); + break; + case CETypes::LeftDir: + result.append(p2 + QPoint(1, 0)); + result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS)); + result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS)); + break; + case CETypes::RightDir: + result.append(p2); + result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS)); + result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS)); + break; + } + + return result; +} + +static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r) +{ + CETypes::LineDir result = CETypes::UpDir; + int min = p.y() - r.top(); + + int d = p.x() - r.left(); + if (d < min) { + min = d; + result = CETypes::LeftDir; + } + + d = r.bottom() - p.y(); + if (d < min) { + min = d; + result = CETypes::DownDir; + } + + d = r.right() - p.x(); + if (d < min) { + min = d; + result = CETypes::RightDir; + } + + return result; +} + +static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p) +{ + if (l1.x() == l2.x()) + return p.x() >= l1.x(); + return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x()); +} + +void Connection::updateKneeList() +{ + const LineDir old_source_label_dir = labelDir(EndPoint::Source); + const LineDir old_target_label_dir = labelDir(EndPoint::Target); + + QPoint s = endPointPos(EndPoint::Source); + QPoint t = endPointPos(EndPoint::Target); + const QRect sr = m_source_rect; + const QRect tr = m_target_rect; + + m_knee_list.clear(); + m_arrow_head.clear(); + + if (m_source == 0 || s == QPoint(-1, -1) || t == QPoint(-1, -1)) + return; + + const QRect r = sr | tr; + + m_knee_list.append(s); + if (m_target == 0) { + m_knee_list.append(QPoint(t.x(), s.y())); + } else if (m_target == m_edit->m_bg_widget) { + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (tr.contains(sr) || sr.contains(tr)) { +/* + +------------------+ + | +----------+ | + | | | | + | | o | | + | +---|------+ | + | | x | + +-----|-----|------+ + +-----+ + + We find out which edge of the outer rectangle is closest to the target + point, and make a loop which exits and re-enters through that edge. +*/ + const LineDir dir = closestEdge(t, tr); + switch (dir) { + case UpDir: + m_knee_list.append(QPoint(s.x(), r.top() - LOOP_MARGIN)); + m_knee_list.append(QPoint(t.x(), r.top() - LOOP_MARGIN)); + break; + case DownDir: + m_knee_list.append(QPoint(s.x(), r.bottom() + LOOP_MARGIN)); + m_knee_list.append(QPoint(t.x(), r.bottom() + LOOP_MARGIN)); + break; + case LeftDir: + m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, s.y())); + m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, t.y())); + break; + case RightDir: + m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, s.y())); + m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, t.y())); + break; + } + } else { + if (r.height() < sr.height() + tr.height()) { + if ((s.y() >= tr.top() && s.y() <= tr.bottom()) || (t.y() >= sr.bottom() || t.y() <= sr.top())) { +/* + +--------+ + | | +--------+ + | o--+---+--x | + | o | | | + +-----|--+ | | + +------+--x | + +--------+ + + When dragging one end point, move the other end point to the same y position, + if that does not cause it to exit it's rectangle. +*/ + if (m_edit->state() == ConnectionEdit::Dragging) { + if (m_edit->m_drag_end_point.type == EndPoint::Source) { + const QPoint p(t.x(), s.y()); + m_knee_list.append(p); + if (tr.contains(p)) + t = m_target_pos = p; + } else { + const QPoint p(s.x(), t.y()); + m_knee_list.append(p); + if (sr.contains(p)) + s = m_source_pos = p; + } + } else { + m_knee_list.append(QPoint(s.x(), t.y())); + } + } else { +/* + +--------+ + | o----+-------+ + | | +---|----+ + +--------+ | | | + | x | + +--------+ +*/ + m_knee_list.append(QPoint(t.x(), s.y())); + } + } else if (r.width() < sr.width() + tr.width()) { + if ((s.x() >= tr.left() && s.x() <= tr.right()) || t.x() >= sr.right() || t.x() <= sr.left()) { +/* + +--------+ + | | + | o o+--+ + +----|---+ | + +-|------|-+ + | x x | + | | + +----------+ + + When dragging one end point, move the other end point to the same x position, + if that does not cause it to exit it's rectangle. +*/ + if (m_edit->state() == ConnectionEdit::Dragging) { + if (m_edit->m_drag_end_point.type == EndPoint::Source) { + const QPoint p(s.x(), t.y()); + m_knee_list.append(p); + if (tr.contains(p)) + t = m_target_pos = p; + } else { + const QPoint p(t.x(), s.y()); + m_knee_list.append(p); + if (sr.contains(p)) + s = m_source_pos = p; + } + } else { + m_knee_list.append(QPoint(t.x(), s.y())); + } + } else { +/* + +--------+ + | | + | o | + +--|-----+ + | +--------+ + +---+-x | + | | + +--------+ + +*/ + m_knee_list.append(QPoint(s.x(), t.y())); + } + } else { +/* + +--------+ + | | + | o o-+--------+ + +--|-----+ | + | +-----|--+ + | | x | + +--------+-x | + +--------+ + + The line enters the target rectangle through the closest edge. +*/ + if (sr.topLeft() == r.topLeft()) { + if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t)) + m_knee_list.append(QPoint(t.x(), s.y())); + else + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (sr.topRight() == r.topRight()) { + if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t)) + m_knee_list.append(QPoint(t.x(), s.y())); + else + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (sr.bottomRight() == r.bottomRight()) { + if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t)) + m_knee_list.append(QPoint(s.x(), t.y())); + else + m_knee_list.append(QPoint(t.x(), s.y())); + } else { + if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t)) + m_knee_list.append(QPoint(s.x(), t.y())); + else + m_knee_list.append(QPoint(t.x(), s.y())); + } + } + } + m_knee_list.append(t); + + if (m_knee_list.size() == 2) + m_knee_list.clear(); + + trimLine(); + + const LineDir new_source_label_dir = labelDir(EndPoint::Source); + const LineDir new_target_label_dir = labelDir(EndPoint::Target); + if (new_source_label_dir != old_source_label_dir) + updatePixmap(EndPoint::Source); + if (new_target_label_dir != old_target_label_dir) + updatePixmap(EndPoint::Target); +} + +void Connection::trimLine() +{ + if (m_source == 0 || m_source_pos == QPoint(-1, -1) || m_target_pos == QPoint(-1, -1)) + return; + int cnt = m_knee_list.size(); + if (cnt < 2) + return; + + const QRect sr = m_source_rect; + const QRect tr = m_target_rect; + + if (sr.contains(m_knee_list.at(1))) + m_knee_list.removeFirst(); + + cnt = m_knee_list.size(); + if (cnt < 2) + return; + + if (!tr.contains(sr) && tr.contains(m_knee_list.at(cnt - 2))) + m_knee_list.removeLast(); + + cnt = m_knee_list.size(); + if (cnt < 2) + return; + + if (sr.contains(m_knee_list.at(0)) && !sr.contains(m_knee_list.at(1))) + m_knee_list[0] = lineEntryPos(m_knee_list.at(1), m_knee_list.at(0), sr); + + if (tr.contains(m_knee_list.at(cnt - 1)) && !tr.contains(m_knee_list.at(cnt - 2))) { + m_knee_list[cnt - 1] + = lineEntryPos(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1), tr); + m_arrow_head = arrowHead(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1)); + } +} + +void Connection::setSource(QObject *source, const QPoint &pos) +{ + if (source == m_source && m_source_pos == pos) + return; + + update(false); + + m_source = source; + if (QWidget *widget = qobject_cast(source)) { + m_source_pos = pos; + m_source_rect = m_edit->widgetRect(widget); + updateKneeList(); + } + + update(false); +} + +void Connection::setTarget(QObject *target, const QPoint &pos) +{ + if (target == m_target && m_target_pos == pos) + return; + + update(false); + + m_target = target; + if (QWidget *widget = qobject_cast(target)) { + m_target_pos = pos; + m_target_rect = m_edit->widgetRect(widget); + updateKneeList(); + } + + update(false); +} + +static QRect lineRect(const QPoint &a, const QPoint &b) +{ + const QPoint c(qMin(a.x(), b.x()), qMin(a.y(), b.y())); + const QPoint d(qMax(a.x(), b.x()), qMax(a.y(), b.y())); + + QRect result(c, d); + return expand(result, LINE_PROXIMITY_RADIUS); +} + +QRect Connection::groundRect() const +{ + if (!ground()) + return QRect(); + if (m_knee_list.isEmpty()) + return QRect(); + + const QPoint p = m_knee_list.last(); + return QRect(p.x() - GROUND_W/2, p.y(), GROUND_W, GROUND_H); +} + +QRegion Connection::region() const +{ + QRegion result; + + for (int i = 0; i < m_knee_list.size() - 1; ++i) + result = result.unite(lineRect(m_knee_list.at(i), m_knee_list.at(i + 1))); + + if (!m_arrow_head.isEmpty()) { + QRect r = m_arrow_head.boundingRect().toRect(); + r = expand(r, 1); + result = result.unite(r); + } else if (ground()) { + result = result.unite(groundRect()); + } + + result = result.unite(labelRect(EndPoint::Source)); + result = result.unite(labelRect(EndPoint::Target)); + + return result; +} + +void Connection::update(bool update_widgets) const +{ + m_edit->update(region()); + if (update_widgets) { + if (m_source != 0) + m_edit->update(m_source_rect); + if (m_target != 0) + m_edit->update(m_target_rect); + } + + m_edit->update(endPointRect(EndPoint::Source)); + m_edit->update(endPointRect(EndPoint::Target)); +} + +void Connection::paint(QPainter *p) const +{ + for (int i = 0; i < m_knee_list.size() - 1; ++i) + p->drawLine(m_knee_list.at(i), m_knee_list.at(i + 1)); + + if (!m_arrow_head.isEmpty()) { + p->save(); + p->setBrush(p->pen().color()); + p->drawPolygon(m_arrow_head); + p->restore(); + } else if (ground()) { + paintGround(p, groundRect()); + } +} + +bool Connection::contains(const QPoint &pos) const +{ + return region().contains(pos); +} + +QRect Connection::endPointRect(EndPoint::Type type) const +{ + if (type == EndPoint::Source) { + if (m_source_pos != QPoint(-1, -1)) + return endPointRectHelper(m_source_pos); + } else { + if (m_target_pos != QPoint(-1, -1)) + return endPointRectHelper(m_target_pos); + } + return QRect(); +} + +CETypes::LineDir Connection::labelDir(EndPoint::Type type) const +{ + const int cnt = m_knee_list.size(); + if (cnt < 2) + return RightDir; + + LineDir dir; + if (type == EndPoint::Source) + dir = classifyLine(m_knee_list.at(0), m_knee_list.at(1)); + else + dir = classifyLine(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1)); + + if (dir == LeftDir) + dir = RightDir; + if (dir == UpDir) + dir = DownDir; + + return dir; +} + +QRect Connection::labelRect(EndPoint::Type type) const +{ + const int cnt = m_knee_list.size(); + if (cnt < 2) + return QRect(); + const QString text = label(type); + if (text.isEmpty()) + return QRect(); + + const QSize size = labelPixmap(type).size(); + QPoint p1, p2; + if (type == EndPoint::Source) { + p1 = m_knee_list.at(0); + p2 = m_knee_list.at(1); + } else { + p1 = m_knee_list.at(cnt - 1); + p2 = m_knee_list.at(cnt - 2); + } + const LineDir dir = classifyLine(p1, p2); + + QRect result; + switch (dir) { + case UpDir: + result = QRect(p1 + QPoint(-size.width()/2, 0), size); + break; + case DownDir: + result = QRect(p1 + QPoint(-size.width()/2, -size.height()), size); + break; + case LeftDir: + result = QRect(p1 + QPoint(0, -size.height()/2), size); + break; + case RightDir: + result = QRect(p1 + QPoint(-size.width(), -size.height()/2), size); + break; + } + + return result; +} + +void Connection::setLabel(EndPoint::Type type, const QString &text) +{ + if (text == label(type)) + return; + + if (type == EndPoint::Source) + m_source_label = text; + else + m_target_label = text; + + updatePixmap(type); +} + +void Connection::updatePixmap(EndPoint::Type type) +{ + QPixmap *pm = type == EndPoint::Source ? &m_source_label_pm : &m_target_label_pm; + + const QString text = label(type); + if (text.isEmpty()) { + *pm = QPixmap(); + return; + } + + const QFontMetrics fm = m_edit->fontMetrics(); + const QSize size = fm.size(Qt::TextSingleLine, text) + QSize(HLABEL_MARGIN*2, VLABEL_MARGIN*2); + *pm = QPixmap(size); + QColor color = m_edit->palette().color(QPalette::Normal, QPalette::Base); + color.setAlpha(190); + pm->fill(color); + + QPainter p(pm); + p.setPen(m_edit->palette().color(QPalette::Normal, QPalette::Text)); + p.drawText(-fm.leftBearing(text.at(0)) + HLABEL_MARGIN, fm.ascent() + VLABEL_MARGIN, text); + p.end(); + + const LineDir dir = labelDir(type); + + if (dir == DownDir) + *pm = pm->transformed(QMatrix(0.0, -1.0, 1.0, 0.0, 0.0, 0.0)); +} + +void Connection::checkWidgets() +{ + bool changed = false; + + if (QWidget *sourceWidget = qobject_cast(m_source)) { + const QRect r = m_edit->widgetRect(sourceWidget); + if (r != m_source_rect) { + if (m_source_pos != QPoint(-1, -1) && !r.contains(m_source_pos)) { + QPoint offset = m_source_pos - m_source_rect.topLeft(); + QPoint old_pos = m_source_pos; + m_source_pos = pointInsideRect(r, r.topLeft() + offset); + } + m_edit->update(m_source_rect); + m_source_rect = r; + changed = true; + } + } + + if (QWidget *targetWidget = qobject_cast(m_target)) { + const QRect r = m_edit->widgetRect(targetWidget); + if (r != m_target_rect) { + if (m_target_pos != QPoint(-1, -1) && !r.contains(m_target_pos)) { + const QPoint offset = m_target_pos - m_target_rect.topLeft(); + const QPoint old_pos = m_target_pos; + m_target_pos = pointInsideRect(r, r.topLeft() + offset); + } + m_edit->update(m_target_rect); + m_target_rect = r; + changed = true; + } + } + + if (changed) { + update(); + updateKneeList(); + update(); + } +} + +/******************************************************************************* +** ConnectionEdit +*/ + +ConnectionEdit::ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form) : + QWidget(parent), + m_bg_widget(0), + m_undo_stack(form->commandHistory()), + m_enable_update_background(false), + m_tmp_con(0), + m_start_connection_on_drag(true), + m_widget_under_mouse(0), + m_inactive_color(Qt::blue), + m_active_color(Qt::red) +{ + setAttribute(Qt::WA_MouseTracking, true); + setFocusPolicy(Qt::ClickFocus); + + connect(form, SIGNAL(widgetRemoved(QWidget*)), this, SLOT(widgetRemoved(QWidget*))); + connect(form, SIGNAL(objectRemoved(QObject*)), this, SLOT(objectRemoved(QObject*))); +} + +ConnectionEdit::~ConnectionEdit() +{ + qDeleteAll(m_con_list); +} + +void ConnectionEdit::clear() +{ + m_con_list.clear(); + m_sel_con_set.clear(); + m_bg_widget = 0; + m_widget_under_mouse = 0; + m_tmp_con = 0; +} + +void ConnectionEdit::setBackground(QWidget *background) +{ + if (background == m_bg_widget) { + // nothing to do + return; + } + + m_bg_widget = background; + updateBackground(); +} + +void ConnectionEdit::enableUpdateBackground(bool enable) +{ + m_enable_update_background = enable; + if (enable) + updateBackground(); +} + +void ConnectionEdit::updateBackground() +{ + // Might happen while reloading a form. + if (m_bg_widget == 0) + return; + + if (!m_enable_update_background) + return; + + foreach(Connection *c, m_con_list) + c->updateVisibility(); + + updateLines(); + update(); +} + +QWidget *ConnectionEdit::widgetAt(const QPoint &pos) const +{ + if (m_bg_widget == 0) + return 0; + QWidget *widget = m_bg_widget->childAt(pos); + if (widget == 0) + widget = m_bg_widget; + + return widget; +} + + +QRect ConnectionEdit::widgetRect(QWidget *w) const +{ + if (w == 0) + return QRect(); + QRect r = w->geometry(); + QPoint pos = w->mapToGlobal(QPoint(0, 0)); + pos = mapFromGlobal(pos); + r.moveTopLeft(pos); + return r; +} + +ConnectionEdit::State ConnectionEdit::state() const +{ + if (m_tmp_con != 0) + return Connecting; + if (!m_drag_end_point.isNull()) + return Dragging; + return Editing; +} + +void ConnectionEdit::paintLabel(QPainter *p, EndPoint::Type type, Connection *con) +{ + if (con->label(type).isEmpty()) + return; + + const bool heavy = selected(con) || con == m_tmp_con; + p->setPen(heavy ? m_active_color : m_inactive_color); + p->setBrush(Qt::NoBrush); + const QRect r = con->labelRect(type); + p->drawPixmap(r.topLeft(), con->labelPixmap(type)); + p->drawRect(fixRect(r)); +} + +void ConnectionEdit::paintConnection(QPainter *p, Connection *con, + WidgetSet *heavy_highlight_set, + WidgetSet *light_highlight_set) const +{ + QWidget *source = con->widget(EndPoint::Source); + QWidget *target = con->widget(EndPoint::Target); + + const bool heavy = selected(con) || con == m_tmp_con; + WidgetSet *set = heavy ? heavy_highlight_set : light_highlight_set; + p->setPen(heavy ? m_active_color : m_inactive_color); + con->paint(p); + + if (source != 0 && source != m_bg_widget) + set->insert(source, source); + + if (target != 0 && target != m_bg_widget) + set->insert(target, target); +} + +void ConnectionEdit::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setClipRegion(e->region()); + + WidgetSet heavy_highlight_set, light_highlight_set; + + foreach (Connection *con, m_con_list) { + if (!con->isVisible()) + continue; + + paintConnection(&p, con, &heavy_highlight_set, &light_highlight_set); + } + + if (m_tmp_con != 0) + paintConnection(&p, m_tmp_con, &heavy_highlight_set, &light_highlight_set); + + if (!m_widget_under_mouse.isNull() && m_widget_under_mouse != m_bg_widget) + heavy_highlight_set.insert(m_widget_under_mouse, m_widget_under_mouse); + + QColor c = m_active_color; + p.setPen(c); + c.setAlpha(BG_ALPHA); + p.setBrush(c); + + foreach (QWidget *w, heavy_highlight_set) { + p.drawRect(fixRect(widgetRect(w))); + light_highlight_set.remove(w); + } + + c = m_inactive_color; + p.setPen(c); + c.setAlpha(BG_ALPHA); + p.setBrush(c); + + foreach (QWidget *w, light_highlight_set) + p.drawRect(fixRect(widgetRect(w))); + + p.setBrush(palette().color(QPalette::Base)); + p.setPen(palette().color(QPalette::Text)); + foreach (Connection *con, m_con_list) { + if (!con->isVisible()) + continue; + + paintLabel(&p, EndPoint::Source, con); + paintLabel(&p, EndPoint::Target, con); + } + + p.setPen(m_active_color); + p.setBrush(m_active_color); + + foreach (Connection *con, m_con_list) { + if (!selected(con) || !con->isVisible()) + continue; + + paintEndPoint(&p, con->endPointPos(EndPoint::Source)); + + if (con->widget(EndPoint::Target) != 0) + paintEndPoint(&p, con->endPointPos(EndPoint::Target)); + } +} + +void ConnectionEdit::abortConnection() +{ + m_tmp_con->update(); + delete m_tmp_con; + m_tmp_con = 0; +#ifndef QT_NO_CURSOR + setCursor(QCursor()); +#endif + if (m_widget_under_mouse == m_bg_widget) + m_widget_under_mouse = 0; +} + +void ConnectionEdit::mousePressEvent(QMouseEvent *e) +{ + // Right click only to cancel + const Qt::MouseButton button = e->button(); + const State cstate = state(); + if (button != Qt::LeftButton && !(button == Qt::RightButton && cstate == Connecting)) { + QWidget::mousePressEvent(e); + return; + } + + e->accept(); + // Prefer a non-background widget over the connection, + // otherwise, widgets covered by the connection labels cannot be accessed + Connection *con_under_mouse = 0; + if (!m_widget_under_mouse || m_widget_under_mouse == m_bg_widget) + con_under_mouse = connectionAt(e->pos()); + + m_start_connection_on_drag = false; + switch (cstate) { + case Connecting: + if (button == Qt::RightButton) + abortConnection(); + break; + case Dragging: + break; + case Editing: + if (!m_end_point_under_mouse.isNull()) { + if (!(e->modifiers() & Qt::ShiftModifier)) { + startDrag(m_end_point_under_mouse, e->pos()); + } + } else if (con_under_mouse != 0) { + if (!(e->modifiers() & Qt::ShiftModifier)) { + selectNone(); + setSelected(con_under_mouse, true); + } else { + setSelected(con_under_mouse, !selected(con_under_mouse)); + } + } else { + if (!(e->modifiers() & Qt::ShiftModifier)) { + selectNone(); + if (!m_widget_under_mouse.isNull()) + m_start_connection_on_drag = true; + } + } + break; + } +} + +void ConnectionEdit::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QWidget::mouseDoubleClickEvent(e); + return; + } + + e->accept(); + switch (state()) { + case Connecting: + abortConnection(); + break; + case Dragging: + break; + case Editing: + if (!m_widget_under_mouse.isNull()) { + emit widgetActivated(m_widget_under_mouse); + } else if (m_sel_con_set.size() == 1) { + Connection *con = m_sel_con_set.keys().first(); + modifyConnection(con); + } + break; + } + +} + +void ConnectionEdit::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QWidget::mouseReleaseEvent(e); + return; + } + e->accept(); + + switch (state()) { + case Connecting: + if (m_widget_under_mouse.isNull()) + abortConnection(); + else + endConnection(m_widget_under_mouse, e->pos()); +#ifndef QT_NO_CURSOR + setCursor(QCursor()); +#endif + break; + case Editing: + break; + case Dragging: + endDrag(e->pos()); + break; + } +} + + +void ConnectionEdit::findObjectsUnderMouse(const QPoint &pos) +{ + Connection *con_under_mouse = connectionAt(pos); + + QWidget *w = widgetAt(pos); + // Prefer a non-background widget over the connection, + // otherwise, widgets covered by the connection labels cannot be accessed + if (w == m_bg_widget && con_under_mouse) + w = 0; + else + con_under_mouse = 0; + + if (w != m_widget_under_mouse) { + if (!m_widget_under_mouse.isNull()) + update(widgetRect(m_widget_under_mouse)); + m_widget_under_mouse = w; + if (!m_widget_under_mouse.isNull()) + update(widgetRect(m_widget_under_mouse)); + } + + const EndPoint hs = endPointAt(pos); + if (hs != m_end_point_under_mouse) { +#ifndef QT_NO_CURSOR + if (m_end_point_under_mouse.isNull()) + setCursor(Qt::PointingHandCursor); + else + setCursor(QCursor()); +#endif + m_end_point_under_mouse = hs; + } +} + +void ConnectionEdit::mouseMoveEvent(QMouseEvent *e) +{ + findObjectsUnderMouse(e->pos()); + switch (state()) { + case Connecting: + continueConnection(m_widget_under_mouse, e->pos()); + break; + case Editing: + if ((e->buttons() & Qt::LeftButton) + && m_start_connection_on_drag + && !m_widget_under_mouse.isNull()) { + m_start_connection_on_drag = false; + startConnection(m_widget_under_mouse, e->pos()); +#ifndef QT_NO_CURSOR + setCursor(Qt::CrossCursor); +#endif + } + break; + case Dragging: + continueDrag(e->pos()); + break; + } + + e->accept(); +} + +void ConnectionEdit::keyPressEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Delete: + if (state() == Editing) + deleteSelected(); + break; + case Qt::Key_Escape: + if (state() == Connecting) + abortConnection(); + break; + } + + e->accept(); +} + +void ConnectionEdit::startConnection(QWidget *source, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con == 0); + + m_tmp_con = new Connection(this); + m_tmp_con->setEndPoint(EndPoint::Source, source, pos); +} + +void ConnectionEdit::endConnection(QWidget *target, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con != 0); + + m_tmp_con->setEndPoint(EndPoint::Target, target, pos); + + QWidget *source = m_tmp_con->widget(EndPoint::Source); + Q_ASSERT(source != 0); + Q_ASSERT(target != 0); + setEnabled(false); + Connection *new_con = createConnection(source, target); + setEnabled(true); + if (new_con != 0) { + new_con->setEndPoint(EndPoint::Source, source, m_tmp_con->endPointPos(EndPoint::Source)); + new_con->setEndPoint(EndPoint::Target, target, m_tmp_con->endPointPos(EndPoint::Target)); + m_undo_stack->push(new AddConnectionCommand(this, new_con)); + emit connectionChanged(new_con); + } + + delete m_tmp_con; + m_tmp_con = 0; + + findObjectsUnderMouse(mapFromGlobal(QCursor::pos())); +} + +void ConnectionEdit::continueConnection(QWidget *target, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con != 0); + + m_tmp_con->setEndPoint(EndPoint::Target, target, pos); +} + +void ConnectionEdit::modifyConnection(Connection *) +{ +} + +Connection *ConnectionEdit::createConnection(QWidget *source, QWidget *target) +{ + Connection *con = new Connection(this, source, target); + return con; +} + +// Find all connections which in which a sequence of objects is involved +template +static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2) +{ + ConnectionEdit::ConnectionSet rc; + + const ConnectionEdit::ConnectionList::const_iterator ccend = cl.constEnd(); + for ( ; oi1 != oi2; ++oi1) { + for (ConnectionEdit::ConnectionList::const_iterator cit = cl.constBegin(); cit != ccend; ++cit) { + Connection *con = *cit; + if (con->object(ConnectionEdit::EndPoint::Source) == *oi1 || con->object(ConnectionEdit::EndPoint::Target) == *oi1) + rc.insert(con, con); + } + } + return rc; +} + +void ConnectionEdit::widgetRemoved(QWidget *widget) +{ + // Remove all connections of that widget and its children. + if (m_con_list.empty()) + return; + + QWidgetList child_list = qFindChildren(widget); + child_list.prepend(widget); + + const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd()); + + if (!remove_set.isEmpty()) + m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys())); + + updateBackground(); +} + +void ConnectionEdit::objectRemoved(QObject *o) +{ + // Remove all connections of that object and its children (in case of action groups). + if (m_con_list.empty()) + return; + + QObjectList child_list = o->children(); + child_list.prepend(o); + const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd()); + if (!remove_set.isEmpty()) + m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys())); + + updateBackground(); +} + +void ConnectionEdit::setSelected(Connection *con, bool sel) +{ + if (!con || sel == m_sel_con_set.contains(con)) + return; + + if (sel) { + m_sel_con_set.insert(con, con); + emit connectionSelected(con); + } else { + m_sel_con_set.remove(con); + } + + con->update(); +} + +bool ConnectionEdit::selected(const Connection *con) const +{ + return m_sel_con_set.contains(const_cast(con)); +} + +void ConnectionEdit::selectNone() +{ + foreach (Connection *con, m_sel_con_set) + con->update(); + + m_sel_con_set.clear(); +} + +void ConnectionEdit::selectAll() +{ + if (m_sel_con_set.size() == m_con_list.size()) + return; + foreach (Connection *con, m_con_list) + setSelected(con, true); +} + +Connection *ConnectionEdit::connectionAt(const QPoint &pos) const +{ + foreach (Connection *con, m_con_list) { + if (con->contains(pos)) + return con; + } + return 0; +} + +CETypes::EndPoint ConnectionEdit::endPointAt(const QPoint &pos) const +{ + foreach (Connection *con, m_con_list) { + if (!selected(con)) + continue; + const QRect sr = con->endPointRect(EndPoint::Source); + const QRect tr = con->endPointRect(EndPoint::Target); + + if (sr.contains(pos)) + return EndPoint(con, EndPoint::Source); + if (tr.contains(pos)) + return EndPoint(con, EndPoint::Target); + } + return EndPoint(); +} + +void ConnectionEdit::startDrag(const EndPoint &end_point, const QPoint &pos) +{ + Q_ASSERT(m_drag_end_point.isNull()); + m_drag_end_point = end_point; + m_old_source_pos = m_drag_end_point.con->endPointPos(EndPoint::Source); + m_old_target_pos = m_drag_end_point.con->endPointPos(EndPoint::Target); + adjustHotSopt(m_drag_end_point, pos); +} + +void ConnectionEdit::continueDrag(const QPoint &pos) +{ + Q_ASSERT(!m_drag_end_point.isNull()); + adjustHotSopt(m_drag_end_point, pos); +} + +void ConnectionEdit::endDrag(const QPoint &pos) +{ + Q_ASSERT(!m_drag_end_point.isNull()); + adjustHotSopt(m_drag_end_point, pos); + + Connection *con = m_drag_end_point.con; + const QPoint new_source_pos = con->endPointPos(EndPoint::Source); + const QPoint new_target_pos = con->endPointPos(EndPoint::Target); + m_undo_stack->push(new AdjustConnectionCommand(this, con, m_old_source_pos, m_old_target_pos, + new_source_pos, new_target_pos)); + + m_drag_end_point = EndPoint(); +} + +void ConnectionEdit::adjustHotSopt(const EndPoint &end_point, const QPoint &pos) +{ + QWidget *w = end_point.con->widget(end_point.type); + end_point.con->setEndPoint(end_point.type, w, pointInsideRect(widgetRect(w), pos)); +} + +void ConnectionEdit::deleteSelected() +{ + if (m_sel_con_set.isEmpty()) + return; + m_undo_stack->push(new DeleteConnectionsCommand(this, m_sel_con_set.keys())); +} + +void ConnectionEdit::addConnection(Connection *con) +{ + m_con_list.append(con); +} + +void ConnectionEdit::updateLines() +{ + foreach (Connection *con, m_con_list) + con->checkWidgets(); +} + +void ConnectionEdit::resizeEvent(QResizeEvent *e) +{ + updateBackground(); + QWidget::resizeEvent(e); +} + +void ConnectionEdit::setSource(Connection *con, const QString &obj_name) +{ + QObject *object = 0; + if (!obj_name.isEmpty()) { + object = qFindChild(m_bg_widget, obj_name); + if (object == 0 && m_bg_widget->objectName() == obj_name) + object = m_bg_widget; + + if (object == con->object(EndPoint::Source)) + return; + } + m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Source, object)); +} + +void ConnectionEdit::setTarget(Connection *con, const QString &obj_name) +{ + QObject *object = 0; + if (!obj_name.isEmpty()) { + object = qFindChild(m_bg_widget, obj_name); + if (object == 0 && m_bg_widget->objectName() == obj_name) + object = m_bg_widget; + + if (object == con->object(EndPoint::Target)) + return; + } + m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Target, object)); +} + +Connection *ConnectionEdit::takeConnection(Connection *con) +{ + if (!m_con_list.contains(con)) + return 0; + m_con_list.removeAll(con); + return con; +} + +void ConnectionEdit::clearNewlyAddedConnection() +{ + delete m_tmp_con; + m_tmp_con = 0; +} + +void ConnectionEdit::createContextMenu(QMenu &menu) +{ + // Select + QAction *selectAllAction = menu.addAction(tr("Select All")); + selectAllAction->setEnabled(connectionList().size()); + connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll())); + QAction *deselectAllAction = menu.addAction(tr("Deselect All")); + deselectAllAction->setEnabled(selection().size()); + connect(deselectAllAction, SIGNAL(triggered()), this, SLOT(selectNone())); + menu.addSeparator(); + // Delete + QAction *deleteAction = menu.addAction(tr("Delete")); + deleteAction->setShortcut(QKeySequence::Delete); + deleteAction->setEnabled(!selection().isEmpty()); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); +} + +void ConnectionEdit::contextMenuEvent(QContextMenuEvent * event) +{ + QMenu menu; + createContextMenu(menu); + menu.exec(event->globalPos()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/connectionedit_p.h b/designer/lib/shared/connectionedit_p.h new file mode 100644 index 0000000..39167ba --- /dev/null +++ b/designer/lib/shared/connectionedit_p.h @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef CONNECTIONEDIT_H +#define CONNECTIONEDIT_H + +#include "shared_global_p.h" + +#include +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QUndoStack; +class QMenu; + +namespace qdesigner_internal { + +class Connection; +class ConnectionEdit; + +class QDESIGNER_SHARED_EXPORT CETypes +{ +public: + typedef QList ConnectionList; + typedef QMap ConnectionSet; + typedef QMap WidgetSet; + + class EndPoint { + public: + enum Type { Source, Target }; + explicit EndPoint(Connection *_con = 0, Type _type = Source) : con(_con), type(_type) {} + bool isNull() const { return con == 0; } + bool operator == (const EndPoint &other) const { return con == other.con && type == other.type; } + bool operator != (const EndPoint &other) const { return !operator == (other); } + Connection *con; + Type type; + }; + enum LineDir { UpDir = 0, DownDir, RightDir, LeftDir }; +}; + +class QDESIGNER_SHARED_EXPORT Connection : public CETypes +{ +public: + explicit Connection(ConnectionEdit *edit); + explicit Connection(ConnectionEdit *edit, QObject *source, QObject *target); + virtual ~Connection() {} + + QObject *object(EndPoint::Type type) const + { + return (type == EndPoint::Source ? m_source : m_target); + } + + QWidget *widget(EndPoint::Type type) const + { + return qobject_cast(object(type)); + } + + QPoint endPointPos(EndPoint::Type type) const; + QRect endPointRect(EndPoint::Type) const; + void setEndPoint(EndPoint::Type type, QObject *w, const QPoint &pos) + { type == EndPoint::Source ? setSource(w, pos) : setTarget(w, pos); } + + bool isVisible() const; + virtual void updateVisibility(); + void setVisible(bool b); + + virtual QRegion region() const; + bool contains(const QPoint &pos) const; + virtual void paint(QPainter *p) const; + + void update(bool update_widgets = true) const; + void checkWidgets(); + + QString label(EndPoint::Type type) const + { return type == EndPoint::Source ? m_source_label : m_target_label; } + void setLabel(EndPoint::Type type, const QString &text); + QRect labelRect(EndPoint::Type type) const; + QPixmap labelPixmap(EndPoint::Type type) const + { return type == EndPoint::Source ? m_source_label_pm : m_target_label_pm; } + + ConnectionEdit *edit() const { return m_edit; } + + virtual void inserted() {} + virtual void removed() {} + +private: + QPoint m_source_pos, m_target_pos; + QObject *m_source, *m_target; + QList m_knee_list; + QPolygonF m_arrow_head; + ConnectionEdit *m_edit; + QString m_source_label, m_target_label; + QPixmap m_source_label_pm, m_target_label_pm; + QRect m_source_rect, m_target_rect; + bool m_visible; + + void setSource(QObject *source, const QPoint &pos); + void setTarget(QObject *target, const QPoint &pos); + void updateKneeList(); + void trimLine(); + void updatePixmap(EndPoint::Type type); + LineDir labelDir(EndPoint::Type type) const; + bool ground() const; + QRect groundRect() const; +}; + +class QDESIGNER_SHARED_EXPORT ConnectionEdit : public QWidget, public CETypes +{ + Q_OBJECT +public: + ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form); + virtual ~ConnectionEdit(); + + inline const QPointer &background() const { return m_bg_widget; } + + void setSelected(Connection *con, bool sel); + bool selected(const Connection *con) const; + + int connectionCount() const { return m_con_list.size(); } + Connection *connection(int i) const { return m_con_list.at(i); } + int indexOfConnection(Connection *con) const { return m_con_list.indexOf(con); } + + virtual void setSource(Connection *con, const QString &obj_name); + virtual void setTarget(Connection *con, const QString &obj_name); + + QUndoStack *undoStack() const { return m_undo_stack; } + + void clear(); + + void showEvent(QShowEvent * /*e*/) + { + updateBackground(); + } + +signals: + void aboutToAddConnection(int idx); + void connectionAdded(Connection *con); + void aboutToRemoveConnection(Connection *con); + void connectionRemoved(int idx); + void connectionSelected(Connection *con); + void widgetActivated(QWidget *wgt); + void connectionChanged(Connection *con); + +public slots: + void selectNone(); + void selectAll(); + virtual void deleteSelected(); + virtual void setBackground(QWidget *background); + virtual void updateBackground(); + virtual void widgetRemoved(QWidget *w); + virtual void objectRemoved(QObject *o); + + void updateLines(); + void enableUpdateBackground(bool enable); + +protected: + virtual void paintEvent(QPaintEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void contextMenuEvent(QContextMenuEvent * event); + + virtual Connection *createConnection(QWidget *source, QWidget *target); + virtual void modifyConnection(Connection *con); + + virtual QWidget *widgetAt(const QPoint &pos) const; + virtual void createContextMenu(QMenu &menu); + void addConnection(Connection *con); + QRect widgetRect(QWidget *w) const; + + enum State { Editing, Connecting, Dragging }; + State state() const; + + virtual void endConnection(QWidget *target, const QPoint &pos); + + const ConnectionList &connectionList() const { return m_con_list; } + const ConnectionSet &selection() const { return m_sel_con_set; } + Connection *takeConnection(Connection *con); + Connection *newlyAddedConnection() { return m_tmp_con; } + void clearNewlyAddedConnection(); + + void findObjectsUnderMouse(const QPoint &pos); + +private: + void startConnection(QWidget *source, const QPoint &pos); + void continueConnection(QWidget *target, const QPoint &pos); + void abortConnection(); + + void startDrag(const EndPoint &end_point, const QPoint &pos); + void continueDrag(const QPoint &pos); + void endDrag(const QPoint &pos); + void adjustHotSopt(const EndPoint &end_point, const QPoint &pos); + Connection *connectionAt(const QPoint &pos) const; + EndPoint endPointAt(const QPoint &pos) const; + void paintConnection(QPainter *p, Connection *con, + WidgetSet *heavy_highlight_set, + WidgetSet *light_highlight_set) const; + void paintLabel(QPainter *p, EndPoint::Type type, Connection *con); + + + QPointer m_bg_widget; + QUndoStack *m_undo_stack; + bool m_enable_update_background; + + Connection *m_tmp_con; // the connection we are currently editing + ConnectionList m_con_list; + bool m_start_connection_on_drag; + EndPoint m_end_point_under_mouse; + QPointer m_widget_under_mouse; + + EndPoint m_drag_end_point; + QPoint m_old_source_pos, m_old_target_pos; + ConnectionSet m_sel_con_set; + const QColor m_inactive_color; + const QColor m_active_color; + +private: + friend class Connection; + friend class AddConnectionCommand; + friend class DeleteConnectionsCommand; + friend class SetEndPointCommand; +}; + +class QDESIGNER_SHARED_EXPORT CECommand : public QUndoCommand, public CETypes +{ +public: + explicit CECommand(ConnectionEdit *edit) + : m_edit(edit) {} + + virtual bool mergeWith(const QUndoCommand *) { return false; } + + ConnectionEdit *edit() const { return m_edit; } + +private: + ConnectionEdit *m_edit; +}; + +class QDESIGNER_SHARED_EXPORT AddConnectionCommand : public CECommand +{ +public: + AddConnectionCommand(ConnectionEdit *edit, Connection *con); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; +}; + +class QDESIGNER_SHARED_EXPORT DeleteConnectionsCommand : public CECommand +{ +public: + DeleteConnectionsCommand(ConnectionEdit *edit, const ConnectionList &con_list); + virtual void redo(); + virtual void undo(); +private: + ConnectionList m_con_list; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CONNECTIONEDIT_H diff --git a/designer/lib/shared/csshighlighter.cpp b/designer/lib/shared/csshighlighter.cpp new file mode 100644 index 0000000..b72b218 --- /dev/null +++ b/designer/lib/shared/csshighlighter.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "csshighlighter_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +CssHighlighter::CssHighlighter(QTextDocument *document) +: QSyntaxHighlighter(document) +{ +} + +void CssHighlighter::highlightBlock(const QString& text) +{ + enum Token { ALNUM, LBRACE, RBRACE, COLON, SEMICOLON, COMMA, QUOTE, SLASH, STAR }; + static const int transitions[10][9] = { + { Selector, Property, Selector, Pseudo, Property, Selector, Quote, MaybeComment, Selector }, // Selector + { Property, Property, Selector, Value, Property, Property, Quote, MaybeComment, Property }, // Property + { Value, Property, Selector, Value, Property, Value, Quote, MaybeComment, Value }, // Value + { Pseudo1, Property, Selector, Pseudo2, Selector, Selector, Quote, MaybeComment, Pseudo }, // Pseudo + { Pseudo1, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo1 }, // Pseudo1 + { Pseudo2, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo2 }, // Pseudo2 + { Quote, Quote, Quote, Quote, Quote, Quote, -1, Quote, Quote }, // Quote + { -1, -1, -1, -1, -1, -1, -1, -1, Comment }, // MaybeComment + { Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, MaybeCommentEnd }, // Comment + { Comment, Comment, Comment, Comment, Comment, Comment, Comment, -1, MaybeCommentEnd } // MaybeCommentEnd + }; + + int lastIndex = 0; + bool lastWasSlash = false; + int state = previousBlockState(), save_state; + if (state == -1) { + // As long as the text is empty, leave the state undetermined + if (text.isEmpty()) { + setCurrentBlockState(-1); + return; + } + // The initial state is based on the precense of a : and the absense of a {. + // This is because Qt style sheets support both a full stylesheet as well as + // an inline form with just properties. + state = save_state = (text.indexOf(QLatin1Char(':')) > -1 && + text.indexOf(QLatin1Char('{')) == -1) ? Property : Selector; + } else { + save_state = state>>16; + state &= 0x00ff; + } + + if (state == MaybeCommentEnd) { + state = Comment; + } else if (state == MaybeComment) { + state = save_state; + } + + for (int i = 0; i < text.length(); i++) { + int token = ALNUM; + const QChar c = text.at(i); + const char a = c.toAscii(); + + if (state == Quote) { + if (a == '\\') { + lastWasSlash = true; + } else { + if (a == '\"' && !lastWasSlash) { + token = QUOTE; + } + lastWasSlash = false; + } + } else { + switch (a) { + case '{': token = LBRACE; break; + case '}': token = RBRACE; break; + case ':': token = COLON; break; + case ';': token = SEMICOLON; break; + case ',': token = COMMA; break; + case '\"': token = QUOTE; break; + case '/': token = SLASH; break; + case '*': token = STAR; break; + default: break; + } + } + + int new_state = transitions[state][token]; + + if (new_state != state) { + bool include_token = new_state == MaybeCommentEnd || (state == MaybeCommentEnd && new_state!= Comment) + || state == Quote; + highlight(text, lastIndex, i-lastIndex+include_token, state); + + if (new_state == Comment) { + lastIndex = i-1; // include the slash and star + } else { + lastIndex = i + ((token == ALNUM || new_state == Quote) ? 0 : 1); + } + } + + if (new_state == -1) { + state = save_state; + } else if (state <= Pseudo2) { + save_state = state; + state = new_state; + } else { + state = new_state; + } + } + + highlight(text, lastIndex, text.length() - lastIndex, state); + setCurrentBlockState(state + (save_state<<16)); +} + +void CssHighlighter::highlight(const QString &text, int start, int length, int state) +{ + if (start >= text.length() || length <= 0) + return; + + QTextCharFormat format; + + switch (state) { + case Selector: + setFormat(start, length, Qt::darkRed); + break; + case Property: + setFormat(start, length, Qt::blue); + break; + case Value: + setFormat(start, length, Qt::black); + break; + case Pseudo1: + setFormat(start, length, Qt::darkRed); + break; + case Pseudo2: + setFormat(start, length, Qt::darkRed); + break; + case Quote: + setFormat(start, length, Qt::darkMagenta); + break; + case Comment: + case MaybeCommentEnd: + format.setForeground(Qt::darkGreen); + setFormat(start, length, format); + break; + default: + break; + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/csshighlighter_p.h b/designer/lib/shared/csshighlighter_p.h new file mode 100644 index 0000000..3105a1b --- /dev/null +++ b/designer/lib/shared/csshighlighter_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef CSSHIGHLIGHTER_H +#define CSSHIGHLIGHTER_H + +#include +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT CssHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + explicit CssHighlighter(QTextDocument *document); + +protected: + void highlightBlock(const QString&); + void highlight(const QString&, int, int, int/*State*/); + +private: + enum State { Selector, Property, Value, Pseudo, Pseudo1, Pseudo2, Quote, + MaybeComment, Comment, MaybeCommentEnd }; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CSSHIGHLIGHTER_H diff --git a/designer/lib/shared/defaultgradients.xml b/designer/lib/shared/defaultgradients.xml new file mode 100644 index 0000000..70559ad --- /dev/null +++ b/designer/lib/shared/defaultgradients.xml @@ -0,0 +1,498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/designer/lib/shared/deviceprofile.cpp b/designer/lib/shared/deviceprofile.cpp new file mode 100644 index 0000000..5761a94 --- /dev/null +++ b/designer/lib/shared/deviceprofile.cpp @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "deviceprofile_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +static const char *dpiXPropertyC = "_q_customDpiX"; +static const char *dpiYPropertyC = "_q_customDpiY"; + +// XML serialization +static const char *xmlVersionC="1.0"; +static const char *rootElementC="deviceprofile"; +static const char *nameElementC = "name"; +static const char *fontFamilyElementC = "fontfamily"; +static const char *fontPointSizeElementC = "fontpointsize"; +static const char *dPIXElementC = "dpix"; +static const char *dPIYElementC = "dpiy"; +static const char *styleElementC = "style"; + +/* DeviceProfile: + * For preview purposes (preview, widget box, new form dialog), the + * QDesignerFormBuilder applies the settings to the form main container + * (Point being here that the DPI must be set directly for size calculations + * to be correct). + * For editing purposes, FormWindow applies the settings to the form container + * as not to interfere with the font settings of the form main container. + * In addition, the widgetfactory maintains the system settings style + * and applies it when creating widgets. */ + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---------------- DeviceProfileData +class DeviceProfileData : public QSharedData { +public: + DeviceProfileData(); + void fromSystem(); + void clear(); + + QString m_fontFamily; + int m_fontPointSize; + QString m_style; + int m_dpiX; + int m_dpiY; + QString m_name; +}; + +DeviceProfileData::DeviceProfileData() : + m_fontPointSize(-1), + m_dpiX(-1), + m_dpiY(-1) +{ +} + +void DeviceProfileData::clear() +{ + m_fontPointSize = -1; + m_dpiX = 0; + m_dpiY = 0; + m_name.clear(); + m_style.clear(); +} + +void DeviceProfileData::fromSystem() +{ + const QFont appFont = QApplication::font(); + m_fontFamily = appFont.family(); + m_fontPointSize = appFont.pointSize(); + DeviceProfile::systemResolution(&m_dpiX, &m_dpiY); + m_style.clear(); +} + +// ---------------- DeviceProfile +DeviceProfile::DeviceProfile() : + m_d(new DeviceProfileData) +{ +} + +DeviceProfile::DeviceProfile(const DeviceProfile &o) : + m_d(o.m_d) + +{ +} + +DeviceProfile& DeviceProfile::operator=(const DeviceProfile &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +DeviceProfile::~DeviceProfile() +{ +} + +void DeviceProfile::clear() +{ + m_d->clear(); +} + +bool DeviceProfile::isEmpty() const +{ + return m_d->m_name.isEmpty(); +} + +QString DeviceProfile::fontFamily() const +{ + return m_d->m_fontFamily; +} + +void DeviceProfile::setFontFamily(const QString &f) +{ + m_d->m_fontFamily = f; +} + +int DeviceProfile::fontPointSize() const +{ + return m_d->m_fontPointSize; +} + +void DeviceProfile::setFontPointSize(int p) +{ + m_d->m_fontPointSize = p; +} + +QString DeviceProfile::style() const +{ + return m_d->m_style; +} + +void DeviceProfile::setStyle(const QString &s) +{ + m_d->m_style = s; +} + +int DeviceProfile::dpiX() const +{ + return m_d->m_dpiX; +} + +void DeviceProfile::setDpiX(int d) +{ + m_d->m_dpiX = d; +} + +int DeviceProfile::dpiY() const +{ + return m_d->m_dpiY; +} + +void DeviceProfile::setDpiY(int d) +{ + m_d->m_dpiY = d; +} + +void DeviceProfile::fromSystem() +{ + m_d->fromSystem(); +} + +QString DeviceProfile::name() const +{ + return m_d->m_name; +} + +void DeviceProfile::setName(const QString &n) +{ + m_d->m_name = n; +} + +void DeviceProfile::systemResolution(int *dpiX, int *dpiY) +{ + const QDesktopWidget *dw = qApp->desktop(); + *dpiX = dw->logicalDpiX(); + *dpiY = dw->logicalDpiY(); +} + +class FriendlyWidget : public QWidget { + friend class DeviceProfile; +}; + +void DeviceProfile::widgetResolution(const QWidget *w, int *dpiX, int *dpiY) +{ + const FriendlyWidget *fw = static_cast(w); + *dpiX = fw->metric(QPaintDevice::PdmDpiX); + *dpiY = fw->metric(QPaintDevice::PdmDpiY); +} + +QString DeviceProfile::toString() const +{ + const DeviceProfileData &d = *m_d; + QString rc; + QTextStream(&rc) << "DeviceProfile:name=" << d.m_name << " Font=" << d.m_fontFamily << ' ' + << d.m_fontPointSize << " Style=" << d.m_style << " DPI=" << d.m_dpiX << ',' << d.m_dpiY; + return rc; +} + +// Apply font to widget +static void applyFont(const QString &family, int size, DeviceProfile::ApplyMode am, QWidget *widget) +{ + QFont currentFont = widget->font(); + if (currentFont.pointSize() == size && currentFont.family() == family) + return; + switch (am) { + case DeviceProfile::ApplyFormParent: + // Invisible form parent: Apply all + widget->setFont(QFont(family, size)); + break; + case DeviceProfile::ApplyPreview: { + // Preview: Apply only subproperties that have not been changed by designer properties + bool apply = false; + const uint resolve = currentFont.resolve(); + if (!(resolve & QFont::FamilyResolved)) { + currentFont.setFamily(family); + apply = true; + } + if (!(resolve & QFont::SizeResolved)) { + currentFont.setPointSize(size); + apply = true; + } + if (apply) + widget->setFont(currentFont); + } + break; + } +} + +void DeviceProfile::applyDPI(int dpiX, int dpiY, QWidget *widget) +{ + int sysDPIX, sysDPIY; // Set dynamic variables in case values are different from system DPI + systemResolution(&sysDPIX, &sysDPIY); + if (dpiX != sysDPIX && dpiY != sysDPIY) { + widget->setProperty(dpiXPropertyC, QVariant(dpiX)); + widget->setProperty(dpiYPropertyC, QVariant(dpiY)); + } +} + +void DeviceProfile::apply(const QDesignerFormEditorInterface *core, QWidget *widget, ApplyMode am) const +{ + if (isEmpty()) + return; + + const DeviceProfileData &d = *m_d; + + if (!d.m_fontFamily.isEmpty()) + applyFont(d.m_fontFamily, d.m_fontPointSize, am, widget); + + applyDPI(d.m_dpiX, d.m_dpiY, widget); + + if (!d.m_style.isEmpty()) { + if (WidgetFactory *wf = qobject_cast(core->widgetFactory())) + wf->applyStyleTopLevel(d.m_style, widget); + } +} + +bool DeviceProfile::equals(const DeviceProfile& rhs) const +{ + const DeviceProfileData &d = *m_d; + const DeviceProfileData &rhs_d = *rhs.m_d; + return d.m_fontPointSize == rhs_d.m_fontPointSize && + d.m_dpiX == rhs_d.m_dpiX && d.m_dpiY == rhs_d.m_dpiY && d.m_fontFamily == rhs_d.m_fontFamily && + d.m_style == rhs_d.m_style && d.m_name == rhs_d.m_name; +} + +static inline void writeElement(QXmlStreamWriter &writer, const QString &element, const QString &cdata) +{ + writer.writeStartElement(element); + writer.writeCharacters(cdata); + writer.writeEndElement(); +} + +QString DeviceProfile::toXml() const +{ + const DeviceProfileData &d = *m_d; + QString rc; + QXmlStreamWriter writer(&rc); + writer.writeStartDocument(QLatin1String(xmlVersionC)); + writer.writeStartElement(QLatin1String(rootElementC)); + writeElement(writer, QLatin1String(nameElementC), d.m_name); + + if (!d.m_fontFamily.isEmpty()) + writeElement(writer, QLatin1String(fontFamilyElementC), d.m_fontFamily); + if (d.m_fontPointSize >= 0) + writeElement(writer, QLatin1String(fontPointSizeElementC), QString::number(d.m_fontPointSize)); + if (d.m_dpiX > 0) + writeElement(writer, QLatin1String(dPIXElementC), QString::number(d.m_dpiX)); + if (d.m_dpiY > 0) + writeElement(writer, QLatin1String(dPIYElementC), QString::number(d.m_dpiY)); + if (!d.m_style.isEmpty()) + writeElement(writer, QLatin1String(styleElementC), d.m_style); + + writer.writeEndElement(); + writer.writeEndDocument(); + return rc; +} + +/* Switch stages when encountering a start element (state table) */ +enum ParseStage { ParseBeginning, ParseWithinRoot, + ParseName, ParseFontFamily, ParseFontPointSize, ParseDPIX, ParseDPIY, ParseStyle, + ParseError }; + +static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement) +{ + switch (currentStage) { + case ParseBeginning: + if (startElement == QLatin1String(rootElementC)) + return ParseWithinRoot; + break; + case ParseWithinRoot: + case ParseName: + case ParseFontFamily: + case ParseFontPointSize: + case ParseDPIX: + case ParseDPIY: + case ParseStyle: + if (startElement == QLatin1String(nameElementC)) + return ParseName; + if (startElement == QLatin1String(fontFamilyElementC)) + return ParseFontFamily; + if (startElement == QLatin1String(fontPointSizeElementC)) + return ParseFontPointSize; + if (startElement == QLatin1String(dPIXElementC)) + return ParseDPIX; + if (startElement == QLatin1String(dPIYElementC)) + return ParseDPIY; + if (startElement == QLatin1String(styleElementC)) + return ParseStyle; + break; + case ParseError: + break; + } + return ParseError; +} + +static bool readIntegerElement(QXmlStreamReader &reader, int *v) +{ + const QString e = reader.readElementText(); + bool ok; + *v = e.toInt(&ok); + //: Reading a number for an embedded device profile + if (!ok) + reader.raiseError(QApplication::translate("DeviceProfile", "'%1' is not a number.").arg(e)); + return ok; +} + +bool DeviceProfile::fromXml(const QString &xml, QString *errorMessage) +{ + DeviceProfileData &d = *m_d; + d.fromSystem(); + + QXmlStreamReader reader(xml); + + ParseStage ps = ParseBeginning; + QXmlStreamReader::TokenType tt = QXmlStreamReader::NoToken; + int iv = 0; + do { + tt = reader.readNext(); + if (tt == QXmlStreamReader::StartElement) { + ps = nextStage(ps, reader.name()); + switch (ps) { + case ParseBeginning: + case ParseWithinRoot: + break; + case ParseError: + reader.raiseError(QApplication::translate("DeviceProfile", "An invalid tag <%1> was encountered.").arg(reader.name().toString())); + tt = QXmlStreamReader::Invalid; + break; + case ParseName: + d.m_name = reader.readElementText(); + break; + case ParseFontFamily: + d.m_fontFamily = reader.readElementText(); + break; + case ParseFontPointSize: + if (readIntegerElement(reader, &iv)) { + d.m_fontPointSize = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseDPIX: + if (readIntegerElement(reader, &iv)) { + d.m_dpiX = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseDPIY: + if (readIntegerElement(reader, &iv)) { + d.m_dpiY = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseStyle: + d.m_style = reader.readElementText(); + break; + } + } + } while (tt != QXmlStreamReader::Invalid && tt != QXmlStreamReader::EndDocument); + + if (reader.hasError()) { + *errorMessage = reader.errorString(); + return false; + } + + return true; +} +} + +QT_END_NAMESPACE + diff --git a/designer/lib/shared/deviceprofile_p.h b/designer/lib/shared/deviceprofile_p.h new file mode 100644 index 0000000..fb58bd9 --- /dev/null +++ b/designer/lib/shared/deviceprofile_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DEVICEPROFILE_H +#define DEVICEPROFILE_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QWidget; +class QStyle; + +namespace qdesigner_internal { + +class DeviceProfileData; + +/* DeviceProfile for embedded design. They influence + * default properties (for example, fonts), dpi and + * style of the form. This class represents a device + * profile. */ + +class QDESIGNER_SHARED_EXPORT DeviceProfile { +public: + DeviceProfile(); + + DeviceProfile(const DeviceProfile&); + DeviceProfile& operator=(const DeviceProfile&); + ~DeviceProfile(); + + void clear(); + + // Device name + QString name() const; + void setName(const QString &); + + // System settings active + bool isEmpty() const; + + // Default font family of the embedded system + QString fontFamily() const; + void setFontFamily(const QString &); + + // Default font size of the embedded system + int fontPointSize() const; + void setFontPointSize(int p); + + // Display resolution of the embedded system + int dpiX() const; + void setDpiX(int d); + int dpiY() const; + void setDpiY(int d); + + // Style + QString style() const; + void setStyle(const QString &); + + // Initialize from desktop system + void fromSystem(); + + static void systemResolution(int *dpiX, int *dpiY); + static void widgetResolution(const QWidget *w, int *dpiX, int *dpiY); + + bool equals(const DeviceProfile& rhs) const; + + // Apply to form/preview (using font inheritance) + enum ApplyMode { + /* Pre-Apply to parent widget of form being edited: Apply font + * and make use of property inheritance to be able to modify the + * font property freely. */ + ApplyFormParent, + /* Post-Apply to preview widget: Change only inherited font + * sub properties. */ + ApplyPreview + }; + void apply(const QDesignerFormEditorInterface *core, QWidget *widget, ApplyMode am) const; + + static void applyDPI(int dpiX, int dpiY, QWidget *widget); + + QString toString() const; + + QString toXml() const; + bool fromXml(const QString &xml, QString *errorMessage); + +private: + QSharedDataPointer m_d; +}; + +inline bool operator==(const DeviceProfile &s1, const DeviceProfile &s2) + { return s1.equals(s2); } +inline bool operator!=(const DeviceProfile &s1, const DeviceProfile &s2) + { return !s1.equals(s2); } + +} + + +QT_END_NAMESPACE + +#endif // DEVICEPROFILE_H diff --git a/designer/lib/shared/dialoggui.cpp b/designer/lib/shared/dialoggui.cpp new file mode 100644 index 0000000..151b059 --- /dev/null +++ b/designer/lib/shared/dialoggui.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dialoggui_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +// QFileDialog on X11 does not provide an image preview. Display icons. +#ifdef Q_WS_X11 +# define IMAGE_PREVIEW +#endif + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Icon provider that reads out the known image formats +class IconProvider : public QFileIconProvider { + Q_DISABLE_COPY(IconProvider) + +public: + IconProvider(); + virtual QIcon icon (const QFileInfo &info) const; + + inline bool loadCheck(const QFileInfo &info) const; + QImage loadImage(const QString &fileName) const; + +private: + QSet m_imageFormats; +}; + +IconProvider::IconProvider() +{ + // Determine a list of readable extensions (upper and lower case) + typedef QList ByteArrayList; + const ByteArrayList fmts = QImageReader::supportedImageFormats(); + const ByteArrayList::const_iterator cend = fmts.constEnd(); + for (ByteArrayList::const_iterator it = fmts.constBegin(); it != cend; ++it) { + const QString suffix = QString::fromUtf8(it->constData()); + m_imageFormats.insert(suffix.toLower()); + m_imageFormats.insert(suffix.toUpper()); + + } +} + +// Check by extension and type if this appears to be a loadable image +bool IconProvider::loadCheck(const QFileInfo &info) const +{ + if (info.isFile() && info.isReadable()) { + const QString suffix = info.suffix(); + if (!suffix.isEmpty()) + return m_imageFormats.contains(suffix); + } + return false; +} + +QImage IconProvider::loadImage(const QString &fileName) const +{ + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + QImageReader imgReader(&file); + if (imgReader.canRead()) { + QImage image; + if (imgReader.read(&image)) + return image; + } + } + return QImage(); +} + +QIcon IconProvider::icon (const QFileInfo &info) const +{ + // Don't get stuck on large images. + const qint64 maxSize = 131072; + if (loadCheck(info) && info.size() < maxSize) { + const QImage image = loadImage(info.absoluteFilePath()); + if (!image.isNull()) + return QIcon(QPixmap::fromImage(image, Qt::ThresholdDither|Qt::AutoColor)); + } + return QFileIconProvider::icon(info); +} + +// ---------------- DialogGui +DialogGui::DialogGui() : + m_iconProvider(0) +{ +} + +DialogGui::~DialogGui() +{ + delete m_iconProvider; +} + +QFileIconProvider *DialogGui::ensureIconProvider() +{ + if (!m_iconProvider) + m_iconProvider = new IconProvider; + return m_iconProvider; +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) +{ + QMessageBox::StandardButton rc = QMessageBox::NoButton; + switch (icon) { + case QMessageBox::Information: + rc = QMessageBox::information(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Warning: + rc = QMessageBox::warning(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Critical: + rc = QMessageBox::critical(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Question: + rc = QMessageBox::question(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::NoIcon: + break; + } + return rc; +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) +{ + QMessageBox msgBox(icon, title, text, buttons, parent); + msgBox.setDefaultButton(defaultButton); + msgBox.setInformativeText(informativeText); + return static_cast(msgBox.exec()); +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, const QString &detailedText, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) +{ + QMessageBox msgBox(icon, title, text, buttons, parent); + msgBox.setDefaultButton(defaultButton); + msgBox.setInformativeText(informativeText); + msgBox.setDetailedText(detailedText); + return static_cast(msgBox.exec()); +} + +QString DialogGui::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) +{ + return QFileDialog::getExistingDirectory(parent, caption, dir, options); +} + +QString DialogGui::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +} + +QStringList DialogGui::getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); +} + +QString DialogGui::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); +} + +void DialogGui::initializeImageFileDialog(QFileDialog &fileDialog, QFileDialog::Options options, QFileDialog::FileMode fm) +{ + fileDialog.setConfirmOverwrite( !(options & QFileDialog::DontConfirmOverwrite) ); + fileDialog.setResolveSymlinks( !(options & QFileDialog::DontResolveSymlinks) ); + fileDialog.setIconProvider(ensureIconProvider()); + fileDialog.setFileMode(fm); +} + +QString DialogGui::getOpenImageFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options ) +{ + +#ifdef IMAGE_PREVIEW + QFileDialog fileDialog(parent, caption, dir, filter); + initializeImageFileDialog(fileDialog, options, QFileDialog::ExistingFile); + if (fileDialog.exec() != QDialog::Accepted) + return QString(); + + const QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.empty()) + return QString(); + + if (selectedFilter) + *selectedFilter = fileDialog.selectedFilter(); + + return selectedFiles.front(); +#else + return getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +#endif +} + +QStringList DialogGui::getOpenImageFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options ) +{ +#ifdef IMAGE_PREVIEW + QFileDialog fileDialog(parent, caption, dir, filter); + initializeImageFileDialog(fileDialog, options, QFileDialog::ExistingFiles); + if (fileDialog.exec() != QDialog::Accepted) + return QStringList(); + + const QStringList selectedFiles = fileDialog.selectedFiles(); + if (!selectedFiles.empty() && selectedFilter) + *selectedFilter = fileDialog.selectedFilter(); + + return selectedFiles; +#else + return getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); +#endif +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/dialoggui_p.h b/designer/lib/shared/dialoggui_p.h new file mode 100644 index 0000000..80075b1 --- /dev/null +++ b/designer/lib/shared/dialoggui_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIALOGGUI +#define DIALOGGUI + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "shared_global_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QFileIconProvider; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT DialogGui : public QDesignerDialogGuiInterface +{ +public: + DialogGui(); + virtual ~DialogGui(); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, const QString &detailedText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QString getExistingDirectory(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), QFileDialog::Options options = QFileDialog::ShowDirsOnly); + virtual QString getOpenFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QStringList getOpenFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QString getSaveFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + + virtual QString getOpenImageFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QStringList getOpenImageFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + +private: + QFileIconProvider *ensureIconProvider(); + void initializeImageFileDialog(QFileDialog &fd, QFileDialog::Options options, QFileDialog::FileMode); + + QFileIconProvider *m_iconProvider; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DIALOGGUI diff --git a/designer/lib/shared/extensionfactory_p.h b/designer/lib/shared/extensionfactory_p.h new file mode 100644 index 0000000..32de45e --- /dev/null +++ b/designer/lib/shared/extensionfactory_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_EXTENSIONFACTORY_H +#define SHARED_EXTENSIONFACTORY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Extension factory for registering an extension for an object type. +template +class ExtensionFactory: public QExtensionFactory +{ +public: + explicit ExtensionFactory(const QString &iid, QExtensionManager *parent = 0); + + // Convenience for registering the extension. Do not use for derived classes. + static void registerExtension(QExtensionManager *mgr, const QString &iid); + +protected: + virtual QObject *createExtension(QObject *qObject, const QString &iid, QObject *parent) const; + +private: + // Can be overwritten to perform checks on the object. + // Default does a qobject_cast to the desired class. + virtual Object *checkObject(QObject *qObject) const; + + const QString m_iid; +}; + +template +ExtensionFactory::ExtensionFactory(const QString &iid, QExtensionManager *parent) : + QExtensionFactory(parent), + m_iid(iid) +{ +} + +template +Object *ExtensionFactory::checkObject(QObject *qObject) const +{ + return qobject_cast(qObject); +} + +template +QObject *ExtensionFactory::createExtension(QObject *qObject, const QString &iid, QObject *parent) const +{ + if (iid != m_iid) + return 0; + + Object *object = checkObject(qObject); + if (!object) + return 0; + + return new Extension(object, parent); +} + +template +void ExtensionFactory::registerExtension(QExtensionManager *mgr, const QString &iid) +{ + ExtensionFactory *factory = new ExtensionFactory(iid, mgr); + mgr->registerExtensions(factory, iid); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHARED_EXTENSIONFACTORY_H diff --git a/designer/lib/shared/filterwidget.cpp b/designer/lib/shared/filterwidget.cpp new file mode 100644 index 0000000..3a16311 --- /dev/null +++ b/designer/lib/shared/filterwidget.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filterwidget_p.h" +#include "iconloader_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum { debugFilter = 0 }; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +HintLineEdit::HintLineEdit(QWidget *parent) : + QLineEdit(parent), + m_defaultFocusPolicy(focusPolicy()), + m_refuseFocus(false) +{ +} + +IconButton::IconButton(QWidget *parent) + : QToolButton(parent) +{ + setCursor(Qt::ArrowCursor); +} + +void IconButton::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + // Note isDown should really use the active state but in most styles + // this has no proper feedback + QIcon::Mode state = QIcon::Disabled; + if (isEnabled()) + state = isDown() ? QIcon::Selected : QIcon::Normal; + QPixmap iconpixmap = icon().pixmap(QSize(ICONBUTTON_SIZE, ICONBUTTON_SIZE), + state, QIcon::Off); + QRect pixmapRect = QRect(0, 0, iconpixmap.width(), iconpixmap.height()); + pixmapRect.moveCenter(rect().center()); + painter.setOpacity(m_fader); + painter.drawPixmap(pixmapRect, iconpixmap); +} + +void IconButton::animateShow(bool visible) +{ + if (visible) { + QPropertyAnimation *animation = new QPropertyAnimation(this, "fader"); + animation->setDuration(160); + animation->setEndValue(1.0); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } else { + QPropertyAnimation *animation = new QPropertyAnimation(this, "fader"); + animation->setDuration(160); + animation->setEndValue(0.0); + animation->start(QAbstractAnimation::DeleteWhenStopped); + } +} + +bool HintLineEdit::refuseFocus() const +{ + return m_refuseFocus; +} + +void HintLineEdit::setRefuseFocus(bool v) +{ + if (v == m_refuseFocus) + return; + m_refuseFocus = v; + setFocusPolicy(m_refuseFocus ? Qt::NoFocus : m_defaultFocusPolicy); +} + +void HintLineEdit::mousePressEvent(QMouseEvent *e) +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + // Explicitly focus on click. + if (m_refuseFocus && !hasFocus()) + setFocus(Qt::OtherFocusReason); + QLineEdit::mousePressEvent(e); +} + +void HintLineEdit::focusInEvent(QFocusEvent *e) +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + if (m_refuseFocus) { + // Refuse the focus if the mouse it outside. In addition to the mouse + // press logic, this prevents a re-focussing which occurs once + // we actually had focus + const Qt::FocusReason reason = e->reason(); + if (reason == Qt::ActiveWindowFocusReason || reason == Qt::PopupFocusReason) { + const QPoint mousePos = mapFromGlobal(QCursor::pos()); + const bool refuse = !geometry().contains(mousePos); + if (debugFilter) + qDebug() << Q_FUNC_INFO << refuse ; + if (refuse) { + e->ignore(); + return; + } + } + } + + QLineEdit::focusInEvent(e); +} + +// ------------------- FilterWidget +FilterWidget::FilterWidget(QWidget *parent, LayoutMode lm) : + QWidget(parent), + m_editor(new HintLineEdit(this)), + m_button(new IconButton(m_editor)), + m_buttonwidth(0) +{ + m_editor->setPlaceholderText(tr("Filter")); + + // Let the style determine minimum height for our widget + QSize size(ICONBUTTON_SIZE + 6, ICONBUTTON_SIZE + 2); + + // Note KDE does not reserve space for the highlight color + if (style()->inherits("OxygenStyle")) { + size = size.expandedTo(QSize(24, 0)); + } + + // Make room for clear icon + QMargins margins = m_editor->textMargins(); + if (layoutDirection() == Qt::LeftToRight) + margins.setRight(size.width()); + else + margins.setLeft(size.width()); + + m_editor->setTextMargins(margins); + + QHBoxLayout *l = new QHBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + if (lm == LayoutAlignRight) + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + l->addWidget(m_editor); + + // KDE has custom icons for this. Notice that icon namings are counter intuitive + // If these icons are not avaiable we use the freedesktop standard name before + // falling back to a bundled resource + QIcon icon = QIcon::fromTheme(layoutDirection() == Qt::LeftToRight ? + QLatin1String("edit-clear-locationbar-rtl") : + QLatin1String("edit-clear-locationbar-ltr"), + QIcon::fromTheme("edit-clear", createIconSet(QLatin1String("cleartext.png")))); + + m_button->setIcon(icon); + m_button->setToolTip(tr("Clear text")); + connect(m_button, SIGNAL(clicked()), this, SLOT(reset())); + connect(m_editor, SIGNAL(textChanged(QString)), this, SLOT(checkButton(QString))); + connect(m_editor, SIGNAL(textEdited(QString)), this, SIGNAL(filterChanged(QString))); +} + +QString FilterWidget::text() const +{ + return m_editor->text(); +} + +void FilterWidget::checkButton(const QString &text) +{ + if (m_oldText.isEmpty() || text.isEmpty()) + m_button->animateShow(!m_editor->text().isEmpty()); + m_oldText = text; +} + +void FilterWidget::reset() +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + + if (!m_editor->text().isEmpty()) { + // Editor has lost focus once this is pressed + m_editor->clear(); + emit filterChanged(QString()); + } +} + +void FilterWidget::resizeEvent(QResizeEvent *) +{ + QRect contentRect = m_editor->rect(); + if (layoutDirection() == Qt::LeftToRight) { + const int iconoffset = m_editor->textMargins().right() + 4; + m_button->setGeometry(contentRect.adjusted(m_editor->width() - iconoffset, 0, 0, 0)); + } else { + const int iconoffset = m_editor->textMargins().left() + 4; + m_button->setGeometry(contentRect.adjusted(0, 0, -m_editor->width() + iconoffset, 0)); + } +} + +bool FilterWidget::refuseFocus() const +{ + return m_editor->refuseFocus(); +} + +void FilterWidget::setRefuseFocus(bool v) +{ + m_editor->setRefuseFocus(v); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/filterwidget_p.h b/designer/lib/shared/filterwidget_p.h new file mode 100644 index 0000000..72c2585 --- /dev/null +++ b/designer/lib/shared/filterwidget_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef FILTERWIDGET_H +#define FILTERWIDGET_H + +#include "shared_global_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QToolButton; + +namespace qdesigner_internal { + +/* This widget should never have initial focus + * (ie, be the first widget of a dialog, else, the hint cannot be displayed. + * For situations, where it is the only focusable control (widget box), + * there is a special "refuseFocus()" mode, in which it clears the focus + * policy and focusses explicitly on click (note that setting Qt::ClickFocus + * is not sufficient for that as an ActivationFocus will occur). */ + +#define ICONBUTTON_SIZE 16 + +class QDESIGNER_SHARED_EXPORT HintLineEdit : public QLineEdit { + Q_OBJECT +public: + explicit HintLineEdit(QWidget *parent = 0); + + bool refuseFocus() const; + void setRefuseFocus(bool v); + +protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void focusInEvent(QFocusEvent *e); + +private: + const Qt::FocusPolicy m_defaultFocusPolicy; + bool m_refuseFocus; +}; + +// IconButton: This is a simple helper class that represents clickable icons + +class IconButton: public QToolButton +{ + Q_OBJECT + Q_PROPERTY(float fader READ fader WRITE setFader) +public: + IconButton(QWidget *parent); + void paintEvent(QPaintEvent *event); + float fader() { return m_fader; } + void setFader(float value) { m_fader = value; update(); } + void animateShow(bool visible); + +private: + float m_fader; +}; + +// FilterWidget: For filtering item views, with reset button Uses HintLineEdit. + +class QDESIGNER_SHARED_EXPORT FilterWidget : public QWidget +{ + Q_OBJECT +public: + enum LayoutMode { + // For use in toolbars: Expand to the right + LayoutAlignRight, + // No special alignment + LayoutAlignNone + }; + + explicit FilterWidget(QWidget *parent = 0, LayoutMode lm = LayoutAlignRight); + + QString text() const; + void resizeEvent(QResizeEvent *); + bool refuseFocus() const; // see HintLineEdit + void setRefuseFocus(bool v); + +signals: + void filterChanged(const QString &); + +public slots: + void reset(); + +private slots: + void checkButton(const QString &text); + +private: + HintLineEdit *m_editor; + IconButton *m_button; + int m_buttonwidth; + QString m_oldText; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/formlayoutmenu.cpp b/designer/lib/shared/formlayoutmenu.cpp new file mode 100644 index 0000000..cb0e94e --- /dev/null +++ b/designer/lib/shared/formlayoutmenu.cpp @@ -0,0 +1,534 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formlayoutmenu_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_propertycommand_p.h" +#include "ui_formlayoutrowdialog.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static const char *buddyPropertyC = "buddy"; +static const char *fieldWidgetBaseClasses[] = { + "QLineEdit", "QComboBox", "QSpinBox", "QDoubleSpinBox", "QCheckBox", + "QDateEdit", "QTimeEdit", "QDateTimeEdit", "QDial", "QWidget" +}; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Struct that describes a row of controls (descriptive label and control) to +// be added to a form layout. +struct FormLayoutRow { + FormLayoutRow() : buddy(false) {} + + QString labelName; + QString labelText; + QString fieldClassName; + QString fieldName; + bool buddy; +}; + +// A Dialog to edit a FormLayoutRow. Lets the user input a label text, label +// name, field widget type, field object name and buddy setting. As the +// user types the label text; the object names to be used for label and field +// are updated. It also checks the buddy setting depending on whether the +// label text contains a buddy marker. +class FormLayoutRowDialog : public QDialog { + Q_DISABLE_COPY(FormLayoutRowDialog) + Q_OBJECT +public: + explicit FormLayoutRowDialog(QDesignerFormEditorInterface *core, + QWidget *parent); + + FormLayoutRow formLayoutRow() const; + + bool buddy() const; + void setBuddy(bool); + + // Accessors for form layout row numbers using 0..[n-1] convention + int row() const; + void setRow(int); + void setRowRange(int, int); + + QString fieldClass() const; + QString labelText() const; + + static QStringList fieldWidgetClasses(QDesignerFormEditorInterface *core); + +private slots: + void labelTextEdited(const QString &text); + void labelNameEdited(const QString &text); + void fieldNameEdited(const QString &text); + void buddyClicked(); + void fieldClassChanged(int); + +private: + bool isValid() const; + void updateObjectNames(bool updateLabel, bool updateField); + void updateOkButton(); + + // Check for buddy marker in string + const QRegExp m_buddyMarkerRegexp; + + Ui::FormLayoutRowDialog m_ui; + bool m_labelNameEdited; + bool m_fieldNameEdited; + bool m_buddyClicked; +}; + +FormLayoutRowDialog::FormLayoutRowDialog(QDesignerFormEditorInterface *core, + QWidget *parent) : + QDialog(parent), + m_buddyMarkerRegexp(QLatin1String("\\&[^&]")), + m_labelNameEdited(false), + m_fieldNameEdited(false), + m_buddyClicked(false) +{ + Q_ASSERT(m_buddyMarkerRegexp.isValid()); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + m_ui.setupUi(this); + connect(m_ui.labelTextLineEdit, SIGNAL(textEdited(QString)), this, SLOT(labelTextEdited(QString))); + + QRegExpValidator *nameValidator = new QRegExpValidator(QRegExp(QLatin1String("^[a-zA-Z0-9_]+$")), this); + Q_ASSERT(nameValidator->regExp().isValid()); + + m_ui.labelNameLineEdit->setValidator(nameValidator); + connect(m_ui.labelNameLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(labelNameEdited(QString))); + + m_ui.fieldNameLineEdit->setValidator(nameValidator); + connect(m_ui.fieldNameLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(fieldNameEdited(QString))); + + connect(m_ui.buddyCheckBox, SIGNAL(clicked()), this, SLOT(buddyClicked())); + + m_ui.fieldClassComboBox->addItems(fieldWidgetClasses(core)); + m_ui.fieldClassComboBox->setCurrentIndex(0); + connect(m_ui.fieldClassComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(fieldClassChanged(int))); + + updateOkButton(); +} + +FormLayoutRow FormLayoutRowDialog::formLayoutRow() const +{ + FormLayoutRow rc; + rc.labelText = labelText(); + rc.labelName = m_ui.labelNameLineEdit->text(); + rc.fieldClassName = fieldClass(); + rc.fieldName = m_ui.fieldNameLineEdit->text(); + rc.buddy = buddy(); + return rc; +} + +bool FormLayoutRowDialog::buddy() const +{ + return m_ui.buddyCheckBox->checkState() == Qt::Checked; +} + +void FormLayoutRowDialog::setBuddy(bool b) +{ + m_ui.buddyCheckBox->setCheckState(b ? Qt::Checked : Qt::Unchecked); +} + +// Convert rows to 1..n convention for users +int FormLayoutRowDialog::row() const +{ + return m_ui.rowSpinBox->value() - 1; +} + +void FormLayoutRowDialog::setRow(int row) +{ + m_ui.rowSpinBox->setValue(row + 1); +} + +void FormLayoutRowDialog::setRowRange(int from, int to) +{ + m_ui.rowSpinBox->setMinimum(from + 1); + m_ui.rowSpinBox->setMaximum(to + 1); + m_ui.rowSpinBox->setEnabled(to - from > 0); +} + +QString FormLayoutRowDialog::fieldClass() const +{ + return m_ui.fieldClassComboBox->itemText(m_ui.fieldClassComboBox->currentIndex()); +} + +QString FormLayoutRowDialog::labelText() const +{ + return m_ui.labelTextLineEdit->text(); +} + +bool FormLayoutRowDialog::isValid() const +{ + // Check for non-empty names and presence of buddy marker if checked + const QString name = labelText(); + if (name.isEmpty() || m_ui.labelNameLineEdit->text().isEmpty() || m_ui.fieldNameLineEdit->text().isEmpty()) + return false; + if (buddy() && !name.contains(m_buddyMarkerRegexp)) + return false; + return true; +} + +void FormLayoutRowDialog::updateOkButton() +{ + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isValid()); +} + +void FormLayoutRowDialog::labelTextEdited(const QString &text) +{ + updateObjectNames(true, true); + // Set buddy if '&' is present unless the user changed it + if (!m_buddyClicked) + setBuddy(text.contains(m_buddyMarkerRegexp)); + + updateOkButton(); +} + +// Get a suitable object name postfix from a class name: +// "namespace::QLineEdit"->"LineEdit" +static inline QString postFixFromClassName(QString className) +{ + const int index = className.lastIndexOf(QLatin1String("::")); + if (index != -1) + className.remove(0, index + 2); + if (className.size() > 2) + if (className.at(0) == QLatin1Char('Q') || className.at(0) == QLatin1Char('K')) + if (className.at(1).isUpper()) + className.remove(0, 1); + return className; +} + +// Helper routines to filter out characters for converting texts into +// class name prefixes. Only accepts ASCII characters/digits and underscores. + +enum PrefixCharacterKind { PC_Digit, PC_UpperCaseLetter, PC_LowerCaseLetter, + PC_Other, PC_Invalid }; + +static inline PrefixCharacterKind prefixCharacterKind(const QChar &c) +{ + switch (c.category()) { + case QChar::Number_DecimalDigit: + return PC_Digit; + case QChar::Letter_Lowercase: { + const char a = c.toAscii(); + if (a >= 'a' && a <= 'z') + return PC_LowerCaseLetter; + } + break; + case QChar::Letter_Uppercase: { + const char a = c.toAscii(); + if (a >= 'A' && a <= 'Z') + return PC_UpperCaseLetter; + } + break; + case QChar::Punctuation_Connector: + if (c.toAscii() == '_') + return PC_Other; + break; + default: + break; + } + return PC_Invalid; +} + +// Convert the text the user types into a usable class name prefix by filtering +// characters, lower-casing the first character and camel-casing subsequent +// words. ("zip code:") --> ("zipCode"). + +static QString prefixFromLabel(const QString &prefix) +{ + QString rc; + const int length = prefix.size(); + bool lastWasAcceptable = false; + for (int i = 0 ; i < length; i++) { + const QChar c = prefix.at(i); + const PrefixCharacterKind kind = prefixCharacterKind(c); + const bool acceptable = kind != PC_Invalid; + if (acceptable) { + if (rc.isEmpty()) { + // Lower-case first character + rc += kind == PC_UpperCaseLetter ? c.toLower() : c; + } else { + // Camel-case words + rc += !lastWasAcceptable && kind == PC_LowerCaseLetter ? c.toUpper() : c; + } + } + lastWasAcceptable = acceptable; + } + return rc; +} + +void FormLayoutRowDialog::updateObjectNames(bool updateLabel, bool updateField) +{ + // Generate label + field object names from the label text, that is, + // "&Zip code:" -> "zipcodeLabel", "zipcodeLineEdit" unless the user + // edited it. + const bool doUpdateLabel = !m_labelNameEdited && updateLabel; + const bool doUpdateField = !m_fieldNameEdited && updateField; + if (!doUpdateLabel && !doUpdateField) + return; + + const QString prefix = prefixFromLabel(labelText()); + // Set names + if (doUpdateLabel) + m_ui.labelNameLineEdit->setText(prefix + QLatin1String("Label")); + if (doUpdateField) + m_ui.fieldNameLineEdit->setText(prefix + postFixFromClassName(fieldClass())); +} + +void FormLayoutRowDialog::fieldClassChanged(int) +{ + updateObjectNames(false, true); +} + +void FormLayoutRowDialog::labelNameEdited(const QString & /*text*/) +{ + m_labelNameEdited = true; // stop auto-updating after user change + updateOkButton(); +} + +void FormLayoutRowDialog::fieldNameEdited(const QString & /*text*/) +{ + m_fieldNameEdited = true; // stop auto-updating after user change + updateOkButton(); +} + +void FormLayoutRowDialog::buddyClicked() +{ + m_buddyClicked = true; // stop auto-updating after user change + updateOkButton(); +} + +/* Create a list of classes suitable for field widgets. Take the fixed base + * classes provided and look in the widget database for custom widgets derived + * from them ("QLineEdit", "CustomLineEdit", "QComboBox"...). */ +QStringList FormLayoutRowDialog::fieldWidgetClasses(QDesignerFormEditorInterface *core) +{ + // Base class -> custom widgets map + typedef QMultiHash ClassMap; + + static QStringList rc; + if (rc.empty()) { + const int fwCount = sizeof(fieldWidgetBaseClasses)/sizeof(const char*); + // Turn known base classes into list + QStringList baseClasses; + for (int i = 0; i < fwCount; i++) + baseClasses.push_back(QLatin1String(fieldWidgetBaseClasses[i])); + // Scan for custom widgets that inherit them and store them in a + // multimap of base class->custom widgets unless we have a language + // extension installed which might do funny things with custom widgets. + ClassMap customClassMap; + if (qt_extension(core->extensionManager(), core) == 0) { + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int wdbCount = wdb->count(); + for (int w = 0; w < wdbCount; ++w) { + // Check for non-container custom types that extend the + // respective base class. + const QDesignerWidgetDataBaseItemInterface *dbItem = wdb->item(w); + if (!dbItem->isPromoted() && !dbItem->isContainer() && dbItem->isCustom()) { + const int index = baseClasses.indexOf(dbItem->extends()); + if (index != -1) + customClassMap.insert(baseClasses.at(index), dbItem->name()); + } + } + } + // Compile final list, taking each base class and append custom widgets + // based on it. + for (int i = 0; i < fwCount; i++) { + rc.push_back(baseClasses.at(i)); + rc += customClassMap.values(baseClasses.at(i)); + } + } + return rc; +} + +// ------------------ Utilities + +static QFormLayout *managedFormLayout(const QDesignerFormEditorInterface *core, const QWidget *w) +{ + QLayout *l = 0; + if (LayoutInfo::managedLayoutType(core, w, &l) == LayoutInfo::Form) + return qobject_cast(l); + return 0; +} + +// Create the widgets of a control row and apply text properties contained +// in the struct, called by addFormLayoutRow() +static QPair + createWidgets(const FormLayoutRow &row, QWidget *parent, + QDesignerFormWindowInterface *formWindow) +{ + QDesignerFormEditorInterface *core = formWindow->core(); + QDesignerWidgetFactoryInterface *wf = core->widgetFactory(); + + QPair rc = QPair(wf->createWidget(QLatin1String("QLabel"), parent), + wf->createWidget(row.fieldClassName, parent)); + // Set up properties of the label + const QString objectNameProperty = QLatin1String("objectName"); + QDesignerPropertySheetExtension *labelSheet = qt_extension(core->extensionManager(), rc.first); + int nameIndex = labelSheet->indexOf(objectNameProperty); + labelSheet->setProperty(nameIndex, qVariantFromValue(PropertySheetStringValue(row.labelName))); + labelSheet->setChanged(nameIndex, true); + formWindow->ensureUniqueObjectName(rc.first); + const int textIndex = labelSheet->indexOf(QLatin1String("text")); + labelSheet->setProperty(textIndex, qVariantFromValue(PropertySheetStringValue(row.labelText))); + labelSheet->setChanged(textIndex, true); + // Set up properties of the control + QDesignerPropertySheetExtension *controlSheet = qt_extension(core->extensionManager(), rc.second); + nameIndex = controlSheet->indexOf(objectNameProperty); + controlSheet->setProperty(nameIndex, qVariantFromValue(PropertySheetStringValue(row.fieldName))); + controlSheet->setChanged(nameIndex, true); + formWindow->ensureUniqueObjectName(rc.second); + return rc; +} + +// Create a command sequence on the undo stack of the form window that creates +// the widgets of the row and inserts them into the form layout. +static void addFormLayoutRow(const FormLayoutRow &formLayoutRow, int row, QWidget *w, + QDesignerFormWindowInterface *formWindow) +{ + QFormLayout *formLayout = managedFormLayout(formWindow->core(), w); + Q_ASSERT(formLayout); + QUndoStack *undoStack = formWindow->commandHistory(); + const QString macroName = QCoreApplication::translate("Command", "Add '%1' to '%2'").arg(formLayoutRow.labelText, formLayout->objectName()); + undoStack->beginMacro(macroName); + + // Create a list of widget insertion commands and pass them a cell position + const QPair widgetPair = createWidgets(formLayoutRow, w, formWindow); + + InsertWidgetCommand *labelCmd = new InsertWidgetCommand(formWindow); + labelCmd->init(widgetPair.first, false, row, 0); + undoStack->push(labelCmd); + InsertWidgetCommand *controlCmd = new InsertWidgetCommand(formWindow); + controlCmd->init(widgetPair.second, false, row, 1); + undoStack->push(controlCmd); + if (formLayoutRow.buddy) { + SetPropertyCommand *buddyCommand = new SetPropertyCommand(formWindow); + buddyCommand->init(widgetPair.first, QLatin1String(buddyPropertyC), widgetPair.second->objectName()); + undoStack->push(buddyCommand); + } + undoStack->endMacro(); +} + +// ---------------- FormLayoutMenu +FormLayoutMenu::FormLayoutMenu(QObject *parent) : + QObject(parent), + m_separator1(new QAction(this)), + m_populateFormAction(new QAction(tr("Add form layout row..."), this)), + m_separator2(new QAction(this)) +{ + m_separator1->setSeparator(true); + connect(m_populateFormAction, SIGNAL(triggered()), this, SLOT(slotAddRow())); + m_separator2->setSeparator(true); +} + +void FormLayoutMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList &actions) +{ + switch (LayoutInfo::managedLayoutType(fw->core(), w)) { + case LayoutInfo::Form: + if (!actions.empty() && !actions.back()->isSeparator()) + actions.push_back(m_separator1); + actions.push_back(m_populateFormAction); + actions.push_back(m_separator2); + m_widget = w; + break; + default: + m_widget = 0; + break; + } +} + +void FormLayoutMenu::slotAddRow() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_widget); + Q_ASSERT(m_widget && fw); + const int rowCount = managedFormLayout(fw->core(), m_widget)->rowCount(); + + FormLayoutRowDialog dialog(fw->core(), fw); + dialog.setRowRange(0, rowCount); + dialog.setRow(rowCount); + + if (dialog.exec() != QDialog::Accepted) + return; + addFormLayoutRow(dialog.formLayoutRow(), dialog.row(), m_widget, fw); +} + +QAction *FormLayoutMenu::preferredEditAction(QWidget *w, QDesignerFormWindowInterface *fw) +{ + if (LayoutInfo::managedLayoutType(fw->core(), w) == LayoutInfo::Form) { + m_widget = w; + return m_populateFormAction; + } + return 0; +} +} + +QT_END_NAMESPACE + +#include "formlayoutmenu.moc" + diff --git a/designer/lib/shared/formlayoutmenu_p.h b/designer/lib/shared/formlayoutmenu_p.h new file mode 100644 index 0000000..b9e35f7 --- /dev/null +++ b/designer/lib/shared/formlayoutmenu_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMLAYOUTMENU +#define FORMLAYOUTMENU + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "shared_global_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QAction; +class QWidget; + +namespace qdesigner_internal { + +// Task menu to be used for form layouts. Offers an options "Add row" which +// pops up a dialog in which the user can specify label name, text and buddy. +class QDESIGNER_SHARED_EXPORT FormLayoutMenu : public QObject +{ + Q_DISABLE_COPY(FormLayoutMenu) + Q_OBJECT +public: + typedef QList ActionList; + + explicit FormLayoutMenu(QObject *parent); + + // Populate a list of actions with the form layout actions. + void populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList &actions); + // For implementing QDesignerTaskMenuExtension::preferredEditAction(): + // Return appropriate action for double clicking. + QAction *preferredEditAction(QWidget *w, QDesignerFormWindowInterface *fw); + +private slots: + void slotAddRow(); + +private: + QAction *m_separator1; + QAction *m_populateFormAction; + QAction *m_separator2; + QPointer m_widget; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMLAYOUTMENU diff --git a/designer/lib/shared/formlayoutrowdialog.ui b/designer/lib/shared/formlayoutrowdialog.ui new file mode 100644 index 0000000..c0e0cfe --- /dev/null +++ b/designer/lib/shared/formlayoutrowdialog.ui @@ -0,0 +1,166 @@ + + + FormLayoutRowDialog + + + Add Form Layout Row + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + &Label text: + + + labelTextLineEdit + + + + + + + + 180 + 0 + + + + + + + + + + + Field &type: + + + fieldClassComboBox + + + + + + + + 0 + 0 + + + + + + + + &Field name: + + + fieldNameLineEdit + + + + + + + &Buddy: + + + buddyCheckBox + + + + + + + + + + + + + + &Row: + + + rowSpinBox + + + + + + + + + + + + + Label &name: + + + labelNameLineEdit + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + FormLayoutRowDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FormLayoutRowDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/formwindowbase.cpp b/designer/lib/shared/formwindowbase.cpp new file mode 100644 index 0000000..bf11fff --- /dev/null +++ b/designer/lib/shared/formwindowbase.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindowbase_p.h" +#include "connectionedit_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "shared_settings_p.h" +#include "grid_p.h" +#include "deviceprofile_p.h" +#include "qdesigner_utils_p.h" + +#include "qsimpleresource_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class FormWindowBasePrivate { +public: + explicit FormWindowBasePrivate(QDesignerFormEditorInterface *core); + + static Grid m_defaultGrid; + + QDesignerFormWindowInterface::Feature m_feature; + Grid m_grid; + bool m_hasFormGrid; + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + QtResourceSet *m_resourceSet; + QMap > m_reloadableResources; // bool is dummy, QMap used as QSet + QMap m_reloadablePropertySheets; + const DeviceProfile m_deviceProfile; + FormWindowBase::LineTerminatorMode m_lineTerminatorMode; + FormWindowBase::SaveResourcesBehaviour m_saveResourcesBehaviour; +}; + +FormWindowBasePrivate::FormWindowBasePrivate(QDesignerFormEditorInterface *core) : + m_feature(QDesignerFormWindowInterface::DefaultFeature), + m_grid(m_defaultGrid), + m_hasFormGrid(false), + m_pixmapCache(0), + m_iconCache(0), + m_resourceSet(0), + m_deviceProfile(QDesignerSharedSettings(core).currentDeviceProfile()), + m_lineTerminatorMode(FormWindowBase::NativeLineTerminator), + m_saveResourcesBehaviour(FormWindowBase::SaveAll) +{ +} + +Grid FormWindowBasePrivate::m_defaultGrid; + +FormWindowBase::FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerFormWindowInterface(parent, flags), + m_d(new FormWindowBasePrivate(core)) +{ + syncGridFeature(); + m_d->m_pixmapCache = new DesignerPixmapCache(this); + m_d->m_iconCache = new DesignerIconCache(m_d->m_pixmapCache, this); +} + +FormWindowBase::~FormWindowBase() +{ + delete m_d; +} + +DesignerPixmapCache *FormWindowBase::pixmapCache() const +{ + return m_d->m_pixmapCache; +} + +DesignerIconCache *FormWindowBase::iconCache() const +{ + return m_d->m_iconCache; +} + +QtResourceSet *FormWindowBase::resourceSet() const +{ + return m_d->m_resourceSet; +} + +void FormWindowBase::setResourceSet(QtResourceSet *resourceSet) +{ + m_d->m_resourceSet = resourceSet; +} + +void FormWindowBase::addReloadableProperty(QDesignerPropertySheet *sheet, int index) +{ + m_d->m_reloadableResources[sheet][index] = true; +} + +void FormWindowBase::removeReloadableProperty(QDesignerPropertySheet *sheet, int index) +{ + m_d->m_reloadableResources[sheet].remove(index); + if (m_d->m_reloadableResources[sheet].count() == 0) + m_d->m_reloadableResources.remove(sheet); +} + +void FormWindowBase::addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object) +{ + if (qobject_cast(object) || + qobject_cast(object) || + qobject_cast(object) || + qobject_cast(object)) + m_d->m_reloadablePropertySheets[sheet] = object; +} + +void FormWindowBase::removeReloadablePropertySheet(QDesignerPropertySheet *sheet) +{ + m_d->m_reloadablePropertySheets.remove(sheet); +} + +void FormWindowBase::reloadProperties() +{ + pixmapCache()->clear(); + iconCache()->clear(); + QMapIterator > itSheet(m_d->m_reloadableResources); + while (itSheet.hasNext()) { + QDesignerPropertySheet *sheet = itSheet.next().key(); + QMapIterator itIndex(itSheet.value()); + while (itIndex.hasNext()) { + const int index = itIndex.next().key(); + const QVariant newValue = sheet->property(index); + if (qobject_cast(sheet->object()) && sheet->propertyName(index) == QLatin1String("text")) { + const PropertySheetStringValue newString = qVariantValue(newValue); + // optimize a bit, reset only if the text value might contain a reference to qt resources + // (however reloading of icons other than taken from resources might not work here) + if (newString.value().contains(QLatin1String(":/"))) { + const QVariant resetValue = qVariantFromValue(PropertySheetStringValue()); + sheet->setProperty(index, resetValue); + } + } + sheet->setProperty(index, newValue); + } + if (QTabWidget *tabWidget = qobject_cast(sheet->object())) { + const int count = tabWidget->count(); + const int current = tabWidget->currentIndex(); + const QString currentTabIcon = QLatin1String("currentTabIcon"); + for (int i = 0; i < count; i++) { + tabWidget->setCurrentIndex(i); + const int index = sheet->indexOf(currentTabIcon); + sheet->setProperty(index, sheet->property(index)); + } + tabWidget->setCurrentIndex(current); + } else if (QToolBox *toolBox = qobject_cast(sheet->object())) { + const int count = toolBox->count(); + const int current = toolBox->currentIndex(); + const QString currentItemIcon = QLatin1String("currentItemIcon"); + for (int i = 0; i < count; i++) { + toolBox->setCurrentIndex(i); + const int index = sheet->indexOf(currentItemIcon); + sheet->setProperty(index, sheet->property(index)); + } + toolBox->setCurrentIndex(current); + } + } + QMapIterator itSh(m_d->m_reloadablePropertySheets); + while (itSh.hasNext()) { + QObject *object = itSh.next().value(); + reloadIconResources(iconCache(), object); + } +} + +void FormWindowBase::resourceSetActivated(QtResourceSet *resource, bool resourceSetChanged) +{ + if (resource == resourceSet() && resourceSetChanged) { + reloadProperties(); + emit pixmapCache()->reloaded(); + emit iconCache()->reloaded(); + if (QDesignerPropertyEditor *propertyEditor = qobject_cast(core()->propertyEditor())) + propertyEditor->reloadResourceProperties(); + } +} + +QVariantMap FormWindowBase::formData() +{ + QVariantMap rc; + if (m_d->m_hasFormGrid) + m_d->m_grid.addToVariantMap(rc, true); + return rc; +} + +void FormWindowBase::setFormData(const QVariantMap &vm) +{ + Grid formGrid; + m_d->m_hasFormGrid = formGrid.fromVariantMap(vm); + if (m_d->m_hasFormGrid) + m_d->m_grid = formGrid; +} + +QPoint FormWindowBase::grid() const +{ + return QPoint(m_d->m_grid.deltaX(), m_d->m_grid.deltaY()); +} + +void FormWindowBase::setGrid(const QPoint &grid) +{ + m_d->m_grid.setDeltaX(grid.x()); + m_d->m_grid.setDeltaY(grid.y()); +} + +bool FormWindowBase::hasFeature(Feature f) const +{ + return f & m_d->m_feature; +} + +static void recursiveUpdate(QWidget *w) +{ + w->update(); + + const QObjectList &l = w->children(); + const QObjectList::const_iterator cend = l.constEnd(); + for (QObjectList::const_iterator it = l.constBegin(); it != cend; ++it) { + if (QWidget *w = qobject_cast(*it)) + recursiveUpdate(w); + } +} + +void FormWindowBase::setFeatures(Feature f) +{ + m_d->m_feature = f; + const bool enableGrid = f & GridFeature; + m_d->m_grid.setVisible(enableGrid); + m_d->m_grid.setSnapX(enableGrid); + m_d->m_grid.setSnapY(enableGrid); + emit featureChanged(f); + recursiveUpdate(this); +} + +FormWindowBase::Feature FormWindowBase::features() const +{ + return m_d->m_feature; +} + +bool FormWindowBase::gridVisible() const +{ + return m_d->m_grid.visible() && currentTool() == 0; +} + +FormWindowBase::SaveResourcesBehaviour FormWindowBase::saveResourcesBehaviour() const +{ + return m_d->m_saveResourcesBehaviour; +} + +void FormWindowBase::setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour) +{ + m_d->m_saveResourcesBehaviour = behaviour; +} + +void FormWindowBase::syncGridFeature() +{ + if (m_d->m_grid.snapX() || m_d->m_grid.snapY()) + m_d->m_feature |= GridFeature; + else + m_d->m_feature &= ~GridFeature; +} + +void FormWindowBase::setDesignerGrid(const Grid& grid) +{ + m_d->m_grid = grid; + syncGridFeature(); + recursiveUpdate(this); +} + +const Grid &FormWindowBase::designerGrid() const +{ + return m_d->m_grid; +} + +bool FormWindowBase::hasFormGrid() const +{ + return m_d->m_hasFormGrid; +} + +void FormWindowBase::setHasFormGrid(bool b) +{ + m_d->m_hasFormGrid = b; +} + +void FormWindowBase::setDefaultDesignerGrid(const Grid& grid) +{ + FormWindowBasePrivate::m_defaultGrid = grid; +} + +const Grid &FormWindowBase::defaultDesignerGrid() +{ + return FormWindowBasePrivate::m_defaultGrid; +} + +QMenu *FormWindowBase::initializePopupMenu(QWidget * /*managedWidget*/) +{ + return 0; +} + +// Widget under mouse for finding the Widget to highlight +// when doing DnD. Restricts to pages by geometry if a container with +// a container extension (or one of its helper widgets) is hit; otherwise +// returns the widget as such (be it managed/unmanaged) + +QWidget *FormWindowBase::widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode /* wum */) +{ + // widget_under_mouse might be some temporary thing like the dropLine. We need + // the actual widget that's part of the edited GUI. + QWidget *rc = widgetAt(formPos); + if (!rc || qobject_cast(rc)) + return 0; + + if (rc == mainContainer()) { + // Refuse main container areas if the main container has a container extension, + // for example when hitting QToolBox/QTabWidget empty areas. + if (qt_extension(core()->extensionManager(), rc)) + return 0; + return rc; + } + + // If we hit on container extension type container, make sure + // we use the top-most current page + if (QWidget *container = findContainer(rc, false)) + if (QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), container)) { + // For container that do not have a "stacked" nature (QToolBox, QMdiArea), + // make sure the position is within the current page + const int ci = c->currentIndex(); + if (ci < 0) + return 0; + QWidget *page = c->widget(ci); + QRect pageGeometry = page->geometry(); + pageGeometry.moveTo(page->mapTo(this, pageGeometry.topLeft())); + if (!pageGeometry.contains(formPos)) + return 0; + return page; + } + + return rc; +} + +void FormWindowBase::deleteWidgetList(const QWidgetList &widget_list) +{ + // We need a macro here even for single widgets because the some components (for example, + // the signal slot editor are connected to widgetRemoved() and add their + // own commands (for example, to delete w's connections) + const QString description = widget_list.size() == 1 ? + tr("Delete '%1'").arg(widget_list.front()->objectName()) : tr("Delete"); + + commandHistory()->beginMacro(description); + foreach (QWidget *w, widget_list) { + emit widgetRemoved(w); + DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this); + cmd->init(w); + commandHistory()->push(cmd); + } + commandHistory()->endMacro(); +} + +QMenu *FormWindowBase::createExtensionTaskMenu(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator) +{ + typedef QList ActionList; + ActionList actions; + // 1) Standard public extension + QExtensionManager *em = fw->core()->extensionManager(); + if (const QDesignerTaskMenuExtension *extTaskMenu = qt_extension(em, o)) + actions += extTaskMenu->taskActions(); + if (const QDesignerTaskMenuExtension *intTaskMenu = qobject_cast(em->extension(o, QLatin1String("QDesignerInternalTaskMenuExtension")))) { + if (!actions.empty()) { + QAction *a = new QAction(fw); + a->setSeparator(true); + actions.push_back(a); + } + actions += intTaskMenu->taskActions(); + } + if (actions.empty()) + return 0; + if (trailingSeparator && !actions.back()->isSeparator()) { + QAction *a = new QAction(fw); + a->setSeparator(true); + actions.push_back(a); + } + QMenu *rc = new QMenu; + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) + rc->addAction(*it); + return rc; +} + +void FormWindowBase::emitObjectRemoved(QObject *o) +{ + emit objectRemoved(o); +} + +DeviceProfile FormWindowBase::deviceProfile() const +{ + return m_d->m_deviceProfile; +} + +QString FormWindowBase::styleName() const +{ + return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.style(); +} + +void FormWindowBase::emitWidgetRemoved(QWidget *w) +{ + emit widgetRemoved(w); +} + +QString FormWindowBase::deviceProfileName() const +{ + return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.name(); +} + +void FormWindowBase::setLineTerminatorMode(FormWindowBase::LineTerminatorMode mode) +{ + m_d->m_lineTerminatorMode = mode; +} + +FormWindowBase::LineTerminatorMode FormWindowBase::lineTerminatorMode() const +{ + return m_d->m_lineTerminatorMode; +} + +void FormWindowBase::triggerDefaultAction(QWidget *widget) +{ + if (QAction *action = qdesigner_internal::preferredEditAction(core(), widget)) + QTimer::singleShot(0, action, SIGNAL(triggered())); +} + +void FormWindowBase::setupDefaultAction(QDesignerFormWindowInterface *fw) +{ + QObject::connect(fw, SIGNAL(activated(QWidget*)), fw, SLOT(triggerDefaultAction(QWidget*))); +} + +QString FormWindowBase::fileContents() const +{ + const bool oldValue = QSimpleResource::setWarningsEnabled(false); + const QString rc = contents(); + QSimpleResource::setWarningsEnabled(oldValue); + return rc; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/formwindowbase_p.h b/designer/lib/shared/formwindowbase_p.h new file mode 100644 index 0000000..c878456 --- /dev/null +++ b/designer/lib/shared/formwindowbase_p.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef FORMWINDOWBASE_H +#define FORMWINDOWBASE_H + +#include "shared_global_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerDnDItemInterface; +class QMenu; +class QtResourceSet; +class QDesignerPropertySheet; + +namespace qdesigner_internal { + +class QEditorFormBuilder; +class DeviceProfile; +class Grid; + +class DesignerPixmapCache; +class DesignerIconCache; +class FormWindowBasePrivate; + +class QDESIGNER_SHARED_EXPORT FormWindowBase: public QDesignerFormWindowInterface +{ + Q_OBJECT +public: + enum HighlightMode { Restore, Highlight }; + enum SaveResourcesBehaviour { SaveAll, SaveOnlyUsedQrcFiles, DontSaveQrcFiles }; + + explicit FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~FormWindowBase(); + + QVariantMap formData(); + void setFormData(const QVariantMap &vm); + + // Return contents without warnings. Should be 'contents(bool quiet)' + QString fileContents() const; + + // Return the widget containing the form. This is used to + // apply embedded design settings to that are inherited (for example font). + // These are meant to be applied to the form only and not to the other editors + // in the widget stack. + virtual QWidget *formContainer() const = 0; + + // Deprecated + virtual QPoint grid() const; + + // Deprecated + virtual void setGrid(const QPoint &grid); + + virtual bool hasFeature(Feature f) const; + virtual Feature features() const; + virtual void setFeatures(Feature f); + + const Grid &designerGrid() const; + void setDesignerGrid(const Grid& grid); + + bool hasFormGrid() const; + void setHasFormGrid(bool b); + + bool gridVisible() const; + + SaveResourcesBehaviour saveResourcesBehaviour() const; + void setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour); + + static const Grid &defaultDesignerGrid(); + static void setDefaultDesignerGrid(const Grid& grid); + + // Overwrite to initialize and return a full popup menu for a managed widget + virtual QMenu *initializePopupMenu(QWidget *managedWidget); + // Helper to create a basic popup menu from task menu extensions (internal/public) + static QMenu *createExtensionTaskMenu(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator = true); + + virtual bool dropWidgets(const QList &item_list, QWidget *target, + const QPoint &global_mouse_pos) = 0; + + // Helper to find the widget at the mouse position with some flags. + enum WidgetUnderMouseMode { FindSingleSelectionDropTarget, FindMultiSelectionDropTarget }; + QWidget *widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode m); + + virtual QWidget *widgetAt(const QPoint &pos) = 0; + virtual QWidget *findContainer(QWidget *w, bool excludeLayout) const = 0; + + void deleteWidgetList(const QWidgetList &widget_list); + + virtual void highlightWidget(QWidget *w, const QPoint &pos, HighlightMode mode = Highlight) = 0; + + enum PasteMode { PasteAll, PasteActionsOnly }; + virtual void paste(PasteMode pasteMode) = 0; + + // Factory method to create a form builder + virtual QEditorFormBuilder *createFormBuilder() = 0; + + virtual bool blockSelectionChanged(bool blocked) = 0; + virtual void emitSelectionChanged() = 0; + + DesignerPixmapCache *pixmapCache() const; + DesignerIconCache *iconCache() const; + QtResourceSet *resourceSet() const; + void setResourceSet(QtResourceSet *resourceSet); + void addReloadableProperty(QDesignerPropertySheet *sheet, int index); + void removeReloadableProperty(QDesignerPropertySheet *sheet, int index); + void addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object); + void removeReloadablePropertySheet(QDesignerPropertySheet *sheet); + void reloadProperties(); + + void emitWidgetRemoved(QWidget *w); + void emitObjectRemoved(QObject *o); + + DeviceProfile deviceProfile() const; + QString styleName() const; + QString deviceProfileName() const; + + enum LineTerminatorMode { + LFLineTerminator, + CRLFLineTerminator, + NativeLineTerminator = +#if defined (Q_OS_WIN) + CRLFLineTerminator +#else + LFLineTerminator +#endif + }; + + void setLineTerminatorMode(LineTerminatorMode mode); + LineTerminatorMode lineTerminatorMode() const; + + // Connect the 'activated' (doubleclicked) signal of the form window to a + // slot triggering the default action (of the task menu) + static void setupDefaultAction(QDesignerFormWindowInterface *fw); + +public slots: + void resourceSetActivated(QtResourceSet *resourceSet, bool resourceSetChanged); + +private slots: + void triggerDefaultAction(QWidget *w); + +private: + void syncGridFeature(); + + FormWindowBasePrivate *m_d; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOWBASE_H diff --git a/designer/lib/shared/grid.cpp b/designer/lib/shared/grid.cpp new file mode 100644 index 0000000..b1033e7 --- /dev/null +++ b/designer/lib/shared/grid.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "grid_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const bool defaultSnap = true; +static const bool defaultVisible = true; +static const int DEFAULT_GRID = 10; +static const char* KEY_VISIBLE = "gridVisible"; +static const char* KEY_SNAPX = "gridSnapX"; +static const char* KEY_SNAPY = "gridSnapY"; +static const char* KEY_DELTAX = "gridDeltaX"; +static const char* KEY_DELTAY = "gridDeltaY"; + +// Insert a value into the serialization map unless default +template + static inline void valueToVariantMap(T value, T defaultValue, const QString &key, QVariantMap &v, bool forceKey) { + if (forceKey || value != defaultValue) + v.insert(key, QVariant(value)); + } + +// Obtain a value form QVariantMap +template + static inline bool valueFromVariantMap(const QVariantMap &v, const QString &key, T &value) { + const QVariantMap::const_iterator it = v.constFind(key); + const bool found = it != v.constEnd(); + if (found) + value = qVariantValue(it.value()); + return found; + } + +namespace qdesigner_internal +{ + +Grid::Grid() : + m_visible(defaultVisible), + m_snapX(defaultSnap), + m_snapY(defaultSnap), + m_deltaX(DEFAULT_GRID), + m_deltaY(DEFAULT_GRID) +{ +} + +bool Grid::fromVariantMap(const QVariantMap& vm) +{ + *this = Grid(); + valueFromVariantMap(vm, QLatin1String(KEY_VISIBLE), m_visible); + valueFromVariantMap(vm, QLatin1String(KEY_SNAPX), m_snapX); + valueFromVariantMap(vm, QLatin1String(KEY_SNAPY), m_snapY); + valueFromVariantMap(vm, QLatin1String(KEY_DELTAX), m_deltaX); + return valueFromVariantMap(vm, QLatin1String(KEY_DELTAY), m_deltaY); +} + +QVariantMap Grid::toVariantMap(bool forceKeys) const +{ + QVariantMap rc; + addToVariantMap(rc, forceKeys); + return rc; +} + +void Grid::addToVariantMap(QVariantMap& vm, bool forceKeys) const +{ + valueToVariantMap(m_visible, defaultVisible, QLatin1String(KEY_VISIBLE), vm, forceKeys); + valueToVariantMap(m_snapX, defaultSnap, QLatin1String(KEY_SNAPX), vm, forceKeys); + valueToVariantMap(m_snapY, defaultSnap, QLatin1String(KEY_SNAPY), vm, forceKeys); + valueToVariantMap(m_deltaX, DEFAULT_GRID, QLatin1String(KEY_DELTAX), vm, forceKeys); + valueToVariantMap(m_deltaY, DEFAULT_GRID, QLatin1String(KEY_DELTAY), vm, forceKeys); +} + +void Grid::paint(QWidget *widget, QPaintEvent *e) const +{ + QPainter p(widget); + paint(p, widget, e); +} + +void Grid::paint(QPainter &p, const QWidget *widget, QPaintEvent *e) const +{ + p.setPen(widget->palette().dark().color()); + + if (m_visible) { + const int xstart = (e->rect().x() / m_deltaX) * m_deltaX; + const int ystart = (e->rect().y() / m_deltaY) * m_deltaY; + + const int xend = e->rect().right(); + const int yend = e->rect().bottom(); + + typedef QVector Points; + static Points points; + points.clear(); + + for (int x = xstart; x <= xend; x += m_deltaX) { + points.reserve((yend - ystart) / m_deltaY + 1); + for (int y = ystart; y <= yend; y += m_deltaY) + points.push_back(QPointF(x, y)); + p.drawPoints( &(*points.begin()), points.count()); + points.clear(); + } + } +} + +int Grid::snapValue(int value, int grid) const +{ + const int rest = value % grid; + const int absRest = (rest < 0) ? -rest : rest; + int offset = 0; + if (2 * absRest > grid) + offset = 1; + if (rest < 0) + offset *= -1; + return (value / grid + offset) * grid; +} + +QPoint Grid::snapPoint(const QPoint &p) const +{ + const int sx = m_snapX ? snapValue(p.x(), m_deltaX) : p.x(); + const int sy = m_snapY ? snapValue(p.y(), m_deltaY) : p.y(); + return QPoint(sx, sy); +} + +int Grid::widgetHandleAdjustX(int x) const +{ + return m_snapX ? (x / m_deltaX) * m_deltaX + 1 : x; +} + +int Grid::widgetHandleAdjustY(int y) const +{ + return m_snapY ? (y / m_deltaY) * m_deltaY + 1 : y; +} + +bool Grid::equals(const Grid &rhs) const +{ + return m_visible == rhs.m_visible && + m_snapX == rhs.m_snapX && + m_snapY == rhs.m_snapY && + m_deltaX == rhs.m_deltaX && + m_deltaY == rhs.m_deltaY; +} +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/grid_p.h b/designer/lib/shared/grid_p.h new file mode 100644 index 0000000..ba554b7 --- /dev/null +++ b/designer/lib/shared/grid_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_GRID_H +#define QDESIGNER_GRID_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QWidget; +class QPaintEvent; +class QPainter; + +namespace qdesigner_internal { + +// Designer grid which is able to serialize to QVariantMap +class QDESIGNER_SHARED_EXPORT Grid +{ +public: + Grid(); + + bool fromVariantMap(const QVariantMap& vm); + + void addToVariantMap(QVariantMap& vm, bool forceKeys = false) const; + QVariantMap toVariantMap(bool forceKeys = false) const; + + inline bool visible() const { return m_visible; } + void setVisible(bool visible) { m_visible = visible; } + + inline bool snapX() const { return m_snapX; } + void setSnapX(bool snap) { m_snapX = snap; } + + inline bool snapY() const { return m_snapY; } + void setSnapY(bool snap) { m_snapY = snap; } + + inline int deltaX() const { return m_deltaX; } + void setDeltaX(int dx) { m_deltaX = dx; } + + inline int deltaY() const { return m_deltaY; } + void setDeltaY(int dy) { m_deltaY = dy; } + + void paint(QWidget *widget, QPaintEvent *e) const; + void paint(QPainter &p, const QWidget *widget, QPaintEvent *e) const; + + QPoint snapPoint(const QPoint &p) const; + + int widgetHandleAdjustX(int x) const; + int widgetHandleAdjustY(int y) const; + + inline bool operator==(const Grid &rhs) const { return equals(rhs); } + inline bool operator!=(const Grid &rhs) const { return !equals(rhs); } + +private: + bool equals(const Grid &rhs) const; + int snapValue(int value, int grid) const; + bool m_visible; + bool m_snapX; + bool m_snapY; + int m_deltaX; + int m_deltaY; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_GRID_H diff --git a/designer/lib/shared/gridpanel.cpp b/designer/lib/shared/gridpanel.cpp new file mode 100644 index 0000000..181d3e9 --- /dev/null +++ b/designer/lib/shared/gridpanel.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gridpanel_p.h" +#include "ui_gridpanel.h" +#include "grid_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +GridPanel::GridPanel(QWidget *parentWidget) : + QWidget(parentWidget) +{ + m_ui = new Ui::GridPanel; + m_ui->setupUi(this); + + connect(m_ui->m_resetButton, SIGNAL(clicked()), this, SLOT(reset())); +} + +GridPanel::~GridPanel() +{ + delete m_ui; +} + +void GridPanel::setGrid(const Grid &g) +{ + m_ui->m_deltaXSpinBox->setValue(g.deltaX()); + m_ui->m_deltaYSpinBox->setValue(g.deltaY()); + m_ui->m_visibleCheckBox->setCheckState(g.visible() ? Qt::Checked : Qt::Unchecked); + m_ui->m_snapXCheckBox->setCheckState(g.snapX() ? Qt::Checked : Qt::Unchecked); + m_ui->m_snapYCheckBox->setCheckState(g.snapY() ? Qt::Checked : Qt::Unchecked); +} + +void GridPanel::setTitle(const QString &title) +{ + m_ui->m_gridGroupBox->setTitle(title); +} + +Grid GridPanel::grid() const +{ + Grid rc; + rc.setDeltaX(m_ui->m_deltaXSpinBox->value()); + rc.setDeltaY(m_ui->m_deltaYSpinBox->value()); + rc.setSnapX(m_ui->m_snapXCheckBox->checkState() == Qt::Checked); + rc.setSnapY(m_ui->m_snapYCheckBox->checkState() == Qt::Checked); + rc.setVisible(m_ui->m_visibleCheckBox->checkState() == Qt::Checked); + return rc; +} + +void GridPanel::reset() +{ + setGrid(Grid()); +} + +void GridPanel::setCheckable (bool c) +{ + m_ui->m_gridGroupBox->setCheckable(c); +} + +bool GridPanel::isCheckable () const +{ + return m_ui->m_gridGroupBox->isCheckable (); +} + +bool GridPanel::isChecked () const +{ + return m_ui->m_gridGroupBox->isChecked (); +} + +void GridPanel::setChecked(bool c) +{ + m_ui->m_gridGroupBox->setChecked(c); +} + +void GridPanel::setResetButtonVisible(bool v) +{ + m_ui->m_resetButton->setVisible(v); +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/gridpanel.ui b/designer/lib/shared/gridpanel.ui new file mode 100644 index 0000000..adfdd36 --- /dev/null +++ b/designer/lib/shared/gridpanel.ui @@ -0,0 +1,144 @@ + + qdesigner_internal::GridPanel + + + + 0 + 0 + 393 + 110 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Grid + + + + + + + 0 + 0 + + + + Visible + + + + + + + Grid &X + + + m_deltaXSpinBox + + + + + + + 2 + + + 100 + + + + + + + + 0 + 0 + + + + Snap + + + + + + + + + Reset + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + Grid &Y + + + m_deltaYSpinBox + + + + + + + 2 + + + 100 + + + + + + + + 0 + 0 + + + + Snap + + + + + + + + + + + diff --git a/designer/lib/shared/gridpanel_p.h b/designer/lib/shared/gridpanel_p.h new file mode 100644 index 0000000..73959a4 --- /dev/null +++ b/designer/lib/shared/gridpanel_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 the Qt tools. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef GRIDPANEL_H +#define GRIDPANEL_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class Grid; + +namespace Ui { + class GridPanel; +} + +class QDESIGNER_SHARED_EXPORT GridPanel : public QWidget +{ + Q_OBJECT +public: + GridPanel(QWidget *parent = 0); + ~GridPanel(); + + void setTitle(const QString &title); + + void setGrid(const Grid &g); + Grid grid() const; + + void setCheckable (bool c); + bool isCheckable () const; + + bool isChecked () const; + void setChecked(bool c); + + void setResetButtonVisible(bool v); + +private slots: + void reset(); + +private: + Ui::GridPanel *m_ui; +}; + +} // qdesigner_internal + +QT_END_NAMESPACE + +#endif // GRIDPANEL_H diff --git a/designer/lib/shared/htmlhighlighter.cpp b/designer/lib/shared/htmlhighlighter.cpp new file mode 100644 index 0000000..ffe04d2 --- /dev/null +++ b/designer/lib/shared/htmlhighlighter.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "htmlhighlighter_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +HtmlHighlighter::HtmlHighlighter(QTextEdit *textEdit) + : QSyntaxHighlighter(textEdit) +{ + QTextCharFormat entityFormat; + entityFormat.setForeground(Qt::red); + setFormatFor(Entity, entityFormat); + + QTextCharFormat tagFormat; + tagFormat.setForeground(Qt::darkMagenta); + tagFormat.setFontWeight(QFont::Bold); + setFormatFor(Tag, tagFormat); + + QTextCharFormat commentFormat; + commentFormat.setForeground(Qt::gray); + commentFormat.setFontItalic(true); + setFormatFor(Comment, commentFormat); + + QTextCharFormat attributeFormat; + attributeFormat.setForeground(Qt::black); + attributeFormat.setFontWeight(QFont::Bold); + setFormatFor(Attribute, attributeFormat); + + QTextCharFormat valueFormat; + valueFormat.setForeground(Qt::blue); + setFormatFor(Value, valueFormat); +} + +void HtmlHighlighter::setFormatFor(Construct construct, + const QTextCharFormat &format) +{ + m_formats[construct] = format; + rehighlight(); +} + +void HtmlHighlighter::highlightBlock(const QString &text) +{ + static const QLatin1Char tab = QLatin1Char('\t'); + static const QLatin1Char space = QLatin1Char(' '); + static const QLatin1Char amp = QLatin1Char('&'); + static const QLatin1Char startTag = QLatin1Char('<'); + static const QLatin1Char endTag = QLatin1Char('>'); + static const QLatin1Char quot = QLatin1Char('"'); + static const QLatin1Char apos = QLatin1Char('\''); + static const QLatin1Char semicolon = QLatin1Char(';'); + static const QLatin1Char equals = QLatin1Char('='); + static const QLatin1String startComment = QLatin1String(""); + static const QLatin1String endElement = QLatin1String("/>"); + + int state = previousBlockState(); + int len = text.length(); + int start = 0; + int pos = 0; + + while (pos < len) { + switch (state) { + case NormalState: + default: + while (pos < len) { + QChar ch = text.at(pos); + if (ch == startTag) { + if (text.mid(pos, 4) == startComment) { + state = InComment; + } else { + state = InTag; + start = pos; + while (pos < len && text.at(pos) != space + && text.at(pos) != endTag + && text.at(pos) != tab + && text.mid(pos, 2) != endElement) + ++pos; + if (text.mid(pos, 2) == endElement) + ++pos; + setFormat(start, pos - start, + m_formats[Tag]); + break; + } + break; + } else if (ch == amp) { + start = pos; + while (pos < len && text.at(pos++) != semicolon) + ; + setFormat(start, pos - start, + m_formats[Entity]); + } else { + // No tag, comment or entity started, continue... + ++pos; + } + } + break; + case InComment: + start = pos; + while (pos < len) { + if (text.mid(pos, 3) == endComment) { + pos += 3; + state = NormalState; + break; + } else { + ++pos; + } + } + setFormat(start, pos - start, m_formats[Comment]); + break; + case InTag: + QChar quote = QChar::Null; + while (pos < len) { + QChar ch = text.at(pos); + if (quote.isNull()) { + start = pos; + if (ch == apos || ch == quot) { + quote = ch; + } else if (ch == endTag) { + ++pos; + setFormat(start, pos - start, m_formats[Tag]); + state = NormalState; + break; + } else if (text.mid(pos, 2) == endElement) { + pos += 2; + setFormat(start, pos - start, m_formats[Tag]); + state = NormalState; + break; + } else if (ch != space && text.at(pos) != tab) { + // Tag not ending, not a quote and no whitespace, so + // we must be dealing with an attribute. + ++pos; + while (pos < len && text.at(pos) != space + && text.at(pos) != tab + && text.at(pos) != equals) + ++pos; + setFormat(start, pos - start, m_formats[Attribute]); + start = pos; + } + } else if (ch == quote) { + quote = QChar::Null; + + // Anything quoted is a value + setFormat(start, pos - start, m_formats[Value]); + } + ++pos; + } + break; + } + } + setCurrentBlockState(state); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/htmlhighlighter_p.h b/designer/lib/shared/htmlhighlighter_p.h new file mode 100644 index 0000000..997293c --- /dev/null +++ b/designer/lib/shared/htmlhighlighter_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef HTMLHIGHLIGHTER_H +#define HTMLHIGHLIGHTER_H + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +/* HTML syntax highlighter based on Qt Quarterly example */ +class HtmlHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + enum Construct { + Entity, + Tag, + Comment, + Attribute, + Value, + LastConstruct = Value + }; + + HtmlHighlighter(QTextEdit *textEdit); + + void setFormatFor(Construct construct, const QTextCharFormat &format); + + QTextCharFormat formatFor(Construct construct) const + { return m_formats[construct]; } + +protected: + enum State { + NormalState = -1, + InComment, + InTag + }; + + void highlightBlock(const QString &text); + +private: + QTextCharFormat m_formats[LastConstruct + 1]; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // HTMLHIGHLIGHTER_H diff --git a/designer/lib/shared/iconloader.cpp b/designer/lib/shared/iconloader.cpp new file mode 100644 index 0000000..2165a5e --- /dev/null +++ b/designer/lib/shared/iconloader.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "iconloader_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDESIGNER_SHARED_EXPORT QIcon createIconSet(const QString &name) +{ + QStringList candidates = QStringList() + << (QString::fromUtf8(":/trolltech/formeditor/images/") + name) +#ifdef Q_WS_MAC + << (QString::fromUtf8(":/trolltech/formeditor/images/mac/") + name) +#else + << (QString::fromUtf8(":/trolltech/formeditor/images/win/") + name) +#endif + << (QString::fromUtf8(":/trolltech/formeditor/images/designer_") + name); + + foreach (const QString &f, candidates) { + if (QFile::exists(f)) + return QIcon(f); + } + + return QIcon(); +} + +QDESIGNER_SHARED_EXPORT QIcon emptyIcon() +{ + return QIcon(QLatin1String(":/trolltech/formeditor/images/emptyicon.png")); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + diff --git a/designer/lib/shared/iconloader_p.h b/designer/lib/shared/iconloader_p.h new file mode 100644 index 0000000..2938b85 --- /dev/null +++ b/designer/lib/shared/iconloader_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ICONLOADER_H +#define ICONLOADER_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QString; +class QIcon; + +namespace qdesigner_internal { + +QDESIGNER_SHARED_EXPORT QIcon createIconSet(const QString &name); +QDESIGNER_SHARED_EXPORT QIcon emptyIcon(); + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ICONLOADER_H diff --git a/designer/lib/shared/iconselector.cpp b/designer/lib/shared/iconselector.cpp new file mode 100644 index 0000000..ca04cb3 --- /dev/null +++ b/designer/lib/shared/iconselector.cpp @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "iconselector_p.h" +#include "qdesigner_utils_p.h" +#include "qtresourcemodel_p.h" +#include "qtresourceview_p.h" +#include "iconloader_p.h" +#include "qdesigner_integration_p.h" +#include "formwindowbase_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// -------------------- LanguageResourceDialogPrivate +class LanguageResourceDialogPrivate { + LanguageResourceDialog *q_ptr; + Q_DECLARE_PUBLIC(LanguageResourceDialog) + +public: + LanguageResourceDialogPrivate(QDesignerResourceBrowserInterface *rb); + void init(LanguageResourceDialog *p); + + void setCurrentPath(const QString &filePath); + QString currentPath() const; + + void slotAccepted(); + void slotPathChanged(const QString &); + +private: + void setOkButtonEnabled(bool v) { m_dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(v); } + static bool checkPath(const QString &p); + + QDesignerResourceBrowserInterface *m_browser; + QDialogButtonBox *m_dialogButtonBox; +}; + +LanguageResourceDialogPrivate::LanguageResourceDialogPrivate(QDesignerResourceBrowserInterface *rb) : + q_ptr(0), + m_browser(rb), + m_dialogButtonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) +{ + setOkButtonEnabled(false); +} + +void LanguageResourceDialogPrivate::init(LanguageResourceDialog *p) +{ + q_ptr = p; + QLayout *layout = new QVBoxLayout(p); + layout->addWidget(m_browser); + layout->addWidget(m_dialogButtonBox); + QObject::connect(m_dialogButtonBox, SIGNAL(accepted()), p, SLOT(slotAccepted())); + QObject::connect(m_dialogButtonBox, SIGNAL(rejected()), p, SLOT(reject())); + QObject::connect(m_browser, SIGNAL(currentPathChanged(QString)), p, SLOT(slotPathChanged(QString))); + QObject::connect(m_browser, SIGNAL(pathActivated(QString)), p, SLOT(slotAccepted())); + p->setModal(true); + p->setWindowTitle(LanguageResourceDialog::tr("Choose Resource")); + p->setWindowFlags(p->windowFlags() & ~Qt::WindowContextHelpButtonHint); + setOkButtonEnabled(false); +} + +void LanguageResourceDialogPrivate::setCurrentPath(const QString &filePath) +{ + m_browser->setCurrentPath(filePath); + setOkButtonEnabled(checkPath(filePath)); +} + +QString LanguageResourceDialogPrivate::currentPath() const +{ + return m_browser->currentPath(); +} + +bool LanguageResourceDialogPrivate::checkPath(const QString &p) +{ + return p.isEmpty() ? false : IconSelector::checkPixmap(p, IconSelector::CheckFast); +} + +void LanguageResourceDialogPrivate::slotAccepted() +{ + if (checkPath(currentPath())) + q_ptr->accept(); +} + +void LanguageResourceDialogPrivate::slotPathChanged(const QString &p) +{ + setOkButtonEnabled(checkPath(p)); +} + +// ------------ LanguageResourceDialog +LanguageResourceDialog::LanguageResourceDialog(QDesignerResourceBrowserInterface *rb, QWidget *parent) : + QDialog(parent), + d_ptr(new LanguageResourceDialogPrivate(rb)) +{ + d_ptr->init( this); +} + +LanguageResourceDialog::~LanguageResourceDialog() +{ +} + +void LanguageResourceDialog::setCurrentPath(const QString &filePath) +{ + d_ptr->setCurrentPath(filePath); +} + +QString LanguageResourceDialog::currentPath() const +{ + return d_ptr->currentPath(); +} + +LanguageResourceDialog* LanguageResourceDialog::create(QDesignerFormEditorInterface *core, QWidget *parent) +{ + if (QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core)) + if (QDesignerResourceBrowserInterface *rb = lang->createResourceBrowser(0)) + return new LanguageResourceDialog(rb, parent); + if (QDesignerIntegration *di = qobject_cast(core->integration())) + if (QDesignerResourceBrowserInterface *rb = di->createResourceBrowser(0)) + return new LanguageResourceDialog(rb, parent); + return 0; +} + +// ------------ IconSelectorPrivate +class IconSelectorPrivate +{ + IconSelector *q_ptr; + Q_DECLARE_PUBLIC(IconSelector) +public: + IconSelectorPrivate(); + + void slotStateActivated(); + void slotSetActivated(); + void slotSetResourceActivated(); + void slotSetFileActivated(); + void slotResetActivated(); + void slotResetAllActivated(); + void slotUpdate(); + + QList, QString> > m_stateToName; // could be static map + + QMap, int> m_stateToIndex; + QMap > m_indexToState; + + QIcon m_emptyIcon; + QComboBox *m_stateComboBox; + QToolButton *m_iconButton; + QAction *m_resetAction; + QAction *m_resetAllAction; + PropertySheetIconValue m_icon; + DesignerIconCache *m_iconCache; + DesignerPixmapCache *m_pixmapCache; + QtResourceModel *m_resourceModel; + QDesignerFormEditorInterface *m_core; +}; + +IconSelectorPrivate::IconSelectorPrivate() : + q_ptr(0), + m_stateComboBox(0), + m_iconButton(0), + m_resetAction(0), + m_resetAllAction(0), + m_iconCache(0), + m_pixmapCache(0), + m_resourceModel(0), + m_core(0) +{ +} +void IconSelectorPrivate::slotUpdate() +{ + QIcon icon; + if (m_iconCache) + icon = m_iconCache->icon(m_icon); + + QMap, PropertySheetPixmapValue> paths = m_icon.paths(); + QMapIterator, int> itIndex(m_stateToIndex); + while (itIndex.hasNext()) { + const QPair state = itIndex.next().key(); + const PropertySheetPixmapValue pixmap = paths.value(state); + const int index = itIndex.value(); + + QIcon pixmapIcon = QIcon(icon.pixmap(16, 16, state.first, state.second)); + if (pixmapIcon.isNull()) + pixmapIcon = m_emptyIcon; + m_stateComboBox->setItemIcon(index, pixmapIcon); + QFont font = q_ptr->font(); + if (!pixmap.path().isEmpty()) + font.setBold(true); + m_stateComboBox->setItemData(index, font, Qt::FontRole); + } + + QPair state = m_indexToState.value(m_stateComboBox->currentIndex()); + PropertySheetPixmapValue currentPixmap = paths.value(state); + m_resetAction->setEnabled(!currentPixmap.path().isEmpty()); + m_resetAllAction->setEnabled(!paths.isEmpty()); + m_stateComboBox->update(); +} + +void IconSelectorPrivate::slotStateActivated() +{ + slotUpdate(); +} + +void IconSelectorPrivate::slotSetActivated() +{ + QPair state = m_indexToState.value(m_stateComboBox->currentIndex()); + const PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + // Default to resource + const PropertySheetPixmapValue::PixmapSource ps = pixmap.path().isEmpty() ? PropertySheetPixmapValue::ResourcePixmap : pixmap.pixmapSource(m_core); + switch (ps) { + case PropertySheetPixmapValue::LanguageResourcePixmap: + case PropertySheetPixmapValue::ResourcePixmap: + slotSetResourceActivated(); + break; + case PropertySheetPixmapValue::FilePixmap: + slotSetFileActivated(); + break; + } +} + +// Choose a pixmap from resource; use language-dependent resource browser if present +QString IconSelector::choosePixmapResource(QDesignerFormEditorInterface *core, QtResourceModel *resourceModel, const QString &oldPath, QWidget *parent) +{ + Q_UNUSED(resourceModel) + QString rc; + + if (LanguageResourceDialog* ldlg = LanguageResourceDialog::create(core, parent)) { + ldlg->setCurrentPath(oldPath); + if (ldlg->exec() == QDialog::Accepted) + rc = ldlg->currentPath(); + delete ldlg; + } else { + QtResourceViewDialog dlg(core, parent); + + QDesignerIntegration *designerIntegration = qobject_cast(core->integration()); + if (designerIntegration) + dlg.setResourceEditingEnabled(designerIntegration->isResourceEditingEnabled()); + + dlg.selectResource(oldPath); + if (dlg.exec() == QDialog::Accepted) + rc = dlg.selectedResource(); + } + return rc; +} + +void IconSelectorPrivate::slotSetResourceActivated() +{ + const QPair state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const QString oldPath = pixmap.path(); + const QString newPath = IconSelector::choosePixmapResource(m_core, m_resourceModel, oldPath, q_ptr); + if (newPath.isEmpty() || newPath == oldPath) + return; + const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath); + if (newPixmap != pixmap) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +// Helpers for choosing image files: Check for valid image. +bool IconSelector::checkPixmap(const QString &fileName, CheckMode cm, QString *errorMessage) +{ + const QFileInfo fi(fileName); + if (!fi.exists() || !fi.isFile() || !fi.isReadable()) { + if (errorMessage) + *errorMessage = tr("The pixmap file '%1' cannot be read.").arg(fileName); + return false; + } + QImageReader reader(fileName); + if (!reader.canRead()) { + if (errorMessage) + *errorMessage = tr("The file '%1' does not appear to be a valid pixmap file: %2").arg(fileName).arg(reader.errorString()); + return false; + } + if (cm == CheckFast) + return true; + + const QImage image = reader.read(); + if (image.isNull()) { + if (errorMessage) + *errorMessage = tr("The file '%1' could not be read: %2").arg(fileName).arg(reader.errorString()); + return false; + } + return true; +} + +// Helpers for choosing image files: Return an image filter for QFileDialog, courtesy of StyledButton +static QString imageFilter() +{ + QString filter = QApplication::translate("IconSelector", "All Pixmaps ("); + const QList supportedImageFormats = QImageReader::supportedImageFormats(); + const QString jpeg = QLatin1String("JPEG"); + const int count = supportedImageFormats.count(); + for (int i = 0; i< count; ++i) { + if (i) + filter += QLatin1Char(' '); + filter += QLatin1String("*."); + const QString outputFormat = QString::fromUtf8(supportedImageFormats.at(i)); + if (outputFormat != jpeg) + filter += outputFormat.toLower(); + else + filter += QLatin1String("jpg *.jpeg"); + } + filter += QLatin1Char(')'); + return filter; +} + +// Helpers for choosing image files: Choose a file +QString IconSelector::choosePixmapFile(const QString &directory, QDesignerDialogGuiInterface *dlgGui,QWidget *parent) +{ + QString errorMessage; + QString newPath; + do { + const QString title = tr("Choose a Pixmap"); + static const QString filter = imageFilter(); + newPath = dlgGui->getOpenImageFileName(parent, title, directory, filter); + if (newPath.isEmpty()) + break; + if (checkPixmap(newPath, CheckFully, &errorMessage)) + break; + dlgGui->message(parent, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, tr("Pixmap Read Error"), errorMessage); + } while(true); + return newPath; +} + +void IconSelectorPrivate::slotSetFileActivated() +{ + QPair state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const QString newPath = IconSelector::choosePixmapFile(pixmap.path(), m_core->dialogGui(), q_ptr); + if (!newPath.isEmpty()) { + const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath); + if (!(newPixmap == pixmap)) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } + } +} + +void IconSelectorPrivate::slotResetActivated() +{ + QPair state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const PropertySheetPixmapValue newPixmap; + if (!(newPixmap == pixmap)) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +void IconSelectorPrivate::slotResetAllActivated() +{ + const PropertySheetIconValue newIcon; + if (!(m_icon == newIcon)) { + m_icon = newIcon; + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +// ------------- IconSelector +IconSelector::IconSelector(QWidget *parent) : + QWidget(parent), d_ptr(new IconSelectorPrivate()) +{ + d_ptr->q_ptr = this; + + d_ptr->m_stateComboBox = new QComboBox(this); + + QHBoxLayout *l = new QHBoxLayout(this); + d_ptr->m_iconButton = new QToolButton(this); + d_ptr->m_iconButton->setText(tr("...")); + d_ptr->m_iconButton->setPopupMode(QToolButton::MenuButtonPopup); + l->addWidget(d_ptr->m_stateComboBox); + l->addWidget(d_ptr->m_iconButton); + l->setMargin(0); + + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Normal, QIcon::Off), tr("Normal Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Normal, QIcon::On), tr("Normal On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Disabled, QIcon::Off), tr("Disabled Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Disabled, QIcon::On), tr("Disabled On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Active, QIcon::Off), tr("Active Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Active, QIcon::On), tr("Active On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Selected, QIcon::Off), tr("Selected Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Selected, QIcon::On), tr("Selected On") ); + + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + d_ptr->m_emptyIcon = QIcon(QPixmap::fromImage(img)); + + QMenu *setMenu = new QMenu(this); + + QAction *setResourceAction = new QAction(tr("Choose Resource..."), this); + QAction *setFileAction = new QAction(tr("Choose File..."), this); + d_ptr->m_resetAction = new QAction(tr("Reset"), this); + d_ptr->m_resetAllAction = new QAction(tr("Reset All"), this); + d_ptr->m_resetAction->setEnabled(false); + d_ptr->m_resetAllAction->setEnabled(false); + //d_ptr->m_resetAction->setIcon(createIconSet(QString::fromUtf8("resetproperty.png"))); + + setMenu->addAction(setResourceAction); + setMenu->addAction(setFileAction); + setMenu->addSeparator(); + setMenu->addAction(d_ptr->m_resetAction); + setMenu->addAction(d_ptr->m_resetAllAction); + + int index = 0; + QStringList items; + QListIterator, QString> > itName(d_ptr->m_stateToName); + while (itName.hasNext()) { + QPair, QString> item = itName.next(); + const QPair state = item.first; + const QString name = item.second; + + items.append(name); + d_ptr->m_stateToIndex[state] = index; + d_ptr->m_indexToState[index] = state; + index++; + } + d_ptr->m_stateComboBox->addItems(items); + + d_ptr->m_iconButton->setMenu(setMenu); + + connect(d_ptr->m_stateComboBox, SIGNAL(activated(int)), this, SLOT(slotStateActivated())); + connect(d_ptr->m_iconButton, SIGNAL(clicked()), this, SLOT(slotSetActivated())); + connect(setResourceAction, SIGNAL(triggered()), this, SLOT(slotSetResourceActivated())); + connect(setFileAction, SIGNAL(triggered()), this, SLOT(slotSetFileActivated())); + connect(d_ptr->m_resetAction, SIGNAL(triggered()), this, SLOT(slotResetActivated())); + connect(d_ptr->m_resetAllAction, SIGNAL(triggered()), this, SLOT(slotResetAllActivated())); + + d_ptr->slotUpdate(); +} + +IconSelector::~IconSelector() +{ +} + +void IconSelector::setIcon(const PropertySheetIconValue &icon) +{ + if (d_ptr->m_icon == icon) + return; + + d_ptr->m_icon = icon; + d_ptr->slotUpdate(); +} + +PropertySheetIconValue IconSelector::icon() const +{ + return d_ptr->m_icon; +} + +void IconSelector::setFormEditor(QDesignerFormEditorInterface *core) +{ + d_ptr->m_core = core; + d_ptr->m_resourceModel = core->resourceModel(); + d_ptr->slotUpdate(); +} + +void IconSelector::setIconCache(DesignerIconCache *iconCache) +{ + d_ptr->m_iconCache = iconCache; + connect(iconCache, SIGNAL(reloaded()), this, SLOT(slotUpdate())); + d_ptr->slotUpdate(); +} + +void IconSelector::setPixmapCache(DesignerPixmapCache *pixmapCache) +{ + d_ptr->m_pixmapCache = pixmapCache; + connect(pixmapCache, SIGNAL(reloaded()), this, SLOT(slotUpdate())); + d_ptr->slotUpdate(); +} + +} // qdesigner_internal + +QT_END_NAMESPACE + +#include "moc_iconselector_p.cpp" + diff --git a/designer/lib/shared/iconselector_p.h b/designer/lib/shared/iconselector_p.h new file mode 100644 index 0000000..c80ad71 --- /dev/null +++ b/designer/lib/shared/iconselector_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef ICONSELECTOR_H +#define ICONSELECTOR_H + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QDesignerFormEditorInterface; +class QDesignerDialogGuiInterface; +class QDesignerResourceBrowserInterface; + +namespace qdesigner_internal { + +class DesignerIconCache; +class DesignerPixmapCache; +class PropertySheetIconValue; + +// Resource Dialog that embeds the language-dependent resource widget as returned by the language extension +class QDESIGNER_SHARED_EXPORT LanguageResourceDialog : public QDialog +{ + Q_OBJECT + + explicit LanguageResourceDialog(QDesignerResourceBrowserInterface *rb, QWidget *parent = 0); + +public: + virtual ~LanguageResourceDialog(); + // Factory: Returns 0 if the language extension does not provide a resource browser. + static LanguageResourceDialog* create(QDesignerFormEditorInterface *core, QWidget *parent); + + void setCurrentPath(const QString &filePath); + QString currentPath() const; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(LanguageResourceDialog) + Q_DISABLE_COPY(LanguageResourceDialog) + Q_PRIVATE_SLOT(d_func(), void slotAccepted()) + Q_PRIVATE_SLOT(d_func(), void slotPathChanged(QString)) + +}; + +class QDESIGNER_SHARED_EXPORT IconSelector: public QWidget +{ + Q_OBJECT +public: + IconSelector(QWidget *parent = 0); + virtual ~IconSelector(); + + void setFormEditor(QDesignerFormEditorInterface *core); // required for dialog gui. + void setIconCache(DesignerIconCache *iconCache); + void setPixmapCache(DesignerPixmapCache *pixmapCache); + + void setIcon(const PropertySheetIconValue &icon); + PropertySheetIconValue icon() const; + + // Check whether a pixmap may be read + enum CheckMode { CheckFast, CheckFully }; + static bool checkPixmap(const QString &fileName, CheckMode cm = CheckFully, QString *errorMessage = 0); + // Choose a pixmap from file + static QString choosePixmapFile(const QString &directory, QDesignerDialogGuiInterface *dlgGui, QWidget *parent); + // Choose a pixmap from resource; use language-dependent resource browser if present + static QString choosePixmapResource(QDesignerFormEditorInterface *core, QtResourceModel *resourceModel, const QString &oldPath, QWidget *parent); + +signals: + void iconChanged(const PropertySheetIconValue &icon); +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(IconSelector) + Q_DISABLE_COPY(IconSelector) + + Q_PRIVATE_SLOT(d_func(), void slotStateActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetResourceActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetFileActivated()) + Q_PRIVATE_SLOT(d_func(), void slotResetActivated()) + Q_PRIVATE_SLOT(d_func(), void slotResetAllActivated()) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) +}; + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ICONSELECTOR_H + diff --git a/designer/lib/shared/invisible_widget.cpp b/designer/lib/shared/invisible_widget.cpp new file mode 100644 index 0000000..7cd1b81 --- /dev/null +++ b/designer/lib/shared/invisible_widget.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "invisible_widget_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +InvisibleWidget::InvisibleWidget(QWidget *parent) + : QWidget() +{ + setAttribute(Qt::WA_NoChildEventsForParent); + setParent(parent); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/invisible_widget_p.h b/designer/lib/shared/invisible_widget_p.h new file mode 100644 index 0000000..668d575 --- /dev/null +++ b/designer/lib/shared/invisible_widget_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef INVISIBLE_WIDGET_H +#define INVISIBLE_WIDGET_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT InvisibleWidget: public QWidget +{ + Q_OBJECT +public: + InvisibleWidget(QWidget *parent = 0); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // INVISIBLE_WIDGET_H diff --git a/designer/lib/shared/layout.cpp b/designer/lib/shared/layout.cpp new file mode 100644 index 0000000..8c29d1e --- /dev/null +++ b/designer/lib/shared/layout.cpp @@ -0,0 +1,1326 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layout_p.h" +#include "qdesigner_utils_p.h" +#include "qlayout_widget_p.h" +#include "spacer_widget_p.h" +#include "layoutdecoration.h" +#include "widgetfactory_p.h" +#include "qdesigner_widgetitem_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { FormLayoutColumns = 2 }; + +namespace qdesigner_internal { + +/* The wizard has a policy of setting a size policy of its external children + * according to the page being expanding or not (in the latter case, the + * page will be pushed to the top). When setting/breaking layouts, this needs + * to be updated, which happens via a fake style change event. */ + +void updateWizardLayout(QWidget *layoutBase); + +class FriendlyWizardPage : public QWizardPage { + friend void updateWizardLayout(QWidget *); +}; + +void updateWizardLayout(QWidget *layoutBase) +{ + if (QWizardPage *wizardPage = qobject_cast(layoutBase)) + if (QWizard *wizard = static_cast(wizardPage)->wizard()) { + QEvent event(QEvent::StyleChange); + QApplication::sendEvent(wizard, &event); + } +} + +/*! + \class Layout layout.h + \brief Baseclass for layouting widgets in the Designer (Helper for Layout commands) + \internal + + Classes derived from this abstract base class are used for layouting + operations in the Designer (creating/breaking layouts). + + Instances live in the Layout/BreakLayout commands. +*/ + +/*! \a p specifies the parent of the layoutBase \a lb. The parent + might be changed in setup(). If the layoutBase is a + container, the parent and the layoutBase are the same. Also they + always have to be a widget known to the designer (e.g. in the case + of the tabwidget parent and layoutBase are the tabwidget and not the + page which actually gets laid out. For actual usage the correct + widget is found later by Layout.) + */ + +Layout::Layout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, LayoutInfo::Type layoutType) : + m_widgets(wl), + m_parentWidget(p), + m_layoutBase(lb), + m_formWindow(fw), + m_layoutType(layoutType), + m_reparentLayoutWidget(true), + m_isBreak(false) +{ + if (m_layoutBase) + m_oldGeometry = m_layoutBase->geometry(); +} + +Layout::~Layout() +{ +} + +/*! The widget list we got in the constructor might contain too much + widgets (like widgets with different parents, already laid out + widgets, etc.). Here we set up the list and so the only the "best" + widgets get laid out. +*/ + +void Layout::setup() +{ + m_startPoint = QPoint(32767, 32767); + + // Go through all widgets of the list we got. As we can only + // layout widgets which have the same parent, we first do some + // sorting which means create a list for each parent containing + // its child here. After that we keep working on the list of + // children which has the most entries. + // Widgets which are already laid out are thrown away here too + + QMultiMap lists; + foreach (QWidget *w, m_widgets) { + QWidget *p = w->parentWidget(); + + if (p && LayoutInfo::layoutType(m_formWindow->core(), p) != LayoutInfo::NoLayout + && m_formWindow->core()->metaDataBase()->item(p->layout()) != 0) + continue; + + lists.insert(p, w); + } + + QWidgetList lastList; + QWidgetList parents = lists.keys(); + foreach (QWidget *p, parents) { + QWidgetList children = lists.values(p); + + if (children.count() > lastList.count()) + lastList = children; + } + + + // If we found no list (because no widget did fit at all) or the + // best list has only one entry and we do not layout a container, + // we leave here. + QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase(); + if (lastList.count() < 2 && + (!m_layoutBase || + (!widgetDataBase->isContainer(m_layoutBase, false) && + m_layoutBase != m_formWindow->mainContainer())) + ) { + m_widgets.clear(); + m_startPoint = QPoint(0, 0); + return; + } + + // Now we have a new and clean widget list, which makes sense + // to layout + m_widgets = lastList; + // Also use the only correct parent later, so store it + + Q_ASSERT(m_widgets.isEmpty() == false); + + m_parentWidget = m_formWindow->core()->widgetFactory()->widgetOfContainer(m_widgets.first()->parentWidget()); + // Now calculate the position where the layout-meta-widget should + // be placed and connect to widgetDestroyed() signals of the + // widgets to get informed if one gets deleted to be able to + // handle that and do not crash in this case + foreach (QWidget *w, m_widgets) { + connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); + m_startPoint = QPoint(qMin(m_startPoint.x(), w->x()), qMin(m_startPoint.y(), w->y())); + const QRect rc(w->geometry()); + + m_geometries.insert(w, rc); + // Change the Z-order, as saving/loading uses the Z-order for + // writing/creating widgets and this has to be the same as in + // the layout. Else saving + loading will give different results + w->raise(); + } + + sort(); +} + +void Layout::widgetDestroyed() +{ + if (QWidget *w = qobject_cast(sender())) { + m_widgets.removeAt(m_widgets.indexOf(w)); + m_geometries.remove(w); + } +} + +bool Layout::prepareLayout(bool &needMove, bool &needReparent) +{ + foreach (QWidget *widget, m_widgets) { + widget->raise(); + } + + needMove = !m_layoutBase; + needReparent = needMove || (m_reparentLayoutWidget && qobject_cast(m_layoutBase)) || qobject_cast(m_layoutBase); + + QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory(); + QDesignerMetaDataBaseInterface *metaDataBase = m_formWindow->core()->metaDataBase(); + + if (m_layoutBase == 0) { + const bool useSplitter = m_layoutType == LayoutInfo::HSplitter || m_layoutType == LayoutInfo::VSplitter; + const QString baseWidgetClassName = useSplitter ? QLatin1String("QSplitter") : QLatin1String("QLayoutWidget"); + m_layoutBase = widgetFactory->createWidget(baseWidgetClassName, widgetFactory->containerOfWidget(m_parentWidget)); + if (useSplitter) { + m_layoutBase->setObjectName(QLatin1String("splitter")); + m_formWindow->ensureUniqueObjectName(m_layoutBase); + } + } else { + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + } + + metaDataBase->add(m_layoutBase); + + Q_ASSERT(m_layoutBase->layout() == 0 || metaDataBase->item(m_layoutBase->layout()) == 0); + + return true; +} + +static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w) +{ + return w && (w == fw || w == fw->mainContainer()); +} + +static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget) +{ + QDesignerContainerExtension *c = qt_extension( + fw->core()->extensionManager(), widget->parentWidget()); + + if (c != 0) { + for (int i = 0; icount(); ++i) { + if (widget == c->widget(i)) + return true; + } + } + + return false; +} +void Layout::finishLayout(bool needMove, QLayout *layout) +{ + if (m_parentWidget == m_layoutBase) { + QWidget *widget = m_layoutBase; + m_oldGeometry = widget->geometry(); + + bool done = false; + while (!isMainContainer(m_formWindow, widget) && !done) { + if (!m_formWindow->isManaged(widget)) { + widget = widget->parentWidget(); + continue; + } else if (LayoutInfo::isWidgetLaidout(m_formWindow->core(), widget)) { + widget = widget->parentWidget(); + continue; + } else if (isPageOfContainerWidget(m_formWindow, widget)) { + widget = widget->parentWidget(); + continue; + } else if (widget->parentWidget()) { + QScrollArea *area = qobject_cast(widget->parentWidget()->parentWidget()); + if (area && area->widget() == widget) { + widget = area; + continue; + } + } + + done = true; + } + updateWizardLayout(m_layoutBase); + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + // We don't want to resize the form window + if (!Utils::isCentralWidget(m_formWindow, widget)) + widget->adjustSize(); + + return; + } + + if (needMove) + m_layoutBase->move(m_startPoint); + + const QRect g(m_layoutBase->pos(), m_layoutBase->size()); + + if (LayoutInfo::layoutType(m_formWindow->core(), m_layoutBase->parentWidget()) == LayoutInfo::NoLayout && !m_isBreak) + m_layoutBase->adjustSize(); + else if (m_isBreak) + m_layoutBase->setGeometry(m_oldGeometry); + + m_oldGeometry = g; + if (layout) + layout->invalidate(); + m_layoutBase->show(); + + if (qobject_cast(m_layoutBase) || qobject_cast(m_layoutBase)) { + m_formWindow->clearSelection(false); + m_formWindow->manageWidget(m_layoutBase); + m_formWindow->selectWidget(m_layoutBase); + } +} + +void Layout::undoLayout() +{ + if (!m_widgets.count()) + return; + + m_formWindow->selectWidget(m_layoutBase, false); + + QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory(); + QHashIterator it(m_geometries); + while (it.hasNext()) { + it.next(); + + if (!it.key()) + continue; + + QWidget* w = it.key(); + const QRect rc = it.value(); + + const bool showIt = w->isVisibleTo(m_formWindow); + QWidget *container = widgetFactory->containerOfWidget(m_parentWidget); + + // ### remove widget here + QWidget *parentWidget = w->parentWidget(); + QDesignerFormEditorInterface *core = m_formWindow->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), parentWidget); + + if (deco) + deco->removeWidget(w); + + w->setParent(container); + w->setGeometry(rc); + + if (showIt) + w->show(); + } + + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + + if (m_parentWidget != m_layoutBase && !qobject_cast(m_layoutBase)) { + m_formWindow->unmanageWidget(m_layoutBase); + m_layoutBase->hide(); + } else { + QMainWindow *mw = qobject_cast(m_formWindow->mainContainer()); + if (m_layoutBase != m_formWindow->mainContainer() && + (!mw || mw->centralWidget() != m_layoutBase)) + m_layoutBase->setGeometry(m_oldGeometry); + } +} + +void Layout::breakLayout() +{ + typedef QMap WidgetRectMap; + WidgetRectMap rects; + /* Store the geometry of the widgets. The idea is to give the user space + * to rearrange them, so, we do a adjustSize() on them, unless they want + * to grow (expanding widgets like QTextEdit), in which the geometry is + * preserved. Note that historically, geometries were re-applied + * only after breaking splitters. */ + foreach (QWidget *w, m_widgets) { + const QRect geom = w->geometry(); + const QSize sizeHint = w->sizeHint(); + const bool restoreGeometry = sizeHint.isEmpty() || sizeHint.width() > geom.width() || sizeHint.height() > geom.height(); + rects.insert(w, restoreGeometry ? w->geometry() : QRect(geom.topLeft(), QSize())); + } + const QPoint m_layoutBasePos = m_layoutBase->pos(); + QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase(); + + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + + const bool needReparent = (m_reparentLayoutWidget && qobject_cast(m_layoutBase)) || + qobject_cast(m_layoutBase) || + (!widgetDataBase->isContainer(m_layoutBase, false) && + m_layoutBase != m_formWindow->mainContainer()); + const bool add = m_geometries.isEmpty(); + + QMapIterator it(rects); + while (it.hasNext()) { + it.next(); + + QWidget *w = it.key(); + if (needReparent) { + w->setParent(m_layoutBase->parentWidget(), 0); + w->move(m_layoutBasePos + it.value().topLeft()); + w->show(); + } + + const QRect oldGeometry = it.value(); + if (oldGeometry.isEmpty()) { + w->adjustSize(); + } else { + w->resize(oldGeometry.size()); + } + + if (add) + m_geometries.insert(w, QRect(w->pos(), w->size())); + } + + if (needReparent) { + m_layoutBase->hide(); + m_parentWidget = m_layoutBase->parentWidget(); + m_formWindow->unmanageWidget(m_layoutBase); + } else { + m_parentWidget = m_layoutBase; + } + updateWizardLayout(m_layoutBase); + + if (!m_widgets.isEmpty() && m_widgets.first() && m_widgets.first()->isVisibleTo(m_formWindow)) + m_formWindow->selectWidget(m_widgets.first()); + else + m_formWindow->selectWidget(m_formWindow); +} + +static QString suggestLayoutName(const char *className) +{ + // Legacy + if (!qstrcmp(className, "QHBoxLayout")) + return QLatin1String("horizontalLayout"); + if (!qstrcmp(className, "QVBoxLayout")) + return QLatin1String("verticalLayout"); + if (!qstrcmp(className, "QGridLayout")) + return QLatin1String("gridLayout"); + + return qtify(QString::fromUtf8(className)); +} +QLayout *Layout::createLayout(int type) +{ + Q_ASSERT(m_layoutType != LayoutInfo::HSplitter && m_layoutType != LayoutInfo::VSplitter); + QLayout *layout = m_formWindow->core()->widgetFactory()->createLayout(m_layoutBase, 0, type); + // set a name + layout->setObjectName(suggestLayoutName(layout->metaObject()->className())); + m_formWindow->ensureUniqueObjectName(layout); + // QLayoutWidget + QDesignerPropertySheetExtension *sheet = qt_extension(m_formWindow->core()->extensionManager(), layout); + if (sheet && qobject_cast(m_layoutBase)) { + sheet->setProperty(sheet->indexOf(QLatin1String("leftMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("topMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("rightMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("bottomMargin")), 0); + } + return layout; +} + +void Layout::reparentToLayoutBase(QWidget *w) +{ + if (w->parent() != m_layoutBase) { + w->setParent(m_layoutBase, 0); + w->move(QPoint(0,0)); + } +} + +namespace { // within qdesigner_internal + +// ----- PositionSortPredicate: Predicate to be usable as LessThan function to sort widgets by position +class PositionSortPredicate { +public: + PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {} + bool operator()(const QWidget* w1, const QWidget* w2) { + return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y(); + } + private: + const Qt::Orientation m_orientation; +}; + +// -------- BoxLayout +class BoxLayout : public Layout +{ +public: + BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation); + + virtual void doLayout(); + virtual void sort(); + +private: + const Qt::Orientation m_orientation; +}; + +BoxLayout::BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation) : + Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox), + m_orientation(orientation) +{ +} + +void BoxLayout::sort() +{ + QWidgetList wl = widgets(); + qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation)); + setWidgets(wl); +} + +void BoxLayout::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + QBoxLayout *layout = static_cast(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox)); + + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (needReparent) + reparentToLayoutBase(w); + + if (const Spacer *spacer = qobject_cast(w)) + layout->addWidget(w, 0, spacer->alignment()); + else + layout->addWidget(w); + w->show(); + } + finishLayout(needMove, layout); +} + +// -------- SplitterLayout +class SplitterLayout : public Layout +{ +public: + SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation); + + virtual void doLayout(); + virtual void sort(); + +private: + const Qt::Orientation m_orientation; +}; + +SplitterLayout::SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation) : + Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HSplitter : LayoutInfo::VSplitter), + m_orientation(orientation) +{ +} + +void SplitterLayout::sort() +{ + QWidgetList wl = widgets(); + qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation)); + setWidgets(wl); +} + +void SplitterLayout::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + QSplitter *splitter = qobject_cast(layoutBaseWidget()); + Q_ASSERT(splitter != 0); + + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (needReparent) + reparentToLayoutBase(w); + splitter->addWidget(w); + w->show(); + } + + splitter->setOrientation(m_orientation); + finishLayout(needMove); +} + +// ---------- Grid: Helper for laying out grids + +class Grid +{ +public: + enum Mode { + GridLayout, // Arbitrary size/supports span + FormLayout // 2-column/no span + }; + + Grid(Mode mode); + void resize(int nrows, int ncols); + + ~Grid(); + + QWidget* cell(int row, int col) const { return m_cells[ row * m_ncols + col]; } + + void setCells(const QRect &c, QWidget* w); + + bool empty() const { return m_nrows * m_ncols; } + int numRows() const { return m_nrows; } + int numCols() const { return m_ncols; } + + void simplify(); + bool locateWidget(QWidget* w, int& row, int& col, int& rowspan, int& colspan) const; + + QDebug debug(QDebug str) const; + +private: + void setCell(int row, int col, QWidget* w) { m_cells[ row * m_ncols + col] = w; } + void swapCells(int r1, int c1, int r2, int c2); + void shrink(); + void reallocFormLayout(); + int countRow(int r, int c) const; + int countCol(int r, int c) const; + void setRow(int r, int c, QWidget* w, int count); + void setCol(int r, int c, QWidget* w, int count); + bool isWidgetStartCol(int c) const; + bool isWidgetEndCol(int c) const; + bool isWidgetStartRow(int r) const; + bool isWidgetEndRow(int r) const; + bool isWidgetTopLeft(int r, int c) const; + void extendLeft(); + void extendRight(); + void extendUp(); + void extendDown(); + bool shrinkFormLayoutSpans(); + + const Mode m_mode; + int m_nrows; + int m_ncols; + + QWidget** m_cells; // widget matrix w11, w12, w21... +}; + +Grid::Grid(Mode mode) : + m_mode(mode), + m_nrows(0), + m_ncols(0), + m_cells(0) +{ +} + +Grid::~Grid() +{ + delete [] m_cells; +} + +void Grid::resize(int nrows, int ncols) +{ + delete [] m_cells; + m_cells = 0; + m_nrows = nrows; + m_ncols = ncols; + if (const int allocSize = m_nrows * m_ncols) { + m_cells = new QWidget*[allocSize]; + qFill(m_cells, m_cells + allocSize, static_cast(0)); + } +} + +QDebug Grid::debug(QDebug str) const +{ + str << m_nrows << 'x' << m_ncols << '\n'; + QSet widgets; + const int cellCount = m_nrows * m_ncols; + int row, col, rowspan, colspan; + for (int c = 0; c < cellCount; c++) + if (QWidget *w = m_cells[c]) + if (!widgets.contains(w)) { + widgets.insert(w); + locateWidget(w, row, col, rowspan, colspan); + str << w << " at " << row << col << rowspan << 'x' << colspan << '\n'; + } + for (int r = 0; r < m_nrows; r++) + for (int c = 0; c < m_ncols; c++) + str << "At " << r << c << cell(r, c) << '\n'; + + return str; +} + +static inline QDebug operator<<(QDebug str, const Grid &g) { return g.debug(str); } + +void Grid::setCells(const QRect &c, QWidget* w) +{ + const int bottom = c.top() + c.height(); + const int width = c.width(); + + for (int r = c.top(); r < bottom; r++) { + QWidget **pos = m_cells + r * m_ncols + c.left(); + qFill(pos, pos + width, w); + } +} + + +void Grid::swapCells(int r1, int c1, int r2, int c2) +{ + QWidget *w1 = cell(r1, c1); + setCell(r1, c1, cell(r2, c2)); + setCell(r2, c2, w1); +} + +int Grid::countRow(int r, int c) const +{ + QWidget* w = cell(r, c); + int i = c + 1; + while (i < m_ncols && cell(r, i) == w) + i++; + return i - c; +} + +int Grid::countCol(int r, int c) const +{ + QWidget* w = cell(r, c); + int i = r + 1; + while (i < m_nrows && cell(i, c) == w) + i++; + return i - r; +} + +void Grid::setCol(int r, int c, QWidget* w, int count) +{ + for (int i = 0; i < count; i++) + setCell(r + i, c, w); +} + +void Grid::setRow(int r, int c, QWidget* w, int count) +{ + for (int i = 0; i < count; i++) + setCell(r, c + i, w); +} + +bool Grid::isWidgetStartCol(int c) const +{ + for (int r = 0; r < m_nrows; r++) { + if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) { + return true; + } + } + return false; +} + +bool Grid::isWidgetEndCol(int c) const +{ + for (int r = 0; r < m_nrows; r++) { + if (cell(r, c) && ((c == m_ncols-1) || (cell(r, c) != cell(r, c+1)))) + return true; + } + return false; +} + +bool Grid::isWidgetStartRow(int r) const +{ + for ( int c = 0; c < m_ncols; c++) { + if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c)))) + return true; + } + return false; +} + +bool Grid::isWidgetEndRow(int r) const +{ + for (int c = 0; c < m_ncols; c++) { + if (cell(r, c) && ((r == m_nrows-1) || (cell(r, c) != cell(r+1, c)))) + return true; + } + return false; +} + + +bool Grid::isWidgetTopLeft(int r, int c) const +{ + QWidget* w = cell(r, c); + if (!w) + return false; + return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w); +} + +void Grid::extendLeft() +{ + for (int c = 1; c < m_ncols; c++) { + for (int r = 0; r < m_nrows; r++) { + QWidget* w = cell(r, c); + if (!w) + continue; + + const int cc = countCol(r, c); + int stretch = 0; + for (int i = c-1; i >= 0; i--) { + if (cell(r, i)) + break; + if (countCol(r, i) < cc) + break; + if (isWidgetEndCol(i)) + break; + if (isWidgetStartCol(i)) { + stretch = c - i; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setCol(r, c-i-1, w, cc); + } + } + } +} + + +void Grid::extendRight() +{ + for (int c = m_ncols - 2; c >= 0; c--) { + for (int r = 0; r < m_nrows; r++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cc = countCol(r, c); + int stretch = 0; + for (int i = c+1; i < m_ncols; i++) { + if (cell(r, i)) + break; + if (countCol(r, i) < cc) + break; + if (isWidgetStartCol(i)) + break; + if (isWidgetEndCol(i)) { + stretch = i - c; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setCol(r, c+i+1, w, cc); + } + } + } + +} + +void Grid::extendUp() +{ + for (int r = 1; r < m_nrows; r++) { + for (int c = 0; c < m_ncols; c++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cr = countRow(r, c); + int stretch = 0; + for (int i = r-1; i >= 0; i--) { + if (cell(i, c)) + break; + if (countRow(i, c) < cr) + break; + if (isWidgetEndRow(i)) + break; + if (isWidgetStartRow(i)) { + stretch = r - i; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setRow(r-i-1, c, w, cr); + } + } + } +} + +void Grid::extendDown() +{ + for (int r = m_nrows - 2; r >= 0; r--) { + for (int c = 0; c < m_ncols; c++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cr = countRow(r, c); + int stretch = 0; + for (int i = r+1; i < m_nrows; i++) { + if (cell(i, c)) + break; + if (countRow(i, c) < cr) + break; + if (isWidgetStartRow(i)) + break; + if (isWidgetEndRow(i)) { + stretch = i - r; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setRow(r+i+1, c, w, cr); + } + } + } +} + +void Grid::simplify() +{ + switch (m_mode) { + case GridLayout: + // Grid: Extend all widgets to occupy most space and delete + // rows/columns that are not bordering on a widget + extendLeft(); + extendRight(); + extendUp(); + extendDown(); + shrink(); + break; + case FormLayout: + // Form: First treat it as a grid to get the same behaviour + // regarding spanning and shrinking. Then restrict the span to + // the horizontal span possible in the form, simplify again + // and spread the widgets over a 2-column layout + extendLeft(); + extendRight(); + extendUp(); + extendDown(); + shrink(); + if (shrinkFormLayoutSpans()) + shrink(); + reallocFormLayout(); + break; + } + +} + +void Grid::shrink() +{ + // tick off the occupied cols/rows (bordering on widget edges) + QVector columns(m_ncols, false); + QVector rows(m_nrows, false); + + for (int c = 0; c < m_ncols; c++) + for (int r = 0; r < m_nrows; r++) + if (isWidgetTopLeft(r, c)) + rows[r] = columns[c] = true; + + // remove empty cols/rows + const int simplifiedNCols = columns.count(true); + const int simplifiedNRows = rows.count(true); + if (simplifiedNCols == m_ncols && simplifiedNRows == m_nrows) + return; + // reallocate and copy omitting the empty cells + QWidget **simplifiedCells = new QWidget*[simplifiedNCols * simplifiedNRows]; + qFill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows, static_cast(0)); + QWidget **simplifiedPtr = simplifiedCells; + + for (int r = 0; r < m_nrows; r++) + if (rows[r]) + for (int c = 0; c < m_ncols; c++) + if (columns[c]) { + if (QWidget *w = cell(r, c)) + *simplifiedPtr = w; + simplifiedPtr++; + } + Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows); + delete [] m_cells; + m_cells = simplifiedCells; + m_nrows = simplifiedNRows; + m_ncols = simplifiedNCols; +} + +bool Grid::shrinkFormLayoutSpans() +{ + bool shrunk = false; + typedef QSet WidgetSet; + // Determine unique set of widgets + WidgetSet widgets; + QWidget **end = m_cells + m_ncols * m_nrows; + for (QWidget **wptr = m_cells; wptr < end; wptr++) + if (QWidget *w = *wptr) + widgets.insert(w); + // Restrict the widget span: max horizontal span at column 0: 2, anything else: 1 + const int maxRowSpan = 1; + const WidgetSet::const_iterator cend = widgets.constEnd(); + for (WidgetSet::const_iterator it = widgets.constBegin(); it != cend ; ++it) { + QWidget *w = *it; + int row, col, rowspan, colspan; + locateWidget(w, row, col, rowspan, colspan); + const int maxColSpan = col == 0 ? 2 : 1; + const int newColSpan = qMin(colspan, maxColSpan); + const int newRowSpan = qMin(rowspan, maxRowSpan); + if (newColSpan != colspan || newRowSpan != rowspan) { + setCells(QRect(col, row, colspan, rowspan), 0); + setCells(QRect(col, row, newColSpan, newRowSpan), w); + shrunk = true; + } + } + return shrunk; +} + +void Grid::reallocFormLayout() +{ + // Columns matching? -> happy! + if (m_ncols == FormLayoutColumns) + return; + + // If there are offset columns (starting past the field column), + // move them to the left and squeeze them. This also prevents the + // following reallocation from creating empty form rows. + int pastRightWidgetCount = 0; + if (m_ncols > FormLayoutColumns) { + for (int r = 0; r < m_nrows; r++) { + // Try to find a column where the form columns are empty and + // there are widgets further to the right. + if (cell(r, 0) == 0 && cell(r, 1) == 0) { + int sourceCol = FormLayoutColumns; + QWidget *firstWidget = 0; + for ( ; sourceCol < m_ncols; sourceCol++) + if (QWidget *w = cell(r, sourceCol)) { + firstWidget = w; + break; + } + if (firstWidget) { + // Move/squeeze. Copy to beginning of column if it is a label, else field + int targetCol = qobject_cast(firstWidget) ? 0 : 1; + for ( ; sourceCol < m_ncols; sourceCol++) + if (QWidget *w = cell(r, sourceCol)) + setCell(r, targetCol++, w); + // Pad with zero + for ( ; targetCol < m_ncols; targetCol++) + setCell(r, targetCol, 0); + } + } + // Any protruding widgets left on that row? + for (int c = FormLayoutColumns; c < m_ncols; c++) + if (cell(r, c)) + pastRightWidgetCount++; + } + } + // Reallocate with 2 columns. Just insert the protruding ones as fields. + const int formNRows = m_nrows + pastRightWidgetCount; + QWidget **formCells = new QWidget*[FormLayoutColumns * formNRows]; + qFill(formCells, formCells + FormLayoutColumns * formNRows, static_cast(0)); + QWidget **formPtr = formCells; + const int matchingColumns = qMin(m_ncols, static_cast(FormLayoutColumns)); + for (int r = 0; r < m_nrows; r++) { + int c = 0; + for ( ; c < matchingColumns; c++) // Just copy over matching columns + *formPtr++ = cell(r, c); + formPtr += FormLayoutColumns - matchingColumns; // In case old format was 1 column + // protruding widgets: Insert as single-field rows + for ( ; c < m_ncols; c++) + if (QWidget *w = cell(r, c)) { + formPtr++; + *formPtr++ = w; + } + } + Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows); + delete [] m_cells; + m_cells = formCells; + m_nrows = formNRows; + m_ncols = FormLayoutColumns; +} + +bool Grid::locateWidget(QWidget *w, int &row, int &col, int &rowspan, int &colspan) const +{ + const int end = m_nrows * m_ncols; + const int startIndex = qFind(m_cells, m_cells + end, w) - m_cells; + if (startIndex == end) + return false; + + row = startIndex / m_ncols; + col = startIndex % m_ncols; + for (rowspan = 1; row + rowspan < m_nrows && cell(row + rowspan, col) == w; rowspan++) {} + for (colspan = 1; col + colspan < m_ncols && cell(row, col + colspan) == w; colspan++) {} + return true; +} + +// QGridLayout/QFormLayout Helpers: get item position/add item (overloads to make templates work) + +void getGridItemPosition(QGridLayout *gridLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + gridLayout->getItemPosition(index, row, column, rowspan, colspan); +} + +void addWidgetToGrid(QGridLayout *lt, QWidget * widget, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment) +{ + lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment); +} + +inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan); +} + +inline void addWidgetToGrid(QFormLayout *lt, QWidget * widget, int row, int column, int, int columnSpan, Qt::Alignment) +{ + formLayoutAddWidget(lt, widget, QRect(column, row, columnSpan, 1), false); +} + +// ----------- Base template for grid like layouts +template +class GridLayout : public Layout +{ +public: + GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb); + + virtual void doLayout(); + virtual void sort() { setWidgets(buildGrid(widgets())); } + +protected: + QWidget *widgetAt(GridLikeLayout *layout, int row, int column) const; + +protected: + QWidgetList buildGrid(const QWidgetList &); + Grid m_grid; +}; + +template +GridLayout::GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb) : + Layout(wl, p, fw, lb, LayoutInfo::Grid), + m_grid(static_cast(GridMode)) +{ +} + +template +QWidget *GridLayout::widgetAt(GridLikeLayout *layout, int row, int column) const +{ + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + if (item->widget()) { + int r, c, rowspan, colspan; + getGridItemPosition(layout, index, &r, &c, &rowspan, &colspan); + if (row == r && column == c) + return item->widget(); + } + ++index; + } + return 0; +} + +template +void GridLayout::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + GridLikeLayout *layout = static_cast(createLayout(LayoutType)); + + if (m_grid.empty()) + sort(); + + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + int r = 0, c = 0, rs = 0, cs = 0; + + if (m_grid.locateWidget(w, r, c, rs, cs)) { + if (needReparent) + reparentToLayoutBase(w); + + Qt::Alignment alignment = Qt::Alignment(0); + if (const Spacer *spacer = qobject_cast(w)) + alignment = spacer->alignment(); + + if (rs * cs == 1) { + addWidgetToGrid(layout, w, r, c, 1, 1, alignment); + } else { + addWidgetToGrid(layout, w, r, c, rs, cs, alignment); + } + + w->show(); + } else { + qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData()); + } + } + + QLayoutSupport::createEmptyCells(layout); + + finishLayout(needMove, layout); +} + +// Remove duplicate entries (Remove next, if equal to current) +void removeIntVecDuplicates(QVector &v) +{ + if (v.size() < 2) + return; + + for (QVector::iterator current = v.begin() ; (current != v.end()) && ((current+1) != v.end()) ; ) + if ( (*current == *(current+1)) ) + v.erase(current+1); + else + ++current; +} + +// Ensure a non-zero size for a widget geometry (squeezed spacers) +inline QRect expandGeometry(const QRect &rect) +{ + return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect; +} + +template +QWidgetList GridLayout::buildGrid(const QWidgetList &widgetList) +{ + if (widgetList.empty()) + return QWidgetList(); + + // Pixel to cell conversion: + // By keeping a list of start'n'stop values (x & y) for each widget, + // it is possible to create a very small grid of cells to represent + // the widget layout. + // ----------------------------------------------------------------- + + // We need a list of both start and stop values for x- & y-axis + const int widgetCount = widgetList.size(); + QVector x( widgetCount * 2 ); + QVector y( widgetCount * 2 ); + + // Using push_back would look nicer, but operator[] is much faster + int index = 0; + for (int i = 0; i < widgetCount; ++i) { + const QRect widgetPos = expandGeometry(widgetList.at(i)->geometry()); + x[index] = widgetPos.left(); + x[index+1] = widgetPos.right(); + y[index] = widgetPos.top(); + y[index+1] = widgetPos.bottom(); + index += 2; + } + + qSort(x); + qSort(y); + + // Remove duplicate x entries (Remove next, if equal to current) + removeIntVecDuplicates(x); + removeIntVecDuplicates(y); + + // Note that left == right and top == bottom for size 1 items; reserve + // enough space + m_grid.resize(y.size(), x.size()); + + const QWidgetList::const_iterator cend = widgetList.constEnd(); + for (QWidgetList::const_iterator it = widgetList.constBegin(); it != cend; ++it) { + QWidget *w = *it; + // Mark the cells in the grid that contains a widget + const QRect widgetPos = expandGeometry(w->geometry()); + QRect c(0, 0, 0, 0); // rect of columns/rows + + // From left til right (not including) + const int leftIdx = x.indexOf(widgetPos.left()); + Q_ASSERT(leftIdx != -1); + c.setLeft(leftIdx); + c.setRight(leftIdx); + for (int cw=leftIdx; cw(widgets, parentWidget, fw, layoutBase); + case LayoutInfo::HBox: + case LayoutInfo::VBox: { + const Qt::Orientation orientation = layoutType == LayoutInfo::HBox ? Qt::Horizontal : Qt::Vertical; + return new BoxLayout(widgets, parentWidget, fw, layoutBase, orientation); + } + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + const Qt::Orientation orientation = layoutType == LayoutInfo::HSplitter ? Qt::Horizontal : Qt::Vertical; + return new SplitterLayout(widgets, parentWidget, fw, layoutBase, orientation); + } + case LayoutInfo::Form: + return new GridLayout(widgets, parentWidget, fw, layoutBase); + default: + break; + } + Q_ASSERT(0); + return 0; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/layout_p.h b/designer/lib/shared/layout_p.h new file mode 100644 index 0000000..6976fbf --- /dev/null +++ b/designer/lib/shared/layout_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef LAYOUT_H +#define LAYOUT_H + +#include "shared_global_p.h" +#include "layoutinfo_p.h" + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +class QDESIGNER_SHARED_EXPORT Layout : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Layout) +protected: + Layout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, LayoutInfo::Type layoutType); + +public: + static Layout* createLayout(const QWidgetList &widgets, QWidget *parentWidget, + QDesignerFormWindowInterface *fw, + QWidget *layoutBase, LayoutInfo::Type layoutType); + + virtual ~Layout(); + + virtual void sort() = 0; + virtual void doLayout() = 0; + + virtual void setup(); + virtual void undoLayout(); + virtual void breakLayout(); + + const QWidgetList &widgets() const { return m_widgets; } + QWidget *parentWidget() const { return m_parentWidget; } + QWidget *layoutBaseWidget() const { return m_layoutBase; } + + /* Determines whether instances of QLayoutWidget are unmanaged/hidden + * after breaking a layout. Default is true. Can be turned off when + * morphing */ + bool reparentLayoutWidget() const { return m_reparentLayoutWidget; } + void setReparentLayoutWidget(bool v) { m_reparentLayoutWidget = v; } + +protected: + virtual void finishLayout(bool needMove, QLayout *layout = 0); + virtual bool prepareLayout(bool &needMove, bool &needReparent); + + void setWidgets(const QWidgetList &widgets) { m_widgets = widgets; } + QLayout *createLayout(int type); + void reparentToLayoutBase(QWidget *w); + +private slots: + void widgetDestroyed(); + +private: + QWidgetList m_widgets; + QWidget *m_parentWidget; + typedef QHash WidgetGeometryHash; + WidgetGeometryHash m_geometries; + QWidget *m_layoutBase; + QDesignerFormWindowInterface *m_formWindow; + const LayoutInfo::Type m_layoutType; + QPoint m_startPoint; + QRect m_oldGeometry; + + bool m_reparentLayoutWidget; + const bool m_isBreak; +}; + +namespace Utils +{ + +inline int indexOfWidget(QLayout *layout, QWidget *widget) +{ + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + if (item->widget() == widget) + return index; + + ++index; + } + + return -1; +} + +} // namespace Utils + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LAYOUT_H diff --git a/designer/lib/shared/layoutinfo.cpp b/designer/lib/shared/layoutinfo.cpp new file mode 100644 index 0000000..4a9b86d --- /dev/null +++ b/designer/lib/shared/layoutinfo.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layoutinfo_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +/*! + \overload +*/ +LayoutInfo::Type LayoutInfo::layoutType(const QDesignerFormEditorInterface *core, const QLayout *layout) +{ + Q_UNUSED(core) + if (!layout) + return NoLayout; + else if (qobject_cast(layout)) + return HBox; + else if (qobject_cast(layout)) + return VBox; + else if (qobject_cast(layout)) + return Grid; + else if (qobject_cast(layout)) + return Form; + return UnknownLayout; +} + +static const QHash &layoutNameTypeMap() +{ + static QHash nameTypeMap; + if (nameTypeMap.empty()) { + nameTypeMap.insert(QLatin1String("QVBoxLayout"), LayoutInfo::VBox); + nameTypeMap.insert(QLatin1String("QHBoxLayout"), LayoutInfo::HBox); + nameTypeMap.insert(QLatin1String("QGridLayout"), LayoutInfo::Grid); + nameTypeMap.insert(QLatin1String("QFormLayout"), LayoutInfo::Form); + } + return nameTypeMap; +} + +LayoutInfo::Type LayoutInfo::layoutType(const QString &typeName) +{ + return layoutNameTypeMap().value(typeName, NoLayout); +} + +QString LayoutInfo::layoutName(Type t) +{ + return layoutNameTypeMap().key(t); +} + +/*! + \overload +*/ +LayoutInfo::Type LayoutInfo::layoutType(const QDesignerFormEditorInterface *core, const QWidget *w) +{ + if (const QSplitter *splitter = qobject_cast(w)) + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + return layoutType(core, w->layout()); +} + +LayoutInfo::Type LayoutInfo::managedLayoutType(const QDesignerFormEditorInterface *core, + const QWidget *w, + QLayout **ptrToLayout) +{ + if (ptrToLayout) + *ptrToLayout = 0; + if (const QSplitter *splitter = qobject_cast(w)) + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + QLayout *layout = managedLayout(core, w); + if (!layout) + return NoLayout; + if (ptrToLayout) + *ptrToLayout = layout; + return layoutType(core, layout); +} + +QWidget *LayoutInfo::layoutParent(const QDesignerFormEditorInterface *core, QLayout *layout) +{ + Q_UNUSED(core) + + QObject *o = layout; + while (o) { + if (QWidget *widget = qobject_cast(o)) + return widget; + + o = o->parent(); + } + return 0; +} + +void LayoutInfo::deleteLayout(const QDesignerFormEditorInterface *core, QWidget *widget) +{ + if (QDesignerContainerExtension *container = qt_extension(core->extensionManager(), widget)) + widget = container->widget(container->currentIndex()); + + Q_ASSERT(widget != 0); + + QLayout *layout = managedLayout(core, widget); + + if (layout == 0 || core->metaDataBase()->item(layout) != 0) { + delete layout; + widget->updateGeometry(); + return; + } + + qDebug() << "trying to delete an unmanaged layout:" << "widget:" << widget << "layout:" << layout; +} + +LayoutInfo::Type LayoutInfo::laidoutWidgetType(const QDesignerFormEditorInterface *core, + QWidget *widget, + bool *isManaged, + QLayout **ptrToLayout) +{ + if (isManaged) + *isManaged = false; + if (ptrToLayout) + *ptrToLayout = 0; + + QWidget *parent = widget->parentWidget(); + if (!parent) + return NoLayout; + + // 1) Splitter + if (QSplitter *splitter = qobject_cast(parent)) { + if (isManaged) + *isManaged = core->metaDataBase()->item(splitter); + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + } + + // 2) Layout of parent + QLayout *parentLayout = parent->layout(); + if (!parentLayout) + return NoLayout; + + if (parentLayout->indexOf(widget) != -1) { + if (isManaged) + *isManaged = core->metaDataBase()->item(parentLayout); + if (ptrToLayout) + *ptrToLayout = parentLayout; + return layoutType(core, parentLayout); + } + + // 3) Some child layout (see below comment about Q3GroupBox) + const QList childLayouts = qFindChildren(parentLayout); + if (childLayouts.empty()) + return NoLayout; + const QList::const_iterator lcend = childLayouts.constEnd(); + for (QList::const_iterator it = childLayouts.constBegin(); it != lcend; ++it) { + QLayout *layout = *it; + if (layout->indexOf(widget) != -1) { + if (isManaged) + *isManaged = core->metaDataBase()->item(layout); + if (ptrToLayout) + *ptrToLayout = layout; + return layoutType(core, layout); + } + } + + return NoLayout; +} + +QLayout *LayoutInfo::internalLayout(const QWidget *widget) +{ + QLayout *widgetLayout = widget->layout(); + if (widgetLayout && widget->inherits("Q3GroupBox")) { + if (widgetLayout->count()) { + widgetLayout = widgetLayout->itemAt(0)->layout(); + } else { + widgetLayout = 0; + } + } + return widgetLayout; +} + + +QLayout *LayoutInfo::managedLayout(const QDesignerFormEditorInterface *core, const QWidget *widget) +{ + if (widget == 0) + return 0; + + QLayout *layout = widget->layout(); + if (!layout) + return 0; + + return managedLayout(core, layout); +} + +QLayout *LayoutInfo::managedLayout(const QDesignerFormEditorInterface *core, QLayout *layout) +{ + QDesignerMetaDataBaseInterface *metaDataBase = core->metaDataBase(); + + if (!metaDataBase) + return layout; + /* This code exists mainly for the Q3GroupBox class, for which + * widget->layout() returns an internal VBoxLayout. */ + const QDesignerMetaDataBaseItemInterface *item = metaDataBase->item(layout); + if (item == 0) { + layout = qFindChild(layout); + item = metaDataBase->item(layout); + } + if (!item) + return 0; + return layout; +} + +// Is it a a dummy grid placeholder created by Designer? +bool LayoutInfo::isEmptyItem(QLayoutItem *item) +{ + if (item == 0) { + qDebug() << "** WARNING Zero-item passed on to isEmptyItem(). This indicates a layout inconsistency."; + return true; + } + return item->spacerItem() != 0; +} + +QDESIGNER_SHARED_EXPORT void getFormLayoutItemPosition(const QFormLayout *formLayout, int index, int *rowPtr, int *columnPtr, int *rowspanPtr, int *colspanPtr) +{ + int row; + QFormLayout::ItemRole role; + formLayout->getItemPosition(index, &row, &role); + const int columnspan = role == QFormLayout::SpanningRole ? 2 : 1; + const int column = (columnspan > 1 || role == QFormLayout::LabelRole) ? 0 : 1; + if (rowPtr) + *rowPtr = row; + if (columnPtr) + *columnPtr = column; + if (rowspanPtr) + *rowspanPtr = 1; + if (colspanPtr) + *colspanPtr = columnspan; +} + +static inline QFormLayout::ItemRole formLayoutRole(int column, int colspan) +{ + if (colspan > 1) + return QFormLayout::SpanningRole; + return column == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; +} + +QDESIGNER_SHARED_EXPORT void formLayoutAddWidget(QFormLayout *formLayout, QWidget *w, const QRect &r, bool insert) +{ + // Consistent API galore... + if (insert) { + const bool spanning = r.width() > 1; + if (spanning) { + formLayout->insertRow(r.y(), w); + } else { + QWidget *label = 0, *field = 0; + if (r.x() == 0) { + label = w; + } else { + field = w; + } + formLayout->insertRow(r.y(), label, field); + } + } else { + formLayout->setWidget(r.y(), formLayoutRole(r.x(), r.width()), w); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/layoutinfo_p.h b/designer/lib/shared/layoutinfo_p.h new file mode 100644 index 0000000..5454bb8 --- /dev/null +++ b/designer/lib/shared/layoutinfo_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef LAYOUTINFO_H +#define LAYOUTINFO_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QWidget; +class QLayout; +class QLayoutItem; +class QDesignerFormEditorInterface; +class QFormLayout; +class QRect; +class QString; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT LayoutInfo +{ +public: + enum Type + { + NoLayout, + HSplitter, + VSplitter, + HBox, + VBox, + Grid, + Form, + UnknownLayout // QDockWindow inside QMainWindow is inside QMainWindowLayout - it doesn't mean there is no layout + }; + + static void deleteLayout(const QDesignerFormEditorInterface *core, QWidget *widget); + + // Examines the immediate layout of the widget (will fail for Q3Group Box). + static Type layoutType(const QDesignerFormEditorInterface *core, const QWidget *w); + // Examines the managed layout of the widget + static Type managedLayoutType(const QDesignerFormEditorInterface *core, const QWidget *w, QLayout **layout = 0); + static Type layoutType(const QDesignerFormEditorInterface *core, const QLayout *layout); + static Type layoutType(const QString &typeName); + static QString layoutName(Type t); + + static QWidget *layoutParent(const QDesignerFormEditorInterface *core, QLayout *layout); + + static Type laidoutWidgetType(const QDesignerFormEditorInterface *core, QWidget *widget, bool *isManaged = 0, QLayout **layout = 0); + static bool inline isWidgetLaidout(const QDesignerFormEditorInterface *core, QWidget *widget) { return laidoutWidgetType(core, widget) != NoLayout; } + + static QLayout *managedLayout(const QDesignerFormEditorInterface *core, const QWidget *widget); + static QLayout *managedLayout(const QDesignerFormEditorInterface *core, QLayout *layout); + static QLayout *internalLayout(const QWidget *widget); + + // Is it a a dummy grid placeholder created by Designer? + static bool isEmptyItem(QLayoutItem *item); +}; + +QDESIGNER_SHARED_EXPORT void getFormLayoutItemPosition(const QFormLayout *formLayout, int index, int *rowPtr, int *columnPtr = 0, int *rowspanPtr = 0, int *colspanPtr = 0); +QDESIGNER_SHARED_EXPORT void formLayoutAddWidget(QFormLayout *formLayout, QWidget *w, const QRect &r, bool insert); +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LAYOUTINFO_H diff --git a/designer/lib/shared/metadatabase.cpp b/designer/lib/shared/metadatabase.cpp new file mode 100644 index 0000000..f1be41c --- /dev/null +++ b/designer/lib/shared/metadatabase.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +// sdk +#include + +// Qt +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + const bool debugMetaDatabase = false; +} + +namespace qdesigner_internal { + +MetaDataBaseItem::MetaDataBaseItem(QObject *object) + : m_object(object), + m_enabled(true) +{ +} + +MetaDataBaseItem::~MetaDataBaseItem() +{ +} + +QString MetaDataBaseItem::name() const +{ + Q_ASSERT(m_object); + return m_object->objectName(); +} + +void MetaDataBaseItem::setName(const QString &name) +{ + Q_ASSERT(m_object); + m_object->setObjectName(name); +} + +QString MetaDataBaseItem::customClassName() const +{ + return m_customClassName; +} +void MetaDataBaseItem::setCustomClassName(const QString &customClassName) +{ + m_customClassName = customClassName; +} + + +MetaDataBaseItem::TabOrder MetaDataBaseItem::tabOrder() const +{ + return m_tabOrder; +} + +void MetaDataBaseItem::setTabOrder(const TabOrder &tabOrder) +{ + m_tabOrder = tabOrder; +} + +bool MetaDataBaseItem::enabled() const +{ + return m_enabled; +} + +void MetaDataBaseItem::setEnabled(bool b) +{ + m_enabled = b; +} + +QString MetaDataBaseItem::script() const +{ + return m_script; +} + +void MetaDataBaseItem::setScript(const QString &script) +{ + m_script = script; +} + +QStringList MetaDataBaseItem::fakeSlots() const +{ + return m_fakeSlots; +} + +void MetaDataBaseItem::setFakeSlots(const QStringList &fs) +{ + m_fakeSlots = fs; +} + +QStringList MetaDataBaseItem::fakeSignals() const +{ + return m_fakeSignals; +} + +void MetaDataBaseItem::setFakeSignals(const QStringList &fs) +{ + m_fakeSignals = fs; +} + +// ----------------------------------------------------- +MetaDataBase::MetaDataBase(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerMetaDataBaseInterface(parent), + m_core(core) +{ +} + +MetaDataBase::~MetaDataBase() +{ + qDeleteAll(m_items); +} + +MetaDataBaseItem *MetaDataBase::metaDataBaseItem(QObject *object) const +{ + MetaDataBaseItem *i = m_items.value(object); + if (i == 0 || !i->enabled()) + return 0; + return i; +} + +void MetaDataBase::add(QObject *object) +{ + MetaDataBaseItem *item = m_items.value(object); + if (item != 0) { + item->setEnabled(true); + if (debugMetaDatabase) { + qDebug() << "MetaDataBase::add: Existing item for " << object->metaObject()->className() << item->name(); + } + return; + } + + item = new MetaDataBaseItem(object); + m_items.insert(object, item); + if (debugMetaDatabase) { + qDebug() << "MetaDataBase::add: New item " << object->metaObject()->className() << item->name(); + } + connect(object, SIGNAL(destroyed(QObject*)), + this, SLOT(slotDestroyed(QObject*))); + + emit changed(); +} + +void MetaDataBase::remove(QObject *object) +{ + Q_ASSERT(object); + + if (MetaDataBaseItem *item = m_items.value(object)) { + item->setEnabled(false); + emit changed(); + } +} + +QList MetaDataBase::objects() const +{ + QList result; + + ItemMap::const_iterator it = m_items.begin(); + for (; it != m_items.end(); ++it) { + if (it.value()->enabled()) + result.append(it.key()); + } + + return result; +} + +QDesignerFormEditorInterface *MetaDataBase::core() const +{ + return m_core; +} + +void MetaDataBase::slotDestroyed(QObject *object) +{ + if (m_items.contains(object)) { + MetaDataBaseItem *item = m_items.value(object); + delete item; + m_items.remove(object); + } +} + +// promotion convenience +QDESIGNER_SHARED_EXPORT bool promoteWidget(QDesignerFormEditorInterface *core,QWidget *widget,const QString &customClassName) +{ + + MetaDataBase *db = qobject_cast(core->metaDataBase()); + if (!db) + return false; + MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) { + db ->add(widget); + item = db->metaDataBaseItem(widget); + } + // Recursive promotion occurs if there is a plugin missing. + const QString oldCustomClassName = item->customClassName(); + if (!oldCustomClassName.isEmpty()) { + qDebug() << "WARNING: Recursive promotion of " << oldCustomClassName << " to " << customClassName + << ". A plugin is missing."; + } + item->setCustomClassName(customClassName); + if (debugMetaDatabase) { + qDebug() << "Promoting " << widget->metaObject()->className() << " to " << customClassName; + } + return true; +} + +QDESIGNER_SHARED_EXPORT void demoteWidget(QDesignerFormEditorInterface *core,QWidget *widget) +{ + MetaDataBase *db = qobject_cast(core->metaDataBase()); + if (!db) + return; + MetaDataBaseItem *item = db->metaDataBaseItem(widget); + item->setCustomClassName(QString()); + if (debugMetaDatabase) { + qDebug() << "Demoting " << widget; + } +} + +QDESIGNER_SHARED_EXPORT bool isPromoted(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const MetaDataBase *db = qobject_cast(core->metaDataBase()); + if (!db) + return false; + const MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) + return false; + return !item->customClassName().isEmpty(); +} + +QDESIGNER_SHARED_EXPORT QString promotedCustomClassName(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const MetaDataBase *db = qobject_cast(core->metaDataBase()); + if (!db) + return QString(); + const MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) + return QString(); + return item->customClassName(); +} + +QDESIGNER_SHARED_EXPORT QString promotedExtends(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const QString customClassName = promotedCustomClassName(core,widget); + if (customClassName.isEmpty()) + return QString(); + const int i = core->widgetDataBase()->indexOfClassName(customClassName); + if (i == -1) + return QString(); + return core->widgetDataBase()->item(i)->extends(); +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/metadatabase_p.h b/designer/lib/shared/metadatabase_p.h new file mode 100644 index 0000000..1c41c0c --- /dev/null +++ b/designer/lib/shared/metadatabase_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef METADATABASE_H +#define METADATABASE_H + +#include "shared_global_p.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT MetaDataBaseItem: public QDesignerMetaDataBaseItemInterface +{ +public: + explicit MetaDataBaseItem(QObject *object); + virtual ~MetaDataBaseItem(); + + virtual QString name() const; + virtual void setName(const QString &name); + + typedef QList TabOrder; + virtual TabOrder tabOrder() const; + virtual void setTabOrder(const TabOrder &tabOrder); + + virtual bool enabled() const; + virtual void setEnabled(bool b); + + QString customClassName() const; + void setCustomClassName(const QString &customClassName); + + QString script() const; + void setScript(const QString &script); + + QStringList fakeSlots() const; + void setFakeSlots(const QStringList &); + + QStringList fakeSignals() const; + void setFakeSignals(const QStringList &); + +private: + QObject *m_object; + TabOrder m_tabOrder; + bool m_enabled; + QString m_customClassName; + QString m_script; + QStringList m_fakeSlots; + QStringList m_fakeSignals; +}; + +class QDESIGNER_SHARED_EXPORT MetaDataBase: public QDesignerMetaDataBaseInterface +{ + Q_OBJECT +public: + explicit MetaDataBase(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~MetaDataBase(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual QDesignerMetaDataBaseItemInterface *item(QObject *object) const { return metaDataBaseItem(object); } + virtual MetaDataBaseItem *metaDataBaseItem(QObject *object) const; + virtual void add(QObject *object); + virtual void remove(QObject *object); + + virtual QList objects() const; + +private slots: + void slotDestroyed(QObject *object); + +private: + QDesignerFormEditorInterface *m_core; + typedef QHash ItemMap; + ItemMap m_items; +}; + + // promotion convenience + QDESIGNER_SHARED_EXPORT bool promoteWidget(QDesignerFormEditorInterface *core,QWidget *widget,const QString &customClassName); + QDESIGNER_SHARED_EXPORT void demoteWidget(QDesignerFormEditorInterface *core,QWidget *widget); + QDESIGNER_SHARED_EXPORT bool isPromoted(QDesignerFormEditorInterface *core, QWidget* w); + QDESIGNER_SHARED_EXPORT QString promotedCustomClassName(QDesignerFormEditorInterface *core, QWidget* w); + QDESIGNER_SHARED_EXPORT QString promotedExtends(QDesignerFormEditorInterface *core, QWidget* w); + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // METADATABASE_H diff --git a/designer/lib/shared/morphmenu.cpp b/designer/lib/shared/morphmenu.cpp new file mode 100644 index 0000000..3249679 --- /dev/null +++ b/designer/lib/shared/morphmenu.cpp @@ -0,0 +1,635 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "morphmenu_p.h" +#include "formwindowbase_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_formwindowcommand_p.h" +#include "qlayout_widget_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_propertycommand_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +// Helpers for the dynamic properties that store Z/Widget order +static const char *widgetOrderPropertyC = "_q_widgetOrder"; +static const char *zOrderPropertyC = "_q_zOrder"; + +/* Morphing in Designer: + * It is possible to morph: + * - Non-Containers into similar widgets by category + * - Simple page containers into similar widgets or page-based containers with + * a single page (in theory also into a QLayoutWidget, but this might + * not always be appropriate). + * - Page-based containers into page-based containers or simple containers if + * they have just one page + * [Page based containers meaning here having a container extension] + * Morphing types are restricted to the basic Qt types. Morphing custom + * widgets is considered risky since they might have unmanaged layouts + * or the like. + * + * Requirements: + * - The widget must be on a non-laid out parent or in a layout managed + * by Designer + * - Its child widgets must be non-laid out or in a layout managed + * by Designer + * Note that child widgets can be + * - On the widget itself in the case of simple containers + * - On several pages in the case of page-based containers + * This is what is called 'childContainers' in the code (the widget itself + * or the list of container extension pages). + * + * The Morphing process encompasses: + * - Create a target widget and apply properties as far as applicable + * If the target widget has a container extension, add a sufficient + * number of pages. + * - Transferring the child widgets over to the new childContainers. + * In the case of a managed layout on a childContainer, this is simply + * set on the target childContainer, which is a new Qt 4.5 + * functionality. + * - Replace the widget itself in the parent layout + */ + +namespace qdesigner_internal { + +enum MorphCategory { + MorphCategoryNone, MorphSimpleContainer, MorphPageContainer, MorphItemView, + MorphButton, MorphSpinBox, MorphTextEdit +}; + +// Determine category of a widget +static MorphCategory category(const QWidget *w) +{ + // Simple containers: Exact match + const QMetaObject *mo = w->metaObject(); + if (mo == &QWidget::staticMetaObject || mo == &QFrame::staticMetaObject || mo == &QGroupBox::staticMetaObject || mo == &QLayoutWidget::staticMetaObject) + return MorphSimpleContainer; + if (mo == &QTabWidget::staticMetaObject || mo == &QStackedWidget::staticMetaObject || mo == &QToolBox::staticMetaObject) + return MorphPageContainer; + if (qobject_cast(w)) + return MorphItemView; + if (qobject_cast(w)) + return MorphButton; + if (qobject_cast(w)) + return MorphSpinBox; + if (qobject_cast(w) || qobject_cast(w)) + return MorphTextEdit; + + return MorphCategoryNone; +} + +/* Return the similar classes of a category. This is currently restricted + * to the known Qt classes with no precautions to parse the Widget Database + * (which is too risky, custom classes might have container extensions + * or non-managed layouts, etc.). */ + +static QStringList classesOfCategory(MorphCategory cat) +{ + typedef QMap CandidateCache; + static CandidateCache candidateCache; + CandidateCache::iterator it = candidateCache.find(cat); + if (it == candidateCache.end()) { + it = candidateCache.insert(cat, QStringList()); + QStringList &l = it.value(); + switch (cat) { + case MorphCategoryNone: + break; + case MorphSimpleContainer: + // Do not generally allow to morph into a layout. + // This can be risky in case of container pages,etc. + l << QLatin1String("QWidget") << QLatin1String("QFrame") << QLatin1String("QGroupBox"); + break; + case MorphPageContainer: + l << QLatin1String("QTabWidget") << QLatin1String("QStackedWidget") << QLatin1String("QToolBox"); + break; + case MorphItemView: + l << QLatin1String("QListView") << QLatin1String("QListWidget") + << QLatin1String("QTreeView") << QLatin1String("QTreeWidget") + << QLatin1String("QTableView") << QLatin1String("QTableWidget") + << QLatin1String("QColumnView"); + break; + case MorphButton: + l << QLatin1String("QCheckBox") << QLatin1String("QRadioButton") + << QLatin1String("QPushButton") << QLatin1String("QToolButton") + << QLatin1String("QCommandLinkButton"); + break; + case MorphSpinBox: + l << QLatin1String("QDateTimeEdit") << QLatin1String("QDateEdit") + << QLatin1String("QTimeEdit") + << QLatin1String("QSpinBox") << QLatin1String("QDoubleSpinBox"); + break; + case MorphTextEdit: + l << QLatin1String("QTextEdit") << QLatin1String("QPlainTextEdit"); + break; + } + } + return it.value(); +} + +// Return the widgets containing the children to be transferred to. This is the +// widget itself in most cases, except for QDesignerContainerExtension cases +static QWidgetList childContainers(const QDesignerFormEditorInterface *core, QWidget *w) +{ + if (const QDesignerContainerExtension *ce = qt_extension(core->extensionManager(), w)) { + QWidgetList children; + if (const int count = ce->count()) { + for (int i = 0; i < count; i++) + children.push_back(ce->widget(i)); + } + return children; + } + QWidgetList self; + self.push_back(w); + return self; +} + +// Suggest a suitable objectname for the widget to be morphed into +// Replace the class name parts: 'xxFrame' -> 'xxGroupBox', 'frame' -> 'groupBox' +static QString suggestObjectName(const QString &oldClassName, const QString &newClassName, const QString &oldName) +{ + QString oldClassPart = oldClassName; + QString newClassPart = newClassName; + if (oldClassPart.startsWith(QLatin1Char('Q'))) + oldClassPart.remove(0, 1); + if (newClassPart.startsWith(QLatin1Char('Q'))) + newClassPart.remove(0, 1); + + QString newName = oldName; + newName.replace(oldClassPart, newClassPart); + oldClassPart[0] = oldClassPart.at(0).toLower(); + newClassPart[0] = newClassPart.at(0).toLower(); + newName.replace(oldClassPart, newClassPart); + return newName; +} + +// Find the label whose buddy the widget is. +QLabel *buddyLabelOf(QDesignerFormWindowInterface *fw, QWidget *w) +{ + typedef QList LabelList; + const LabelList labelList = qFindChildren(fw); + if (labelList.empty()) + return 0; + const LabelList::const_iterator cend = labelList.constEnd(); + for (LabelList::const_iterator it = labelList.constBegin(); it != cend; ++it ) + if ( (*it)->buddy() == w) + return *it; + return 0; +} + +// Replace widgets in a widget-list type dynamic property of the parent +// used for Z-order, etc. +static void replaceWidgetListDynamicProperty(QWidget *parentWidget, + QWidget *oldWidget, QWidget *newWidget, + const char *name) +{ + QWidgetList list = qVariantValue(parentWidget->property(name)); + const int index = list.indexOf(oldWidget); + if (index != -1) { + list.replace(index, newWidget); + parentWidget->setProperty(name, qVariantFromValue(list)); + } +} + +/* Morph a widget into another class. Use the static addMorphMacro() to + * add a respective command sequence to the undo stack as it emits signals + * which cause other commands to be added. */ +class MorphWidgetCommand : public QDesignerFormWindowCommand +{ + Q_DISABLE_COPY(MorphWidgetCommand) +public: + + explicit MorphWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~MorphWidgetCommand(); + + // Convenience to add a morph command sequence macro + static bool addMorphMacro(QDesignerFormWindowInterface *formWindow, QWidget *w, const QString &newClass); + + bool init(QWidget *widget, const QString &newClass); + + QString newWidgetName() const { return m_afterWidget->objectName(); } + + virtual void redo(); + virtual void undo(); + + static QStringList candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w); + +private: + static bool canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *childContainerCount = 0, MorphCategory *cat = 0); + void morph(QWidget *before, QWidget *after); + + QWidget *m_beforeWidget; + QWidget *m_afterWidget; +}; + +bool MorphWidgetCommand::addMorphMacro(QDesignerFormWindowInterface *fw, QWidget *w, const QString &newClass) +{ + MorphWidgetCommand *morphCmd = new MorphWidgetCommand(fw); + if (!morphCmd->init(w, newClass)) { + qWarning("*** Unable to create a MorphWidgetCommand"); + delete morphCmd; + return false; + } + QLabel *buddyLabel = buddyLabelOf(fw, w); + // Need a macro since it adds further commands + QUndoStack *us = fw->commandHistory(); + us->beginMacro(morphCmd->text()); + // Have the signal slot/buddy editors add their commands to delete widget + if (FormWindowBase *fwb = qobject_cast(fw)) + fwb->emitWidgetRemoved(w); + + const QString newWidgetName = morphCmd->newWidgetName(); + us->push(morphCmd); + + // restore buddy using the QByteArray name. + if (buddyLabel) { + SetPropertyCommand *buddyCmd = new SetPropertyCommand(fw); + buddyCmd->init(buddyLabel, QLatin1String("buddy"), QVariant(newWidgetName.toUtf8())); + us->push(buddyCmd); + } + us->endMacro(); + return true; +} + +MorphWidgetCommand::MorphWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_beforeWidget(0), + m_afterWidget(0) +{ +} + +MorphWidgetCommand::~MorphWidgetCommand() +{ +} + +bool MorphWidgetCommand::init(QWidget *widget, const QString &newClassName) +{ + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + + if (!canMorph(fw, widget)) + return false; + + const QString oldClassName = WidgetFactory::classNameOf(core, widget); + const QString oldName = widget->objectName(); + //: MorphWidgetCommand description + setText(QApplication::translate("Command", "Morph %1/'%2' into %3").arg(oldClassName, oldName, newClassName)); + + m_beforeWidget = widget; + m_afterWidget = core->widgetFactory()->createWidget(newClassName, fw); + if (!m_afterWidget) + return false; + + // Set object name. Do not unique it (as to maintain it). + m_afterWidget->setObjectName(suggestObjectName(oldClassName, newClassName, oldName)); + + // If the target has a container extension, we add enough new pages to take + // up the children of the before widget + if (QDesignerContainerExtension* c = qt_extension(core->extensionManager(), m_afterWidget)) { + if (const int pageCount = childContainers(core, m_beforeWidget).size()) { + const QString qWidget = QLatin1String("QWidget"); + const QString containerName = m_afterWidget->objectName(); + for (int i = 0; i < pageCount; i++) { + QString name = containerName; + name += QLatin1String("Page"); + name += QString::number(i + 1); + QWidget *page = core->widgetFactory()->createWidget(qWidget); + page->setObjectName(name); + fw->ensureUniqueObjectName(page); + c->addWidget(page); + core->metaDataBase()->add(page); + } + } + } + + // Copy over applicable properties + const QDesignerPropertySheetExtension *beforeSheet = qt_extension(core->extensionManager(), widget); + QDesignerPropertySheetExtension *afterSheet = qt_extension(core->extensionManager(), m_afterWidget); + const QString objectNameProperty = QLatin1String("objectName"); + const int count = beforeSheet->count(); + for (int i = 0; i < count; i++) + if (beforeSheet->isVisible(i) && beforeSheet->isChanged(i)) { + const QString name = beforeSheet->propertyName(i); + if (name != objectNameProperty) { + const int afterIndex = afterSheet->indexOf(name); + if (afterIndex != -1 && afterSheet->isVisible(afterIndex) && afterSheet->propertyGroup(afterIndex) == beforeSheet->propertyGroup(i)) { + afterSheet->setProperty(i, beforeSheet->property(i)); + afterSheet->setChanged(i, true); + } else { + // Some mismatch. The rest won't match, either + break; + } + } + } + return true; +} + +void MorphWidgetCommand::redo() +{ + morph(m_beforeWidget, m_afterWidget); +} + +void MorphWidgetCommand::undo() +{ + morph(m_afterWidget, m_beforeWidget); +} + +void MorphWidgetCommand::morph(QWidget *before, QWidget *after) +{ + QDesignerFormWindowInterface *fw = formWindow(); + + fw->unmanageWidget(before); + + const QRect oldGeom = before->geometry(); + QWidget *parent = before->parentWidget(); + Q_ASSERT(parent); + /* Morphing consists of main 2 steps + * 1) Move over children (laid out, non-laid out) + * 2) Register self with new parent (laid out, non-laid out) */ + + // 1) Move children. Loop over child containers + QWidgetList beforeChildContainers = childContainers(fw->core(), before); + QWidgetList afterChildContainers = childContainers(fw->core(), after); + Q_ASSERT(beforeChildContainers.size() == afterChildContainers.size()); + const int childContainerCount = beforeChildContainers.size(); + for (int i = 0; i < childContainerCount; i++) { + QWidget *beforeChildContainer = beforeChildContainers.at(i); + QWidget *afterChildContainer = afterChildContainers.at(i); + if (QLayout *childLayout = beforeChildContainer->layout()) { + // Laid-out: Move the layout (since 4.5) + afterChildContainer->setLayout(childLayout); + } else { + // Non-Laid-out: Reparent, move over + const QObjectList c = beforeChildContainer->children(); + const QObjectList::const_iterator cend = c.constEnd(); + for (QObjectList::const_iterator it = c.constBegin(); it != cend; ++it) { + if ( (*it)->isWidgetType()) { + QWidget *w = static_cast(*it); + if (fw->isManaged(w)) { + const QRect geom = w->geometry(); + w->setParent(afterChildContainer); + w->setGeometry(geom); + } + } + } + } + afterChildContainer->setProperty(widgetOrderPropertyC, beforeChildContainer->property(widgetOrderPropertyC)); + afterChildContainer->setProperty(zOrderPropertyC, beforeChildContainer->property(zOrderPropertyC)); + } + + // 2) Replace the actual widget in the parent layout + after->setGeometry(oldGeom); + if (QLayout *containingLayout = LayoutInfo::managedLayout(fw->core(), parent)) { + LayoutHelper *lh = LayoutHelper::createLayoutHelper(LayoutInfo::layoutType(fw->core(), containingLayout)); + Q_ASSERT(lh); + lh->replaceWidget(containingLayout, before, after); + delete lh; + } else { + before->hide(); + before->setParent(0); + after->setParent(parent); + after->setGeometry(oldGeom); + } + + // Check various properties: Z order, form tab order + replaceWidgetListDynamicProperty(parent, before, after, widgetOrderPropertyC); + replaceWidgetListDynamicProperty(parent, before, after, zOrderPropertyC); + + QDesignerMetaDataBaseItemInterface *formItem = fw->core()->metaDataBase()->item(fw); + QWidgetList tabOrder = formItem->tabOrder(); + const int tabIndex = tabOrder.indexOf(before); + if (tabIndex != -1) { + tabOrder.replace(tabIndex, after); + formItem->setTabOrder(tabOrder); + } + + after->show(); + fw->manageWidget(after); + + fw->clearSelection(false); + fw->selectWidget(after); +} + +/* Check if morphing is possible. It must be a valid category and the parent/ + * child relationships must be either non-laidout or directly on + * Designer-managed layouts. */ +bool MorphWidgetCommand::canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *ptrToChildContainerCount, MorphCategory *ptrToCat) +{ + if (ptrToChildContainerCount) + *ptrToChildContainerCount = 0; + const MorphCategory cat = category(w); + if (ptrToCat) + *ptrToCat = cat; + if (cat == MorphCategoryNone) + return false; + + QDesignerFormEditorInterface *core = fw->core(); + // Don't know how to fiddle class names in Jambi.. + if (qt_extension(core->extensionManager(), core)) + return false; + if (!fw->isManaged(w) || w == fw->mainContainer()) + return false; + // Check the parent relationship. We accept only managed parent widgets + // with a single, managed layout in which widget is a member. + QWidget *parent = w->parentWidget(); + if (parent == 0) + return false; + if (QLayout *pl = LayoutInfo::managedLayout(core, parent)) + if (pl->indexOf(w) < 0 || !core->metaDataBase()->item(pl)) + return false; + // Check Widget database + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int wdbindex = wdb->indexOfObject(w); + if (wdbindex == -1) + return false; + const bool isContainer = wdb->item(wdbindex)->isContainer(); + if (!isContainer) + return true; + // Check children. All child containers must be non-laid-out or have managed layouts + const QWidgetList pages = childContainers(core, w); + const int pageCount = pages.size(); + if (ptrToChildContainerCount) + *ptrToChildContainerCount = pageCount; + if (pageCount) { + for (int i = 0; i < pageCount; i++) + if (QLayout *cl = pages.at(i)->layout()) + if (!core->metaDataBase()->item(cl)) + return false; + } + return true; +} + +QStringList MorphWidgetCommand::candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w) +{ + int childContainerCount; + MorphCategory cat; + if (!canMorph(fw, w, &childContainerCount, &cat)) + return QStringList(); + + QStringList rc = classesOfCategory(cat); + switch (cat) { + // Frames, etc can always be morphed into one-page page containers + case MorphSimpleContainer: + rc += classesOfCategory(MorphPageContainer); + break; + // Multipage-Containers can be morphed into simple containers if they + // have 1 page. + case MorphPageContainer: + if (childContainerCount == 1) + rc += classesOfCategory(MorphSimpleContainer); + break; + default: + break; + } + return rc; +} + +// MorphMenu +MorphMenu::MorphMenu(QObject *parent) : + QObject(parent), + m_subMenuAction(0), + m_menu(0), + m_mapper(0), + m_widget(0), + m_formWindow(0) +{ +} + +void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al) +{ + if (populateMenu(w, fw)) + al.push_back(m_subMenuAction); +} + +void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m) +{ + if (populateMenu(w, fw)) + m.addAction(m_subMenuAction); +} + +void MorphMenu::slotMorph(const QString &newClassName) +{ + MorphWidgetCommand::addMorphMacro(m_formWindow, m_widget, newClassName); +} + +bool MorphMenu::populateMenu(QWidget *w, QDesignerFormWindowInterface *fw) +{ + m_widget = 0; + m_formWindow = 0; + + // Clear menu + if (m_subMenuAction) { + m_subMenuAction->setVisible(false); + m_menu->clear(); + } + + // Checks: Must not be main container + if (w == fw->mainContainer()) + return false; + + const QStringList c = MorphWidgetCommand::candidateClasses(fw, w); + if (c.empty()) + return false; + + // Pull up + m_widget = w; + m_formWindow = fw; + const QString oldClassName = WidgetFactory::classNameOf(fw->core(), w); + + if (!m_subMenuAction) { + m_subMenuAction = new QAction(tr("Morph into"), this); + m_menu = new QMenu; + m_subMenuAction->setMenu(m_menu); + m_mapper = new QSignalMapper(this); + connect(m_mapper , SIGNAL(mapped(QString)), this, SLOT(slotMorph(QString))); + } + + // Add actions + const QStringList::const_iterator cend = c.constEnd(); + for (QStringList::const_iterator it = c.constBegin(); it != cend; ++it) { + if (*it != oldClassName) { + QAction *a = m_menu->addAction(*it); + m_mapper->setMapping (a, *it); + connect(a, SIGNAL(triggered()), m_mapper, SLOT(map())); + } + } + m_subMenuAction->setVisible(true); + return true; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/morphmenu_p.h b/designer/lib/shared/morphmenu_p.h new file mode 100644 index 0000000..52468e6 --- /dev/null +++ b/designer/lib/shared/morphmenu_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef MORPH_COMMAND_H +#define MORPH_COMMAND_H + +#include "shared_global_p.h" +#include "qdesigner_formwindowcommand_p.h" + +QT_BEGIN_NAMESPACE + +class QAction; +class QSignalMapper; +class QMenu; + +namespace qdesigner_internal { + +/* Conveniene morph menu that acts on a single widget. */ +class QDESIGNER_SHARED_EXPORT MorphMenu : public QObject { + Q_DISABLE_COPY(MorphMenu) + Q_OBJECT +public: + typedef QList ActionList; + + explicit MorphMenu(QObject *parent = 0); + + void populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al); + void populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m); + +private slots: + void slotMorph(const QString &newClassName); + +private: + bool populateMenu(QWidget *w, QDesignerFormWindowInterface *fw); + + QAction *m_subMenuAction; + QMenu *m_menu; + QSignalMapper *m_mapper; + + QWidget *m_widget; + QDesignerFormWindowInterface *m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // MORPH_COMMAND_H diff --git a/designer/lib/shared/newactiondialog.cpp b/designer/lib/shared/newactiondialog.cpp new file mode 100644 index 0000000..086e2af --- /dev/null +++ b/designer/lib/shared/newactiondialog.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newactiondialog_p.h" +#include "ui_newactiondialog.h" +#include "richtexteditor_p.h" +#include "actioneditor_p.h" +#include "formwindowbase_p.h" +#include "qdesigner_utils_p.h" +#include "iconloader_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// -------------------- ActionData + +ActionData::ActionData() : + checkable(false) +{ +} + +// Returns a combination of ChangeMask flags +unsigned ActionData::compare(const ActionData &rhs) const +{ + unsigned rc = 0; + if (text != rhs.text) + rc |= TextChanged; + if (name != rhs.name) + rc |= NameChanged; + if (toolTip != rhs.toolTip) + rc |= ToolTipChanged ; + if (icon != rhs.icon) + rc |= IconChanged ; + if (checkable != rhs.checkable) + rc |= CheckableChanged; + if (keysequence != rhs.keysequence) + rc |= KeysequenceChanged ; + return rc; +} + +// -------------------- NewActionDialog +NewActionDialog::NewActionDialog(ActionEditor *parent) : + QDialog(parent, Qt::Sheet), + m_ui(new Ui::NewActionDialog), + m_actionEditor(parent) +{ + m_ui->setupUi(this); + + m_ui->tooltipEditor->setTextPropertyValidationMode(ValidationRichText); + connect(m_ui->toolTipToolButton, SIGNAL(clicked()), this, SLOT(slotEditToolTip())); + + m_ui->keysequenceResetToolButton->setIcon(createIconSet(QLatin1String("resetproperty.png"))); + connect(m_ui->keysequenceResetToolButton, SIGNAL(clicked()), this, SLOT(slotResetKeySequence())); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->editActionText->setFocus(); + m_auto_update_object_name = true; + updateButtons(); + + QDesignerFormWindowInterface *form = parent->formWindow(); + m_ui->iconSelector->setFormEditor(form->core()); + FormWindowBase *formBase = qobject_cast(form); + + if (formBase) { + m_ui->iconSelector->setPixmapCache(formBase->pixmapCache()); + m_ui->iconSelector->setIconCache(formBase->iconCache()); + } +} + +NewActionDialog::~NewActionDialog() +{ + delete m_ui; +} + +QString NewActionDialog::actionText() const +{ + return m_ui->editActionText->text(); +} + +QString NewActionDialog::actionName() const +{ + return m_ui->editObjectName->text(); +} + +ActionData NewActionDialog::actionData() const +{ + ActionData rc; + rc.text = actionText(); + rc.name = actionName(); + rc.toolTip = m_ui->tooltipEditor->text(); + rc.icon = m_ui->iconSelector->icon(); + rc.checkable = m_ui->checkableCheckBox->checkState() == Qt::Checked; + rc.keysequence = PropertySheetKeySequenceValue(m_ui->keySequenceEdit->keySequence()); + return rc; +} + +void NewActionDialog::setActionData(const ActionData &d) +{ + m_ui->editActionText->setText(d.text); + m_ui->editObjectName->setText(d.name); + m_ui->iconSelector->setIcon(d.icon); + m_ui->tooltipEditor->setText(d.toolTip); + m_ui->keySequenceEdit->setKeySequence(d.keysequence.value()); + m_ui->checkableCheckBox->setCheckState(d.checkable ? Qt::Checked : Qt::Unchecked); + + m_auto_update_object_name = false; + updateButtons(); +} + +void NewActionDialog::on_editActionText_textEdited(const QString &text) +{ + if (text.isEmpty()) + m_auto_update_object_name = true; + + if (m_auto_update_object_name) + m_ui->editObjectName->setText(ActionEditor::actionTextToName(text)); + + updateButtons(); +} + +void NewActionDialog::on_editObjectName_textEdited(const QString&) +{ + updateButtons(); + m_auto_update_object_name = false; +} + +void NewActionDialog::slotEditToolTip() +{ + const QString oldToolTip = m_ui->tooltipEditor->text(); + RichTextEditorDialog richTextDialog(m_actionEditor->core(), this); + richTextDialog.setText(oldToolTip); + if (richTextDialog.showDialog() == QDialog::Rejected) + return; + const QString newToolTip = richTextDialog.text(); + if (newToolTip != oldToolTip) + m_ui->tooltipEditor->setText(newToolTip); +} + +void NewActionDialog::slotResetKeySequence() +{ + m_ui->keySequenceEdit->setKeySequence(QKeySequence()); + m_ui->keySequenceEdit->setFocus(Qt::MouseFocusReason); +} + +void NewActionDialog::updateButtons() +{ + QPushButton *okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(!actionText().isEmpty() && !actionName().isEmpty()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/newactiondialog.ui b/designer/lib/shared/newactiondialog.ui new file mode 100644 index 0000000..9b10ff2 --- /dev/null +++ b/designer/lib/shared/newactiondialog.ui @@ -0,0 +1,277 @@ + + + ********************************************************************* +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::NewActionDialog + + + New Action... + + + + + + + + &Text: + + + editActionText + + + + + + + + 255 + 0 + + + + + + + + Object &name: + + + editObjectName + + + + + + + + + + &Icon: + + + iconSelector + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Shortcut: + + + + + + + + + + + + + + Checkable: + + + + + + + ToolTip: + + + + + + + + + + 0 + 0 + + + + + + + + ... + + + + + + + + + + + + 0 + 0 + + + + + + + + ... + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + qdesigner_internal::IconSelector + QWidget +
iconselector_p.h
+ 1 +
+ + QtKeySequenceEdit + QWidget +
qtpropertybrowserutils_p.h
+ 1 +
+ + TextPropertyEditor + QWidget +
textpropertyeditor_p.h
+ 1 +
+
+ + editActionText + editObjectName + + + + + buttonBox + accepted() + qdesigner_internal::NewActionDialog + accept() + + + 165 + 162 + + + 291 + 94 + + + + + buttonBox + rejected() + qdesigner_internal::NewActionDialog + reject() + + + 259 + 162 + + + 293 + 128 + + + + +
diff --git a/designer/lib/shared/newactiondialog_p.h b/designer/lib/shared/newactiondialog_p.h new file mode 100644 index 0000000..5f48250 --- /dev/null +++ b/designer/lib/shared/newactiondialog_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWACTIONDIALOG_P_H +#define NEWACTIONDIALOG_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 "qdesigner_utils_p.h" // PropertySheetIconValue + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +namespace Ui { + class NewActionDialog; +} + +class ActionEditor; + +struct ActionData { + + enum ChangeMask { + TextChanged = 0x1, NameChanged = 0x2, ToolTipChanged = 0x4, + IconChanged = 0x8, CheckableChanged = 0x10, KeysequenceChanged = 0x20 + }; + + ActionData(); + // Returns a combination of ChangeMask flags + unsigned compare(const ActionData &rhs) const; + + QString text; + QString name; + QString toolTip; + PropertySheetIconValue icon; + bool checkable; + PropertySheetKeySequenceValue keysequence; +}; + +inline bool operator==(const ActionData &a1, const ActionData &a2) { return a1.compare(a2) == 0u; } +inline bool operator!=(const ActionData &a1, const ActionData &a2) { return a1.compare(a2) != 0u; } + +class NewActionDialog: public QDialog +{ + Q_OBJECT +public: + explicit NewActionDialog(ActionEditor *parent); + virtual ~NewActionDialog(); + + ActionData actionData() const; + void setActionData(const ActionData &d); + + QString actionText() const; + QString actionName() const; + +private slots: + void on_editActionText_textEdited(const QString &text); + void on_editObjectName_textEdited(const QString &text); + void slotEditToolTip(); + void slotResetKeySequence(); + +private: + Ui::NewActionDialog *m_ui; + ActionEditor *m_actionEditor; + bool m_auto_update_object_name; + + void updateButtons(); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // NEWACTIONDIALOG_P_H diff --git a/designer/lib/shared/newformwidget.cpp b/designer/lib/shared/newformwidget.cpp new file mode 100644 index 0000000..a3f853c --- /dev/null +++ b/designer/lib/shared/newformwidget.cpp @@ -0,0 +1,586 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newformwidget_p.h" +#include "ui_newformwidget.h" +#include "qdesigner_formbuilder_p.h" +#include "sheet_delegate_p.h" +#include "widgetdatabase_p.h" +#include "shared_settings_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { profileComboIndexOffset = 1 }; +enum { debugNewFormWidget = 0 }; + +enum NewForm_CustomRole { + // File name (templates from resources, paths) + TemplateNameRole = Qt::UserRole + 100, + // Class name (widgets from Widget data base) + ClassNameRole = Qt::UserRole + 101 +}; + +static const char *newFormObjectNameC = "Form"; + +// Create a form name for an arbitrary class. If it is Qt, qtify it, +// else return "Form". +static QString formName(const QString &className) +{ + if (!className.startsWith(QLatin1Char('Q'))) + return QLatin1String(newFormObjectNameC); + QString rc = className; + rc.remove(0, 1); + return rc; +} + +namespace qdesigner_internal { + +struct TemplateSize { + const char *name; + int width; + int height; +}; + +static const struct TemplateSize templateSizes[] = +{ + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "Default size"), 0, 0 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA portrait (240x320)"), 240, 320 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA landscape (320x240)"), 320, 240 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA portrait (480x640)"), 480, 640 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA landscape (640x480)"), 640, 480 } +}; + +/* -------------- NewForm dialog. + * Designer takes new form templates from: + * 1) Files located in directories specified in resources + * 2) Files located in directories specified as user templates + * 3) XML from container widgets deemed usable for form templates by the widget + * database + * 4) XML from custom container widgets deemed usable for form templates by the + * widget database + * + * The widget database provides helper functions to obtain lists of names + * and xml for 3,4. + * + * Fixed-size forms for embedded platforms are obtained as follows: + * 1) If the origin is a file: + * - Check if the file exists in the subdirectory "/x/" of + * the path (currently the case for the dialog box because the button box + * needs to be positioned) + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * 2) If the origin is XML: + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * + * The tree widget item roles indicate which type of entry it is + * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4) + */ + +NewFormWidget::NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget) : + QDesignerNewFormWidgetInterface(parentWidget), + m_core(core), + m_ui(new Ui::NewFormWidget), + m_currentItem(0), + m_acceptedItem(0) +{ + typedef QList DeviceProfileList; + + m_ui->setupUi(this); + m_ui->treeWidget->setItemDelegate(new qdesigner_internal::SheetDelegate(m_ui->treeWidget, this)); + m_ui->treeWidget->header()->hide(); + m_ui->treeWidget->header()->setStretchLastSection(true); + m_ui->lblPreview->setBackgroundRole(QPalette::Base); + QDesignerSharedSettings settings(m_core); + + QString uiExtension = QLatin1String("ui"); + QString templatePath = QLatin1String(":/trolltech/designer/templates/forms"); + + QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core); + if (lang) { + templatePath = QLatin1String(":/templates/forms"); + uiExtension = lang->uiExtension(); + } + + // Resource templates + const QString formTemplate = settings.formTemplate(); + QTreeWidgetItem *selectedItem = 0; + loadFrom(templatePath, true, uiExtension, formTemplate, selectedItem); + // Additional template paths + const QStringList formTemplatePaths = settings.formTemplatePaths(); + const QStringList::const_iterator ftcend = formTemplatePaths.constEnd(); + for (QStringList::const_iterator it = formTemplatePaths.constBegin(); it != ftcend; ++it) + loadFrom(*it, false, uiExtension, formTemplate, selectedItem); + + // Widgets/custom widgets + if (!lang) { + //: New Form Dialog Categories + loadFrom(tr("Widgets"), qdesigner_internal::WidgetDataBase::formWidgetClasses(core), formTemplate, selectedItem); + loadFrom(tr("Custom Widgets"), qdesigner_internal::WidgetDataBase::customFormWidgetClasses(core), formTemplate, selectedItem); + } + + // Still no selection - default to first item + if (selectedItem == 0 && m_ui->treeWidget->topLevelItemCount() != 0) { + QTreeWidgetItem *firstTopLevel = m_ui->treeWidget->topLevelItem(0); + if (firstTopLevel->childCount() > 0) + selectedItem = firstTopLevel->child(0); + } + + // Open parent, select and make visible + if (selectedItem) { + m_ui->treeWidget->setCurrentItem(selectedItem); + m_ui->treeWidget->setItemSelected(selectedItem, true); + m_ui->treeWidget->scrollToItem(selectedItem->parent()); + } + // Fill profile combo + m_deviceProfiles = settings.deviceProfiles(); + m_ui->profileComboBox->addItem(tr("None")); + connect(m_ui->profileComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDeviceProfileIndexChanged(int))); + if (m_deviceProfiles.empty()) { + m_ui->profileComboBox->setEnabled(false); + } else { + const DeviceProfileList::const_iterator dcend = m_deviceProfiles.constEnd(); + for (DeviceProfileList::const_iterator it = m_deviceProfiles.constBegin(); it != dcend; ++it) + m_ui->profileComboBox->addItem(it->name()); + const int ci = settings.currentDeviceProfileIndex(); + if (ci >= 0) + m_ui->profileComboBox->setCurrentIndex(ci + profileComboIndexOffset); + } + // Fill size combo + const int sizeCount = sizeof(templateSizes)/ sizeof(TemplateSize); + for (int i = 0; i < sizeCount; i++) { + const QSize size = QSize(templateSizes[i].width, templateSizes[i].height); + m_ui->sizeComboBox->addItem(tr(templateSizes[i].name), size); + } + + setTemplateSize(settings.newFormSize()); + + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << "Leaving"; +} + +NewFormWidget::~NewFormWidget() +{ + QDesignerSharedSettings settings (m_core); + settings.setNewFormSize(templateSize()); + // Do not change previously stored item if dialog was rejected + if (m_acceptedItem) + settings.setFormTemplate(m_acceptedItem->text(0)); + delete m_ui; +} + +void NewFormWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << current; + if (!current) + return; + + if (!current->parent()) { // Top level item: Ensure expanded when browsing down + return; + } + + m_currentItem = current; + + emit currentTemplateChanged(showCurrentItemPixmap()); +} + +bool NewFormWidget::showCurrentItemPixmap() +{ + bool rc = false; + if (m_currentItem) { + const QPixmap pixmap = formPreviewPixmap(m_currentItem); + if (pixmap.isNull()) { + m_ui->lblPreview->setText(tr("Error loading form")); + } else { + m_ui->lblPreview->setPixmap(pixmap); + rc = true; + } + } + return rc; +} + +void NewFormWidget::on_treeWidget_itemActivated(QTreeWidgetItem *item) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << item; + + if (item->data(0, TemplateNameRole).isValid() || item->data(0, ClassNameRole).isValid()) + emit templateActivated(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QTreeWidgetItem *item) +{ + // Cache pixmaps per item/device profile + const ItemPixmapCacheKey cacheKey(item, profileComboIndex()); + ItemPixmapCache::iterator it = m_itemPixmapCache.find(cacheKey); + if (it == m_itemPixmapCache.end()) { + // file or string? + const QVariant fileName = item->data(0, TemplateNameRole); + QPixmap rc; + if (fileName.type() == QVariant::String) { + rc = formPreviewPixmap(fileName.toString()); + } else { + const QVariant classNameV = item->data(0, ClassNameRole); + Q_ASSERT(classNameV.type() == QVariant::String); + const QString className = classNameV.toString(); + QByteArray data = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)).toUtf8(); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + rc = formPreviewPixmap(buffer); + } + if (rc.isNull()) // Retry invalid ones + return rc; + it = m_itemPixmapCache.insert(cacheKey, rc); + } + return it.value(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QString &fileName) const +{ + QFile f(fileName); + if (f.open(QFile::ReadOnly)) { + QFileInfo fi(fileName); + const QPixmap rc = formPreviewPixmap(f, fi.absolutePath()); + f.close(); + return rc; + } + qWarning() << "The file " << fileName << " could not be opened: " << f.errorString(); + return QPixmap(); +} + +QImage NewFormWidget::grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp) +{ + qdesigner_internal::NewFormWidgetFormBuilder + formBuilder(core, qdesigner_internal::QDesignerFormBuilder::DisableScripts, dp); + if (!workingDir.isEmpty()) + formBuilder.setWorkingDirectory(workingDir); + + QWidget *widget = formBuilder.load(&file, 0); + if (!widget) + return QImage(); + + const QPixmap pixmap = QPixmap::grabWidget(widget); + widget->deleteLater(); + return pixmap.toImage(); +} + +QPixmap NewFormWidget::formPreviewPixmap(QIODevice &file, const QString &workingDir) const +{ + const int margin = 7; + const int shadow = 7; + const int previewSize = 256; + + const QImage wimage = grabForm(m_core, file, workingDir, currentDeviceProfile()); + if (wimage.isNull()) + return QPixmap(); + const QImage image = wimage.scaled(previewSize - margin * 2, previewSize - margin * 2, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + + QImage dest(previewSize, previewSize, QImage::Format_ARGB32_Premultiplied); + dest.fill(0); + + QPainter p(&dest); + p.drawImage(margin, margin, image); + + p.setPen(QPen(palette().brush(QPalette::WindowText), 0)); + + p.drawRect(margin-1, margin-1, image.width() + 1, image.height() + 1); + + const QColor dark(Qt::darkGray); + const QColor light(Qt::transparent); + + // right shadow + { + const QRect rect(margin + image.width() + 1, margin + shadow, shadow, image.height() - shadow + 1); + QLinearGradient lg(rect.topLeft(), rect.topRight()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom shadow + { + const QRect rect(margin + shadow, margin + image.height() + 1, image.width() - shadow + 1, shadow); + QLinearGradient lg(rect.topLeft(), rect.bottomLeft()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom/right corner shadow + { + const QRect rect(margin + image.width() + 1, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // top/right corner + { + const QRect rect(margin + image.width() + 1, margin, shadow, shadow); + QRadialGradient g(rect.bottomLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // bottom/left corner + { + const QRect rect(margin, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topRight(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + p.end(); + + return QPixmap::fromImage(dest); +} + +void NewFormWidget::loadFrom(const QString &path, bool resourceFile, const QString &uiExtension, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + const QDir dir(path); + + if (!dir.exists()) + return; + + // Iterate through the directory and add the templates + const QFileInfoList list = dir.entryInfoList(QStringList(QLatin1String("*.") + uiExtension), + QDir::Files); + + if (list.isEmpty()) + return; + + const QChar separator = resourceFile ? QChar(QLatin1Char('/')) + : QDir::separator(); + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + // Try to get something that is easy to read. + QString visiblePath = path; + int index = visiblePath.lastIndexOf(separator); + if (index != -1) { + // try to find a second slash, just to be a bit better. + const int index2 = visiblePath.lastIndexOf(separator, index - 1); + if (index2 != -1) + index = index2; + visiblePath = visiblePath.mid(index + 1); + visiblePath = QDir::toNativeSeparators(visiblePath); + } + + const QChar underscore = QLatin1Char('_'); + const QChar blank = QLatin1Char(' '); + root->setText(0, visiblePath.replace(underscore, blank)); + root->setToolTip(0, path); + + const QFileInfoList::const_iterator lcend = list.constEnd(); + for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) { + if (!it->isFile()) + continue; + + QTreeWidgetItem *item = new QTreeWidgetItem(root); + const QString text = it->baseName().replace(underscore, blank); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setText(0, text); + item->setData(0, TemplateNameRole, it->absoluteFilePath()); + } +} + +void NewFormWidget::loadFrom(const QString &title, const QStringList &nameList, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + if (nameList.empty()) + return; + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + root->setText(0, title); + const QStringList::const_iterator cend = nameList.constEnd(); + for (QStringList::const_iterator it = nameList.constBegin(); it != cend; ++it) { + const QString text = *it; + QTreeWidgetItem *item = new QTreeWidgetItem(root); + item->setText(0, text); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setData(0, ClassNameRole, *it); + } +} + +void NewFormWidget::on_treeWidget_itemPressed(QTreeWidgetItem *item) +{ + if (item && !item->parent()) + m_ui->treeWidget->setItemExpanded(item, !m_ui->treeWidget->isItemExpanded(item)); +} + +QSize NewFormWidget::templateSize() const +{ + return m_ui->sizeComboBox->itemData(m_ui->sizeComboBox->currentIndex()).toSize(); +} + +void NewFormWidget::setTemplateSize(const QSize &s) +{ + const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s); + if (index != -1) + m_ui->sizeComboBox->setCurrentIndex(index); +} + +static QString readAll(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString()); + return QString(); + } + return QString::fromUtf8(file.readAll()); +} + +QString NewFormWidget::itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const +{ + const QSize size = templateSize(); + // file name or string contents? + const QVariant templateFileName = item->data(0, TemplateNameRole); + if (templateFileName.type() == QVariant::String) { + const QString fileName = templateFileName.toString(); + // No fixed size: just open. + if (size.isNull()) + return readAll(fileName, errorMessage); + // try to find a file matching the size, like "../640x480/xx.ui" + const QFileInfo fiBase(fileName); + QString sizeFileName; + QTextStream(&sizeFileName) << fiBase.path() << QDir::separator() + << size.width() << QLatin1Char('x') << size.height() << QDir::separator() + << fiBase.fileName(); + if (QFileInfo(sizeFileName).isFile()) + return readAll(sizeFileName, errorMessage); + // Nothing found, scale via DOM/temporary file + QString contents = readAll(fileName, errorMessage); + if (!contents.isEmpty()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; + } + // Content. + const QString className = item->data(0, ClassNameRole).toString(); + QString contents = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)); + if (!size.isNull()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; +} + +void NewFormWidget::slotDeviceProfileIndexChanged(int idx) +{ + // Store index for form windows to take effect and refresh pixmap + QDesignerSharedSettings settings(m_core); + settings.setCurrentDeviceProfileIndex(idx - profileComboIndexOffset); + showCurrentItemPixmap(); +} + +int NewFormWidget::profileComboIndex() const +{ + return m_ui->profileComboBox->currentIndex(); +} + +qdesigner_internal::DeviceProfile NewFormWidget::currentDeviceProfile() const +{ + const int ci = profileComboIndex(); + if (ci > 0) + return m_deviceProfiles.at(ci - profileComboIndexOffset); + return qdesigner_internal::DeviceProfile(); +} + +bool NewFormWidget::hasCurrentTemplate() const +{ + return m_currentItem != 0; +} + +QString NewFormWidget::currentTemplateI(QString *ptrToErrorMessage) +{ + if (m_currentItem == 0) { + *ptrToErrorMessage = tr("Internal error: No template selected."); + return QString(); + } + const QString contents = itemToTemplate(m_currentItem, ptrToErrorMessage); + if (contents.isEmpty()) + return contents; + + m_acceptedItem = m_currentItem; + return contents; +} + +QString NewFormWidget::currentTemplate(QString *ptrToErrorMessage) +{ + if (ptrToErrorMessage) + return currentTemplateI(ptrToErrorMessage); + // Do not loose the error + QString errorMessage; + const QString contents = currentTemplateI(&errorMessage); + if (!errorMessage.isEmpty()) + qWarning("%s", errorMessage.toUtf8().constData()); + return contents; +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/newformwidget.ui b/designer/lib/shared/newformwidget.ui new file mode 100644 index 0000000..8f2c6f3 --- /dev/null +++ b/designer/lib/shared/newformwidget.ui @@ -0,0 +1,192 @@ + + + ********************************************************************* +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::NewFormWidget + + + + 0 + 0 + 480 + 194 + + + + + 6 + + + 1 + + + + + + 200 + 0 + + + + + 128 + 128 + + + + false + + + 1 + + + + 0 + + + + + + + + + + + 0 + 0 + + + + 1 + + + Choose a template for a preview + + + Qt::AlignCenter + + + 5 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 7 + 0 + + + + + + + + Embedded Design + + + + + + + + + + + + Device: + + + + + + + Screen Size: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/designer/lib/shared/newformwidget_p.h b/designer/lib/shared/newformwidget_p.h new file mode 100644 index 0000000..88afcdc --- /dev/null +++ b/designer/lib/shared/newformwidget_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWFORMWIDGET_H +#define NEWFORMWIDGET_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 "shared_global_p.h" +#include "deviceprofile_p.h" + +#include + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QIODevice; +class QTreeWidgetItem; + +namespace qdesigner_internal { + +namespace Ui { + class NewFormWidget; +} + +class QDesignerWorkbench; + +class QDESIGNER_SHARED_EXPORT NewFormWidget : public QDesignerNewFormWidgetInterface +{ + Q_OBJECT + Q_DISABLE_COPY(NewFormWidget) + +public: + typedef QList DeviceProfileList; + + explicit NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget); + virtual ~NewFormWidget(); + + virtual bool hasCurrentTemplate() const; + virtual QString currentTemplate(QString *errorMessage = 0); + + // Convenience for implementing file dialogs with preview + static QImage grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp); + +private slots: + void on_treeWidget_itemActivated(QTreeWidgetItem *item); + void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *); + void on_treeWidget_itemPressed(QTreeWidgetItem *item); + void slotDeviceProfileIndexChanged(int idx); + +private: + QPixmap formPreviewPixmap(const QString &fileName) const; + QPixmap formPreviewPixmap(QIODevice &file, const QString &workingDir = QString()) const; + QPixmap formPreviewPixmap(const QTreeWidgetItem *item); + + void loadFrom(const QString &path, bool resourceFile, const QString &uiExtension, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound); + void loadFrom(const QString &title, const QStringList &nameList, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound); + +private: + QString itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const; + QString currentTemplateI(QString *ptrToErrorMessage); + + QSize templateSize() const; + void setTemplateSize(const QSize &s); + int profileComboIndex() const; + qdesigner_internal::DeviceProfile currentDeviceProfile() const; + bool showCurrentItemPixmap(); + + // Pixmap cache (item, profile combo index) + typedef QPair ItemPixmapCacheKey; + typedef QMap ItemPixmapCache; + ItemPixmapCache m_itemPixmapCache; + + QDesignerFormEditorInterface *m_core; + Ui::NewFormWidget *m_ui; + QTreeWidgetItem *m_currentItem; + QTreeWidgetItem *m_acceptedItem; + DeviceProfileList m_deviceProfiles; +}; + +} + +QT_END_NAMESPACE + +#endif // NEWFORMWIDGET_H diff --git a/designer/lib/shared/orderdialog.cpp b/designer/lib/shared/orderdialog.cpp new file mode 100644 index 0000000..76303fc --- /dev/null +++ b/designer/lib/shared/orderdialog.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "orderdialog_p.h" +#include "iconloader_p.h" +#include "ui_orderdialog.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// OrderDialog: Used to reorder the pages of QStackedWidget and QToolBox. +// Provides up and down buttons as well as DnD via QAbstractItemView::InternalMove mode +namespace qdesigner_internal { + +OrderDialog::OrderDialog(QWidget *parent) : + QDialog(parent), + m_ui(new Ui::OrderDialog), + m_format(PageOrderFormat) +{ + m_ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->upButton->setIcon(createIconSet(QString::fromUtf8("up.png"))); + m_ui->downButton->setIcon(createIconSet(QString::fromUtf8("down.png"))); + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + connect(m_ui->buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(slotReset())); + // Catch the remove operation of a DnD operation in QAbstractItemView::InternalMove mode to enable buttons + // Selection mode is 'contiguous' to enable DnD of groups + connect(m_ui->pageList->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotEnableButtonsAfterDnD())); + + m_ui->upButton->setEnabled(false); + m_ui->downButton->setEnabled(false); +} + +OrderDialog::~OrderDialog() +{ + delete m_ui; +} + +void OrderDialog::setDescription(const QString &d) +{ + m_ui->groupBox->setTitle(d); +} + +void OrderDialog::setPageList(const QWidgetList &pages) +{ + // The QWidget* are stored in a map indexed by the old index. + // The old index is set as user data on the item instead of the QWidget* + // because DnD is enabled which requires the user data to serializable + m_orderMap.clear(); + const int count = pages.count(); + for (int i=0; i < count; ++i) + m_orderMap.insert(i, pages.at(i)); + buildList(); +} + +void OrderDialog::buildList() +{ + m_ui->pageList->clear(); + const OrderMap::const_iterator cend = m_orderMap.constEnd(); + for (OrderMap::const_iterator it = m_orderMap.constBegin(); it != cend; ++it) { + QListWidgetItem *item = new QListWidgetItem(); + const int index = it.key(); + switch (m_format) { + case PageOrderFormat: + item->setText(tr("Index %1 (%2)").arg(index).arg(it.value()->objectName())); + break; + case TabOrderFormat: + item->setText(tr("%1 %2").arg(index+1).arg(it.value()->objectName())); + break; + } + item->setData(Qt::UserRole, QVariant(index)); + m_ui->pageList->addItem(item); + } + + if (m_ui->pageList->count() > 0) + m_ui->pageList->setCurrentRow(0); +} + +void OrderDialog::slotReset() +{ + buildList(); +} + +QWidgetList OrderDialog::pageList() const +{ + QWidgetList rc; + const int count = m_ui->pageList->count(); + for (int i=0; i < count; ++i) { + const int oldIndex = m_ui->pageList->item(i)->data(Qt::UserRole).toInt(); + rc.append(m_orderMap.value(oldIndex)); + } + return rc; +} + +void OrderDialog::on_upButton_clicked() +{ + const int row = m_ui->pageList->currentRow(); + if (row <= 0) + return; + + m_ui->pageList->insertItem(row - 1, m_ui->pageList->takeItem(row)); + m_ui->pageList->setCurrentRow(row - 1); +} + +void OrderDialog::on_downButton_clicked() +{ + const int row = m_ui->pageList->currentRow(); + if (row == -1 || row == m_ui->pageList->count() - 1) + return; + + m_ui->pageList->insertItem(row + 1, m_ui->pageList->takeItem(row)); + m_ui->pageList->setCurrentRow(row + 1); +} + +void OrderDialog::slotEnableButtonsAfterDnD() +{ + enableButtons(m_ui->pageList->currentRow()); +} + +void OrderDialog::on_pageList_currentRowChanged(int r) +{ + enableButtons(r); +} + +void OrderDialog::enableButtons(int r) +{ + m_ui->upButton->setEnabled(r > 0); + m_ui->downButton->setEnabled(r >= 0 && r < m_ui->pageList->count() - 1); +} + +QWidgetList OrderDialog::pagesOfContainer(const QDesignerFormEditorInterface *core, QWidget *container) +{ + QWidgetList rc; + if (QDesignerContainerExtension* ce = qt_extension(core->extensionManager(), container)) { + const int count = ce->count(); + for (int i = 0; i < count ;i ++) + rc.push_back(ce->widget(i)); + } + return rc; +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/orderdialog.ui b/designer/lib/shared/orderdialog.ui new file mode 100644 index 0000000..946ad60 --- /dev/null +++ b/designer/lib/shared/orderdialog.ui @@ -0,0 +1,198 @@ + + ********************************************************************* +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::OrderDialog + + + + 0 + 0 + 467 + 310 + + + + Change Page Order + + + + + + Page Order + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 344 + 0 + + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ContiguousSelection + + + QListView::Snap + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Move page up + + + + + + + Move page down + + + + + + + + 0 + 0 + + + + Qt::Vertical + + + + 20 + 99 + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset + + + + + + + + + buttonBox + accepted() + qdesigner_internal::OrderDialog + accept() + + + 50 + 163 + + + 6 + 151 + + + + + buttonBox + rejected() + qdesigner_internal::OrderDialog + reject() + + + 300 + 160 + + + 348 + 148 + + + + + diff --git a/designer/lib/shared/orderdialog_p.h b/designer/lib/shared/orderdialog_p.h new file mode 100644 index 0000000..1ccf26f --- /dev/null +++ b/designer/lib/shared/orderdialog_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 ORDERDIALOG_P_H +#define ORDERDIALOG_P_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +namespace Ui { + class OrderDialog; +} + +class QDESIGNER_SHARED_EXPORT OrderDialog: public QDialog +{ + Q_OBJECT +public: + OrderDialog(QWidget *parent); + virtual ~OrderDialog(); + + static QWidgetList pagesOfContainer(const QDesignerFormEditorInterface *core, QWidget *container); + + void setPageList(const QWidgetList &pages); + QWidgetList pageList() const; + + void setDescription(const QString &d); + + enum Format { // Display format + PageOrderFormat, // Container pages, ranging 0..[n-1] + TabOrderFormat // List of widgets, ranging 1..1 + }; + + void setFormat(Format f) { m_format = f; } + Format format() const { return m_format; } + +private slots: + void on_upButton_clicked(); + void on_downButton_clicked(); + void on_pageList_currentRowChanged(int row); + void slotEnableButtonsAfterDnD(); + void slotReset(); + +private: + void buildList(); + void enableButtons(int r); + + typedef QMap OrderMap; + OrderMap m_orderMap; + Ui::OrderDialog* m_ui; + Format m_format; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ORDERDIALOG_P_H diff --git a/designer/lib/shared/plaintexteditor.cpp b/designer/lib/shared/plaintexteditor.cpp new file mode 100644 index 0000000..d2c96f0 --- /dev/null +++ b/designer/lib/shared/plaintexteditor.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "plaintexteditor_p.h" +#include "abstractsettings_p.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *PlainTextDialogC = "PlainTextDialog"; +static const char *Geometry = "Geometry"; + + +namespace qdesigner_internal { + +PlainTextEditorDialog::PlainTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + m_editor(new QPlainTextEdit), + m_core(core) +{ + setWindowTitle(tr("Edit text")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->addWidget(m_editor); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + QPushButton *ok_button = buttonBox->button(QDialogButtonBox::Ok); + ok_button->setDefault(true); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + vlayout->addWidget(buttonBox); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(PlainTextDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +PlainTextEditorDialog::~PlainTextEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(PlainTextDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +int PlainTextEditorDialog::showDialog() +{ + m_editor->setFocus(); + return exec(); +} + +void PlainTextEditorDialog::setDefaultFont(const QFont &font) +{ + m_editor->setFont(font); +} + +void PlainTextEditorDialog::setText(const QString &text) +{ + m_editor->setPlainText(text); +} + +QString PlainTextEditorDialog::text() const +{ + return m_editor->toPlainText(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/plaintexteditor_p.h b/designer/lib/shared/plaintexteditor_p.h new file mode 100644 index 0000000..baf9a09 --- /dev/null +++ b/designer/lib/shared/plaintexteditor_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PLAINTEXTEDITOR_H +#define PLAINTEXTEDITOR_H + +#include +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QPlainTextEdit; +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT PlainTextEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit PlainTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~PlainTextEditorDialog(); + + int showDialog(); + + void setDefaultFont(const QFont &font); + + void setText(const QString &text); + QString text() const; + +private: + QPlainTextEdit *m_editor; + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // RITCHTEXTEDITOR_H diff --git a/designer/lib/shared/plugindialog.cpp b/designer/lib/shared/plugindialog.cpp new file mode 100644 index 0000000..9b5073d --- /dev/null +++ b/designer/lib/shared/plugindialog.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "plugindialog_p.h" +#include "pluginmanager_p.h" +#include "qdesigner_integration_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +PluginDialog::PluginDialog(QDesignerFormEditorInterface *core, QWidget *parent) + : QDialog(parent +#ifdef Q_WS_MAC + , Qt::Tool +#endif + ), m_core(core) +{ + ui.setupUi(this); + + ui.message->hide(); + + const QStringList headerLabels(tr("Components")); + + ui.treeWidget->setAlternatingRowColors(false); + ui.treeWidget->setSelectionMode(QAbstractItemView::NoSelection); + ui.treeWidget->setHeaderLabels(headerLabels); + ui.treeWidget->header()->hide(); + + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon), + QIcon::Normal, QIcon::On); + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), + QIcon::Normal, QIcon::Off); + featureIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); + + setWindowTitle(tr("Plugin Information")); + populateTreeWidget(); + + if (qobject_cast(m_core->integration())) { + QPushButton *updateButton = new QPushButton(tr("Refresh")); + const QString tooltip = tr("Scan for newly installed custom widget plugins."); + updateButton->setToolTip(tooltip); + updateButton->setWhatsThis(tooltip); + connect(updateButton, SIGNAL(clicked()), this, SLOT(updateCustomWidgetPlugins())); + ui.buttonBox->addButton(updateButton, QDialogButtonBox::ActionRole); + } +} + +void PluginDialog::populateTreeWidget() +{ + ui.treeWidget->clear(); + QDesignerPluginManager *pluginManager = m_core->pluginManager(); + const QStringList fileNames = pluginManager->registeredPlugins(); + + if (!fileNames.isEmpty()) { + QTreeWidgetItem *topLevelItem = setTopLevelItem(QLatin1String("Loaded Plugins")); + QFont boldFont = topLevelItem->font(0); + + foreach (const QString &fileName, fileNames) { + QPluginLoader loader(fileName); + const QFileInfo fileInfo(fileName); + + QTreeWidgetItem *pluginItem = setPluginItem(topLevelItem, fileInfo.fileName(), boldFont); + + if (QObject *plugin = loader.instance()) { + if (const QDesignerCustomWidgetCollectionInterface *c = qobject_cast(plugin)) { + foreach (const QDesignerCustomWidgetInterface *p, c->customWidgets()) + setItem(pluginItem, p->name(), p->toolTip(), p->whatsThis(), p->icon()); + } else { + if (const QDesignerCustomWidgetInterface *p = qobject_cast(plugin)) + setItem(pluginItem, p->name(), p->toolTip(), p->whatsThis(), p->icon()); + } + } + } + } + + const QStringList notLoadedPlugins = pluginManager->failedPlugins(); + if (!notLoadedPlugins.isEmpty()) { + QTreeWidgetItem *topLevelItem = setTopLevelItem(QLatin1String("Failed Plugins")); + const QFont boldFont = topLevelItem->font(0); + foreach (const QString &plugin, notLoadedPlugins) { + const QString failureReason = pluginManager->failureReason(plugin); + QTreeWidgetItem *pluginItem = setPluginItem(topLevelItem, plugin, boldFont); + setItem(pluginItem, failureReason, failureReason, QString(), QIcon()); + } + } + + if (ui.treeWidget->topLevelItemCount() == 0) { + ui.label->setText(tr("Qt Designer couldn't find any plugins")); + ui.treeWidget->hide(); + } else { + ui.label->setText(tr("Qt Designer found the following plugins")); + } +} + +QIcon PluginDialog::pluginIcon(const QIcon &icon) +{ + if (icon.isNull()) + return QIcon(QLatin1String(":/trolltech/formeditor/images/qtlogo.png")); + + return icon; +} + +QTreeWidgetItem* PluginDialog::setTopLevelItem(const QString &itemName) +{ + QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(ui.treeWidget); + topLevelItem->setText(0, itemName); + ui.treeWidget->setItemExpanded(topLevelItem, true); + topLevelItem->setIcon(0, style()->standardPixmap(QStyle::SP_DirOpenIcon)); + + QFont boldFont = topLevelItem->font(0); + boldFont.setBold(true); + topLevelItem->setFont(0, boldFont); + + return topLevelItem; +} + +QTreeWidgetItem* PluginDialog::setPluginItem(QTreeWidgetItem *topLevelItem, + const QString &itemName, const QFont &font) +{ + QTreeWidgetItem *pluginItem = new QTreeWidgetItem(topLevelItem); + pluginItem->setFont(0, font); + pluginItem->setText(0, itemName); + ui.treeWidget->setItemExpanded(pluginItem, true); + pluginItem->setIcon(0, style()->standardPixmap(QStyle::SP_DirOpenIcon)); + + return pluginItem; +} + +void PluginDialog::setItem(QTreeWidgetItem *pluginItem, const QString &name, + const QString &toolTip, const QString &whatsThis, const QIcon &icon) +{ + QTreeWidgetItem *item = new QTreeWidgetItem(pluginItem); + item->setText(0, name); + item->setToolTip(0, toolTip); + item->setWhatsThis(0, whatsThis); + item->setIcon(0, pluginIcon(icon)); +} + +void PluginDialog::updateCustomWidgetPlugins() +{ + if (qdesigner_internal::QDesignerIntegration *integration = qobject_cast(m_core->integration())) { + const int before = m_core->widgetDataBase()->count(); + integration->updateCustomWidgetPlugins(); + const int after = m_core->widgetDataBase()->count(); + if (after > before) { + ui.message->setText(tr("New custom widget plugins have been found.")); + ui.message->show(); + } else { + ui.message->setText(QString()); + } + populateTreeWidget(); + } +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/plugindialog.ui b/designer/lib/shared/plugindialog.ui new file mode 100644 index 0000000..c726023 --- /dev/null +++ b/designer/lib/shared/plugindialog.ui @@ -0,0 +1,136 @@ + + + ********************************************************************* +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +********************************************************************* + PluginDialog + + + + 0 + 0 + 401 + 331 + + + + Plugin Information + + + + 6 + + + 8 + + + + + TextLabel + + + true + + + + + + + Qt::ElideNone + + + + 1 + + + + + + + + TextLabel + + + true + + + + + + + 6 + + + 0 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + rejected() + PluginDialog + reject() + + + 154 + 307 + + + 401 + 280 + + + + + diff --git a/designer/lib/shared/plugindialog_p.h b/designer/lib/shared/plugindialog_p.h new file mode 100644 index 0000000..f4c2119 --- /dev/null +++ b/designer/lib/shared/plugindialog_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_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 "ui_plugindialog.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class PluginDialog : public QDialog +{ + Q_OBJECT +public: + explicit PluginDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +private slots: + void updateCustomWidgetPlugins(); + +private: + void populateTreeWidget(); + QIcon pluginIcon(const QIcon &icon); + QTreeWidgetItem* setTopLevelItem(const QString &itemName); + QTreeWidgetItem* setPluginItem(QTreeWidgetItem *topLevelItem, + const QString &itemName, const QFont &font); + void setItem(QTreeWidgetItem *pluginItem, const QString &name, + const QString &toolTip, const QString &whatsThis, const QIcon &icon); + + QDesignerFormEditorInterface *m_core; + Ui::PluginDialog ui; + QIcon interfaceIcon; + QIcon featureIcon; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/pluginmanager.cpp b/designer/lib/shared/pluginmanager.cpp new file mode 100644 index 0000000..1ef9080 --- /dev/null +++ b/designer/lib/shared/pluginmanager.cpp @@ -0,0 +1,786 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pluginmanager_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_qsettings_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char *uiElementC = "ui"; +static const char *languageAttributeC = "language"; +static const char *widgetElementC = "widget"; +static const char *displayNameAttributeC = "displayname"; +static const char *classAttributeC = "class"; +static const char *customwidgetElementC = "customwidget"; +static const char *extendsElementC = "extends"; +static const char *addPageMethodC = "addpagemethod"; +static const char *propertySpecsC = "propertyspecifications"; +static const char *stringPropertySpecC = "stringpropertyspecification"; +static const char *stringPropertyNameAttrC = "name"; +static const char *stringPropertyTypeAttrC = "type"; +static const char *stringPropertyNoTrAttrC = "notr"; +static const char *jambiLanguageC = "jambi"; + +enum { debugPluginManager = 0 }; + +/* Custom widgets: Loading custom widgets is a 2-step process: PluginManager + * scans for its plugins in the constructor. At this point, it might not be safe + * to immediately initialize the custom widgets it finds, because the rest of + * Designer is not initialized yet. + * Later on, in ensureInitialized(), the plugin instances (including static ones) + * are iterated and the custom widget plugins are initialized and added to internal + * list of custom widgets and parsed data. Should there be a parse error or a language + * mismatch, it kicks out the respective custom widget. The m_initialized flag + * is used to indicate the state. + * Later, someone might call registerNewPlugins(), which agains clears the flag via + * registerPlugin() and triggers the process again. + * Also note that Jambi fakes a custom widget collection that changes its contents + * every time the project is switched. So, custom widget plugins can actually + * disappear, and the custom widget list must be cleared and refilled in + * ensureInitialized() after registerNewPlugins. */ + +QT_BEGIN_NAMESPACE + +static QStringList unique(const QStringList &lst) +{ + const QSet s = QSet::fromList(lst); + return s.toList(); +} + +QStringList QDesignerPluginManager::defaultPluginPaths() +{ + QStringList result; + + const QStringList path_list = QCoreApplication::libraryPaths(); + + const QString designer = QLatin1String("designer"); + foreach (const QString &path, path_list) { + QString libPath = path; + libPath += QDir::separator(); + libPath += designer; + result.append(libPath); + } + + QString homeLibPath = QDir::homePath(); + homeLibPath += QDir::separator(); + homeLibPath += QLatin1String(".designer"); + homeLibPath += QDir::separator(); + homeLibPath += QLatin1String("plugins"); + + result.append(homeLibPath); + return result; +} + +// Figure out the language designer is running. ToDo: Introduce some +// Language name API to QDesignerLanguageExtension? + +static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core) +{ + if (QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core)) { + if (lang->uiExtension() == QLatin1String("jui")) + return QLatin1String(jambiLanguageC); + return QLatin1String("unknown"); + } + return QLatin1String("c++"); +} + +// ---------------- QDesignerCustomWidgetSharedData + +class QDesignerCustomWidgetSharedData : public QSharedData { +public: + // Type of a string property + typedef QPair StringPropertyType; + typedef QHash StringPropertyTypeMap; + + explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} + void clearXML(); + + QString pluginPath; + + QString xmlClassName; + QString xmlDisplayName; + QString xmlLanguage; + QString xmlAddPageMethod; + QString xmlExtends; + + StringPropertyTypeMap xmlStringPropertyTypeMap; +}; + +void QDesignerCustomWidgetSharedData::clearXML() +{ + xmlClassName.clear(); + xmlDisplayName.clear(); + xmlLanguage.clear(); + xmlAddPageMethod.clear(); + xmlExtends.clear(); + xmlStringPropertyTypeMap.clear(); +} + +// ---------------- QDesignerCustomWidgetData + +QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QString &pluginPath) : + m_d(new QDesignerCustomWidgetSharedData(pluginPath)) +{ +} + +QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QDesignerCustomWidgetData &o) : + m_d(o.m_d) +{ +} + +QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(const QDesignerCustomWidgetData &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +QDesignerCustomWidgetData::~QDesignerCustomWidgetData() +{ +} + +bool QDesignerCustomWidgetData::isNull() const +{ + return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty(); +} + +QString QDesignerCustomWidgetData::xmlClassName() const +{ + return m_d->xmlClassName; +} + +QString QDesignerCustomWidgetData::xmlLanguage() const +{ + return m_d->xmlLanguage; +} + +QString QDesignerCustomWidgetData::xmlAddPageMethod() const +{ + return m_d->xmlAddPageMethod; +} + +QString QDesignerCustomWidgetData::xmlExtends() const +{ + return m_d->xmlExtends; +} + +QString QDesignerCustomWidgetData::xmlDisplayName() const +{ + return m_d->xmlDisplayName; +} + +QString QDesignerCustomWidgetData::pluginPath() const +{ + return m_d->pluginPath; +} + +bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, StringPropertyType *type) const +{ + QDesignerCustomWidgetSharedData::StringPropertyTypeMap::const_iterator it = m_d->xmlStringPropertyTypeMap.constFind(name); + if (it == m_d->xmlStringPropertyTypeMap.constEnd()) { + *type = StringPropertyType(qdesigner_internal::ValidationRichText, true); + return false; + } + *type = it.value(); + return true; +} + +// Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult +enum FindResult { FindError = -2, ElementNotFound = -1 }; + +static int findElement(const QStringList &desiredElts, QXmlStreamReader &sr) +{ + while (true) { + switch(sr.readNext()) { + case QXmlStreamReader::EndDocument: + return ElementNotFound; + case QXmlStreamReader::Invalid: + return FindError; + case QXmlStreamReader::StartElement: { + const int index = desiredElts.indexOf(sr.name().toString().toLower()); + if (index >= 0) + return index; + } + break; + default: + break; + } + } + return FindError; +} + +static inline QString msgXmlError(const QString &name, const QString &errorMessage) +{ + return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage); +} + +static inline QString msgAttributeMissing(const QString &name) +{ + return QDesignerPluginManager::tr("A required attribute ('%1') is missing.").arg(name); +} + +static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok) +{ + *ok = true; + if (v == QLatin1String("multiline")) + return qdesigner_internal::ValidationMultiLine; + if (v == QLatin1String("richtext")) + return qdesigner_internal::ValidationRichText; + if (v == QLatin1String("stylesheet")) + return qdesigner_internal::ValidationStyleSheet; + if (v == QLatin1String("singleline")) + return qdesigner_internal::ValidationSingleLine; + if (v == QLatin1String("objectname")) + return qdesigner_internal::ValidationObjectName; + if (v == QLatin1String("objectnamescope")) + return qdesigner_internal::ValidationObjectNameScope; + if (v == QLatin1String("url")) + return qdesigner_internal::ValidationURL; + *ok = false; + return qdesigner_internal::ValidationRichText; +} + +static bool parsePropertySpecs(QXmlStreamReader &sr, + QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc, + QString *errorMessage) +{ + const QString propertySpecs = QLatin1String(propertySpecsC); + const QString stringPropertySpec = QLatin1String(stringPropertySpecC); + const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC); + const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC); + const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC); + + while (!sr.atEnd()) { + switch(sr.readNext()) { + case QXmlStreamReader::StartElement: { + if (sr.name() != stringPropertySpec) { + *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec); + return false; + } + const QXmlStreamAttributes atts = sr.attributes(); + const QString name = atts.value(stringPropertyNameAttr).toString(); + const QString type = atts.value(stringPropertyTypeAttr).toString(); + const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional + + if (type.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); + return false; + } + if (name.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyNameAttr); + return false; + } + bool typeOk; + const bool noTr = notrS == QLatin1String("true") || notrS == QLatin1String("1"); + QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); + if (!typeOk) { + *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification.").arg(type); + return false; + } + rc->insert(name, v); + } + break; + case QXmlStreamReader::EndElement: // Outer + if (sr.name() == propertySpecs) + return true; + default: + break; + } + } + return true; +} + +QDesignerCustomWidgetData::ParseResult + QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << name; + + QDesignerCustomWidgetSharedData &data = *m_d; + data.clearXML(); + + QXmlStreamReader sr(xml); + + bool foundUI = false; + bool foundWidget = false; + ParseResult rc = ParseOk; + // Parse for the (optional) or the first element + QStringList elements; + elements.push_back(QLatin1String(uiElementC)); + elements.push_back(QLatin1String(widgetElementC)); + for (int i = 0; i < 2 && !foundWidget; i++) { + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + *errorMessage = QDesignerPluginManager::tr("The XML of the custom widget %1 does not contain any of the elements or .").arg(name); + return ParseError; + case 0: { // + const QXmlStreamAttributes attributes = sr.attributes(); + data.xmlLanguage = attributes.value(QLatin1String(languageAttributeC)).toString(); + data.xmlDisplayName = attributes.value(QLatin1String(displayNameAttributeC)).toString(); + foundUI = true; + } + break; + case 1: // : Do some sanity checks + data.xmlClassName = sr.attributes().value(QLatin1String(classAttributeC)).toString(); + if (data.xmlClassName.isEmpty()) { + *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 is missing.").arg(name); + rc = ParseWarning; + } else { + if (data.xmlClassName != name) { + *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name); + rc = ParseWarning; + } + } + foundWidget = true; + break; + } + } + // Parse out the element which might be present if was there + if (!foundUI) + return rc; + elements.clear(); + elements.push_back(QLatin1String(customwidgetElementC)); + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + return rc; + default: + break; + } + // Find , , + elements.clear(); + elements.push_back(QLatin1String(extendsElementC)); + elements.push_back(QLatin1String(addPageMethodC)); + elements.push_back(QLatin1String(propertySpecsC)); + while (true) { + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + return rc; + case 0: // + data.xmlExtends = sr.readElementText(); + if (sr.tokenType() != QXmlStreamReader::EndElement) { + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + } + break; + case 1: // + data.xmlAddPageMethod = sr.readElementText(); + if (sr.tokenType() != QXmlStreamReader::EndElement) { + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + } + break; + case 2: // + if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) { + *errorMessage = msgXmlError(name, *errorMessage); + return ParseError; + } + break; + } + } + return rc; +} + +// ---------------- QDesignerPluginManagerPrivate + +class QDesignerPluginManagerPrivate { + public: + typedef QPair ClassNamePropertyNameKey; + + QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core); + + void clearCustomWidgets(); + bool addCustomWidget(QDesignerCustomWidgetInterface *c, + const QString &pluginPath, + const QString &designerLanguage); + void addCustomWidgets(const QObject *o, + const QString &pluginPath, + const QString &designerLanguage); + + QDesignerFormEditorInterface *m_core; + QStringList m_pluginPaths; + QStringList m_registeredPlugins; + // TODO: QPluginLoader also caches invalid plugins -> This seems to be dead code + QStringList m_disabledPlugins; + + typedef QMap FailedPluginMap; + FailedPluginMap m_failedPlugins; + + // Synced lists of custom widgets and their data. Note that the list + // must be ordered for collections to appear in order. + QList m_customWidgets; + QList m_customWidgetData; + + QStringList defaultPluginPaths() const; + + bool m_initialized; +}; + +QDesignerPluginManagerPrivate::QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core) : + m_core(core), + m_initialized(false) +{ +} + +void QDesignerPluginManagerPrivate::clearCustomWidgets() +{ + m_customWidgets.clear(); + m_customWidgetData.clear(); +} + +// Add a custom widget to the list if it parses correctly +// and is of the right language +bool QDesignerPluginManagerPrivate::addCustomWidget(QDesignerCustomWidgetInterface *c, + const QString &pluginPath, + const QString &designerLanguage) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << c->name(); + + if (!c->isInitialized()) + c->initialize(m_core); + // Parse the XML even if the plugin is initialized as Jambi might play tricks here + QDesignerCustomWidgetData data(pluginPath); + const QString domXml = c->domXml(); + if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box. + QString errorMessage; + const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage); + switch (pr) { + case QDesignerCustomWidgetData::ParseOk: + break; + case QDesignerCustomWidgetData::ParseWarning: + qdesigner_internal::designerWarning(errorMessage); + break; + case QDesignerCustomWidgetData::ParseError: + qdesigner_internal::designerWarning(errorMessage); + return false; + } + // Does the language match? + const QString pluginLanguage = data.xmlLanguage(); + if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive)) + return false; + } + m_customWidgets.push_back(c); + m_customWidgetData.push_back(data); + return true; +} + +// Check the plugin interface for either a custom widget or a collection and +// add all contained custom widgets. +void QDesignerPluginManagerPrivate::addCustomWidgets(const QObject *o, + const QString &pluginPath, + const QString &designerLanguage) +{ + if (QDesignerCustomWidgetInterface *c = qobject_cast(o)) { + addCustomWidget(c, pluginPath, designerLanguage); + return; + } + if (const QDesignerCustomWidgetCollectionInterface *coll = qobject_cast(o)) { + foreach(QDesignerCustomWidgetInterface *c, coll->customWidgets()) + addCustomWidget(c, pluginPath, designerLanguage); + } +} + + +// ---------------- QDesignerPluginManager +// As of 4.4, the header will be distributed with the Eclipse plugin. + +QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) : + QObject(core), + m_d(new QDesignerPluginManagerPrivate(core)) +{ + m_d->m_pluginPaths = defaultPluginPaths(); + const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); + m_d->m_disabledPlugins = unique(settings.value(QLatin1String("PluginManager/DisabledPlugins")).toStringList()); + + // Register plugins + updateRegisteredPlugins(); + + if (debugPluginManager) + qDebug() << "QDesignerPluginManager::disabled: " << m_d->m_disabledPlugins << " static " << m_d->m_customWidgets.size(); +} + +QDesignerPluginManager::~QDesignerPluginManager() +{ + syncSettings(); + delete m_d; +} + +QDesignerFormEditorInterface *QDesignerPluginManager::core() const +{ + return m_d->m_core; +} + +QStringList QDesignerPluginManager::findPlugins(const QString &path) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << path; + const QDir dir(path); + if (!dir.exists()) + return QStringList(); + + const QFileInfoList infoList = dir.entryInfoList(QDir::Files); + if (infoList.empty()) + return QStringList(); + + // Load symbolic links but make sure all file names are unique as not + // to fall for something like 'libplugin.so.1 -> libplugin.so' + QStringList result; + const QFileInfoList::const_iterator icend = infoList.constEnd(); + for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) { + QString fileName; + if (it->isSymLink()) { + const QFileInfo linkTarget = QFileInfo(it->symLinkTarget()); + if (linkTarget.exists() && linkTarget.isFile()) + fileName = linkTarget.absoluteFilePath(); + } else { + fileName = it->absoluteFilePath(); + } + if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName)) + result += fileName; + } + return result; +} + +void QDesignerPluginManager::setDisabledPlugins(const QStringList &disabled_plugins) +{ + m_d->m_disabledPlugins = disabled_plugins; + updateRegisteredPlugins(); +} + +void QDesignerPluginManager::setPluginPaths(const QStringList &plugin_paths) +{ + m_d->m_pluginPaths = plugin_paths; + updateRegisteredPlugins(); +} + +QStringList QDesignerPluginManager::disabledPlugins() const +{ + return m_d->m_disabledPlugins; +} + +QStringList QDesignerPluginManager::failedPlugins() const +{ + return m_d->m_failedPlugins.keys(); +} + +QString QDesignerPluginManager::failureReason(const QString &pluginName) const +{ + return m_d->m_failedPlugins.value(pluginName); +} + +QStringList QDesignerPluginManager::registeredPlugins() const +{ + return m_d->m_registeredPlugins; +} + +QStringList QDesignerPluginManager::pluginPaths() const +{ + return m_d->m_pluginPaths; +} + +QObject *QDesignerPluginManager::instance(const QString &plugin) const +{ + if (m_d->m_disabledPlugins.contains(plugin)) + return 0; + + QPluginLoader loader(plugin); + return loader.instance(); +} + +void QDesignerPluginManager::updateRegisteredPlugins() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO; + m_d->m_registeredPlugins.clear(); + foreach (const QString &path, m_d->m_pluginPaths) + registerPath(path); +} + +bool QDesignerPluginManager::registerNewPlugins() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO; + + const int before = m_d->m_registeredPlugins.size(); + foreach (const QString &path, m_d->m_pluginPaths) + registerPath(path); + const bool newPluginsFound = m_d->m_registeredPlugins.size() > before; + // We force a re-initialize as Jambi collection might return + // different widget lists when switching projects. + m_d->m_initialized = false; + ensureInitialized(); + + return newPluginsFound; +} + +void QDesignerPluginManager::registerPath(const QString &path) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << path; + QStringList candidates = findPlugins(path); + + foreach (const QString &plugin, candidates) + registerPlugin(plugin); +} + +void QDesignerPluginManager::registerPlugin(const QString &plugin) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << plugin; + if (m_d->m_disabledPlugins.contains(plugin)) + return; + if (m_d->m_registeredPlugins.contains(plugin)) + return; + + QPluginLoader loader(plugin); + if (loader.isLoaded() || loader.load()) { + m_d->m_registeredPlugins += plugin; + QDesignerPluginManagerPrivate::FailedPluginMap::iterator fit = m_d->m_failedPlugins.find(plugin); + if (fit != m_d->m_failedPlugins.end()) + m_d->m_failedPlugins.erase(fit); + return; + } + + const QString errorMessage = loader.errorString(); + m_d->m_failedPlugins.insert(plugin, errorMessage); +} + + + +bool QDesignerPluginManager::syncSettings() +{ + QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); + settings.beginGroup(QLatin1String("PluginManager")); + settings.setValue(QLatin1String("DisabledPlugins"), m_d->m_disabledPlugins); + settings.endGroup(); + return settings.status() == QSettings::NoError; +} + +void QDesignerPluginManager::ensureInitialized() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << m_d->m_initialized << m_d->m_customWidgets.size(); + + if (m_d->m_initialized) + return; + + const QString designerLanguage = getDesignerLanguage(m_d->m_core); + + m_d->clearCustomWidgets(); + // Add the static custom widgets + const QObjectList staticPluginObjects = QPluginLoader::staticInstances(); + if (!staticPluginObjects.empty()) { + const QString staticPluginPath = QCoreApplication::applicationFilePath(); + foreach(QObject *o, staticPluginObjects) + m_d->addCustomWidgets(o, staticPluginPath, designerLanguage); + } + foreach (const QString &plugin, m_d->m_registeredPlugins) + if (QObject *o = instance(plugin)) + m_d->addCustomWidgets(o, plugin, designerLanguage); + + m_d->m_initialized = true; +} + +QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets() const +{ + const_cast(this)->ensureInitialized(); + return m_d->m_customWidgets; +} + +QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w) const +{ + const int index = m_d->m_customWidgets.indexOf(w); + if (index == -1) + return QDesignerCustomWidgetData(); + return m_d->m_customWidgetData.at(index); +} + +QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(const QString &name) const +{ + const int count = m_d->m_customWidgets.size(); + for (int i = 0; i < count; i++) + if (m_d->m_customWidgets.at(i)->name() == name) + return m_d->m_customWidgetData.at(i); + return QDesignerCustomWidgetData(); +} + +QObjectList QDesignerPluginManager::instances() const +{ + QStringList plugins = registeredPlugins(); + + QObjectList lst; + foreach (const QString &plugin, plugins) { + if (QObject *o = instance(plugin)) + lst.append(o); + } + + return lst; +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/pluginmanager_p.h b/designer/lib/shared/pluginmanager_p.h new file mode 100644 index 0000000..6465e55 --- /dev/null +++ b/designer/lib/shared/pluginmanager_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerCustomWidgetInterface; +class QDesignerPluginManagerPrivate; + +class QDesignerCustomWidgetSharedData; + +/* Information contained in the Dom XML of a custom widget. */ +class QDESIGNER_SHARED_EXPORT QDesignerCustomWidgetData { +public: + // StringPropertyType: validation mode and translatable flag. + typedef QPair StringPropertyType; + + explicit QDesignerCustomWidgetData(const QString &pluginPath = QString()); + + enum ParseResult { ParseOk, ParseWarning, ParseError }; + ParseResult parseXml(const QString &xml, const QString &name, QString *errorMessage); + + QDesignerCustomWidgetData(const QDesignerCustomWidgetData&); + QDesignerCustomWidgetData& operator=(const QDesignerCustomWidgetData&); + ~QDesignerCustomWidgetData(); + + bool isNull() const; + + QString pluginPath() const; + + // Data as parsed from the widget's domXML(). + QString xmlClassName() const; + // Optional. The language the plugin is supposed to be used with. + QString xmlLanguage() const; + // Optional. method used to add pages to a container with a container extension + QString xmlAddPageMethod() const; + // Optional. Base class + QString xmlExtends() const; + // Optional. The name to be used in the widget box. + QString xmlDisplayName() const; + // Type of a string property + bool xmlStringPropertyType(const QString &name, StringPropertyType *type) const; + +private: + QSharedDataPointer m_d; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerPluginManager: public QObject +{ + Q_OBJECT +public: + typedef QList CustomWidgetList; + + explicit QDesignerPluginManager(QDesignerFormEditorInterface *core); + virtual ~QDesignerPluginManager(); + + QDesignerFormEditorInterface *core() const; + + QObject *instance(const QString &plugin) const; + + QStringList registeredPlugins() const; + + QStringList findPlugins(const QString &path); + + QStringList pluginPaths() const; + void setPluginPaths(const QStringList &plugin_paths); + + QStringList disabledPlugins() const; + void setDisabledPlugins(const QStringList &disabled_plugins); + + QStringList failedPlugins() const; + QString failureReason(const QString &pluginName) const; + + QObjectList instances() const; + + CustomWidgetList registeredCustomWidgets() const; + QDesignerCustomWidgetData customWidgetData(QDesignerCustomWidgetInterface *w) const; + QDesignerCustomWidgetData customWidgetData(const QString &className) const; + + bool registerNewPlugins(); + +public slots: + bool syncSettings(); + void ensureInitialized(); + +private: + void updateRegisteredPlugins(); + void registerPath(const QString &path); + void registerPlugin(const QString &plugin); + +private: + static QStringList defaultPluginPaths(); + + QDesignerPluginManagerPrivate *m_d; +}; + +QT_END_NAMESPACE + +#endif // PLUGINMANAGER_H diff --git a/designer/lib/shared/previewconfigurationwidget.cpp b/designer/lib/shared/previewconfigurationwidget.cpp new file mode 100644 index 0000000..d2b59cc --- /dev/null +++ b/designer/lib/shared/previewconfigurationwidget.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* It is possible to link the skins as resources into Designer by specifying: + * QVFB_ROOT=$$QT_SOURCE_TREE/tools/qvfb + * RESOURCES += $$QVFB_ROOT/ClamshellPhone.qrc $$QVFB_ROOT/TouchScreenPhone.qrc ... + * in lib/shared/shared.pri. However, this exceeds a limit of Visual Studio 6. */ + +#include "previewconfigurationwidget_p.h" +#include "ui_previewconfigurationwidget.h" +#include "previewmanager_p.h" +#include "abstractsettings_p.h" +#include "shared_settings_p.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *skinResourcePathC = ":/skins/"; + +QT_BEGIN_NAMESPACE + +static const char *skinExtensionC = "skin"; + +// Pair of skin name, path +typedef QPair SkinNamePath; +typedef QList Skins; +enum { SkinComboNoneIndex = 0 }; + +// find default skins (resources) +static const Skins &defaultSkins() { + static Skins rc; + if (rc.empty()) { + const QString skinPath = QLatin1String(skinResourcePathC); + QString pattern = QLatin1String("*."); + pattern += QLatin1String(skinExtensionC); + const QDir dir(skinPath, pattern); + const QFileInfoList list = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot, QDir::Name); + if (list.empty()) + return rc; + const QFileInfoList::const_iterator lcend = list.constEnd(); + for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) + rc.push_back(SkinNamePath(it->baseName(), it->filePath())); + } + return rc; +} + +namespace qdesigner_internal { + +// ------------- PreviewConfigurationWidgetPrivate +class PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate { +public: + PreviewConfigurationWidgetPrivate(QDesignerFormEditorInterface *core, QGroupBox *g); + + void slotEditAppStyleSheet(); + void slotDeleteSkinEntry(); + void slotSkinChanged(int index); + + void retrieveSettings(); + void storeSettings() const; + + QAbstractButton *appStyleSheetChangeButton() const { return m_ui.m_appStyleSheetChangeButton; } + QAbstractButton *skinRemoveButton() const { return m_ui.m_skinRemoveButton; } + QComboBox *skinCombo() const { return m_ui.m_skinCombo; } + + QDesignerFormEditorInterface *m_core; + +private: + PreviewConfiguration previewConfiguration() const; + void setPreviewConfiguration(const PreviewConfiguration &pc); + + QStringList userSkins() const; + void addUserSkins(const QStringList &files); + bool canRemoveSkin(int index) const; + int browseSkin(); + + const QString m_defaultStyle; + QGroupBox *m_parent; + Ui::PreviewConfigurationWidget m_ui; + + int m_firstUserSkinIndex; + int m_browseSkinIndex; + int m_lastSkinIndex; // required in case browse fails +}; + +PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::PreviewConfigurationWidgetPrivate( + QDesignerFormEditorInterface *core, QGroupBox *g) : + m_core(core), + m_defaultStyle(PreviewConfigurationWidget::tr("Default")), + m_parent(g), + m_firstUserSkinIndex(0), + m_browseSkinIndex(0), + m_lastSkinIndex(0) +{ + m_ui.setupUi(g); + // styles + m_ui.m_styleCombo->setEditable(false); + QStringList styleItems(m_defaultStyle); + styleItems += QStyleFactory::keys(); + m_ui.m_styleCombo->addItems(styleItems); + + // sheet + m_ui.m_appStyleSheetLineEdit->setTextPropertyValidationMode(qdesigner_internal::ValidationStyleSheet); + m_ui.m_appStyleSheetClearButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("resetproperty.png"))); + QObject::connect(m_ui.m_appStyleSheetClearButton, SIGNAL(clicked()), m_ui.m_appStyleSheetLineEdit, SLOT(clear())); + + m_ui.m_skinRemoveButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("editdelete.png"))); + // skins: find default skins (resources) + m_ui.m_skinRemoveButton->setEnabled(false); + Skins skins = defaultSkins(); + skins.push_front(SkinNamePath(PreviewConfigurationWidget::tr("None"), QString())); + + const Skins::const_iterator scend = skins.constEnd(); + for (Skins::const_iterator it = skins.constBegin(); it != scend; ++it) + m_ui.m_skinCombo->addItem (it->first, QVariant(it->second)); + m_browseSkinIndex = m_firstUserSkinIndex = skins.size(); + m_ui.m_skinCombo->addItem(PreviewConfigurationWidget::tr("Browse..."), QString()); + + m_ui.m_skinCombo->setMaxVisibleItems (qMax(15, 2 * m_browseSkinIndex)); + m_ui.m_skinCombo->setEditable(false); + + retrieveSettings(); +} + +bool PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::canRemoveSkin(int index) const +{ + return index >= m_firstUserSkinIndex && index != m_browseSkinIndex; +} + +QStringList PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::userSkins() const +{ + QStringList rc; + for (int i = m_firstUserSkinIndex; i < m_browseSkinIndex; i++) + rc.push_back(m_ui.m_skinCombo->itemData(i).toString()); + return rc; +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::addUserSkins(const QStringList &files) +{ + if (files.empty()) + return; + const QStringList ::const_iterator fcend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != fcend; ++it) { + const QFileInfo fi(*it); + if (fi.isDir() && fi.isReadable()) { + m_ui.m_skinCombo->insertItem(m_browseSkinIndex++, fi.baseName(), QVariant(*it)); + } else { + qWarning() << "Unable to access the skin directory '" << *it << "'."; + } + } +} + +PreviewConfiguration PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::previewConfiguration() const +{ + PreviewConfiguration rc; + QString style = m_ui.m_styleCombo->currentText(); + if (style == m_defaultStyle) + style.clear(); + const QString applicationStyleSheet = m_ui.m_appStyleSheetLineEdit->text(); + // Figure out skin. 0 is None by definition.. + const int skinIndex = m_ui.m_skinCombo->currentIndex(); + QString deviceSkin; + if (skinIndex != SkinComboNoneIndex && skinIndex != m_browseSkinIndex) + deviceSkin = m_ui.m_skinCombo->itemData(skinIndex).toString(); + + return PreviewConfiguration(style, applicationStyleSheet, deviceSkin); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::setPreviewConfiguration(const PreviewConfiguration &pc) +{ + int styleIndex = m_ui.m_styleCombo->findText(pc.style()); + if (styleIndex == -1) + styleIndex = m_ui.m_styleCombo->findText(m_defaultStyle); + m_ui.m_styleCombo->setCurrentIndex(styleIndex); + m_ui.m_appStyleSheetLineEdit->setText(pc.applicationStyleSheet()); + // find skin by file name. 0 is "none" + const QString deviceSkin = pc.deviceSkin(); + int skinIndex = deviceSkin.isEmpty() ? 0 : m_ui.m_skinCombo->findData(QVariant(deviceSkin)); + if (skinIndex == -1) { + qWarning() << "Unable to find skin '" << deviceSkin << "'."; + skinIndex = 0; + } + m_ui.m_skinCombo->setCurrentIndex(skinIndex); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotEditAppStyleSheet() +{ + StyleSheetEditorDialog dlg(m_core, m_parent, StyleSheetEditorDialog::ModeGlobal); + dlg.setText(m_ui.m_appStyleSheetLineEdit->text()); + if (dlg.exec() == QDialog::Accepted) + m_ui.m_appStyleSheetLineEdit->setText(dlg.text()); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotDeleteSkinEntry() +{ + const int index = m_ui.m_skinCombo->currentIndex(); + if (canRemoveSkin(index)) { + m_ui.m_skinCombo->setCurrentIndex(SkinComboNoneIndex); + m_ui.m_skinCombo->removeItem(index); + m_browseSkinIndex--; + } +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotSkinChanged(int index) +{ + if (index == m_browseSkinIndex) { + m_ui.m_skinCombo->setCurrentIndex(browseSkin()); + } else { + m_lastSkinIndex = index; + m_ui.m_skinRemoveButton->setEnabled(canRemoveSkin(index)); + m_ui.m_skinCombo->setToolTip(index != SkinComboNoneIndex ? m_ui.m_skinCombo->itemData(index).toString() : QString()); + } +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::retrieveSettings() +{ + QDesignerSharedSettings settings(m_core); + m_parent->setChecked(settings.isCustomPreviewConfigurationEnabled()); + setPreviewConfiguration(settings.customPreviewConfiguration()); + addUserSkins(settings.userDeviceSkins()); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::storeSettings() const +{ + QDesignerSharedSettings settings(m_core); + settings.setCustomPreviewConfigurationEnabled(m_parent->isChecked()); + settings.setCustomPreviewConfiguration(previewConfiguration()); + settings.setUserDeviceSkins(userSkins()); +} + +int PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::browseSkin() +{ + QFileDialog dlg(m_parent); + dlg.setFileMode(QFileDialog::DirectoryOnly); + const QString title = tr("Load Custom Device Skin"); + dlg.setWindowTitle(title); + dlg.setFilter(tr("All QVFB Skins (*.%1)").arg(QLatin1String(skinExtensionC))); + + int rc = m_lastSkinIndex; + do { + if (!dlg.exec()) + break; + + const QStringList directories = dlg.selectedFiles(); + if (directories.size() != 1) + break; + + // check: 1) name already there + const QString directory = directories.front(); + const QString name = QFileInfo(directory).baseName(); + const int existingIndex = m_ui.m_skinCombo->findText(name); + if (existingIndex != -1 && existingIndex != SkinComboNoneIndex && existingIndex != m_browseSkinIndex) { + const QString msgTitle = tr("%1 - Duplicate Skin").arg(title); + const QString msg = tr("The skin '%1' already exists.").arg(name); + QMessageBox::information(m_parent, msgTitle, msg); + break; + } + // check: 2) can read + DeviceSkinParameters parameters; + QString readError; + if (parameters.read(directory, DeviceSkinParameters::ReadSizeOnly, &readError)) { + const QString name = QFileInfo(directory).baseName(); + m_ui.m_skinCombo->insertItem(m_browseSkinIndex, name, QVariant(directory)); + rc = m_browseSkinIndex++; + + break; + } else { + const QString msgTitle = tr("%1 - Error").arg(title); + const QString msg = tr("%1 is not a valid skin directory:\n%2").arg(directory).arg(readError); + QMessageBox::warning (m_parent, msgTitle, msg); + } + } while (true); + return rc; +} + +// ------------- PreviewConfigurationWidget +PreviewConfigurationWidget::PreviewConfigurationWidget(QDesignerFormEditorInterface *core, + QWidget *parent) : + QGroupBox(parent), + m_impl(new PreviewConfigurationWidgetPrivate(core, this)) +{ + connect(m_impl->appStyleSheetChangeButton(), SIGNAL(clicked()), this, SLOT(slotEditAppStyleSheet())); + connect(m_impl->skinRemoveButton(), SIGNAL(clicked()), this, SLOT(slotDeleteSkinEntry())); + connect(m_impl->skinCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(slotSkinChanged(int))); + + m_impl->retrieveSettings(); +} + +PreviewConfigurationWidget::~PreviewConfigurationWidget() +{ + delete m_impl; +} + +void PreviewConfigurationWidget::saveState() +{ + m_impl->storeSettings(); +} + +void PreviewConfigurationWidget::slotEditAppStyleSheet() +{ + m_impl->slotEditAppStyleSheet(); +} + +void PreviewConfigurationWidget::slotDeleteSkinEntry() +{ + m_impl->slotDeleteSkinEntry(); +} + +void PreviewConfigurationWidget::slotSkinChanged(int index) +{ + m_impl->slotSkinChanged(index); +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/previewconfigurationwidget.ui b/designer/lib/shared/previewconfigurationwidget.ui new file mode 100644 index 0000000..2f18766 --- /dev/null +++ b/designer/lib/shared/previewconfigurationwidget.ui @@ -0,0 +1,91 @@ + + PreviewConfigurationWidget + + + Form + + + Print/Preview Configuration + + + true + + + + + + Style + + + + + + + + + + Style sheet + + + + + + + + + + 149 + 0 + + + + + + + + ... + + + + + + + ... + + + + + + + + + Device skin + + + + + + + + + + + + ... + + + + + + + + + + qdesigner_internal::TextPropertyEditor + QLineEdit +
textpropertyeditor_p.h
+
+
+ + +
diff --git a/designer/lib/shared/previewconfigurationwidget_p.h b/designer/lib/shared/previewconfigurationwidget_p.h new file mode 100644 index 0000000..334fe7a --- /dev/null +++ b/designer/lib/shared/previewconfigurationwidget_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PREVIEWCONFIGURATIONWIDGET_H +#define PREVIEWCONFIGURATIONWIDGET_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettingsInterface; + +namespace qdesigner_internal { + +// ----------- PreviewConfigurationWidget: Widget to edit the preview configuration. + +class QDESIGNER_SHARED_EXPORT PreviewConfigurationWidget : public QGroupBox +{ + Q_OBJECT +public: + explicit PreviewConfigurationWidget(QDesignerFormEditorInterface *core, + QWidget *parent = 0); + virtual ~PreviewConfigurationWidget(); + void saveState(); + +private slots: + void slotEditAppStyleSheet(); + void slotDeleteSkinEntry(); + void slotSkinChanged(int); + +private: + class PreviewConfigurationWidgetPrivate; + PreviewConfigurationWidgetPrivate *m_impl; + + PreviewConfigurationWidget(const PreviewConfigurationWidget &other); + PreviewConfigurationWidget &operator =(const PreviewConfigurationWidget &other); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PREVIEWCONFIGURATIONWIDGET_H diff --git a/designer/lib/shared/previewmanager.cpp b/designer/lib/shared/previewmanager.cpp new file mode 100644 index 0000000..2399cc0 --- /dev/null +++ b/designer/lib/shared/previewmanager.cpp @@ -0,0 +1,943 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "previewmanager_p.h" +#include "qdesigner_formbuilder_p.h" +#include "shared_settings_p.h" +#include "shared_settings_p.h" +#include "zoomwidget_p.h" +#include "formwindowbase_p.h" +#include "widgetfactory_p.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static inline int compare(const qdesigner_internal::PreviewConfiguration &pc1, const qdesigner_internal::PreviewConfiguration &pc2) +{ + int rc = pc1.style().compare(pc2.style()); + if (rc) + return rc; + rc = pc1.applicationStyleSheet().compare(pc2.applicationStyleSheet()); + if (rc) + return rc; + return pc1.deviceSkin().compare(pc2.deviceSkin()); +} + +namespace { + // ------ PreviewData (data associated with a preview window) + struct PreviewData { + PreviewData(const QPointer &widget, const QDesignerFormWindowInterface *formWindow, const qdesigner_internal::PreviewConfiguration &pc); + QPointer m_widget; + const QDesignerFormWindowInterface *m_formWindow; + qdesigner_internal::PreviewConfiguration m_configuration; + }; + + PreviewData::PreviewData(const QPointer& widget, + const QDesignerFormWindowInterface *formWindow, + const qdesigner_internal::PreviewConfiguration &pc) : + m_widget(widget), + m_formWindow(formWindow), + m_configuration(pc) + { + } +} + +namespace qdesigner_internal { + +/* In designer, we have the situation that laid-out maincontainers have + * a geometry set (which might differ from their sizeHint()). The QGraphicsItem + * should return that in its size hint, else such cases won't work */ + +class DesignerZoomProxyWidget : public ZoomProxyWidget { + Q_DISABLE_COPY(DesignerZoomProxyWidget) +public: + DesignerZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); +protected: + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; +}; + +DesignerZoomProxyWidget::DesignerZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) : + ZoomProxyWidget(parent, wFlags) +{ +} + +QSizeF DesignerZoomProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const +{ + if (const QWidget *w = widget()) + return QSizeF(w->size()); + return ZoomProxyWidget::sizeHint(which, constraint); +} + +// DesignerZoomWidget which returns DesignerZoomProxyWidget in its factory function +class DesignerZoomWidget : public ZoomWidget { + Q_DISABLE_COPY(DesignerZoomWidget) +public: + DesignerZoomWidget(QWidget *parent = 0); +private: + virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const; +}; + +DesignerZoomWidget::DesignerZoomWidget(QWidget *parent) : + ZoomWidget(parent) +{ +} + +QGraphicsProxyWidget *DesignerZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const +{ + return new DesignerZoomProxyWidget(parent, wFlags); +} + +// PreviewDeviceSkin: Forwards the key events to the window and +// provides context menu with rotation options. Derived class +// can apply additional transformations to the skin. + +class PreviewDeviceSkin : public DeviceSkin +{ + Q_OBJECT +public: + enum Direction { DirectionUp, DirectionLeft, DirectionRight }; + + explicit PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); + virtual void setPreview(QWidget *w); + QSize screenSize() const { return m_screenSize; } + +private slots: + void slotSkinKeyPressEvent(int code, const QString& text, bool autorep); + void slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep); + void slotPopupMenu(); + +protected: + virtual void populateContextMenu(QMenu *) {} + +private slots: + void slotDirection(QAction *); + +protected: + // Fit the widget in case the orientation changes (transposing screensize) + virtual void fitWidget(const QSize &size); + // Calculate the complete transformation for the skin + // (base class implementation provides rotation). + virtual QMatrix skinTransform() const; + +private: + const QSize m_screenSize; + Direction m_direction; + + QAction *m_directionUpAction; + QAction *m_directionLeftAction; + QAction *m_directionRightAction; + QAction *m_closeAction; +}; + +PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : + DeviceSkin(parameters, parent), + m_screenSize(parameters.screenSize()), + m_direction(DirectionUp), + m_directionUpAction(0), + m_directionLeftAction(0), + m_directionRightAction(0), + m_closeAction(0) +{ + connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)), + this, SLOT(slotSkinKeyPressEvent(int,QString,bool))); + connect(this, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), + this, SLOT(slotSkinKeyReleaseEvent(int,QString,bool))); + connect(this, SIGNAL(popupMenu()), this, SLOT(slotPopupMenu())); +} + +void PreviewDeviceSkin::setPreview(QWidget *formWidget) +{ + formWidget->setFixedSize(m_screenSize); + formWidget->setParent(this, Qt::SubWindow); + formWidget->setAutoFillBackground(true); + setView(formWidget); +} + +void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyPress,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } +} + +void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyRelease,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } +} + +// Create a checkable action with integer data and +// set it checked if it matches the currentState. +static inline QAction + *createCheckableActionIntData(const QString &label, + int actionValue, int currentState, + QActionGroup *ag, QObject *parent) +{ + QAction *a = new QAction(label, parent); + a->setData(actionValue); + a->setCheckable(true); + if (actionValue == currentState) + a->setChecked(true); + ag->addAction(a); + return a; +} + +void PreviewDeviceSkin::slotPopupMenu() +{ + QMenu menu(this); + // Create actions + if (!m_directionUpAction) { + QActionGroup *directionGroup = new QActionGroup(this); + connect(directionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotDirection(QAction*))); + directionGroup->setExclusive(true); + m_directionUpAction = createCheckableActionIntData(tr("&Portrait"), DirectionUp, m_direction, directionGroup, this); + //: Rotate form preview counter-clockwise + m_directionLeftAction = createCheckableActionIntData(tr("Landscape (&CCW)"), DirectionLeft, m_direction, directionGroup, this); + //: Rotate form preview clockwise + m_directionRightAction = createCheckableActionIntData(tr("&Landscape (CW)"), DirectionRight, m_direction, directionGroup, this); + m_closeAction = new QAction(tr("&Close"), this); + connect(m_closeAction, SIGNAL(triggered()), parentWidget(), SLOT(close())); + } + menu.addAction(m_directionUpAction); + menu.addAction(m_directionLeftAction); + menu.addAction(m_directionRightAction); + menu.addSeparator(); + populateContextMenu(&menu); + menu.addAction(m_closeAction); + menu.exec(QCursor::pos()); +} + +void PreviewDeviceSkin::slotDirection(QAction *a) +{ + const Direction newDirection = static_cast(a->data().toInt()); + if (m_direction == newDirection) + return; + const Qt::Orientation newOrientation = newDirection == DirectionUp ? Qt::Vertical : Qt::Horizontal; + const Qt::Orientation oldOrientation = m_direction == DirectionUp ? Qt::Vertical : Qt::Horizontal; + m_direction = newDirection; + QApplication::setOverrideCursor(Qt::WaitCursor); + if (oldOrientation != newOrientation) { + QSize size = screenSize(); + if (newOrientation == Qt::Horizontal) + size.transpose(); + fitWidget(size); + } + setTransform(skinTransform()); + QApplication::restoreOverrideCursor(); +} + +void PreviewDeviceSkin::fitWidget(const QSize &size) +{ + view()->setFixedSize(size); +} + +QMatrix PreviewDeviceSkin::skinTransform() const +{ + QMatrix newTransform; + switch (m_direction) { + case DirectionUp: + break; + case DirectionLeft: + newTransform.rotate(270.0); + break; + case DirectionRight: + newTransform.rotate(90.0); + break; + } + return newTransform; +} + +// ------------ PreviewConfigurationPrivate +class PreviewConfigurationData : public QSharedData { +public: + PreviewConfigurationData() {} + explicit PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin); + + QString m_style; + // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). + QString m_applicationStyleSheet; + QString m_deviceSkin; +}; + +PreviewConfigurationData::PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin) : + m_style(style), + m_applicationStyleSheet(applicationStyleSheet), + m_deviceSkin(deviceSkin) +{ +} + +/* ZoomablePreviewDeviceSkin: A Zoomable Widget Preview skin. Embeds preview + * into a ZoomWidget and this in turn into the DeviceSkin view and keeps + * Device skin zoom + ZoomWidget zoom in sync. */ + +class ZoomablePreviewDeviceSkin : public PreviewDeviceSkin +{ + Q_OBJECT +public: + explicit ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); + virtual void setPreview(QWidget *w); + + int zoomPercent() const; // Device Skins have a double 'zoom' property + +public slots: + void setZoomPercent(int); + +signals: + void zoomPercentChanged(int); + +protected: + virtual void populateContextMenu(QMenu *m); + virtual QMatrix skinTransform() const; + virtual void fitWidget(const QSize &size); + +private: + ZoomMenu *m_zoomMenu; + QAction *m_zoomSubMenuAction; + ZoomWidget *m_zoomWidget; +}; + +ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : + PreviewDeviceSkin(parameters, parent), + m_zoomMenu(new ZoomMenu(this)), + m_zoomSubMenuAction(0), + m_zoomWidget(new DesignerZoomWidget) +{ + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int))); + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SIGNAL(zoomPercentChanged(int))); + m_zoomWidget->setZoomContextMenuEnabled(false); + m_zoomWidget->setWidgetZoomContextMenuEnabled(false); + m_zoomWidget->resize(screenSize()); + m_zoomWidget->setParent(this, Qt::SubWindow); + m_zoomWidget->setAutoFillBackground(true); + setView(m_zoomWidget); +} + +static inline qreal zoomFactor(int percent) +{ + return qreal(percent) / 100.0; +} + +static inline QSize scaleSize(int zoomPercent, const QSize &size) +{ + return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize(); +} + +void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +{ + m_zoomWidget->setWidget(formWidget); + m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize())); +} + +int ZoomablePreviewDeviceSkin::zoomPercent() const +{ + return m_zoomWidget->zoom(); +} + +void ZoomablePreviewDeviceSkin::setZoomPercent(int zp) +{ + if (zp == zoomPercent()) + return; + + // If not triggered by the menu itself: Update it + if (m_zoomMenu->zoom() != zp) + m_zoomMenu->setZoom(zp); + + QApplication::setOverrideCursor(Qt::WaitCursor); + m_zoomWidget->setZoom(zp); + setTransform(skinTransform()); + QApplication::restoreOverrideCursor(); +} + +void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu) +{ + if (!m_zoomSubMenuAction) { + m_zoomSubMenuAction = new QAction(tr("&Zoom"), this); + QMenu *zoomSubMenu = new QMenu; + m_zoomSubMenuAction->setMenu(zoomSubMenu); + m_zoomMenu->addActions(zoomSubMenu); + } + menu->addAction(m_zoomSubMenuAction); + menu->addSeparator(); +} + +QMatrix ZoomablePreviewDeviceSkin::skinTransform() const +{ + // Complete transformation consisting of base class rotation and zoom. + QMatrix rc = PreviewDeviceSkin::skinTransform(); + const int zp = zoomPercent(); + if (zp != 100) { + const qreal factor = zoomFactor(zp); + rc.scale(factor, factor); + } + return rc; +} + +void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size) +{ + m_zoomWidget->resize(scaleSize(zoomPercent(), size)); +} + +// ------------- PreviewConfiguration + +static const char *styleKey = "Style"; +static const char *appStyleSheetKey = "AppStyleSheet"; +static const char *skinKey = "Skin"; + +PreviewConfiguration::PreviewConfiguration() : + m_d(new PreviewConfigurationData) +{ +} + +PreviewConfiguration::PreviewConfiguration(const QString &sty, const QString &applicationSheet, const QString &skin) : + m_d(new PreviewConfigurationData(sty, applicationSheet, skin)) +{ +} + +PreviewConfiguration::PreviewConfiguration(const PreviewConfiguration &o) : + m_d(o.m_d) +{ +} + +PreviewConfiguration &PreviewConfiguration::operator=(const PreviewConfiguration &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +PreviewConfiguration::~PreviewConfiguration() +{ +} + +void PreviewConfiguration::clear() +{ + PreviewConfigurationData &d = *m_d; + d.m_style.clear(); + d.m_applicationStyleSheet.clear(); + d.m_deviceSkin.clear(); +} + +QString PreviewConfiguration::style() const +{ + return m_d->m_style; +} + +void PreviewConfiguration::setStyle(const QString &s) +{ + m_d->m_style = s; +} + +// Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). +QString PreviewConfiguration::applicationStyleSheet() const +{ + return m_d->m_applicationStyleSheet; +} + +void PreviewConfiguration::setApplicationStyleSheet(const QString &as) +{ + m_d->m_applicationStyleSheet = as; +} + +QString PreviewConfiguration::deviceSkin() const +{ + return m_d->m_deviceSkin; +} + +void PreviewConfiguration::setDeviceSkin(const QString &s) +{ + m_d->m_deviceSkin = s; +} + +void PreviewConfiguration::toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const +{ + const PreviewConfigurationData &d = *m_d; + settings->beginGroup(prefix); + settings->setValue(QLatin1String(styleKey), d.m_style); + settings->setValue(QLatin1String(appStyleSheetKey), d.m_applicationStyleSheet); + settings->setValue(QLatin1String(skinKey), d.m_deviceSkin); + settings->endGroup(); +} + +void PreviewConfiguration::fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings) +{ + clear(); + QString key = prefix; + key += QLatin1Char('/'); + const int prefixSize = key.size(); + + PreviewConfigurationData &d = *m_d; + + const QVariant emptyString = QVariant(QString()); + + key += QLatin1String(styleKey); + d.m_style = settings->value(key, emptyString).toString(); + + key.replace(prefixSize, key.size() - prefixSize, QLatin1String(appStyleSheetKey)); + d.m_applicationStyleSheet = settings->value(key, emptyString).toString(); + + key.replace(prefixSize, key.size() - prefixSize, QLatin1String(skinKey)); + d.m_deviceSkin = settings->value(key, emptyString).toString(); +} + + +QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) < 0; +} + +QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) == 0; +} + +QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) != 0; +} + +// ------------- PreviewManagerPrivate +class PreviewManagerPrivate { +public: + PreviewManagerPrivate(PreviewManager::PreviewMode mode); + + const PreviewManager::PreviewMode m_mode; + + QPointer m_activePreview; + + typedef QList PreviewDataList; + + PreviewDataList m_previews; + + typedef QMap DeviceSkinConfigCache; + DeviceSkinConfigCache m_deviceSkinConfigCache; + + QDesignerFormEditorInterface *m_core; + bool m_updateBlocked; +}; + +PreviewManagerPrivate::PreviewManagerPrivate(PreviewManager::PreviewMode mode) : + m_mode(mode), + m_core(0), + m_updateBlocked(false) +{ +} + +// ------------- PreviewManager + +PreviewManager::PreviewManager(PreviewMode mode, QObject *parent) : + QObject(parent), + d(new PreviewManagerPrivate(mode)) +{ +} + +PreviewManager:: ~PreviewManager() +{ + delete d; +} + + +Qt::WindowFlags PreviewManager::previewWindowFlags(const QWidget *widget) const +{ +#ifdef Q_WS_WIN + Qt::WindowFlags windowFlags = (widget->windowType() == Qt::Window) ? Qt::Window | Qt::WindowMaximizeButtonHint : Qt::WindowFlags(Qt::Dialog); +#else + Q_UNUSED(widget) + // Only Dialogs have close buttons on Mac. + // On Linux, we don't want an additional task bar item and we don't want a minimize button; + // we want the preview to be on top. + Qt::WindowFlags windowFlags = Qt::Dialog; +#endif + return windowFlags; +} + +QWidget *PreviewManager::createDeviceSkinContainer(const QDesignerFormWindowInterface *fw) const +{ + return new QDialog(fw->window()); +} + +// Some widgets might require fake containers + +static QWidget *fakeContainer(QWidget *w) +{ + // Prevent a dock widget from trying to dock to Designer's main window + // (which can be found in the parent hierarchy in MDI mode) by + // providing a fake mainwindow + if (QDockWidget *dock = qobject_cast(w)) { + // Reparent: Clear modality, propagate title and resize outer container + const QSize size = w->size(); + w->setWindowModality(Qt::NonModal); + dock->setFeatures(dock->features() & ~(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetClosable)); + dock->setAllowedAreas(Qt::LeftDockWidgetArea); + QMainWindow *mw = new QMainWindow; + int leftMargin, topMargin, rightMargin, bottomMargin; + mw->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); + mw->addDockWidget(Qt::LeftDockWidgetArea, dock); + mw->resize(size + QSize(leftMargin + rightMargin, topMargin + bottomMargin)); + return mw; + } + return w; +} + +static PreviewConfiguration configurationFromSettings(QDesignerFormEditorInterface *core, const QString &style) +{ + qdesigner_internal::PreviewConfiguration pc; + const QDesignerSharedSettings settings(core); + if (settings.isCustomPreviewConfigurationEnabled()) + pc = settings.customPreviewConfiguration(); + if (!style.isEmpty()) + pc.setStyle(style); + return pc; +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage) +{ + return showPreview(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage); +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage) +{ + return showPreview(fw, style, -1, errorMessage); +} + +QWidget *PreviewManager::createPreview(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage, + int initialZoom) +{ + if (!d->m_core) + d->m_core = fw->core(); + + const bool zoomable = initialZoom > 0; + // Figure out which profile to apply + DeviceProfile deviceProfile; + if (deviceProfileIndex >= 0) { + deviceProfile = QDesignerSharedSettings(fw->core()).deviceProfileAt(deviceProfileIndex); + } else { + if (const FormWindowBase *fwb = qobject_cast(fw)) + deviceProfile = fwb->deviceProfile(); + } + // Create + QWidget *formWidget = QDesignerFormBuilder::createPreview(fw, pc.style(), pc.applicationStyleSheet(), deviceProfile, errorMessage); + if (!formWidget) + return 0; + + const QString title = tr("%1 - [Preview]").arg(formWidget->windowTitle()); + formWidget = fakeContainer(formWidget); + formWidget->setWindowTitle(title); + + // Clear any modality settings, child widget modalities must not be higher than parent's + formWidget->setWindowModality(Qt::NonModal); + // No skin + const QString deviceSkin = pc.deviceSkin(); + if (deviceSkin.isEmpty()) { + if (zoomable) { // Embed into ZoomWidget + ZoomWidget *zw = new DesignerZoomWidget; + connect(zw->zoomMenu(), SIGNAL(zoomChanged(int)), this, SLOT(slotZoomChanged(int))); + zw->setWindowTitle(title); + zw->setWidget(formWidget); + // Keep any widgets' context menus working, do not use global menu + zw->setWidgetZoomContextMenuEnabled(true); + zw->setParent(fw->window(), previewWindowFlags(formWidget)); + // Make preview close when Widget closes (Dialog/accept, etc) + formWidget->setAttribute(Qt::WA_DeleteOnClose, true); + connect(formWidget, SIGNAL(destroyed()), zw, SLOT(close())); + zw->setZoom(initialZoom); + zw->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return zw; + } + formWidget->setParent(fw->window(), previewWindowFlags(formWidget)); + formWidget->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return formWidget; + } + // Embed into skin. find config in cache + PreviewManagerPrivate::DeviceSkinConfigCache::iterator it = d->m_deviceSkinConfigCache.find(deviceSkin); + if (it == d->m_deviceSkinConfigCache.end()) { + DeviceSkinParameters parameters; + if (!parameters.read(deviceSkin, DeviceSkinParameters::ReadAll, errorMessage)) { + formWidget->deleteLater(); + return 0; + } + it = d->m_deviceSkinConfigCache.insert(deviceSkin, parameters); + } + + QWidget *skinContainer = createDeviceSkinContainer(fw); + PreviewDeviceSkin *skin = 0; + if (zoomable) { + ZoomablePreviewDeviceSkin *zds = new ZoomablePreviewDeviceSkin(it.value(), skinContainer); + zds->setZoomPercent(initialZoom); + connect(zds, SIGNAL(zoomPercentChanged(int)), this, SLOT(slotZoomChanged(int))); + skin = zds; + } else { + skin = new PreviewDeviceSkin(it.value(), skinContainer); + } + skin->setPreview(formWidget); + // Make preview close when Widget closes (Dialog/accept, etc) + formWidget->setAttribute(Qt::WA_DeleteOnClose, true); + connect(formWidget, SIGNAL(destroyed()), skinContainer, SLOT(close())); + skinContainer->setWindowTitle(title); + skinContainer->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return skinContainer; +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage) +{ + enum { Spacing = 10 }; + if (QWidget *existingPreviewWidget = raise(fw, pc)) + return existingPreviewWidget; + + const QDesignerSharedSettings settings(fw->core()); + const int initialZoom = settings.zoomEnabled() ? settings.zoom() : -1; + + QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage, initialZoom); + if (!widget) + return 0; + // Install filter for Escape key + widget->setAttribute(Qt::WA_DeleteOnClose, true); + widget->installEventFilter(this); + + switch (d->m_mode) { + case ApplicationModalPreview: + // Cannot do this on the Mac as the dialog would have no close button + widget->setWindowModality(Qt::ApplicationModal); + break; + case SingleFormNonModalPreview: + case MultipleFormNonModalPreview: + widget->setWindowModality(Qt::NonModal); + connect(fw, SIGNAL(changed()), widget, SLOT(close())); + connect(fw, SIGNAL(destroyed()), widget, SLOT(close())); + if (d->m_mode == SingleFormNonModalPreview) + connect(fw->core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), widget, SLOT(close())); + break; + } + // Semi-smart algorithm to position previews: + // If its the first one, position relative to form. + // 2nd, attempt to tile right (for comparing styles) or cascade + const QSize size = widget->size(); + const bool firstPreview = d->m_previews.empty(); + if (firstPreview) { + widget->move(fw->mapToGlobal(QPoint(Spacing, Spacing))); + } else { + if (QWidget *lastPreview = d->m_previews.back().m_widget) { + QDesktopWidget *desktop = qApp->desktop(); + const QRect lastPreviewGeometry = lastPreview->frameGeometry(); + const QRect availGeometry = desktop->availableGeometry(desktop->screenNumber(lastPreview)); + const QPoint newPos = lastPreviewGeometry.topRight() + QPoint(Spacing, 0); + if (newPos.x() + size.width() < availGeometry.right()) + widget->move(newPos); + else + widget->move(lastPreviewGeometry.topLeft() + QPoint(Spacing, Spacing)); + } + + } + d->m_previews.push_back(PreviewData(widget, fw, pc)); + widget->show(); + if (firstPreview) + emit firstPreviewOpened(); + return widget; +} + +QWidget *PreviewManager::raise(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc) +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (d->m_previews.empty()) + return false; + + // find matching window + const PreviewDataList::const_iterator cend = d->m_previews.constEnd(); + for (PreviewDataList::const_iterator it = d->m_previews.constBegin(); it != cend ;++it) { + QWidget * w = it->m_widget; + if (w && it->m_formWindow == fw && it->m_configuration == pc) { + w->raise(); + w->activateWindow(); + return w; + } + } + return 0; +} + +void PreviewManager::closeAllPreviews() +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (!d->m_previews.empty()) { + d->m_updateBlocked = true; + d->m_activePreview = 0; + const PreviewDataList::iterator cend = d->m_previews.end(); + for (PreviewDataList::iterator it = d->m_previews.begin(); it != cend ;++it) { + if (it->m_widget) + it->m_widget->close(); + } + d->m_previews.clear(); + d->m_updateBlocked = false; + emit lastPreviewClosed(); + } +} + +void PreviewManager::updatePreviewClosed(QWidget *w) +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (d->m_updateBlocked) + return; + // Purge out all 0 or widgets to be deleted + for (PreviewDataList::iterator it = d->m_previews.begin(); it != d->m_previews.end() ; ) { + QWidget *iw = it->m_widget; // Might be 0 when catching QEvent::Destroyed + if (iw == 0 || iw == w) { + it = d->m_previews.erase(it); + } else { + ++it; + } + } + if (d->m_previews.empty()) + emit lastPreviewClosed(); +} + +bool PreviewManager::eventFilter(QObject *watched, QEvent *event) +{ + // Courtesy of designer + do { + if (!watched->isWidgetType()) + break; + QWidget *previewWindow = qobject_cast(watched); + if (!previewWindow || !previewWindow->isWindow()) + break; + + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::ShortcutOverride: { + const QKeyEvent *keyEvent = static_cast(event); + const int key = keyEvent->key(); + if ((key == Qt::Key_Escape +#ifdef Q_WS_MAC + || (keyEvent->modifiers() == Qt::ControlModifier && key == Qt::Key_Period) +#endif + )) { + previewWindow->close(); + return true; + } + } + break; + case QEvent::WindowActivate: + d->m_activePreview = previewWindow; + break; + case QEvent::Destroy: // We don't get QEvent::Close if someone accepts a QDialog. + updatePreviewClosed(previewWindow); + break; + case QEvent::Close: + updatePreviewClosed(previewWindow); + previewWindow->removeEventFilter (this); + break; + default: + break; + } + } while(false); + return QObject::eventFilter(watched, event); +} + +int PreviewManager::previewCount() const +{ + return d->m_previews.size(); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage) +{ + return createPreviewPixmap(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage) +{ + return createPreviewPixmap(fw, style, -1, errorMessage); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage) +{ + QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage); + if (!widget) + return QPixmap(); + const QPixmap rc = QPixmap::grabWidget(widget); + widget->deleteLater(); + return rc; +} + +void PreviewManager::slotZoomChanged(int z) +{ + if (d->m_core) { // Save the last zoom chosen by the user. + QDesignerSharedSettings settings(d->m_core); + settings.setZoom(z); + } +} +} + +QT_END_NAMESPACE + +#include "previewmanager.moc" diff --git a/designer/lib/shared/previewmanager_p.h b/designer/lib/shared/previewmanager_p.h new file mode 100644 index 0000000..da1c40b --- /dev/null +++ b/designer/lib/shared/previewmanager_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PREVIEWMANAGER_H +#define PREVIEWMANAGER_H + +#include "shared_global_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QWidget; +class QPixmap; +class QAction; +class QActionGroup; +class QMenu; +class QWidget; +class QDesignerSettingsInterface; + +namespace qdesigner_internal { + +// ----------- PreviewConfiguration + +class PreviewConfigurationData; + +class QDESIGNER_SHARED_EXPORT PreviewConfiguration { +public: + PreviewConfiguration(); + explicit PreviewConfiguration(const QString &style, + const QString &applicationStyleSheet = QString(), + const QString &deviceSkin = QString()); + + PreviewConfiguration(const PreviewConfiguration&); + PreviewConfiguration& operator=(const PreviewConfiguration&); + ~PreviewConfiguration(); + + QString style() const; + void setStyle(const QString &); + + // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). + QString applicationStyleSheet() const; + void setApplicationStyleSheet(const QString &); + + QString deviceSkin() const; + void setDeviceSkin(const QString &); + + void clear(); + void toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const; + void fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings); + +private: + QSharedDataPointer m_d; +}; + +QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); +QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); +QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); + +// ----------- Preview window manager. +// Maintains a list of preview widgets with their associated form windows and configuration. + +class PreviewManagerPrivate; + +class QDESIGNER_SHARED_EXPORT PreviewManager : public QObject +{ + Q_OBJECT +public: + + enum PreviewMode { + // Modal preview. Do not use on Macs as dialogs would have no close button + ApplicationModalPreview, + // Non modal previewing of one form in different configurations (closes if form window changes) + SingleFormNonModalPreview, + // Non modal previewing of several forms in different configurations + MultipleFormNonModalPreview }; + + explicit PreviewManager(PreviewMode mode, QObject *parent); + virtual ~PreviewManager(); + + // Show preview. Raise existing preview window if there is one with a matching + // configuration, else create a new preview. + QWidget *showPreview(const QDesignerFormWindowInterface *, const PreviewConfiguration &pc, int deviceProfileIndex /*=-1*/, QString *errorMessage); + // Convenience that creates a preview using a configuration taken from the settings. + QWidget *showPreview(const QDesignerFormWindowInterface *, const QString &style, int deviceProfileIndex /*=-1*/, QString *errorMessage); + QWidget *showPreview(const QDesignerFormWindowInterface *, const QString &style, QString *errorMessage); + + int previewCount() const; + + // Create a pixmap for printing. + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc, int deviceProfileIndex /*=-1*/, QString *errorMessage); + // Convenience that creates a pixmap using a configuration taken from the settings. + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex /*=-1*/, QString *errorMessage); + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage); + + virtual bool eventFilter(QObject *watched, QEvent *event); + +public slots: + void closeAllPreviews(); + +signals: + void firstPreviewOpened(); + void lastPreviewClosed(); + +private slots: + void slotZoomChanged(int); + +private: + + virtual Qt::WindowFlags previewWindowFlags(const QWidget *widget) const; + virtual QWidget *createDeviceSkinContainer(const QDesignerFormWindowInterface *) const; + + QWidget *raise(const QDesignerFormWindowInterface *, const PreviewConfiguration &pc); + QWidget *createPreview(const QDesignerFormWindowInterface *, + const PreviewConfiguration &pc, + int deviceProfileIndex /* = -1 */, + QString *errorMessage, + /*Disabled by default, <0 */ + int initialZoom = -1); + + void updatePreviewClosed(QWidget *w); + + PreviewManagerPrivate *d; + + PreviewManager(const PreviewManager &other); + PreviewManager &operator =(const PreviewManager &other); +}; +} + +QT_END_NAMESPACE + +#endif // PREVIEWMANAGER_H diff --git a/designer/lib/shared/promotionmodel.cpp b/designer/lib/shared/promotionmodel.cpp new file mode 100644 index 0000000..3400422 --- /dev/null +++ b/designer/lib/shared/promotionmodel.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "promotionmodel_p.h" +#include "widgetdatabase_p.h" + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList StandardItemList; + + // Model columns. + enum { ClassNameColumn, IncludeFileColumn, IncludeTypeColumn, ReferencedColumn, NumColumns }; + + // Create a model row. + StandardItemList modelRow() { + StandardItemList rc; + for (int i = 0; i < NumColumns; i++) { + rc.push_back(new QStandardItem()); + } + return rc; + } + + // Create a model row for a base class (read-only, cannot be selected). + StandardItemList baseModelRow(const QDesignerWidgetDataBaseItemInterface *dbItem) { + StandardItemList rc = modelRow(); + + rc[ClassNameColumn]->setText(dbItem->name()); + for (int i = 0; i < NumColumns; i++) { + rc[i]->setFlags(Qt::ItemIsEnabled); + } + return rc; + } + + // Create an editable model row for a promoted class. + StandardItemList promotedModelRow(const QDesignerWidgetDataBaseInterface *widgetDataBase, + QDesignerWidgetDataBaseItemInterface *dbItem, + bool referenced = false) { + + const int index = widgetDataBase->indexOf(dbItem); + + // Associate user data: database index and enabled flag + QVariantList userDataList; + userDataList.push_back(QVariant(index)); + userDataList.push_back(QVariant(referenced)); + const QVariant userData(userDataList); + + StandardItemList rc = modelRow(); + // name + rc[ClassNameColumn]->setText(dbItem->name()); + rc[ClassNameColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable); + rc[ClassNameColumn]->setData(userData); + // header + const qdesigner_internal::IncludeSpecification spec = qdesigner_internal::includeSpecification(dbItem->includeFile()); + rc[IncludeFileColumn]->setText(spec.first); + rc[IncludeFileColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable); + rc[IncludeFileColumn]->setData(userData); + // global include + rc[IncludeTypeColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsUserCheckable); + rc[IncludeTypeColumn]->setData(userData); + rc[IncludeTypeColumn]->setCheckState(spec.second == qdesigner_internal::IncludeGlobal ? Qt::Checked : Qt::Unchecked); + // referenced + rc[ReferencedColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + rc[ClassNameColumn]->setData(userData); + if (!referenced) { + //: Usage of promoted widgets + static const QString notUsed = QCoreApplication::translate("PromotionModel", "Not used"); + rc[ReferencedColumn]->setText(notUsed); + } + return rc; + } +} + +namespace qdesigner_internal { + + PromotionModel::PromotionModel(QDesignerFormEditorInterface *core) : + m_core(core) + { + connect(this, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(slotItemChanged(QStandardItem*))); + } + + void PromotionModel::initializeHeaders() { + setColumnCount(NumColumns); + QStringList horizontalLabels(tr("Name")); + horizontalLabels += tr("Header file"); + horizontalLabels += tr("Global include"); + horizontalLabels += tr("Usage"); + setHorizontalHeaderLabels (horizontalLabels); + } + + void PromotionModel::updateFromWidgetDatabase() { + typedef QDesignerPromotionInterface::PromotedClasses PromotedClasses; + + clear(); + initializeHeaders(); + + // retrieve list of pairs from DB and convert into a tree structure. + // Set the item index as user data on the item. + const PromotedClasses promotedClasses = m_core->promotion()->promotedClasses(); + + if (promotedClasses.empty()) + return; + + const QSet usedPromotedClasses = m_core->promotion()->referencedPromotedClassNames(); + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *baseClass = 0; + QStandardItem *baseItem = 0; + + const PromotedClasses::const_iterator bcend = promotedClasses.constEnd(); + for (PromotedClasses::const_iterator it = promotedClasses.constBegin(); it != bcend; ++it) { + // Start a new base class? + if (baseClass != it->baseItem) { + baseClass = it->baseItem; + const StandardItemList baseRow = baseModelRow(it->baseItem); + baseItem = baseRow.front(); + appendRow(baseRow); + } + Q_ASSERT(baseItem); + // Append derived + baseItem->appendRow(promotedModelRow(widgetDataBase, it->promotedItem, usedPromotedClasses.contains(it->promotedItem->name()))); + } + } + + void PromotionModel::slotItemChanged(QStandardItem * changedItem) { + // Retrieve DB item + bool referenced; + QDesignerWidgetDataBaseItemInterface *dbItem = databaseItem(changedItem, &referenced); + Q_ASSERT(dbItem); + // Change header or type + switch (changedItem->column()) { + case ClassNameColumn: + emit classNameChanged(dbItem, changedItem->text()); + break; + case IncludeTypeColumn: + case IncludeFileColumn: { + // Get both file and type items via parent. + const QStandardItem *baseClassItem = changedItem->parent(); + const QStandardItem *fileItem = baseClassItem->child(changedItem->row(), IncludeFileColumn); + const QStandardItem *typeItem = baseClassItem->child(changedItem->row(), IncludeTypeColumn); + emit includeFileChanged(dbItem, buildIncludeFile(fileItem->text(), typeItem->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal)); + } + break; + } + } + + QDesignerWidgetDataBaseItemInterface *PromotionModel::databaseItemAt(const QModelIndex &index, bool *referenced) const { + if (const QStandardItem *item = itemFromIndex (index)) + return databaseItem(item, referenced); + + *referenced = false; + return 0; + } + + QDesignerWidgetDataBaseItemInterface *PromotionModel::databaseItem(const QStandardItem * item, bool *referenced) const { + // Decode user data associated with item. + const QVariant data = item->data(); + if (data.type() != QVariant::List) { + *referenced = false; + return 0; + } + + const QVariantList dataList = data.toList(); + const int index = dataList[0].toInt(); + *referenced = dataList[1].toBool(); + return m_core->widgetDataBase()->item(index); + } + + QModelIndex PromotionModel::indexOfClass(const QString &className) const { + const StandardItemList matches = findItems (className, Qt::MatchFixedString|Qt::MatchCaseSensitive|Qt::MatchRecursive); + return matches.empty() ? QModelIndex() : indexFromItem (matches.front()); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/promotionmodel_p.h b/designer/lib/shared/promotionmodel_p.h new file mode 100644 index 0000000..2f92122 --- /dev/null +++ b/designer/lib/shared/promotionmodel_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONMODEL_H +#define PROMOTIONMODEL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerWidgetDataBaseItemInterface; + +namespace qdesigner_internal { + + // Item model representing the promoted widgets. + class PromotionModel : public QStandardItemModel { + Q_OBJECT + + public: + explicit PromotionModel(QDesignerFormEditorInterface *core); + + void updateFromWidgetDatabase(); + + // Return item at model index or 0. + QDesignerWidgetDataBaseItemInterface *databaseItemAt(const QModelIndex &, bool *referenced) const; + + QModelIndex indexOfClass(const QString &className) const; + + signals: + void includeFileChanged(QDesignerWidgetDataBaseItemInterface *, const QString &includeFile); + void classNameChanged(QDesignerWidgetDataBaseItemInterface *, const QString &newName); + + private slots: + void slotItemChanged(QStandardItem * item); + + private: + void initializeHeaders(); + // Retrieve data base item of item or return 0. + QDesignerWidgetDataBaseItemInterface *databaseItem(const QStandardItem * item, bool *referenced) const; + + QDesignerFormEditorInterface *m_core; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONMODEL_H diff --git a/designer/lib/shared/promotiontaskmenu.cpp b/designer/lib/shared/promotiontaskmenu.cpp new file mode 100644 index 0000000..3053f11 --- /dev/null +++ b/designer/lib/shared/promotiontaskmenu.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "promotiontaskmenu_p.h" +#include "qdesigner_promotiondialog_p.h" +#include "widgetfactory_p.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" +#include "qdesigner_command_p.h" +#include "signalslotdialog_p.h" +#include "qdesigner_objectinspector_p.h" +#include "abstractintrospection_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QAction *separatorAction(QObject *parent) +{ + QAction *rc = new QAction(parent); + rc->setSeparator(true); + return rc; +} + +static inline QDesignerLanguageExtension *languageExtension(QDesignerFormEditorInterface *core) +{ + return qt_extension(core->extensionManager(), core); +} + +namespace qdesigner_internal { + +PromotionTaskMenu::PromotionTaskMenu(QWidget *widget,Mode mode, QObject *parent) : + QObject(parent), + m_mode(mode), + m_widget(widget), + m_promotionMapper(0), + m_globalEditAction(new QAction(tr("Promoted widgets..."), this)), + m_EditPromoteToAction(new QAction(tr("Promote to ..."), this)), + m_EditSignalsSlotsAction(new QAction(tr("Change signals/slots..."), this)), + m_promoteLabel(tr("Promote to")), + m_demoteLabel(tr("Demote to %1")) +{ + connect(m_globalEditAction, SIGNAL(triggered()), this, SLOT(slotEditPromotedWidgets())); + connect(m_EditPromoteToAction, SIGNAL(triggered()), this, SLOT(slotEditPromoteTo())); + connect(m_EditSignalsSlotsAction, SIGNAL(triggered()), this, SLOT(slotEditSignalsSlots())); +} + +PromotionTaskMenu::Mode PromotionTaskMenu::mode() const +{ + return m_mode; +} + +void PromotionTaskMenu::setMode(Mode m) +{ + m_mode = m; +} + +void PromotionTaskMenu::setWidget(QWidget *widget) +{ + m_widget = widget; +} + +void PromotionTaskMenu::setPromoteLabel(const QString &promoteLabel) +{ + m_promoteLabel = promoteLabel; +} + +void PromotionTaskMenu::setEditPromoteToLabel(const QString &promoteEditLabel) +{ + m_EditPromoteToAction->setText(promoteEditLabel); +} + +void PromotionTaskMenu::setDemoteLabel(const QString &demoteLabel) +{ + m_demoteLabel = demoteLabel; +} + +PromotionTaskMenu::PromotionState PromotionTaskMenu::createPromotionActions(QDesignerFormWindowInterface *formWindow) +{ + // clear out old + if (!m_promotionActions.empty()) { + qDeleteAll(m_promotionActions); + m_promotionActions.clear(); + } + // No promotion of main container + if (formWindow->mainContainer() == m_widget) + return NotApplicable; + + // Check for a homogenous selection + const PromotionSelectionList promotionSelection = promotionSelectionList(formWindow); + + if (promotionSelection.empty()) + return NoHomogenousSelection; + + QDesignerFormEditorInterface *core = formWindow->core(); + // if it is promoted: demote only. + if (isPromoted(formWindow->core(), m_widget)) { + const QString label = m_demoteLabel.arg( promotedExtends(core , m_widget)); + QAction *demoteAction = new QAction(label, this); + connect(demoteAction, SIGNAL(triggered()), this, SLOT(slotDemoteFromCustomWidget())); + m_promotionActions.push_back(demoteAction); + return CanDemote; + } + // figure out candidates + const QString baseClassName = WidgetFactory::classNameOf(core, m_widget); + const WidgetDataBaseItemList candidates = promotionCandidates(core->widgetDataBase(), baseClassName ); + if (candidates.empty()) { + // Is this thing promotable at all? + return QDesignerPromotionDialog::baseClassNames(core->promotion()).contains(baseClassName) ? CanPromote : NotApplicable; + } + // Set up a signal mapper to associate class names + if (!m_promotionMapper) { + m_promotionMapper = new QSignalMapper(this); + connect(m_promotionMapper, SIGNAL(mapped(QString)), this, SLOT(slotPromoteToCustomWidget(QString))); + } + + QMenu *candidatesMenu = new QMenu(); + // Create a sub menu + const WidgetDataBaseItemList::const_iterator cend = candidates.constEnd(); + // Set up actions and map class names + for (WidgetDataBaseItemList::const_iterator it = candidates.constBegin(); it != cend; ++it) { + const QString customClassName = (*it)->name(); + QAction *action = new QAction((*it)->name(), this); + connect(action, SIGNAL(triggered()), m_promotionMapper, SLOT(map())); + m_promotionMapper->setMapping(action, customClassName); + candidatesMenu->addAction(action); + } + // Sub menu action + QAction *subMenuAction = new QAction(m_promoteLabel, this); + subMenuAction->setMenu(candidatesMenu); + m_promotionActions.push_back(subMenuAction); + return CanPromote; +} + +void PromotionTaskMenu::addActions(unsigned separatorFlags, ActionList &actionList) +{ + addActions(formWindow(), separatorFlags, actionList); +} + +void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags, + ActionList &actionList) +{ + Q_ASSERT(m_widget); + const int previousSize = actionList.size(); + const PromotionState promotionState = createPromotionActions(fw); + + // Promotion candidates/demote + actionList += m_promotionActions; + + // Edit action depending on context + switch (promotionState) { + case CanPromote: + actionList += m_EditPromoteToAction; + break; + case CanDemote: + if (!(flags & SuppressGlobalEdit)) + actionList += m_globalEditAction; + if (!languageExtension(fw->core())) { + actionList += separatorAction(this); + actionList += m_EditSignalsSlotsAction; + } + break; + default: + if (!(flags & SuppressGlobalEdit)) + actionList += m_globalEditAction; + break; + } + // Add separators if required + if (actionList.size() > previousSize) { + if (flags & LeadingSeparator) + actionList.insert(previousSize, separatorAction(this)); + if (flags & TrailingSeparator) + actionList += separatorAction(this); + } +} + +void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *menu) +{ + ActionList actionList; + addActions(fw, flags, actionList); + menu->addActions(actionList); +} + +void PromotionTaskMenu::addActions(unsigned flags, QMenu *menu) +{ + addActions(formWindow(), flags, menu); +} + +void PromotionTaskMenu::promoteTo(QDesignerFormWindowInterface *fw, const QString &customClassName) +{ + Q_ASSERT(m_widget); + PromoteToCustomWidgetCommand *cmd = new PromoteToCustomWidgetCommand(fw); + cmd->init(promotionSelectionList(fw), customClassName); + fw->commandHistory()->push(cmd); +} + + +void PromotionTaskMenu::slotPromoteToCustomWidget(const QString &customClassName) +{ + promoteTo(formWindow(), customClassName); +} + +void PromotionTaskMenu::slotDemoteFromCustomWidget() +{ + QDesignerFormWindowInterface *fw = formWindow(); + const PromotionSelectionList promotedWidgets = promotionSelectionList(fw); + Q_ASSERT(!promotedWidgets.empty() && isPromoted(fw->core(), promotedWidgets.front())); + + // ### use the undo stack + DemoteFromCustomWidgetCommand *cmd = new DemoteFromCustomWidgetCommand(fw); + cmd->init(promotedWidgets); + fw->commandHistory()->push(cmd); +} + +void PromotionTaskMenu::slotEditPromoteTo() +{ + Q_ASSERT(m_widget); + // Check whether invoked over a promotable widget + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + const QString base_class_name = WidgetFactory::classNameOf(core, m_widget); + Q_ASSERT(QDesignerPromotionDialog::baseClassNames(core->promotion()).contains(base_class_name)); + // Show over promotable widget + QString promoteToClassName; + QDialog *promotionEditor = 0; + if (QDesignerLanguageExtension *lang = languageExtension(core)) + promotionEditor = lang->createPromotionDialog(core, base_class_name, &promoteToClassName, fw); + if (!promotionEditor) + promotionEditor = new QDesignerPromotionDialog(core, fw, base_class_name, &promoteToClassName); + if (promotionEditor->exec() == QDialog::Accepted && !promoteToClassName.isEmpty()) { + promoteTo(fw, promoteToClassName); + } + delete promotionEditor; +} + +void PromotionTaskMenu::slotEditPromotedWidgets() +{ + // Global context, show over non-promotable widget + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + editPromotedWidgets(fw->core(), fw); +} + +PromotionTaskMenu::PromotionSelectionList PromotionTaskMenu::promotionSelectionList(QDesignerFormWindowInterface *formWindow) const +{ + // In multi selection mode, check for a homogenous selection (same class, same promotion state) + // and return the list if this is the case. Also make sure m_widget + // is the last widget in the list so that it is re-selected as the last + // widget by the promotion commands. + + PromotionSelectionList rc; + + if (m_mode != ModeSingleWidget) { + QDesignerFormEditorInterface *core = formWindow->core(); + const QDesignerIntrospectionInterface *intro = core->introspection(); + const QString className = intro->metaObject(m_widget)->className(); + const bool promoted = isPromoted(formWindow->core(), m_widget); + // Just in case someone plugged an old-style Object Inspector + if (QDesignerObjectInspector *designerObjectInspector = qobject_cast(core->objectInspector())) { + Selection s; + designerObjectInspector->getSelection(s); + // Find objects of similar state + const QWidgetList &source = m_mode == ModeManagedMultiSelection ? s.managed : s.unmanaged; + const QWidgetList::const_iterator cend = source.constEnd(); + for (QWidgetList::const_iterator it = source.constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (w != m_widget) { + // Selection state mismatch + if (intro->metaObject(w)->className() != className || isPromoted(core, w) != promoted) + return PromotionSelectionList(); + rc.push_back(w); + } + } + } + } + + rc.push_back(m_widget); + return rc; +} + +QDesignerFormWindowInterface *PromotionTaskMenu::formWindow() const +{ + // Use the QObject overload of QDesignerFormWindowInterface::findFormWindow since that works + // for QDesignerMenus also. + QObject *o = m_widget; + QDesignerFormWindowInterface *result = QDesignerFormWindowInterface::findFormWindow(o); + Q_ASSERT(result != 0); + return result; +} + +void PromotionTaskMenu::editPromotedWidgets(QDesignerFormEditorInterface *core, QWidget* parent) { + QDesignerLanguageExtension *lang = languageExtension(core); + // Show over non-promotable widget + QDialog *promotionEditor = 0; + if (lang) + lang->createPromotionDialog(core, parent); + if (!promotionEditor) + promotionEditor = new QDesignerPromotionDialog(core, parent); + promotionEditor->exec(); + delete promotionEditor; +} + +void PromotionTaskMenu::slotEditSignalsSlots() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + SignalSlotDialog::editPromotedClass(fw->core(), m_widget, fw); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/promotiontaskmenu_p.h b/designer/lib/shared/promotiontaskmenu_p.h new file mode 100644 index 0000000..8ebb128 --- /dev/null +++ b/designer/lib/shared/promotiontaskmenu_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONTASKMENU_H +#define PROMOTIONTASKMENU_H + +#include "shared_global_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QAction; +class QMenu; +class QWidget; +class QSignalMapper; + +namespace qdesigner_internal { + +// A helper class for creating promotion context menus and handling promotion actions. + +class QDESIGNER_SHARED_EXPORT PromotionTaskMenu: public QObject +{ + Q_OBJECT +public: + enum Mode { + ModeSingleWidget, + ModeManagedMultiSelection, + ModeUnmanagedMultiSelection + }; + + explicit PromotionTaskMenu(QWidget *widget,Mode mode = ModeManagedMultiSelection, QObject *parent = 0); + + Mode mode() const; + void setMode(Mode m); + + void setWidget(QWidget *widget); + + // Set menu labels + void setPromoteLabel(const QString &promoteLabel); + void setEditPromoteToLabel(const QString &promoteEditLabel); + // Defaults to "Demote to %1".arg(class). + void setDemoteLabel(const QString &demoteLabel); + + typedef QList ActionList; + + enum AddFlags { LeadingSeparator = 1, TrailingSeparator = 2, SuppressGlobalEdit = 4}; + + // Adds a list of promotion actions according to the current promotion state of the widget. + void addActions(QDesignerFormWindowInterface *fw, unsigned flags, ActionList &actionList); + // Convenience that finds the form window. + void addActions(unsigned flags, ActionList &actionList); + + void addActions(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *menu); + void addActions(unsigned flags, QMenu *menu); + + // Pop up the editor in a global context. + static void editPromotedWidgets(QDesignerFormEditorInterface *core, QWidget* parent); + +private slots: + void slotPromoteToCustomWidget(const QString &customClassName); + void slotDemoteFromCustomWidget(); + void slotEditPromotedWidgets(); + void slotEditPromoteTo(); + void slotEditSignalsSlots(); + +private: + void promoteTo(QDesignerFormWindowInterface *fw, const QString &customClassName); + + enum PromotionState { NotApplicable, NoHomogenousSelection, CanPromote, CanDemote }; + PromotionState createPromotionActions(QDesignerFormWindowInterface *formWindow); + QDesignerFormWindowInterface *formWindow() const; + + typedef QList > PromotionSelectionList; + PromotionSelectionList promotionSelectionList(QDesignerFormWindowInterface *formWindow) const; + + Mode m_mode; + + QPointer m_widget; + + QSignalMapper *m_promotionMapper; + // Per-Widget actions + QList m_promotionActions; + + QAction *m_globalEditAction; + QAction *m_EditPromoteToAction; + QAction *m_EditSignalsSlotsAction; + + QString m_promoteLabel; + QString m_demoteLabel; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONTASKMENU_H diff --git a/designer/lib/shared/propertylineedit.cpp b/designer/lib/shared/propertylineedit.cpp new file mode 100644 index 0000000..f003586 --- /dev/null +++ b/designer/lib/shared/propertylineedit.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertylineedit_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + PropertyLineEdit::PropertyLineEdit(QWidget *parent) : + QLineEdit(parent), m_wantNewLine(false) + { + } + + bool PropertyLineEdit::event(QEvent *e) + { + // handle 'Select all' here as it is not done in the QLineEdit + if (e->type() == QEvent::ShortcutOverride && !isReadOnly()) { + QKeyEvent* ke = static_cast (e); + if (ke->modifiers() & Qt::ControlModifier) { + if(ke->key() == Qt::Key_A) { + ke->accept(); + return true; + } + } + } + return QLineEdit::event(e); + } + + void PropertyLineEdit::insertNewLine() { + insertText(QLatin1String("\\n")); + } + + void PropertyLineEdit::insertText(const QString &text) { + // position cursor after new text and grab focus + const int oldCursorPosition = cursorPosition (); + insert(text); + setCursorPosition (oldCursorPosition + text.length()); + setFocus(Qt::OtherFocusReason); + } + + void PropertyLineEdit::contextMenuEvent(QContextMenuEvent *event) { + QMenu *menu = createStandardContextMenu (); + + if (m_wantNewLine) { + menu->addSeparator(); + QAction* nlAction = menu->addAction(tr("Insert line break")); + connect(nlAction, SIGNAL(triggered()), this, SLOT(insertNewLine())); + } + + menu->exec(event->globalPos()); + } +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/propertylineedit_p.h b/designer/lib/shared/propertylineedit_p.h new file mode 100644 index 0000000..940ed2a --- /dev/null +++ b/designer/lib/shared/propertylineedit_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROPERTYLINEEDIT_H +#define PROPERTYLINEEDIT_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // A line edit with a special context menu allowing for adding (escaped) new lines + class PropertyLineEdit : public QLineEdit { + Q_OBJECT + public: + explicit PropertyLineEdit(QWidget *parent); + void setWantNewLine(bool nl) { m_wantNewLine = nl; } + bool wantNewLine() const { return m_wantNewLine; } + + bool event(QEvent *e); + protected: + void contextMenuEvent (QContextMenuEvent *event ); + private slots: + void insertNewLine(); + private: + void insertText(const QString &); + bool m_wantNewLine; + }; +} + +QT_END_NAMESPACE + +#endif // PROPERTYLINEEDIT_H diff --git a/designer/lib/shared/qdesigner_command.cpp b/designer/lib/shared/qdesigner_command.cpp new file mode 100644 index 0000000..21d2519 --- /dev/null +++ b/designer/lib/shared/qdesigner_command.cpp @@ -0,0 +1,2968 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "layout_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_widget_p.h" +#include "qdesigner_menu_p.h" +#include "shared_enums_p.h" +#include "metadatabase_p.h" +#include "formwindowbase_p.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +static inline void setPropertySheetWindowTitle(const QDesignerFormEditorInterface *core, QObject *o, const QString &t) +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), o)) { + const int idx = sheet->indexOf(QLatin1String("windowTitle")); + if (idx != -1) { + sheet->setProperty(idx, t); + sheet->setChanged(idx, true); + } + } +} + +namespace qdesigner_internal { + +// Helpers for the dynamic properties that store Z/Widget order +static const char *widgetOrderPropertyC = "_q_widgetOrder"; +static const char *zOrderPropertyC = "_q_zOrder"; + +static void addToWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name, int index = -1) +{ + QWidgetList list = qVariantValue(parentWidget->property(name)); + list.removeAll(widget); + if (index >= 0 && index < list.size()) { + list.insert(index, widget); + } else { + list.append(widget); + } + parentWidget->setProperty(name, qVariantFromValue(list)); +} + +static int removeFromWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name) +{ + QWidgetList list = qVariantValue(parentWidget->property(name)); + const int firstIndex = list.indexOf(widget); + if (firstIndex != -1) { + list.removeAll(widget); + parentWidget->setProperty(name, qVariantFromValue(list)); + } + return firstIndex; +} + +// ---- InsertWidgetCommand ---- +InsertWidgetCommand::InsertWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_insertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode), + m_layoutHelper(0), + m_widgetWasManaged(false) +{ +} + +InsertWidgetCommand::~InsertWidgetCommand() +{ + delete m_layoutHelper; +} + +void InsertWidgetCommand::init(QWidget *widget, bool already_in_form, int layoutRow, int layoutColumn) +{ + m_widget = widget; + + setText(QApplication::translate("Command", "Insert '%1'").arg(widget->objectName())); + + QWidget *parentWidget = m_widget->parentWidget(); + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), parentWidget); + + m_insertMode = deco ? deco->currentInsertMode() : QDesignerLayoutDecorationExtension::InsertWidgetMode; + if (layoutRow >= 0 && layoutColumn >= 0) { + m_cell.first = layoutRow; + m_cell.second = layoutColumn; + } else { + m_cell = deco ? deco->currentCell() : qMakePair(0, 0); + } + m_widgetWasManaged = already_in_form; +} + +static void recursiveUpdate(QWidget *w) +{ + w->update(); + + const QObjectList &l = w->children(); + const QObjectList::const_iterator cend = l.end(); + for ( QObjectList::const_iterator it = l.begin(); it != cend; ++it) { + if (QWidget *w = qobject_cast(*it)) + recursiveUpdate(w); + } +} + +void InsertWidgetCommand::redo() +{ + QWidget *parentWidget = m_widget->parentWidget(); + Q_ASSERT(parentWidget); + + addToWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC); + addToWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC); + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), parentWidget); + + if (deco != 0) { + const LayoutInfo::Type type = LayoutInfo::layoutType(core, LayoutInfo::managedLayout(core, parentWidget)); + m_layoutHelper = LayoutHelper::createLayoutHelper(type); + m_layoutHelper->pushState(core, parentWidget); + if (type == LayoutInfo::Grid) { + switch (m_insertMode) { + case QDesignerLayoutDecorationExtension::InsertRowMode: { + deco->insertRow(m_cell.first); + } break; + + case QDesignerLayoutDecorationExtension::InsertColumnMode: { + deco->insertColumn(m_cell.second); + } break; + + default: break; + } // end switch + } + deco->insertWidget(m_widget, m_cell); + } + + if (!m_widgetWasManaged) + formWindow()->manageWidget(m_widget); + m_widget->show(); + formWindow()->emitSelectionChanged(); + + if (parentWidget && parentWidget->layout()) { + recursiveUpdate(parentWidget); + parentWidget->layout()->invalidate(); + } + + refreshBuddyLabels(); +} + +void InsertWidgetCommand::undo() +{ + QWidget *parentWidget = m_widget->parentWidget(); + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), parentWidget); + + if (deco) { + deco->removeWidget(m_widget); + m_layoutHelper->popState(core, parentWidget); + } + + if (!m_widgetWasManaged) { + formWindow()->unmanageWidget(m_widget); + m_widget->hide(); + } + + removeFromWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC); + removeFromWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC); + + formWindow()->emitSelectionChanged(); + + refreshBuddyLabels(); +} + +void InsertWidgetCommand::refreshBuddyLabels() +{ + typedef QList LabelList; + + const LabelList label_list = qFindChildren(formWindow()); + if (label_list.empty()) + return; + + const QString buddyProperty = QLatin1String("buddy"); + const QByteArray objectNameU8 = m_widget->objectName().toUtf8(); + // Re-set the buddy (The sheet locates the object by name and sets it) + const LabelList::const_iterator cend = label_list.constEnd(); + for (LabelList::const_iterator it = label_list.constBegin(); it != cend; ++it ) { + if (QDesignerPropertySheetExtension* sheet = propertySheet(*it)) { + const int idx = sheet->indexOf(buddyProperty); + if (idx != -1) { + const QVariant value = sheet->property(idx); + if (value.toByteArray() == objectNameU8) + sheet->setProperty(idx, value); + } + } + } +} + +// ---- ChangeZOrderCommand ---- +ChangeZOrderCommand::ChangeZOrderCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void ChangeZOrderCommand::init(QWidget *widget) +{ + Q_ASSERT(widget); + + m_widget = widget; + + setText(QApplication::translate("Command", "Change Z-order of '%1'").arg(widget->objectName())); + + m_oldParentZOrder = qVariantValue(widget->parentWidget()->property("_q_zOrder")); + const int index = m_oldParentZOrder.indexOf(m_widget); + if (index != -1 && index + 1 < m_oldParentZOrder.count()) + m_oldPreceding = m_oldParentZOrder.at(index + 1); +} + +void ChangeZOrderCommand::redo() +{ + m_widget->parentWidget()->setProperty("_q_zOrder", qVariantFromValue(reorderWidget(m_oldParentZOrder, m_widget))); + + reorder(m_widget); +} + +void ChangeZOrderCommand::undo() +{ + m_widget->parentWidget()->setProperty("_q_zOrder", qVariantFromValue(m_oldParentZOrder)); + + if (m_oldPreceding) + m_widget->stackUnder(m_oldPreceding); + else + m_widget->raise(); +} + +// ---- RaiseWidgetCommand ---- +RaiseWidgetCommand::RaiseWidgetCommand(QDesignerFormWindowInterface *formWindow) + : ChangeZOrderCommand(formWindow) +{ +} + +void RaiseWidgetCommand::init(QWidget *widget) +{ + ChangeZOrderCommand::init(widget); + setText(QApplication::translate("Command", "Raise '%1'").arg(widget->objectName())); +} + +QWidgetList RaiseWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const +{ + QWidgetList l = list; + l.removeAll(widget); + l.append(widget); + return l; +} + +void RaiseWidgetCommand::reorder(QWidget *widget) const +{ + widget->raise(); +} + +// ---- LowerWidgetCommand ---- +LowerWidgetCommand::LowerWidgetCommand(QDesignerFormWindowInterface *formWindow) + : ChangeZOrderCommand(formWindow) +{ +} + +QWidgetList LowerWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const +{ + QWidgetList l = list; + l.removeAll(widget); + l.prepend(widget); + return l; +} + +void LowerWidgetCommand::init(QWidget *widget) +{ + ChangeZOrderCommand::init(widget); + setText(QApplication::translate("Command", "Lower '%1'").arg(widget->objectName())); +} + +void LowerWidgetCommand::reorder(QWidget *widget) const +{ + widget->lower(); +} + +// ---- ManageWidgetCommandHelper +ManageWidgetCommandHelper::ManageWidgetCommandHelper() : + m_widget(0) +{ +} + +void ManageWidgetCommandHelper::init(const QDesignerFormWindowInterface *fw, QWidget *widget) +{ + m_widget = widget; + m_managedChildren.clear(); + + const QWidgetList children = qFindChildren(m_widget); + if (children.empty()) + return; + + m_managedChildren.reserve(children.size()); + const QWidgetList::const_iterator lcend = children.constEnd(); + for (QWidgetList::const_iterator it = children.constBegin(); it != lcend; ++it) + if (fw->isManaged(*it)) + m_managedChildren.push_back(*it); +} + +void ManageWidgetCommandHelper::init(QWidget *widget, const WidgetVector &managedChildren) +{ + m_widget = widget; + m_managedChildren = managedChildren; +} + +void ManageWidgetCommandHelper::manage(QDesignerFormWindowInterface *fw) +{ + // Manage the managed children after parent + fw->manageWidget(m_widget); + if (!m_managedChildren.empty()) { + const WidgetVector::const_iterator lcend = m_managedChildren.constEnd(); + for (WidgetVector::const_iterator it = m_managedChildren.constBegin(); it != lcend; ++it) + fw->manageWidget(*it); + } +} + +void ManageWidgetCommandHelper::unmanage(QDesignerFormWindowInterface *fw) +{ + // Unmanage the managed children first + if (!m_managedChildren.empty()) { + const WidgetVector::const_iterator lcend = m_managedChildren.constEnd(); + for (WidgetVector::const_iterator it = m_managedChildren.constBegin(); it != lcend; ++it) + fw->unmanageWidget(*it); + } + fw->unmanageWidget(m_widget); +} + +// ---- DeleteWidgetCommand ---- +DeleteWidgetCommand::DeleteWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_layoutType(LayoutInfo::NoLayout), + m_layoutHelper(0), + m_flags(0), + m_splitterIndex(-1), + m_layoutSimplified(false), + m_formItem(0), + m_tabOrderIndex(-1), + m_widgetOrderIndex(-1), + m_zOrderIndex(-1) +{ +} + +DeleteWidgetCommand::~DeleteWidgetCommand() +{ + delete m_layoutHelper; +} + +void DeleteWidgetCommand::init(QWidget *widget, unsigned flags) +{ + m_widget = widget; + m_parentWidget = widget->parentWidget(); + m_geometry = widget->geometry(); + m_flags = flags; + m_layoutType = LayoutInfo::NoLayout; + m_splitterIndex = -1; + bool isManaged; // Check for a managed layout + QLayout *layout; + m_layoutType = LayoutInfo::laidoutWidgetType(formWindow()->core(), m_widget, &isManaged, &layout); + if (!isManaged) + m_layoutType = LayoutInfo::NoLayout; + switch (m_layoutType) { + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + QSplitter *splitter = qobject_cast(m_parentWidget); + Q_ASSERT(splitter); + m_splitterIndex = splitter->indexOf(widget); + } + break; + case LayoutInfo::NoLayout: + break; + default: + m_layoutHelper = LayoutHelper::createLayoutHelper(m_layoutType); + m_layoutPosition = m_layoutHelper->itemInfo(layout, m_widget); + break; + } + + m_formItem = formWindow()->core()->metaDataBase()->item(formWindow()); + m_tabOrderIndex = m_formItem->tabOrder().indexOf(widget); + + // Build the list of managed children + m_manageHelper.init(formWindow(), m_widget); + + setText(QApplication::translate("Command", "Delete '%1'").arg(widget->objectName())); +} + +void DeleteWidgetCommand::redo() +{ + formWindow()->clearSelection(); + QDesignerFormEditorInterface *core = formWindow()->core(); + + if (QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_parentWidget)) { + const int count = c->count(); + for (int i=0; iwidget(i) == m_widget) { + c->remove(i); + return; + } + } + } + + m_widgetOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC); + m_zOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC); + + if (QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), m_parentWidget)) + deco->removeWidget(m_widget); + + if (m_layoutHelper) + switch (m_layoutType) { + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + break; + default: + // Attempt to simplify grids if a row/column becomes empty + m_layoutSimplified = (m_flags & DoNotSimplifyLayout) ? false : m_layoutHelper->canSimplify(core, m_parentWidget, m_layoutPosition); + if (m_layoutSimplified) { + m_layoutHelper->pushState(core, m_parentWidget); + m_layoutHelper->simplify(core, m_parentWidget, m_layoutPosition); + } + break; + } + + if (!(m_flags & DoNotUnmanage)) + m_manageHelper.unmanage(formWindow()); + + m_widget->setParent(formWindow()); + m_widget->hide(); + + if (m_tabOrderIndex != -1) { + QList tab_order = m_formItem->tabOrder(); + tab_order.removeAt(m_tabOrderIndex); + m_formItem->setTabOrder(tab_order); + } +} + +void DeleteWidgetCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + formWindow()->clearSelection(); + + m_widget->setParent(m_parentWidget); + + if (QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_parentWidget)) { + c->addWidget(m_widget); + return; + } + + addToWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC, m_widgetOrderIndex); + addToWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC, m_zOrderIndex); + + m_widget->setGeometry(m_geometry); + + if (!(m_flags & DoNotUnmanage)) + m_manageHelper.manage(formWindow()); + // ### set up alignment + switch (m_layoutType) { + case LayoutInfo::NoLayout: + break; + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + QSplitter *splitter = qobject_cast(m_widget->parent()); + Q_ASSERT(splitter); + splitter->insertWidget(m_splitterIndex, m_widget); + } break; + default: { + Q_ASSERT(m_layoutHelper); + if (m_layoutSimplified) + m_layoutHelper->popState(core, m_parentWidget); + QLayout *layout = LayoutInfo::managedLayout(core, m_parentWidget); + Q_ASSERT(m_layoutType == LayoutInfo::layoutType(core, layout)); + m_layoutHelper->insertWidget(layout, m_layoutPosition, m_widget); + } + break; + } + + m_widget->show(); + + if (m_tabOrderIndex != -1) { + QList tab_order = m_formItem->tabOrder(); + tab_order.insert(m_tabOrderIndex, m_widget); + m_formItem->setTabOrder(tab_order); + } +} + +// ---- ReparentWidgetCommand ---- +ReparentWidgetCommand::ReparentWidgetCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void ReparentWidgetCommand::init(QWidget *widget, QWidget *parentWidget) +{ + Q_ASSERT(widget); + + m_widget = widget; + m_oldParentWidget = widget->parentWidget(); + m_newParentWidget = parentWidget; + + m_oldPos = m_widget->pos(); + m_newPos = m_newParentWidget->mapFromGlobal(m_oldParentWidget->mapToGlobal(m_oldPos)); + + setText(QApplication::translate("Command", "Reparent '%1'").arg(widget->objectName())); + + m_oldParentList = qVariantValue(m_oldParentWidget->property("_q_widgetOrder")); + m_oldParentZOrder = qVariantValue(m_oldParentWidget->property("_q_zOrder")); +} + +void ReparentWidgetCommand::redo() +{ + m_widget->setParent(m_newParentWidget); + m_widget->move(m_newPos); + + QWidgetList oldList = m_oldParentList; + oldList.removeAll(m_widget); + m_oldParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(oldList)); + + QWidgetList newList = qVariantValue(m_newParentWidget->property("_q_widgetOrder")); + newList.append(m_widget); + m_newParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(newList)); + + QWidgetList oldZOrder = m_oldParentZOrder; + oldZOrder.removeAll(m_widget); + m_oldParentWidget->setProperty("_q_zOrder", qVariantFromValue(oldZOrder)); + + QWidgetList newZOrder = qVariantValue(m_newParentWidget->property("_q_zOrder")); + newZOrder.append(m_widget); + m_newParentWidget->setProperty("_q_zOrder", qVariantFromValue(newZOrder)); + + m_widget->show(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +void ReparentWidgetCommand::undo() +{ + m_widget->setParent(m_oldParentWidget); + m_widget->move(m_oldPos); + + m_oldParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(m_oldParentList)); + + QWidgetList newList = qVariantValue(m_newParentWidget->property("_q_widgetOrder")); + newList.removeAll(m_widget); + m_newParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(newList)); + + m_oldParentWidget->setProperty("_q_zOrder", qVariantFromValue(m_oldParentZOrder)); + + QWidgetList newZOrder = qVariantValue(m_newParentWidget->property("_q_zOrder")); + m_newParentWidget->setProperty("_q_zOrder", qVariantFromValue(newZOrder)); + + m_widget->show(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +PromoteToCustomWidgetCommand::PromoteToCustomWidgetCommand + (QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Promote to custom widget"), formWindow) +{ +} + +void PromoteToCustomWidgetCommand::init(const WidgetList &widgets,const QString &customClassName) +{ + m_widgets = widgets; + m_customClassName = customClassName; +} + +void PromoteToCustomWidgetCommand::redo() +{ + foreach (QWidget *w, m_widgets) { + if (w) + promoteWidget(core(), w, m_customClassName); + } + updateSelection(); +} + +void PromoteToCustomWidgetCommand::updateSelection() +{ + // Update class names in ObjectInspector, PropertyEditor + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + core->objectInspector()->setFormWindow(fw); + if (QObject *o = core->propertyEditor()->object()) + core->propertyEditor()->setObject(o); +} + +void PromoteToCustomWidgetCommand::undo() +{ + foreach (QWidget *w, m_widgets) { + if (w) + demoteWidget(core(), w); + } + updateSelection(); +} + +// ---- DemoteFromCustomWidgetCommand ---- + +DemoteFromCustomWidgetCommand::DemoteFromCustomWidgetCommand + (QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Demote from custom widget"), formWindow), + m_promote_cmd(formWindow) +{ +} + +void DemoteFromCustomWidgetCommand::init(const WidgetList &promoted) +{ + m_promote_cmd.init(promoted, promotedCustomClassName(core(), promoted.front())); +} + +void DemoteFromCustomWidgetCommand::redo() +{ + m_promote_cmd.undo(); +} + +void DemoteFromCustomWidgetCommand::undo() +{ + m_promote_cmd.redo(); +} + +// ---------- CursorSelectionState +CursorSelectionState::CursorSelectionState() +{ +} + +void CursorSelectionState::save(const QDesignerFormWindowInterface *formWindow) +{ + const QDesignerFormWindowCursorInterface *cursor = formWindow->cursor(); + m_selection.clear(); + m_current = cursor->current(); + if (cursor->hasSelection()) { + const int count = cursor->selectedWidgetCount(); + for(int i = 0; i < count; i++) + m_selection.push_back(cursor->selectedWidget(i)); + } +} + +void CursorSelectionState::restore(QDesignerFormWindowInterface *formWindow) const +{ + if (m_selection.empty()) { + formWindow->clearSelection(true); + } else { + // Select current as last + formWindow->clearSelection(false); + const WidgetPointerList::const_iterator cend = m_selection.constEnd(); + for (WidgetPointerList::const_iterator it = m_selection.constBegin(); it != cend; ++it) + if (QWidget *w = *it) + if (w != m_current) + formWindow->selectWidget(*it, true); + if (m_current) + formWindow->selectWidget(m_current, true); + } +} + +// ---- LayoutCommand ---- + +LayoutCommand::LayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_setup(false) +{ +} + +LayoutCommand::~LayoutCommand() +{ + delete m_layout; +} + +void LayoutCommand::init(QWidget *parentWidget, const QWidgetList &widgets, + LayoutInfo::Type layoutType, QWidget *layoutBase, + bool reparentLayoutWidget) +{ + m_parentWidget = parentWidget; + m_widgets = widgets; + formWindow()->simplifySelection(&m_widgets); + m_layout = Layout::createLayout(widgets, parentWidget, formWindow(), layoutBase, layoutType); + m_layout->setReparentLayoutWidget(reparentLayoutWidget); + + switch (layoutType) { + case LayoutInfo::Grid: + setText(QApplication::translate("Command", "Lay out using grid")); + break; + case LayoutInfo::VBox: + setText(QApplication::translate("Command", "Lay out vertically")); + break; + case LayoutInfo::HBox: + setText(QApplication::translate("Command", "Lay out horizontally")); + break; + default: + break; + } + // Delayed setup to avoid confusion in case we are chained + // with a BreakLayout in a morph layout macro + m_setup = false; +} + +void LayoutCommand::redo() +{ + if (!m_setup) { + m_layout->setup(); + m_cursorSelectionState.save(formWindow()); + m_setup = true; + } + m_layout->doLayout(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +void LayoutCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + + QWidget *lb = m_layout->layoutBaseWidget(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), lb); + m_layout->undoLayout(); + delete deco; // release the extension + + // ### generalize (put in function) + if (!m_layoutBase && lb != 0 && !(qobject_cast(lb) || qobject_cast(lb))) { + core->metaDataBase()->add(lb); + lb->show(); + } + m_cursorSelectionState.restore(formWindow()); + core->objectInspector()->setFormWindow(formWindow()); +} + +// ---- BreakLayoutCommand ---- +BreakLayoutCommand::BreakLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Break layout"), formWindow), + m_layoutHelper(0), + m_properties(0), + m_propertyMask(0) +{ +} + +BreakLayoutCommand::~BreakLayoutCommand() +{ + delete m_layoutHelper; + delete m_layout; + delete m_properties; +} + +const LayoutProperties *BreakLayoutCommand::layoutProperties() const +{ + return m_properties; +} + +int BreakLayoutCommand::propertyMask() const +{ + return m_propertyMask; +} + +void BreakLayoutCommand::init(const QWidgetList &widgets, QWidget *layoutBase, bool reparentLayoutWidget) +{ + enum Type { SplitterLayout, LayoutHasMarginSpacing, LayoutHasState }; + + const QDesignerFormEditorInterface *core = formWindow()->core(); + m_widgets = widgets; + m_layoutBase = core->widgetFactory()->containerOfWidget(layoutBase); + QLayout *layoutToBeBroken; + const LayoutInfo::Type layoutType = LayoutInfo::managedLayoutType(core, m_layoutBase, &layoutToBeBroken); + m_layout = Layout::createLayout(widgets, m_layoutBase, formWindow(), layoutBase, layoutType); + m_layout->setReparentLayoutWidget(reparentLayoutWidget); + + Type type = LayoutHasState; + switch (layoutType) { + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + type = SplitterLayout; + break; + case LayoutInfo::HBox: + case LayoutInfo::VBox: // Margin/spacing need to be saved + type = LayoutHasMarginSpacing; + break; + default: // Margin/spacing need to be saved + has a state (empty rows/columns of a grid) + type = LayoutHasState; + break; + } + Q_ASSERT(m_layout != 0); + m_layout->sort(); + + + if (type >= LayoutHasMarginSpacing) { + m_properties = new LayoutProperties; + m_propertyMask = m_properties->fromPropertySheet(core, layoutToBeBroken, LayoutProperties::AllProperties); + } + if (type >= LayoutHasState) + m_layoutHelper = LayoutHelper::createLayoutHelper(layoutType); + m_cursorSelectionState.save(formWindow()); +} + +void BreakLayoutCommand::redo() +{ + if (!m_layout) + return; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QWidget *lb = m_layout->layoutBaseWidget(); + QDesignerLayoutDecorationExtension *deco = qt_extension(core->extensionManager(), lb); + formWindow()->clearSelection(false); + if (m_layoutHelper) + m_layoutHelper->pushState(core, m_layoutBase); + m_layout->breakLayout(); + delete deco; // release the extension + + foreach (QWidget *widget, m_widgets) { + widget->resize(widget->size().expandedTo(QSize(16, 16))); + } + // Update unless we are in an intermediate state of morphing layout + // in which a QLayoutWidget will have no layout at all. + if (m_layout->reparentLayoutWidget()) + core->objectInspector()->setFormWindow(formWindow()); +} + +void BreakLayoutCommand::undo() +{ + if (!m_layout) + return; + + formWindow()->clearSelection(false); + m_layout->doLayout(); + if (m_layoutHelper) + m_layoutHelper->popState(formWindow()->core(), m_layoutBase); + + QLayout *layoutToRestored = LayoutInfo::managedLayout(formWindow()->core(), m_layoutBase); + if (m_properties && m_layoutBase && layoutToRestored) + m_properties->toPropertySheet(formWindow()->core(), layoutToRestored, m_propertyMask); + m_cursorSelectionState.restore(formWindow()); + core()->objectInspector()->setFormWindow(formWindow()); +} +// ---- SimplifyLayoutCommand +SimplifyLayoutCommand::SimplifyLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Simplify Grid Layout"), formWindow), + m_area(0, 0, 32767, 32767), + m_layoutBase(0), + m_layoutHelper(0), + m_layoutSimplified(false) +{ +} + +SimplifyLayoutCommand::~SimplifyLayoutCommand() +{ + delete m_layoutHelper; +} + +bool SimplifyLayoutCommand::canSimplify(QDesignerFormEditorInterface *core, const QWidget *w, int *layoutType) +{ + if (!w) + return false; + QLayout *layout; + const LayoutInfo::Type type = LayoutInfo::managedLayoutType(core, w, &layout); + if (layoutType) + *layoutType = type; + if (!layout) + return false; + switch (type) { // Known negatives + case LayoutInfo::NoLayout: + case LayoutInfo::UnknownLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + case LayoutInfo::HBox: + case LayoutInfo::VBox: + return false; + default: + break; + } + switch (type) { + case LayoutInfo::Grid: + return QLayoutSupport::canSimplifyQuickCheck(qobject_cast(layout)); + case LayoutInfo::Form: + return QLayoutSupport::canSimplifyQuickCheck(qobject_cast(layout)); + default: + break; + } + return false; +} + +bool SimplifyLayoutCommand::init(QWidget *layoutBase) +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + m_layoutSimplified = false; + int type; + if (canSimplify(core, layoutBase, &type)) { + m_layoutBase = layoutBase; + m_layoutHelper = LayoutHelper::createLayoutHelper(type); + m_layoutSimplified = m_layoutHelper->canSimplify(core, layoutBase, m_area); + } + return m_layoutSimplified; +} + +void SimplifyLayoutCommand::redo() +{ + const QDesignerFormEditorInterface *core = formWindow()->core(); + if (m_layoutSimplified) { + m_layoutHelper->pushState(core, m_layoutBase); + m_layoutHelper->simplify(core, m_layoutBase, m_area); + } +} +void SimplifyLayoutCommand::undo() +{ + if (m_layoutSimplified) + m_layoutHelper->popState(formWindow()->core(), m_layoutBase); +} + +// ---- ToolBoxCommand ---- +ToolBoxCommand::ToolBoxCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +ToolBoxCommand::~ToolBoxCommand() +{ +} + +void ToolBoxCommand::init(QToolBox *toolBox) +{ + m_toolBox = toolBox; + m_index = m_toolBox->currentIndex(); + m_widget = m_toolBox->widget(m_index); + m_itemText = m_toolBox->itemText(m_index); + m_itemIcon = m_toolBox->itemIcon(m_index); +} + +void ToolBoxCommand::removePage() +{ + m_toolBox->removeItem(m_index); + + m_widget->hide(); + m_widget->setParent(formWindow()); + formWindow()->clearSelection(); + formWindow()->selectWidget(m_toolBox, true); + +} + +void ToolBoxCommand::addPage() +{ + m_widget->setParent(m_toolBox); + m_toolBox->insertItem(m_index, m_widget, m_itemIcon, m_itemText); + m_toolBox->setCurrentIndex(m_index); + + QDesignerPropertySheetExtension *sheet = qt_extension(formWindow()->core()->extensionManager(), m_toolBox); + if (sheet) { + qdesigner_internal::PropertySheetStringValue itemText(m_itemText); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemText")), qVariantFromValue(itemText)); + } + + m_widget->show(); + formWindow()->clearSelection(); + formWindow()->selectWidget(m_toolBox, true); +} + +// ---- MoveToolBoxPageCommand ---- +MoveToolBoxPageCommand::MoveToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) : + ToolBoxCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveToolBoxPageCommand::~MoveToolBoxPageCommand() +{ +} + +void MoveToolBoxPageCommand::init(QToolBox *toolBox, QWidget *page, int newIndex) +{ + ToolBoxCommand::init(toolBox); + setText(QApplication::translate("Command", "Move Page")); + + m_widget = page; + m_oldIndex = m_toolBox->indexOf(m_widget); + m_itemText = m_toolBox->itemText(m_oldIndex); + m_itemIcon = m_toolBox->itemIcon(m_oldIndex); + m_newIndex = newIndex; +} + +void MoveToolBoxPageCommand::redo() +{ + m_toolBox->removeItem(m_oldIndex); + m_toolBox->insertItem(m_newIndex, m_widget, m_itemIcon, m_itemText); +} + +void MoveToolBoxPageCommand::undo() +{ + m_toolBox->removeItem(m_newIndex); + m_toolBox->insertItem(m_oldIndex, m_widget, m_itemIcon, m_itemText); +} + +// ---- DeleteToolBoxPageCommand ---- +DeleteToolBoxPageCommand::DeleteToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) + : ToolBoxCommand(formWindow) +{ +} + +DeleteToolBoxPageCommand::~DeleteToolBoxPageCommand() +{ +} + +void DeleteToolBoxPageCommand::init(QToolBox *toolBox) +{ + ToolBoxCommand::init(toolBox); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteToolBoxPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteToolBoxPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddToolBoxPageCommand ---- +AddToolBoxPageCommand::AddToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) + : ToolBoxCommand(formWindow) +{ +} + +AddToolBoxPageCommand::~AddToolBoxPageCommand() +{ +} + +void AddToolBoxPageCommand::init(QToolBox *toolBox) +{ + init(toolBox, InsertBefore); +} + +void AddToolBoxPageCommand::init(QToolBox *toolBox, InsertionMode mode) +{ + m_toolBox = toolBox; + + m_index = m_toolBox->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_toolBox); + m_itemText = QApplication::translate("Command", "Page"); + m_itemIcon = QIcon(); + m_widget->setObjectName(QLatin1String("page")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddToolBoxPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddToolBoxPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- TabWidgetCommand ---- +TabWidgetCommand::TabWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +TabWidgetCommand::~TabWidgetCommand() +{ +} + +void TabWidgetCommand::init(QTabWidget *tabWidget) +{ + m_tabWidget = tabWidget; + m_index = m_tabWidget->currentIndex(); + m_widget = m_tabWidget->widget(m_index); + m_itemText = m_tabWidget->tabText(m_index); + m_itemIcon = m_tabWidget->tabIcon(m_index); +} + +void TabWidgetCommand::removePage() +{ + m_tabWidget->removeTab(m_index); + + m_widget->hide(); + m_widget->setParent(formWindow()); + m_tabWidget->setCurrentIndex(qMin(m_index, m_tabWidget->count())); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_tabWidget, true); +} + +void TabWidgetCommand::addPage() +{ + m_widget->setParent(0); + m_tabWidget->insertTab(m_index, m_widget, m_itemIcon, m_itemText); + m_widget->show(); + m_tabWidget->setCurrentIndex(m_index); + + QDesignerPropertySheetExtension *sheet = qt_extension(formWindow()->core()->extensionManager(), m_tabWidget); + if (sheet) { + qdesigner_internal::PropertySheetStringValue itemText(m_itemText); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabText")), qVariantFromValue(itemText)); + } + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_tabWidget, true); +} + +// ---- DeleteTabPageCommand ---- +DeleteTabPageCommand::DeleteTabPageCommand(QDesignerFormWindowInterface *formWindow) + : TabWidgetCommand(formWindow) +{ +} + +DeleteTabPageCommand::~DeleteTabPageCommand() +{ +} + +void DeleteTabPageCommand::init(QTabWidget *tabWidget) +{ + TabWidgetCommand::init(tabWidget); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteTabPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteTabPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddTabPageCommand ---- +AddTabPageCommand::AddTabPageCommand(QDesignerFormWindowInterface *formWindow) + : TabWidgetCommand(formWindow) +{ +} + +AddTabPageCommand::~AddTabPageCommand() +{ +} + +void AddTabPageCommand::init(QTabWidget *tabWidget) +{ + init(tabWidget, InsertBefore); +} + +void AddTabPageCommand::init(QTabWidget *tabWidget, InsertionMode mode) +{ + m_tabWidget = tabWidget; + + m_index = m_tabWidget->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_tabWidget); + m_itemText = QApplication::translate("Command", "Page"); + m_itemIcon = QIcon(); + m_widget->setObjectName(QLatin1String("tab")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddTabPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddTabPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- MoveTabPageCommand ---- +MoveTabPageCommand::MoveTabPageCommand(QDesignerFormWindowInterface *formWindow) : + TabWidgetCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveTabPageCommand::~MoveTabPageCommand() +{ +} + +void MoveTabPageCommand::init(QTabWidget *tabWidget, QWidget *page, + const QIcon &icon, const QString &label, + int index, int newIndex) +{ + TabWidgetCommand::init(tabWidget); + setText(QApplication::translate("Command", "Move Page")); + + m_page = page; + m_newIndex = newIndex; + m_oldIndex = index; + m_label = label; + m_icon = icon; +} + +void MoveTabPageCommand::redo() +{ + m_tabWidget->removeTab(m_oldIndex); + m_tabWidget->insertTab(m_newIndex, m_page, m_icon, m_label); + m_tabWidget->setCurrentIndex(m_newIndex); +} + +void MoveTabPageCommand::undo() +{ + m_tabWidget->removeTab(m_newIndex); + m_tabWidget->insertTab(m_oldIndex, m_page, m_icon, m_label); + m_tabWidget->setCurrentIndex(m_oldIndex); +} + +// ---- StackedWidgetCommand ---- +StackedWidgetCommand::StackedWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +StackedWidgetCommand::~StackedWidgetCommand() +{ +} + +void StackedWidgetCommand::init(QStackedWidget *stackedWidget) +{ + m_stackedWidget = stackedWidget; + m_index = m_stackedWidget->currentIndex(); + m_widget = m_stackedWidget->widget(m_index); +} + +void StackedWidgetCommand::removePage() +{ + m_stackedWidget->removeWidget(m_stackedWidget->widget(m_index)); + + m_widget->hide(); + m_widget->setParent(formWindow()); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_stackedWidget, true); +} + +void StackedWidgetCommand::addPage() +{ + m_stackedWidget->insertWidget(m_index, m_widget); + + m_widget->show(); + m_stackedWidget->setCurrentIndex(m_index); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_stackedWidget, true); +} + +// ---- MoveStackedWidgetCommand ---- +MoveStackedWidgetCommand::MoveStackedWidgetCommand(QDesignerFormWindowInterface *formWindow) : + StackedWidgetCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveStackedWidgetCommand::~MoveStackedWidgetCommand() +{ +} + +void MoveStackedWidgetCommand::init(QStackedWidget *stackedWidget, QWidget *page, int newIndex) +{ + StackedWidgetCommand::init(stackedWidget); + setText(QApplication::translate("Command", "Move Page")); + + m_widget = page; + m_newIndex = newIndex; + m_oldIndex = m_stackedWidget->indexOf(m_widget); +} + +void MoveStackedWidgetCommand::redo() +{ + m_stackedWidget->removeWidget(m_widget); + m_stackedWidget->insertWidget(m_newIndex, m_widget); +} + +void MoveStackedWidgetCommand::undo() +{ + m_stackedWidget->removeWidget(m_widget); + m_stackedWidget->insertWidget(m_oldIndex, m_widget); +} + +// ---- DeleteStackedWidgetPageCommand ---- +DeleteStackedWidgetPageCommand::DeleteStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : StackedWidgetCommand(formWindow) +{ +} + +DeleteStackedWidgetPageCommand::~DeleteStackedWidgetPageCommand() +{ +} + +void DeleteStackedWidgetPageCommand::init(QStackedWidget *stackedWidget) +{ + StackedWidgetCommand::init(stackedWidget); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteStackedWidgetPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteStackedWidgetPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddStackedWidgetPageCommand ---- +AddStackedWidgetPageCommand::AddStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : StackedWidgetCommand(formWindow) +{ +} + +AddStackedWidgetPageCommand::~AddStackedWidgetPageCommand() +{ +} + +void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget) +{ + init(stackedWidget, InsertBefore); +} + +void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget, InsertionMode mode) +{ + m_stackedWidget = stackedWidget; + + m_index = m_stackedWidget->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_stackedWidget); + m_widget->setObjectName(QLatin1String("page")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddStackedWidgetPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddStackedWidgetPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- TabOrderCommand ---- +TabOrderCommand::TabOrderCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tab order"), formWindow), + m_widgetItem(0) +{ +} + +void TabOrderCommand::init(const QList &newTabOrder) +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + Q_ASSERT(core); + + m_widgetItem = core->metaDataBase()->item(formWindow()); + Q_ASSERT(m_widgetItem); + m_oldTabOrder = m_widgetItem->tabOrder(); + m_newTabOrder = newTabOrder; +} + +void TabOrderCommand::redo() +{ + m_widgetItem->setTabOrder(m_newTabOrder); +} + +void TabOrderCommand::undo() +{ + m_widgetItem->setTabOrder(m_oldTabOrder); +} + +// ---- CreateMenuBarCommand ---- +CreateMenuBarCommand::CreateMenuBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Menu Bar"), formWindow) +{ +} + +void CreateMenuBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_menuBar = qobject_cast(core->widgetFactory()->createWidget(QLatin1String("QMenuBar"), m_mainWindow)); + core->widgetFactory()->initialize(m_menuBar); +} + +void CreateMenuBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension(core->extensionManager(), m_mainWindow); + c->addWidget(m_menuBar); + + m_menuBar->setObjectName(QLatin1String("menuBar")); + formWindow()->ensureUniqueObjectName(m_menuBar); + core->metaDataBase()->add(m_menuBar); + formWindow()->emitSelectionChanged(); + m_menuBar->setFocus(); +} + +void CreateMenuBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_menuBar) { + c->remove(i); + break; + } + } + + core->metaDataBase()->remove(m_menuBar); + formWindow()->emitSelectionChanged(); +} + +// ---- DeleteMenuBarCommand ---- +DeleteMenuBarCommand::DeleteMenuBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Menu Bar"), formWindow) +{ +} + +void DeleteMenuBarCommand::init(QMenuBar *menuBar) +{ + m_menuBar = menuBar; + m_mainWindow = qobject_cast(menuBar->parentWidget()); +} + +void DeleteMenuBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c; + c = qt_extension(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; icount(); ++i) { + if (c->widget(i) == m_menuBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_menuBar); + m_menuBar->hide(); + m_menuBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteMenuBarCommand::undo() +{ + if (m_mainWindow) { + m_menuBar->setParent(m_mainWindow); + QDesignerContainerExtension *c; + c = qt_extension(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_menuBar); + + core()->metaDataBase()->add(m_menuBar); + m_menuBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +// ---- CreateStatusBarCommand ---- +CreateStatusBarCommand::CreateStatusBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Status Bar"), formWindow) +{ +} + +void CreateStatusBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_statusBar = qobject_cast(core->widgetFactory()->createWidget(QLatin1String("QStatusBar"), m_mainWindow)); + core->widgetFactory()->initialize(m_statusBar); +} + +void CreateStatusBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension(core->extensionManager(), m_mainWindow); + c->addWidget(m_statusBar); + + m_statusBar->setObjectName(QLatin1String("statusBar")); + formWindow()->ensureUniqueObjectName(m_statusBar); + core->metaDataBase()->add(m_statusBar); + formWindow()->emitSelectionChanged(); +} + +void CreateStatusBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_statusBar) { + c->remove(i); + break; + } + } + + core->metaDataBase()->remove(m_statusBar); + formWindow()->emitSelectionChanged(); +} + +// ---- DeleteStatusBarCommand ---- +DeleteStatusBarCommand::DeleteStatusBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Status Bar"), formWindow) +{ +} + +void DeleteStatusBarCommand::init(QStatusBar *statusBar) +{ + m_statusBar = statusBar; + m_mainWindow = qobject_cast(statusBar->parentWidget()); +} + +void DeleteStatusBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; icount(); ++i) { + if (c->widget(i) == m_statusBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_statusBar); + m_statusBar->hide(); + m_statusBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteStatusBarCommand::undo() +{ + if (m_mainWindow) { + m_statusBar->setParent(m_mainWindow); + QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_statusBar); + + core()->metaDataBase()->add(m_statusBar); + m_statusBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +// ---- AddToolBarCommand ---- +AddToolBarCommand::AddToolBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Tool Bar"), formWindow) +{ +} + +void AddToolBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerWidgetFactoryInterface * wf = formWindow()->core()->widgetFactory(); + // Pass on 0 parent first to avoid reparenting flicker. + m_toolBar = qobject_cast(wf->createWidget(QLatin1String("QToolBar"), 0)); + wf->initialize(m_toolBar); + m_toolBar->hide(); +} + +void AddToolBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_toolBar); + + QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_mainWindow); + c->addWidget(m_toolBar); + + m_toolBar->setObjectName(QLatin1String("toolBar")); + formWindow()->ensureUniqueObjectName(m_toolBar); + setPropertySheetWindowTitle(core, m_toolBar, m_toolBar->objectName()); + formWindow()->emitSelectionChanged(); +} + +void AddToolBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->remove(m_toolBar); + QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_toolBar) { + c->remove(i); + break; + } + } + formWindow()->emitSelectionChanged(); +} + +// ---- DockWidgetCommand:: ---- +DockWidgetCommand::DockWidgetCommand(const QString &description, QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(description, formWindow) +{ +} + +DockWidgetCommand::~DockWidgetCommand() +{ +} + +void DockWidgetCommand::init(QDockWidget *dockWidget) +{ + m_dockWidget = dockWidget; +} + +// ---- AddDockWidgetCommand ---- +AddDockWidgetCommand::AddDockWidgetCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Dock Window"), formWindow) +{ +} + +void AddDockWidgetCommand::init(QMainWindow *mainWindow, QDockWidget *dockWidget) +{ + m_mainWindow = mainWindow; + m_dockWidget = dockWidget; +} + +void AddDockWidgetCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_dockWidget = qobject_cast(core->widgetFactory()->createWidget(QLatin1String("QDockWidget"), m_mainWindow)); +} + +void AddDockWidgetCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_mainWindow); + c->addWidget(m_dockWidget); + + m_dockWidget->setObjectName(QLatin1String("dockWidget")); + formWindow()->ensureUniqueObjectName(m_dockWidget); + formWindow()->manageWidget(m_dockWidget); + formWindow()->emitSelectionChanged(); +} + +void AddDockWidgetCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_dockWidget) { + c->remove(i); + break; + } + } + + formWindow()->unmanageWidget(m_dockWidget); + formWindow()->emitSelectionChanged(); +} + +// ---- AdjustWidgetSizeCommand ---- +AdjustWidgetSizeCommand::AdjustWidgetSizeCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void AdjustWidgetSizeCommand::init(QWidget *widget) +{ + m_widget = widget; + setText(QApplication::translate("Command", "Adjust Size of '%1'").arg(widget->objectName())); +} + +QWidget *AdjustWidgetSizeCommand::widgetForAdjust() const +{ + QDesignerFormWindowInterface *fw = formWindow(); + // Return the outer, embedding widget if it is the main container + if (Utils::isCentralWidget(fw, m_widget)) + return fw->core()->integration()->containerWindow(m_widget); + return m_widget; +} + +void AdjustWidgetSizeCommand::redo() +{ + QWidget *aw = widgetForAdjust(); + m_geometry = aw->geometry(); + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + aw->adjustSize(); + const bool isMainContainer = aw != m_widget; + if (!isMainContainer) { + /* When doing adjustsize on a selected non-laid out child that has been enlarged + * and pushed partially over the top/left edge[s], it is possible that it "disappears" + * when shrinking. In that case, move it back so that it remains visible. */ + if (aw->parentWidget()->layout() == 0) { + const QRect contentsRect = aw->parentWidget()->contentsRect(); + const QRect newGeometry = aw->geometry(); + QPoint newPos = m_geometry.topLeft(); + if (newGeometry.bottom() <= contentsRect.y()) + newPos.setY(contentsRect.y()); + if (newGeometry.right() <= contentsRect.x()) + newPos.setX(contentsRect.x()); + if (newPos != m_geometry.topLeft()) + aw->move(newPos); + } + } + updatePropertyEditor(); +} + +void AdjustWidgetSizeCommand::undo() +{ + QWidget *aw = widgetForAdjust(); + aw->resize(m_geometry.size()); + if (m_geometry.topLeft() != aw->geometry().topLeft()) + aw->move(m_geometry.topLeft()); + updatePropertyEditor(); +} + +void AdjustWidgetSizeCommand::updatePropertyEditor() const +{ + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == m_widget) + propertyEditor->setPropertyValue(QLatin1String("geometry"), m_widget->geometry(), true); + } +} +// ------------ ChangeFormLayoutItemRoleCommand + +ChangeFormLayoutItemRoleCommand::ChangeFormLayoutItemRoleCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Change Form Layout Item Geometry"), formWindow), + m_operation(SpanningToLabel) +{ +} + +void ChangeFormLayoutItemRoleCommand::init(QWidget *widget, Operation op) +{ + m_widget = widget; + m_operation = op; +} + +void ChangeFormLayoutItemRoleCommand::redo() +{ + doOperation(m_operation); +} + +void ChangeFormLayoutItemRoleCommand::undo() +{ + doOperation(reverseOperation(m_operation)); +} + +ChangeFormLayoutItemRoleCommand::Operation ChangeFormLayoutItemRoleCommand::reverseOperation(Operation op) +{ + switch (op) { + case SpanningToLabel: + return LabelToSpanning; + case SpanningToField: + return FieldToSpanning; + case LabelToSpanning: + return SpanningToLabel; + case FieldToSpanning: + return SpanningToField; + } + return SpanningToField; +} + +void ChangeFormLayoutItemRoleCommand::doOperation(Operation op) +{ + QFormLayout *fl = ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(formWindow()->core(), m_widget); + const int index = fl->indexOf(m_widget); + Q_ASSERT(index != -1); + int row; + QFormLayout::ItemRole role; + fl->getItemPosition (index, &row, &role); + Q_ASSERT(index != -1); + QLayoutItem *item = fl->takeAt(index); + const QRect area = QRect(0, row, 2, 1); + switch (op) { + case SpanningToLabel: + fl->setItem(row, QFormLayout::LabelRole, item); + QLayoutSupport::createEmptyCells(fl); + break; + case SpanningToField: + fl->setItem(row, QFormLayout::FieldRole, item); + QLayoutSupport::createEmptyCells(fl); + break; + case LabelToSpanning: + case FieldToSpanning: + QLayoutSupport::removeEmptyCells(fl, area); + fl->setItem(row, QFormLayout::SpanningRole, item); + break; + } +} + +unsigned ChangeFormLayoutItemRoleCommand::possibleOperations(QDesignerFormEditorInterface *core, QWidget *w) +{ + QFormLayout *fl = managedFormLayoutOf(core, w); + if (!fl) + return 0; + const int index = fl->indexOf(w); + if (index == -1) + return 0; + int row, col, colspan; + getFormLayoutItemPosition(fl, index, &row, &col, 0, &colspan); + // Spanning item? + if (colspan > 1) + return SpanningToLabel|SpanningToField; + // Is the neighbouring column free, that is, can the current item be expanded? + const QFormLayout::ItemRole neighbouringRole = col == 0 ? QFormLayout::FieldRole : QFormLayout::LabelRole; + const bool empty = LayoutInfo::isEmptyItem(fl->itemAt(row, neighbouringRole)); + if (empty) + return col == 0 ? LabelToSpanning : FieldToSpanning; + return 0; +} + +QFormLayout *ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(QDesignerFormEditorInterface *core, QWidget *w) +{ + if (QLayout *layout = LayoutInfo::managedLayout(core, w->parentWidget())) + if (QFormLayout *fl = qobject_cast(layout)) + return fl; + return 0; +} + +// ---- ChangeLayoutItemGeometry ---- +ChangeLayoutItemGeometry::ChangeLayoutItemGeometry(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Layout Item Geometry"), formWindow) +{ +} + +void ChangeLayoutItemGeometry::init(QWidget *widget, int row, int column, int rowspan, int colspan) +{ + m_widget = widget; + Q_ASSERT(m_widget->parentWidget() != 0); + + QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget()); + Q_ASSERT(layout != 0); + + QGridLayout *grid = qobject_cast(layout); + Q_ASSERT(grid != 0); + + const int itemIndex = grid->indexOf(m_widget); + Q_ASSERT(itemIndex != -1); + + int current_row, current_column, current_rowspan, current_colspan; + grid->getItemPosition(itemIndex, ¤t_row, ¤t_column, ¤t_rowspan, ¤t_colspan); + + m_oldInfo.setRect(current_column, current_row, current_colspan, current_rowspan); + m_newInfo.setRect(column, row, colspan, rowspan); +} + +void ChangeLayoutItemGeometry::changeItemPosition(const QRect &g) +{ + QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget()); + Q_ASSERT(layout != 0); + + QGridLayout *grid = qobject_cast(layout); + Q_ASSERT(grid != 0); + + const int itemIndex = grid->indexOf(m_widget); + Q_ASSERT(itemIndex != -1); + + QLayoutItem *item = grid->takeAt(itemIndex); + delete item; + + if (!QLayoutSupport::removeEmptyCells(grid, g)) + qWarning() << "ChangeLayoutItemGeometry::changeItemPosition: Nonempty cell at " << g << '.'; + + grid->addWidget(m_widget, g.top(), g.left(), g.height(), g.width()); + + grid->invalidate(); + grid->activate(); + + QLayoutSupport::createEmptyCells(grid); + + formWindow()->clearSelection(false); + formWindow()->selectWidget(m_widget, true); +} + +void ChangeLayoutItemGeometry::redo() +{ + changeItemPosition(m_newInfo); +} + +void ChangeLayoutItemGeometry::undo() +{ + changeItemPosition(m_oldInfo); +} + +// ---- ContainerWidgetCommand ---- +ContainerWidgetCommand::ContainerWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +ContainerWidgetCommand::~ContainerWidgetCommand() +{ +} + +QDesignerContainerExtension *ContainerWidgetCommand::containerExtension() const +{ + QExtensionManager *mgr = core()->extensionManager(); + return qt_extension(mgr, m_containerWidget); +} + +void ContainerWidgetCommand::init(QWidget *containerWidget) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_index = c->currentIndex(); + m_widget = c->widget(m_index); + } +} + +void ContainerWidgetCommand::removePage() +{ + if (QDesignerContainerExtension *c = containerExtension()) { + if (const int count = c->count()) { + // Undo add after last? + const int deleteIndex = m_index >= 0 ? m_index : count - 1; + c->remove(deleteIndex); + m_widget->hide(); + m_widget->setParent(formWindow()); + } + } +} + +void ContainerWidgetCommand::addPage() +{ + if (QDesignerContainerExtension *c = containerExtension()) { + int newCurrentIndex; + if (m_index >= 0) { + c->insertWidget(m_index, m_widget); + newCurrentIndex = m_index; + } else { + c->addWidget(m_widget); + newCurrentIndex = c->count() -1 ; + } + m_widget->show(); + c->setCurrentIndex(newCurrentIndex); + } +} + +// ---- DeleteContainerWidgetPageCommand ---- +DeleteContainerWidgetPageCommand::DeleteContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : ContainerWidgetCommand(formWindow) +{ +} + +DeleteContainerWidgetPageCommand::~DeleteContainerWidgetPageCommand() +{ +} + +void DeleteContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct) +{ + ContainerWidgetCommand::init(containerWidget); + switch (ct) { + case WizardContainer: + case PageContainer: + setText(QApplication::translate("Command", "Delete Page")); + break; + case MdiContainer: + setText(QApplication::translate("Command", "Delete Subwindow")); + break; + } +} + +void DeleteContainerWidgetPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteContainerWidgetPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddContainerWidgetPageCommand ---- +AddContainerWidgetPageCommand::AddContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : ContainerWidgetCommand(formWindow) +{ +} + +AddContainerWidgetPageCommand::~AddContainerWidgetPageCommand() +{ +} + +void AddContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct, InsertionMode mode) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_index = c->currentIndex(); + if (m_index >= 0 && mode == InsertAfter) + m_index++; + m_widget = 0; + const QDesignerFormEditorInterface *core = formWindow()->core(); + switch (ct) { + case PageContainer: + setText(QApplication::translate("Command", "Insert Page")); + m_widget = new QDesignerWidget(formWindow(), m_containerWidget); + m_widget->setObjectName(QApplication::translate("Command", "page")); + break; + case MdiContainer: + setText(QApplication::translate("Command", "Insert Subwindow")); + m_widget = new QDesignerWidget(formWindow(), m_containerWidget); + m_widget->setObjectName(QApplication::translate("Command", "subwindow")); + setPropertySheetWindowTitle(core, m_widget, QApplication::translate("Command", "Subwindow")); + break; + case WizardContainer: // Apply style, don't manage + m_widget = core->widgetFactory()->createWidget(QLatin1String("QWizardPage"), 0); + break; + } + formWindow()->ensureUniqueObjectName(m_widget); + core->metaDataBase()->add(m_widget); + } +} + +void AddContainerWidgetPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddContainerWidgetPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +ChangeCurrentPageCommand::ChangeCurrentPageCommand(QDesignerFormWindowInterface *formWindow) + : + QDesignerFormWindowCommand(QString(), formWindow), m_oldIndex(0), m_newIndex(0) +{ +} + +ChangeCurrentPageCommand::~ChangeCurrentPageCommand() +{ +} + +QDesignerContainerExtension *ChangeCurrentPageCommand::containerExtension() const +{ + QExtensionManager *mgr = core()->extensionManager(); + return qt_extension(mgr, m_containerWidget); +} + +void ChangeCurrentPageCommand::init(QWidget *containerWidget, int newIndex) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_newIndex = newIndex; + m_oldIndex = c->currentIndex(); + m_widget = c->widget(m_oldIndex); + } +} + +void ChangeCurrentPageCommand::redo() +{ + containerExtension()->setCurrentIndex(m_newIndex); +} + +void ChangeCurrentPageCommand::undo() +{ + containerExtension()->setCurrentIndex(m_oldIndex); +} + +static int itemRoles[] = { + Qt::DecorationPropertyRole, + Qt::DisplayPropertyRole, + Qt::ToolTipPropertyRole, + Qt::StatusTipPropertyRole, + Qt::WhatsThisPropertyRole, + Qt::FontRole, + Qt::TextAlignmentRole, + Qt::BackgroundRole, + Qt::ForegroundRole, + Qt::CheckStateRole, + -1 +}; + +template +static void copyRoleFromItem(ItemData *id, int role, const T *item) +{ + QVariant v = item->data(role); + if (v.isValid()) + id->m_properties.insert(role, v); +} + +template +static void copyRolesFromItem(ItemData *id, const T *item, bool editor) +{ + static const int defaultFlags = T().flags(); + + for (int i = 0; itemRoles[i] != -1; i++) + copyRoleFromItem(id, itemRoles[i], item); + + if (editor) + copyRoleFromItem(id, ItemFlagsShadowRole, item); + else if (item->flags() != defaultFlags) + id->m_properties.insert(ItemFlagsShadowRole, qVariantFromValue((int)item->flags())); +} + +template +static void copyRolesToItem(const ItemData *id, T *item, DesignerIconCache *iconCache, bool editor) +{ + QHash::const_iterator it = id->m_properties.constBegin(), + end = id->m_properties.constEnd(); + for (; it != end; ++it) + if (it.value().isValid()) { + if (!editor && it.key() == ItemFlagsShadowRole) { + item->setFlags((Qt::ItemFlags)it.value().toInt()); + } else { + item->setData(it.key(), it.value()); + switch (it.key()) { + case Qt::DecorationPropertyRole: + if (iconCache) + item->setIcon(iconCache->icon(qVariantValue(it.value()))); + break; + case Qt::DisplayPropertyRole: + item->setText(qVariantValue(it.value()).value()); + break; + case Qt::ToolTipPropertyRole: + item->setToolTip(qVariantValue(it.value()).value()); + break; + case Qt::StatusTipPropertyRole: + item->setStatusTip(qVariantValue(it.value()).value()); + break; + case Qt::WhatsThisPropertyRole: + item->setWhatsThis(qVariantValue(it.value()).value()); + break; + } + } + } + + if (editor) + item->setFlags(item->flags() | Qt::ItemIsEditable); +} + +ItemData::ItemData(const QListWidgetItem *item, bool editor) +{ + copyRolesFromItem(this, item, editor); +} + +QListWidgetItem *ItemData::createListItem(DesignerIconCache *iconCache, bool editor) const +{ + QListWidgetItem *item = new QListWidgetItem(); + copyRolesToItem(this, item, iconCache, editor); + return item; +} + +ItemData::ItemData(const QTableWidgetItem *item, bool editor) +{ + copyRolesFromItem(this, item, editor); +} + +QTableWidgetItem *ItemData::createTableItem(DesignerIconCache *iconCache, bool editor) const +{ + QTableWidgetItem *item = new QTableWidgetItem; + copyRolesToItem(this, item, iconCache, editor); + return item; +} + +static void copyRoleFromItem(ItemData *id, int role, const QTreeWidgetItem *item, int column) +{ + QVariant v = item->data(column, role); + if (v.isValid()) + id->m_properties.insert(role, v); +} + +ItemData::ItemData(const QTreeWidgetItem *item, int column) +{ + copyRoleFromItem(this, Qt::EditRole, item, column); + PropertySheetStringValue str(item->text(column)); + m_properties.insert(Qt::DisplayPropertyRole, qVariantFromValue(str)); + + for (int i = 0; itemRoles[i] != -1; i++) + copyRoleFromItem(this, itemRoles[i], item, column); +} + +void ItemData::fillTreeItemColumn(QTreeWidgetItem *item, int column, DesignerIconCache *iconCache) const +{ + QHash::const_iterator it = m_properties.constBegin(), end = m_properties.constEnd(); + for (; it != end; ++it) + if (it.value().isValid()) { + item->setData(column, it.key(), it.value()); + switch (it.key()) { + case Qt::DecorationPropertyRole: + if (iconCache) + item->setIcon(column, iconCache->icon(qVariantValue(it.value()))); + break; + case Qt::DisplayPropertyRole: + item->setText(column, qVariantValue(it.value()).value()); + break; + case Qt::ToolTipPropertyRole: + item->setToolTip(column, qVariantValue(it.value()).value()); + break; + case Qt::StatusTipPropertyRole: + item->setStatusTip(column, qVariantValue(it.value()).value()); + break; + case Qt::WhatsThisPropertyRole: + item->setWhatsThis(column, qVariantValue(it.value()).value()); + break; + } + } +} + +ListContents::ListContents(const QTreeWidgetItem *item) +{ + for (int i = 0; i < item->columnCount(); i++) + m_items.append(ItemData(item, i)); +} + +QTreeWidgetItem *ListContents::createTreeItem(DesignerIconCache *iconCache) const +{ + QTreeWidgetItem *item = new QTreeWidgetItem; + int i = 0; + foreach (const ItemData &id, m_items) + id.fillTreeItemColumn(item, i++, iconCache); + return item; +} + +void ListContents::createFromListWidget(const QListWidget *listWidget, bool editor) +{ + m_items.clear(); + + for (int i = 0; i < listWidget->count(); i++) + m_items.append(ItemData(listWidget->item(i), editor)); +} + +void ListContents::applyToListWidget(QListWidget *listWidget, DesignerIconCache *iconCache, bool editor) const +{ + listWidget->clear(); + + int i = 0; + foreach (const ItemData &entry, m_items) { + if (!entry.isValid()) + new QListWidgetItem(TableWidgetContents::defaultHeaderText(i), listWidget); + else + listWidget->addItem(entry.createListItem(iconCache, editor)); + i++; + } +} + +void ListContents::createFromComboBox(const QComboBox *comboBox) +{ + m_items.clear(); + + const int count = comboBox->count(); + for (int i = 0; i < count; i++) { + // We might encounter items added in a custom combo + // constructor. Ignore those. + const QVariant textValue = comboBox->itemData(i, Qt::DisplayPropertyRole); + if (!textValue.isNull()) { + ItemData entry; + entry.m_properties.insert(Qt::DisplayPropertyRole, textValue); + const QVariant iconValue = comboBox->itemData(i, Qt::DecorationPropertyRole); + if (!iconValue.isNull()) + entry.m_properties.insert(Qt::DecorationPropertyRole, iconValue); + m_items.append(entry); + } + } +} + +void ListContents::applyToComboBox(QComboBox *comboBox, DesignerIconCache *iconCache) const +{ + comboBox->clear(); + + foreach (const ItemData &hash, m_items) { + QIcon icon; + if (iconCache) + icon = iconCache->icon(qVariantValue( + hash.m_properties[Qt::DecorationPropertyRole])); + QVariant var = hash.m_properties[Qt::DisplayPropertyRole]; + PropertySheetStringValue str = qVariantValue(var); + comboBox->addItem(icon, str.value()); + comboBox->setItemData(comboBox->count() - 1, + var, + Qt::DisplayPropertyRole); + comboBox->setItemData(comboBox->count() - 1, + hash.m_properties[Qt::DecorationPropertyRole], + Qt::DecorationPropertyRole); + } +} + +// --------- TableWidgetContents + +TableWidgetContents::TableWidgetContents() : + m_columnCount(0), + m_rowCount(0) +{ +} + +void TableWidgetContents::clear() +{ + m_horizontalHeader.m_items.clear(); + m_verticalHeader.m_items.clear(); + m_items.clear(); + m_columnCount = 0; + m_rowCount = 0; +} + +QString TableWidgetContents::defaultHeaderText(int i) +{ + return QString::number(i + 1); +} + +bool TableWidgetContents::nonEmpty(const QTableWidgetItem *item, int headerColumn) +{ + static int defaultFlags = QTableWidgetItem().flags(); + + if (item->flags() != defaultFlags) + return true; + + QString text = qVariantValue(item->data(Qt::DisplayPropertyRole)).value(); + if (!text.isEmpty()) { + if (headerColumn < 0 || text != defaultHeaderText(headerColumn)) + return true; + } else { + // FIXME: This doesn't seem to make sense + return true; + } + + for (int i = 0; itemRoles[i] != -1; i++) + if (itemRoles[i] != Qt::DisplayPropertyRole && item->data(itemRoles[i]).isValid()) + return true; + + return false; +} + +void TableWidgetContents::insertHeaderItem(const QTableWidgetItem *item, int i, ListContents *header, bool editor) +{ + if (nonEmpty(item, i)) + header->m_items.append(ItemData(item, editor)); + else + header->m_items.append(ItemData()); +} + +void TableWidgetContents::fromTableWidget(const QTableWidget *tableWidget, bool editor) +{ + clear(); + m_columnCount = tableWidget->columnCount(); + m_rowCount = tableWidget->rowCount(); + // horiz header: Legacy behaviour: auto-generate number for empty items + for (int col = 0; col < m_columnCount; col++) + if (const QTableWidgetItem *item = tableWidget->horizontalHeaderItem(col)) + insertHeaderItem(item, col, &m_horizontalHeader, editor); + // vertical header: Legacy behaviour: auto-generate number for empty items + for (int row = 0; row < m_rowCount; row++) + if (const QTableWidgetItem *item = tableWidget->verticalHeaderItem(row)) + insertHeaderItem(item, row, &m_verticalHeader, editor); + // cell data + for (int col = 0; col < m_columnCount; col++) + for (int row = 0; row < m_rowCount; row++) + if (const QTableWidgetItem *item = tableWidget->item(row, col)) + if (nonEmpty(item, -1)) + m_items.insert(CellRowColumnAddress(row, col), ItemData(item, editor)); +} + +void TableWidgetContents::applyToTableWidget(QTableWidget *tableWidget, DesignerIconCache *iconCache, bool editor) const +{ + tableWidget->clear(); + + tableWidget->setColumnCount(m_columnCount); + tableWidget->setRowCount(m_rowCount); + + // horiz header + int col = 0; + foreach (const ItemData &id, m_horizontalHeader.m_items) { + if (id.isValid()) + tableWidget->setHorizontalHeaderItem(col, id.createTableItem(iconCache, editor)); + col++; + } + // vertical header + int row = 0; + foreach (const ItemData &id, m_verticalHeader.m_items) { + if (id.isValid()) + tableWidget->setVerticalHeaderItem(row, id.createTableItem(iconCache, editor)); + row++; + } + // items + const TableItemMap::const_iterator icend = m_items.constEnd(); + for (TableItemMap::const_iterator it = m_items.constBegin(); it != icend; ++ it) + tableWidget->setItem(it.key().first, it.key().second, it.value().createTableItem(iconCache, editor)); +} + +bool TableWidgetContents::operator==(const TableWidgetContents &rhs) const +{ + if (m_columnCount != rhs.m_columnCount || m_rowCount != rhs.m_rowCount) + return false; + + return m_horizontalHeader.m_items == rhs.m_horizontalHeader.m_items && + m_verticalHeader.m_items == rhs.m_verticalHeader.m_items && + m_items == rhs.m_items; +} + +// ---- ChangeTableContentsCommand ---- +ChangeTableContentsCommand::ChangeTableContentsCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Change Table Contents"), + formWindow), m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeTableContentsCommand::init(QTableWidget *tableWidget, + const TableWidgetContents &oldCont, const TableWidgetContents &newCont) +{ + m_tableWidget = tableWidget; + m_oldContents = oldCont; + m_newContents = newCont; +} + +void ChangeTableContentsCommand::redo() +{ + m_newContents.applyToTableWidget(m_tableWidget, m_iconCache, false); + QMetaObject::invokeMethod(m_tableWidget, "updateGeometries"); +} + +void ChangeTableContentsCommand::undo() +{ + m_oldContents.applyToTableWidget(m_tableWidget, m_iconCache, false); + QMetaObject::invokeMethod(m_tableWidget, "updateGeometries"); +} + +// --------- TreeWidgetContents +TreeWidgetContents::ItemContents::ItemContents(const QTreeWidgetItem *item, bool editor) : + ListContents(item) +{ + static const int defaultFlags = QTreeWidgetItem().flags(); + + if (editor) { + QVariant v = item->data(0, ItemFlagsShadowRole); + m_itemFlags = v.isValid() ? v.toInt() : -1; + } else { + m_itemFlags = (item->flags() != defaultFlags) ? (int)item->flags() : -1; + } + + for (int i = 0; i < item->childCount(); i++) + m_children.append(ItemContents(item->child(i), editor)); +} + +QTreeWidgetItem *TreeWidgetContents::ItemContents::createTreeItem(DesignerIconCache *iconCache, bool editor) const +{ + QTreeWidgetItem *item = ListContents::createTreeItem(iconCache); + + if (editor) + item->setFlags(item->flags() | Qt::ItemIsEditable); + + if (m_itemFlags != -1) { + if (editor) + item->setData(0, ItemFlagsShadowRole, qVariantFromValue(m_itemFlags)); + else + item->setFlags((Qt::ItemFlags)m_itemFlags); + } + + foreach (const ItemContents &ic, m_children) + item->addChild(ic.createTreeItem(iconCache, editor)); + + return item; +} + +bool TreeWidgetContents::ItemContents::operator==(const TreeWidgetContents::ItemContents &rhs) const +{ + return + m_itemFlags == rhs.m_itemFlags && + m_items == rhs.m_items && + m_children == rhs.m_children; +} + +void TreeWidgetContents::clear() +{ + m_headerItem.m_items.clear(); + m_rootItems.clear(); +} + +void TreeWidgetContents::fromTreeWidget(const QTreeWidget *treeWidget, bool editor) +{ + clear(); + m_headerItem = ListContents(treeWidget->headerItem()); + for (int col = 0; col < treeWidget->topLevelItemCount(); col++) + m_rootItems.append(ItemContents(treeWidget->topLevelItem(col), editor)); +} + +void TreeWidgetContents::applyToTreeWidget(QTreeWidget *treeWidget, DesignerIconCache *iconCache, bool editor) const +{ + treeWidget->clear(); + + treeWidget->setColumnCount(m_headerItem.m_items.count()); + treeWidget->setHeaderItem(m_headerItem.createTreeItem(iconCache)); + foreach (const ItemContents &ic, m_rootItems) + treeWidget->addTopLevelItem(ic.createTreeItem(iconCache, editor)); + treeWidget->expandAll(); +} + +bool TreeWidgetContents::operator==(const TreeWidgetContents &rhs) const +{ + return + m_headerItem == rhs.m_headerItem && + m_rootItems == rhs.m_rootItems; +} + +// ---- ChangeTreeContentsCommand ---- +ChangeTreeContentsCommand::ChangeTreeContentsCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tree Contents"), formWindow), + m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeTreeContentsCommand::init(QTreeWidget *treeWidget, + const TreeWidgetContents &oldState, const TreeWidgetContents &newState) +{ + m_treeWidget = treeWidget; + m_oldState = oldState; + m_newState = newState; +} + +void ChangeTreeContentsCommand::redo() +{ + m_newState.applyToTreeWidget(m_treeWidget, m_iconCache, false); +} + +void ChangeTreeContentsCommand::undo() +{ + m_oldState.applyToTreeWidget(m_treeWidget, m_iconCache, false); +} + +// ---- ChangeListContentsCommand ---- +ChangeListContentsCommand::ChangeListContentsCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow), m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeListContentsCommand::init(QListWidget *listWidget, + const ListContents &oldItems, const ListContents &items) +{ + m_listWidget = listWidget; + m_comboBox = 0; + + m_newItemsState = items; + m_oldItemsState = oldItems; +} + +void ChangeListContentsCommand::init(QComboBox *comboBox, + const ListContents &oldItems, const ListContents &items) +{ + m_listWidget = 0; + m_comboBox = comboBox; + + m_newItemsState = items; + m_oldItemsState = oldItems; +} + +void ChangeListContentsCommand::redo() +{ + if (m_listWidget) + m_newItemsState.applyToListWidget(m_listWidget, m_iconCache, false); + else if (m_comboBox) + m_newItemsState.applyToComboBox(m_comboBox, m_iconCache); +} + +void ChangeListContentsCommand::undo() +{ + if (m_listWidget) + m_oldItemsState.applyToListWidget(m_listWidget, m_iconCache, false); + else if (m_comboBox) + m_oldItemsState.applyToComboBox(m_comboBox, m_iconCache); +} + +// ---- AddActionCommand ---- + +AddActionCommand::AddActionCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Add action"), formWindow) +{ + m_action = 0; +} + +void AddActionCommand::init(QAction *action) +{ + Q_ASSERT(m_action == 0); + m_action = action; +} + +void AddActionCommand::redo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->manageAction(m_action); +} + +void AddActionCommand::undo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->unmanageAction(m_action); +} + +// ---- RemoveActionCommand ---- + +RemoveActionCommand::RemoveActionCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Remove action"), formWindow), + m_action(0) +{ +} + +static RemoveActionCommand::ActionData findActionIn(QAction *action) +{ + RemoveActionCommand::ActionData result; + // We only want menus and toolbars, no toolbuttons. + foreach (QWidget *widget, action->associatedWidgets()) + if (qobject_cast(widget) || qobject_cast(widget)) { + const QList actionList = widget->actions(); + const int size = actionList.size(); + for (int i = 0; i < size; ++i) { + if (actionList.at(i) == action) { + QAction *before = 0; + if (i + 1 < size) + before = actionList.at(i + 1); + result.append(RemoveActionCommand::ActionDataItem(before, widget)); + break; + } + } + } + return result; +} + +void RemoveActionCommand::init(QAction *action) +{ + Q_ASSERT(m_action == 0); + m_action = action; + + m_actionData = findActionIn(action); +} + +void RemoveActionCommand::redo() +{ + QDesignerFormWindowInterface *fw = formWindow(); + foreach (const ActionDataItem &item, m_actionData) { + item.widget->removeAction(m_action); + } + // Notify components (for example, signal slot editor) + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(fw)) + fwb->emitObjectRemoved(m_action); + + core()->actionEditor()->setFormWindow(fw); + core()->actionEditor()->unmanageAction(m_action); + if (!m_actionData.empty()) + core()->objectInspector()->setFormWindow(fw); +} + +void RemoveActionCommand::undo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->manageAction(m_action); + foreach (const ActionDataItem &item, m_actionData) { + item.widget->insertAction(item.before, m_action); + } + if (!m_actionData.empty()) + core()->objectInspector()->setFormWindow(formWindow()); +} + +// ---- ActionInsertionCommand ---- + +ActionInsertionCommand::ActionInsertionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(text, formWindow), + m_parentWidget(0), + m_action(0), + m_beforeAction(0), + m_update(false) +{ +} + +void ActionInsertionCommand::init(QWidget *parentWidget, QAction *action, QAction *beforeAction, bool update) +{ + Q_ASSERT(m_parentWidget == 0); + Q_ASSERT(m_action == 0); + + m_parentWidget = parentWidget; + m_action = action; + m_beforeAction = beforeAction; + m_update = update; +} + +void ActionInsertionCommand::insertAction() +{ + Q_ASSERT(m_action != 0); + Q_ASSERT(m_parentWidget != 0); + + if (m_beforeAction) + m_parentWidget->insertAction(m_beforeAction, m_action); + else + m_parentWidget->addAction(m_action); + + if (m_update) { + cheapUpdate(); + if (QMenu *menu = m_action->menu()) + selectUnmanagedObject(menu); + else + selectUnmanagedObject(m_action); + PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor. + } +} +void ActionInsertionCommand::removeAction() +{ + Q_ASSERT(m_action != 0); + Q_ASSERT(m_parentWidget != 0); + + if (QDesignerMenu *menu = qobject_cast(m_parentWidget)) + menu->hideSubMenu(); + + m_parentWidget->removeAction(m_action); + + if (m_update) { + cheapUpdate(); + selectUnmanagedObject(m_parentWidget); + PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor. + } +} + +InsertActionIntoCommand::InsertActionIntoCommand(QDesignerFormWindowInterface *formWindow) : + ActionInsertionCommand(QApplication::translate("Command", "Add action"), formWindow) +{ +} +// ---- RemoveActionFromCommand ---- + +RemoveActionFromCommand::RemoveActionFromCommand(QDesignerFormWindowInterface *formWindow) : + ActionInsertionCommand(QApplication::translate("Command", "Remove action"), formWindow) +{ +} + +// ---- AddMenuActionCommand ---- + +MenuActionCommand::MenuActionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(text, formWindow), + m_action(0), + m_actionBefore(0), + m_menuParent(0), + m_associatedWidget(0), + m_objectToSelect(0) +{ +} + +void MenuActionCommand::init(QAction *action, QAction *actionBefore, + QWidget *associatedWidget, QWidget *objectToSelect) +{ + QMenu *menu = action->menu(); + Q_ASSERT(menu); + m_menuParent = menu->parentWidget(); + m_action = action; + m_actionBefore = actionBefore; + m_associatedWidget = associatedWidget; + m_objectToSelect = objectToSelect; +} + +void MenuActionCommand::insertMenu() +{ + core()->metaDataBase()->add(m_action); + QMenu *menu = m_action->menu(); + if (m_menuParent && menu->parentWidget() != m_menuParent) + menu->setParent(m_menuParent); + core()->metaDataBase()->add(menu); + m_associatedWidget->insertAction(m_actionBefore, m_action); + cheapUpdate(); + selectUnmanagedObject(menu); +} + +void MenuActionCommand::removeMenu() +{ + m_action->menu()->setParent(0); + QMenu *menu = m_action->menu(); + core()->metaDataBase()->remove(menu); + menu->setParent(0); + core()->metaDataBase()->remove(m_action); + m_associatedWidget->removeAction(m_action); + cheapUpdate(); + selectUnmanagedObject(m_objectToSelect); +} + +AddMenuActionCommand::AddMenuActionCommand(QDesignerFormWindowInterface *formWindow) : + MenuActionCommand(QApplication::translate("Command", "Add menu"), formWindow) +{ +} + +// ---- RemoveMenuActionCommand ---- +RemoveMenuActionCommand::RemoveMenuActionCommand(QDesignerFormWindowInterface *formWindow) : + MenuActionCommand(QApplication::translate("Command", "Remove menu"), formWindow) +{ +} + +// ---- CreateSubmenuCommand ---- +CreateSubmenuCommand::CreateSubmenuCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Create submenu"), formWindow), + m_action(0), + m_menu(0), + m_objectToSelect(0) +{ +} + +void CreateSubmenuCommand::init(QDesignerMenu *menu, QAction *action, QObject *objectToSelect) +{ + m_menu = menu; + m_action = action; + m_objectToSelect = objectToSelect; +} + +void CreateSubmenuCommand::redo() +{ + m_menu->createRealMenuAction(m_action); + cheapUpdate(); + if (m_objectToSelect) + selectUnmanagedObject(m_objectToSelect); +} + +void CreateSubmenuCommand::undo() +{ + m_menu->removeRealMenu(m_action); + cheapUpdate(); + selectUnmanagedObject(m_menu); +} + +// ---- DeleteToolBarCommand ---- +DeleteToolBarCommand::DeleteToolBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Tool Bar"), formWindow) +{ +} + +void DeleteToolBarCommand::init(QToolBar *toolBar) +{ + m_toolBar = toolBar; + m_mainWindow = qobject_cast(toolBar->parentWidget()); +} + +void DeleteToolBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; icount(); ++i) { + if (c->widget(i) == m_toolBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_toolBar); + m_toolBar->hide(); + m_toolBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteToolBarCommand::undo() +{ + if (m_mainWindow) { + m_toolBar->setParent(m_mainWindow); + QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_toolBar); + + core()->metaDataBase()->add(m_toolBar); + m_toolBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_command2.cpp b/designer/lib/shared/qdesigner_command2.cpp new file mode 100644 index 0000000..ac3be2d --- /dev/null +++ b/designer/lib/shared/qdesigner_command2.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_command2_p.h" +#include "formwindowbase_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_command_p.h" +#include "widgetfactory_p.h" +#include "qlayout_widget_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +MorphLayoutCommand::MorphLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_breakLayoutCommand(new BreakLayoutCommand(formWindow)), + m_layoutCommand(new LayoutCommand(formWindow)), + m_newType(LayoutInfo::VBox), + m_layoutBase(0) +{ +} + +MorphLayoutCommand::~MorphLayoutCommand() +{ + delete m_layoutCommand; + delete m_breakLayoutCommand; +} + +bool MorphLayoutCommand::init(QWidget *w, int newType) +{ + int oldType; + QDesignerFormWindowInterface *fw = formWindow(); + if (!canMorph(fw, w, &oldType) || oldType == newType) + return false; + m_layoutBase = w; + m_newType = newType; + // Find all managed widgets + m_widgets.clear(); + const QLayout *layout = LayoutInfo::managedLayout(fw->core(), w); + const int count = layout->count(); + for (int i = 0; i < count ; i++) { + if (QWidget *w = layout->itemAt(i)->widget()) + if (fw->isManaged(w)) + m_widgets.push_back(w); + } + const bool reparentLayoutWidget = false; // leave QLayoutWidget intact + m_breakLayoutCommand->init(m_widgets, m_layoutBase, reparentLayoutWidget); + m_layoutCommand->init(m_layoutBase, m_widgets, static_cast(m_newType), m_layoutBase, reparentLayoutWidget); + setText(formatDescription(core(), m_layoutBase, oldType, newType)); + return true; +} + +bool MorphLayoutCommand::canMorph(const QDesignerFormWindowInterface *formWindow, QWidget *w, int *ptrToCurrentType) +{ + if (ptrToCurrentType) + *ptrToCurrentType = LayoutInfo::NoLayout; + // We want a managed widget or a container page + // with a level-0 managed layout + QDesignerFormEditorInterface *core = formWindow->core(); + QLayout *layout = LayoutInfo::managedLayout(core, w); + if (!layout) + return false; + const LayoutInfo::Type type = LayoutInfo::layoutType(core, layout); + if (ptrToCurrentType) + *ptrToCurrentType = type; + switch (type) { + case LayoutInfo::HBox: + case LayoutInfo::VBox: + case LayoutInfo::Grid: + case LayoutInfo::Form: + return true; + break; + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: // Nothing doing + case LayoutInfo::VSplitter: + case LayoutInfo::UnknownLayout: + break; + } + return false; +} + +void MorphLayoutCommand::redo() +{ + m_breakLayoutCommand->redo(); + m_layoutCommand->redo(); + /* Transfer applicable properties which is a cross-section of the modified + * properties except object name. */ + if (const LayoutProperties *properties = m_breakLayoutCommand->layoutProperties()) { + const int oldMask = m_breakLayoutCommand->propertyMask(); + QLayout *newLayout = LayoutInfo::managedLayout(core(), m_layoutBase); + const int newMask = LayoutProperties::visibleProperties(newLayout); + const int applicableMask = (oldMask & newMask) & ~LayoutProperties::ObjectNameProperty; + if (applicableMask) + properties->toPropertySheet(core(), newLayout, applicableMask); + } +} + +void MorphLayoutCommand::undo() +{ + m_layoutCommand->undo(); + m_breakLayoutCommand->undo(); +} + +QString MorphLayoutCommand::formatDescription(QDesignerFormEditorInterface * /* core*/, const QWidget *w, int oldType, int newType) +{ + const QString oldName = LayoutInfo::layoutName(static_cast(oldType)); + const QString newName = LayoutInfo::layoutName(static_cast(newType)); + const QString widgetName = qobject_cast(w) ? w->layout()->objectName() : w->objectName(); + return QApplication::translate("Command", "Change layout of '%1' from %2 to %3").arg(widgetName, oldName, newName); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_command2_p.h b/designer/lib/shared/qdesigner_command2_p.h new file mode 100644 index 0000000..f3a00c7 --- /dev/null +++ b/designer/lib/shared/qdesigner_command2_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_COMMAND2_H +#define QDESIGNER_COMMAND2_H + +#include "shared_global_p.h" +#include "qdesigner_formwindowcommand_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class LayoutCommand; +class BreakLayoutCommand; + +/* This command changes the type of a managed layout on a widget (including + * red layouts of type 'QLayoutWidget') into another type, maintaining the + * applicable properties. It does this by chaining BreakLayoutCommand and + * LayoutCommand, parametrizing them not to actually delete/reparent + * QLayoutWidget's. */ + +class QDESIGNER_SHARED_EXPORT MorphLayoutCommand : public QDesignerFormWindowCommand { + Q_DISABLE_COPY(MorphLayoutCommand) +public: + explicit MorphLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MorphLayoutCommand(); + + bool init(QWidget *w, int newType); + + static bool canMorph(const QDesignerFormWindowInterface *formWindow, QWidget *w, int *ptrToCurrentType = 0); + + virtual void redo(); + virtual void undo(); + +private: + static QString formatDescription(QDesignerFormEditorInterface *core, const QWidget *w, int oldType, int newType); + + BreakLayoutCommand *m_breakLayoutCommand; + LayoutCommand *m_layoutCommand; + int m_newType; + QWidgetList m_widgets; + QWidget *m_layoutBase; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND2_H diff --git a/designer/lib/shared/qdesigner_command_p.h b/designer/lib/shared/qdesigner_command_p.h new file mode 100644 index 0000000..de9c5b0 --- /dev/null +++ b/designer/lib/shared/qdesigner_command_p.h @@ -0,0 +1,1136 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_COMMAND_H +#define QDESIGNER_COMMAND_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_formwindowcommand_p.h" +#include "qdesigner_formeditorcommand_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerContainerExtension; +class QDesignerMetaDataBaseItemInterface; +class QDesignerMenu; + +class QMenuBar; +class QStatusBar; +class QToolBar; +class QToolBox; +class QTabWidget; +class QTableWidget; +class QTableWidgetItem; +class QTreeWidget; +class QTreeWidgetItem; +class QListWidget; +class QListWidgetItem; +class QComboBox; +class QStackedWidget; +class QDockWidget; +class QMainWindow; +class QFormLayout; + +namespace qdesigner_internal { + +class Layout; +class LayoutHelper; +class PropertySheetIconValue; +class DesignerIconCache; +struct LayoutProperties; + +class QDESIGNER_SHARED_EXPORT InsertWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit InsertWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~InsertWidgetCommand(); + + void init(QWidget *widget, bool already_in_form = false, int layoutRow = -1, int layoutColumn = -1); + + virtual void redo(); + virtual void undo(); + +private: + void refreshBuddyLabels(); + + QPointer m_widget; + QDesignerLayoutDecorationExtension::InsertMode m_insertMode; + QPair m_cell; + LayoutHelper* m_layoutHelper; + bool m_widgetWasManaged; +}; + +class QDESIGNER_SHARED_EXPORT ChangeZOrderCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeZOrderCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + + virtual void redo(); + virtual void undo(); +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const = 0; + virtual void reorder(QWidget *widget) const = 0; + +private: + QPointer m_widget; + QPointer m_oldPreceding; + QList m_oldParentZOrder; +}; + +class QDESIGNER_SHARED_EXPORT RaiseWidgetCommand: public ChangeZOrderCommand +{ + +public: + explicit RaiseWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const; + virtual void reorder(QWidget *widget) const; +}; + +class QDESIGNER_SHARED_EXPORT LowerWidgetCommand: public ChangeZOrderCommand +{ + +public: + explicit LowerWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const; + virtual void reorder(QWidget *widget) const; +}; + +class QDESIGNER_SHARED_EXPORT AdjustWidgetSizeCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AdjustWidgetSizeCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + + virtual void redo(); + virtual void undo(); + +private: + QWidget *widgetForAdjust() const; + bool adjustNonLaidOutMainContainer(QWidget *integrationContainer); + void updatePropertyEditor() const; + + QPointer m_widget; + QRect m_geometry; +}; + +// Helper to correctly unmanage a widget and its children for delete operations +class QDESIGNER_SHARED_EXPORT ManageWidgetCommandHelper { +public: + typedef QVector WidgetVector; + + ManageWidgetCommandHelper(); + void init(const QDesignerFormWindowInterface *fw, QWidget *widget); + void init(QWidget *widget, const WidgetVector &managedChildren); + + void manage(QDesignerFormWindowInterface *fw); + void unmanage(QDesignerFormWindowInterface *fw); + + const WidgetVector &managedChildren() const { return m_managedChildren; } +private: + QWidget *m_widget; + WidgetVector m_managedChildren; +}; + +class QDESIGNER_SHARED_EXPORT DeleteWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~DeleteWidgetCommand(); + + enum DeleteFlags { DoNotUnmanage = 0x1, DoNotSimplifyLayout = 0x2 }; + + void init(QWidget *widget, unsigned flags = 0); + + virtual void redo(); + virtual void undo(); + +private: + QPointer m_widget; + QPointer m_parentWidget; + QRect m_geometry; + LayoutInfo::Type m_layoutType; + LayoutHelper* m_layoutHelper; + unsigned m_flags; + QRect m_layoutPosition; + int m_splitterIndex; + bool m_layoutSimplified; + QDesignerMetaDataBaseItemInterface *m_formItem; + int m_tabOrderIndex; + int m_widgetOrderIndex; + int m_zOrderIndex; + ManageWidgetCommandHelper m_manageHelper; +}; + +class QDESIGNER_SHARED_EXPORT ReparentWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ReparentWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, QWidget *parentWidget); + + virtual void redo(); + virtual void undo(); + +private: + QPointer m_widget; + QPoint m_oldPos; + QPoint m_newPos; + QPointer m_oldParentWidget; + QPointer m_newParentWidget; + QList m_oldParentList; + QList m_oldParentZOrder; +}; + +class QDESIGNER_SHARED_EXPORT ChangeFormLayoutItemRoleCommand : public QDesignerFormWindowCommand +{ +public: + enum Operation { SpanningToLabel = 0x1, SpanningToField = 0x2, LabelToSpanning = 0x4, FieldToSpanning =0x8 }; + + explicit ChangeFormLayoutItemRoleCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, Operation op); + + virtual void redo(); + virtual void undo(); + + // Return a mask of possible operations of that item + static unsigned possibleOperations(QDesignerFormEditorInterface *core, QWidget *w); + +private: + static QFormLayout *managedFormLayoutOf(QDesignerFormEditorInterface *core, QWidget *w); + static Operation reverseOperation(Operation op); + void doOperation(Operation op); + + QPointer m_widget; + Operation m_operation; +}; + +class QDESIGNER_SHARED_EXPORT ChangeLayoutItemGeometry: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeLayoutItemGeometry(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, int row, int column, int rowspan, int colspan); + + virtual void redo(); + virtual void undo(); + +protected: + void changeItemPosition(const QRect &g); + +private: + QPointer m_widget; + QRect m_oldInfo; + QRect m_newInfo; +}; + +class QDESIGNER_SHARED_EXPORT TabOrderCommand: public QDesignerFormWindowCommand +{ + +public: + explicit TabOrderCommand(QDesignerFormWindowInterface *formWindow); + + void init(const QList &newTabOrder); + + inline QList oldTabOrder() const + { return m_oldTabOrder; } + + inline QList newTabOrder() const + { return m_newTabOrder; } + + virtual void redo(); + virtual void undo(); + +private: + QDesignerMetaDataBaseItemInterface *m_widgetItem; + QList m_oldTabOrder; + QList m_newTabOrder; +}; + +class QDESIGNER_SHARED_EXPORT PromoteToCustomWidgetCommand : public QDesignerFormWindowCommand +{ +public: + typedef QList > WidgetList; + + explicit PromoteToCustomWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(const WidgetList &widgets, const QString &customClassName); + virtual void redo(); + virtual void undo(); + +private: + void updateSelection(); + WidgetList m_widgets; + QString m_customClassName; +}; + +class QDESIGNER_SHARED_EXPORT DemoteFromCustomWidgetCommand : public QDesignerFormWindowCommand +{ +public: + typedef PromoteToCustomWidgetCommand::WidgetList WidgetList; + + explicit DemoteFromCustomWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(const WidgetList &promoted); + virtual void redo(); + virtual void undo(); +private: + PromoteToCustomWidgetCommand m_promote_cmd; +}; + +// Mixin class for storing the selection state +class QDESIGNER_SHARED_EXPORT CursorSelectionState { + Q_DISABLE_COPY(CursorSelectionState) +public: + CursorSelectionState(); + + void save(const QDesignerFormWindowInterface *formWindow); + void restore(QDesignerFormWindowInterface *formWindow) const; + +private: + typedef QList > WidgetPointerList; + WidgetPointerList m_selection; + QPointer m_current; +}; + +class QDESIGNER_SHARED_EXPORT LayoutCommand: public QDesignerFormWindowCommand +{ + +public: + explicit LayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~LayoutCommand(); + + inline QWidgetList widgets() const { return m_widgets; } + + void init(QWidget *parentWidget, const QWidgetList &widgets, LayoutInfo::Type layoutType, + QWidget *layoutBase = 0, + // Reparent/Hide instances of QLayoutWidget. + bool reparentLayoutWidget = true); + + virtual void redo(); + virtual void undo(); + +private: + QPointer m_parentWidget; + QWidgetList m_widgets; + QPointer m_layoutBase; + QPointer m_layout; + CursorSelectionState m_cursorSelectionState; + bool m_setup; +}; + +class QDESIGNER_SHARED_EXPORT BreakLayoutCommand: public QDesignerFormWindowCommand +{ + +public: + explicit BreakLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~BreakLayoutCommand(); + + inline QWidgetList widgets() const { return m_widgets; } + + void init(const QWidgetList &widgets, QWidget *layoutBase, + // Reparent/Hide instances of QLayoutWidget. + bool reparentLayoutWidget = true); + + virtual void redo(); + virtual void undo(); + + // Access the properties of the layout, 0 in case of splitters. + const LayoutProperties *layoutProperties() const; + int propertyMask() const; + +private: + QWidgetList m_widgets; + QPointer m_layoutBase; + QPointer m_layout; + LayoutHelper* m_layoutHelper; + LayoutProperties *m_properties; + int m_propertyMask; + CursorSelectionState m_cursorSelectionState; +}; + +class QDESIGNER_SHARED_EXPORT SimplifyLayoutCommand: public QDesignerFormWindowCommand +{ +public: + explicit SimplifyLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~SimplifyLayoutCommand(); + + bool init(QWidget *layoutBase); + + // Quick check + static bool canSimplify(QDesignerFormEditorInterface *core, const QWidget *w, int *layoutType = 0); + + virtual void redo(); + virtual void undo(); + +private: + const QRect m_area; + QWidget *m_layoutBase; + LayoutHelper* m_layoutHelper; + bool m_layoutSimplified; +}; + +class QDESIGNER_SHARED_EXPORT ToolBoxCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ToolBoxCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ToolBoxCommand(); + + void init(QToolBox *toolBox); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer m_toolBox; + QPointer m_widget; + int m_index; + QString m_itemText; + QIcon m_itemIcon; +}; + +class QDESIGNER_SHARED_EXPORT MoveToolBoxPageCommand: public ToolBoxCommand +{ + +public: + explicit MoveToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveToolBoxPageCommand(); + + void init(QToolBox *toolBox, QWidget *page, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; +}; + +class QDESIGNER_SHARED_EXPORT DeleteToolBoxPageCommand: public ToolBoxCommand +{ + +public: + explicit DeleteToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteToolBoxPageCommand(); + + void init(QToolBox *toolBox); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddToolBoxPageCommand: public ToolBoxCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddToolBoxPageCommand(); + + void init(QToolBox *toolBox); + void init(QToolBox *toolBox, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT TabWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit TabWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~TabWidgetCommand(); + + void init(QTabWidget *tabWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer m_tabWidget; + QPointer m_widget; + int m_index; + QString m_itemText; + QIcon m_itemIcon; +}; + +class QDESIGNER_SHARED_EXPORT DeleteTabPageCommand: public TabWidgetCommand +{ + +public: + explicit DeleteTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteTabPageCommand(); + + void init(QTabWidget *tabWidget); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddTabPageCommand: public TabWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddTabPageCommand(); + + void init(QTabWidget *tabWidget); + void init(QTabWidget *tabWidget, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT MoveTabPageCommand: public TabWidgetCommand +{ + +public: + explicit MoveTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveTabPageCommand(); + + void init(QTabWidget *tabWidget, QWidget *page, + const QIcon &icon, const QString &label, + int index, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; + QPointer m_page; + QString m_label; + QIcon m_icon; +}; + +class QDESIGNER_SHARED_EXPORT StackedWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit StackedWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~StackedWidgetCommand(); + + void init(QStackedWidget *stackedWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer m_stackedWidget; + QPointer m_widget; + int m_index; +}; + +class QDESIGNER_SHARED_EXPORT MoveStackedWidgetCommand: public StackedWidgetCommand +{ + +public: + explicit MoveStackedWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveStackedWidgetCommand(); + + void init(QStackedWidget *stackedWidget, QWidget *page, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; +}; + +class QDESIGNER_SHARED_EXPORT DeleteStackedWidgetPageCommand: public StackedWidgetCommand +{ + +public: + explicit DeleteStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteStackedWidgetPageCommand(); + + void init(QStackedWidget *stackedWidget); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddStackedWidgetPageCommand: public StackedWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddStackedWidgetPageCommand(); + + void init(QStackedWidget *stackedWidget); + void init(QStackedWidget *stackedWidget, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT CreateMenuBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit CreateMenuBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_menuBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteMenuBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteMenuBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMenuBar *menuBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_menuBar; +}; + +class QDESIGNER_SHARED_EXPORT CreateStatusBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit CreateStatusBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_statusBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteStatusBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteStatusBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QStatusBar *statusBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_statusBar; +}; + +class QDESIGNER_SHARED_EXPORT AddToolBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddToolBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_toolBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteToolBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteToolBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QToolBar *toolBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_toolBar; +}; + +class QDESIGNER_SHARED_EXPORT DockWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DockWidgetCommand(const QString &description, QDesignerFormWindowInterface *formWindow); + virtual ~DockWidgetCommand(); + + void init(QDockWidget *dockWidget); + +protected: + QPointer m_dockWidget; +}; + +class QDESIGNER_SHARED_EXPORT AddDockWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddDockWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow, QDockWidget *dockWidget); + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer m_mainWindow; + QPointer m_dockWidget; +}; + +class QDESIGNER_SHARED_EXPORT ContainerWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ContainerWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ContainerWidgetCommand(); + + QDesignerContainerExtension *containerExtension() const; + + void init(QWidget *containerWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer m_containerWidget; + QPointer m_widget; + int m_index; +}; + +class QDESIGNER_SHARED_EXPORT DeleteContainerWidgetPageCommand: public ContainerWidgetCommand +{ + +public: + explicit DeleteContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteContainerWidgetPageCommand(); + + void init(QWidget *containerWidget, ContainerType ct); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddContainerWidgetPageCommand: public ContainerWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddContainerWidgetPageCommand(); + + void init(QWidget *containerWidget, ContainerType ct, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT ChangeCurrentPageCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeCurrentPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ChangeCurrentPageCommand(); + + QDesignerContainerExtension *containerExtension() const; + + void init(QWidget *containerWidget, int newIndex); + + virtual void redo(); + virtual void undo(); + +protected: + QPointer m_containerWidget; + QPointer m_widget; + int m_oldIndex; + int m_newIndex; +}; + +struct QDESIGNER_SHARED_EXPORT ItemData { + ItemData() {} + + ItemData(const QListWidgetItem *item, bool editor); + ItemData(const QTableWidgetItem *item, bool editor); + ItemData(const QTreeWidgetItem *item, int column); + QListWidgetItem *createListItem(DesignerIconCache *iconCache, bool editor) const; + QTableWidgetItem *createTableItem(DesignerIconCache *iconCache, bool editor) const; + void fillTreeItemColumn(QTreeWidgetItem *item, int column, DesignerIconCache *iconCache) const; + + bool isValid() const { return !m_properties.isEmpty(); } + bool operator==(const ItemData &rhs) const { return m_properties == rhs.m_properties; } + bool operator!=(const ItemData &rhs) const { return m_properties != rhs.m_properties; } + + QHash m_properties; +}; + +struct QDESIGNER_SHARED_EXPORT ListContents { + ListContents() {} + + ListContents(const QTreeWidgetItem *item); + QTreeWidgetItem *createTreeItem(DesignerIconCache *iconCache) const; + + void createFromListWidget(const QListWidget *listWidget, bool editor); + void applyToListWidget(QListWidget *listWidget, DesignerIconCache *iconCache, bool editor) const; + void createFromComboBox(const QComboBox *listWidget); + void applyToComboBox(QComboBox *listWidget, DesignerIconCache *iconCache) const; + + bool operator==(const ListContents &rhs) const { return m_items == rhs.m_items; } + bool operator!=(const ListContents &rhs) const { return m_items != rhs.m_items; } + + QList m_items; +}; + +// Data structure representing the contents of a QTableWidget with +// methods to retrieve and apply for ChangeTableContentsCommand +struct QDESIGNER_SHARED_EXPORT TableWidgetContents { + + typedef QPair CellRowColumnAddress; + typedef QMap TableItemMap; + + TableWidgetContents(); + void clear(); + + void fromTableWidget(const QTableWidget *tableWidget, bool editor); + void applyToTableWidget(QTableWidget *tableWidget, DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const TableWidgetContents &rhs) const; + bool operator!=(const TableWidgetContents &rhs) const { return !(*this == rhs); } + + static bool nonEmpty(const QTableWidgetItem *item, int headerColumn); + static QString defaultHeaderText(int i); + static void insertHeaderItem(const QTableWidgetItem *item, int i, ListContents *header, bool editor); + + int m_columnCount; + int m_rowCount; + ListContents m_horizontalHeader; + ListContents m_verticalHeader; + TableItemMap m_items; +}; + +class QDESIGNER_SHARED_EXPORT ChangeTableContentsCommand: public QDesignerFormWindowCommand +{ +public: + explicit ChangeTableContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QTableWidget *tableWidget, const TableWidgetContents &oldCont, const TableWidgetContents &newCont); + virtual void redo(); + virtual void undo(); + +private: + QPointer m_tableWidget; + TableWidgetContents m_oldContents; + TableWidgetContents m_newContents; + DesignerIconCache *m_iconCache; +}; + +// Data structure representing the contents of a QTreeWidget with +// methods to retrieve and apply for ChangeTreeContentsCommand +struct QDESIGNER_SHARED_EXPORT TreeWidgetContents { + + struct ItemContents : public ListContents { + ItemContents() : m_itemFlags(-1) {} + ItemContents(const QTreeWidgetItem *item, bool editor); + QTreeWidgetItem *createTreeItem(DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const ItemContents &rhs) const; + bool operator!=(const ItemContents &rhs) const { return !(*this == rhs); } + + int m_itemFlags; + //bool m_firstColumnSpanned:1; + //bool m_hidden:1; + //bool m_expanded:1; + QList m_children; + }; + + void clear(); + + void fromTreeWidget(const QTreeWidget *treeWidget, bool editor); + void applyToTreeWidget(QTreeWidget *treeWidget, DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const TreeWidgetContents &rhs) const; + bool operator!=(const TreeWidgetContents &rhs) const { return !(*this == rhs); } + + ListContents m_headerItem; + QList m_rootItems; +}; + +class QDESIGNER_SHARED_EXPORT ChangeTreeContentsCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeTreeContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QTreeWidget *treeWidget, const TreeWidgetContents &oldState, const TreeWidgetContents &newState); + virtual void redo(); + virtual void undo(); + enum ApplyIconStrategy { + SetIconStrategy, + ResetIconStrategy + }; +private: + QPointer m_treeWidget; + TreeWidgetContents m_oldState; + TreeWidgetContents m_newState; + DesignerIconCache *m_iconCache; +}; + +class QDESIGNER_SHARED_EXPORT ChangeListContentsCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeListContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QListWidget *listWidget, const ListContents &oldItems, const ListContents &items); + void init(QComboBox *comboBox, const ListContents &oldItems, const ListContents &items); + virtual void redo(); + virtual void undo(); +private: + QPointer m_listWidget; + QPointer m_comboBox; + ListContents m_oldItemsState; + ListContents m_newItemsState; + DesignerIconCache *m_iconCache; +}; + +class QDESIGNER_SHARED_EXPORT AddActionCommand : public QDesignerFormWindowCommand +{ + +public: + explicit AddActionCommand(QDesignerFormWindowInterface *formWindow); + void init(QAction *action); + virtual void redo(); + virtual void undo(); +private: + QAction *m_action; +}; + +// Note: This command must be executed within a macro since it +// makes the form emit objectRemoved() which might cause other components +// to add commands (for example, removal of signals and slots +class QDESIGNER_SHARED_EXPORT RemoveActionCommand : public QDesignerFormWindowCommand +{ + +public: + explicit RemoveActionCommand(QDesignerFormWindowInterface *formWindow); + void init(QAction *action); + virtual void redo(); + virtual void undo(); + + struct ActionDataItem { + ActionDataItem(QAction *_before = 0, QWidget *_widget = 0) + : before(_before), widget(_widget) {} + QAction *before; + QWidget *widget; + }; + typedef QList ActionData; + +private: + QAction *m_action; + + ActionData m_actionData; +}; + +class QDESIGNER_SHARED_EXPORT ActionInsertionCommand : public QDesignerFormWindowCommand +{ + +protected: + ActionInsertionCommand(const QString &text, QDesignerFormWindowInterface *formWindow); + +public: + void init(QWidget *parentWidget, QAction *action, QAction *beforeAction = 0, bool update = true); + +protected: + void insertAction(); + void removeAction(); + +private: + QWidget *m_parentWidget; + QAction *m_action; + QAction *m_beforeAction; + bool m_update; +}; + +class QDESIGNER_SHARED_EXPORT InsertActionIntoCommand : public ActionInsertionCommand +{ + +public: + explicit InsertActionIntoCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { insertAction(); } + virtual void undo() { removeAction(); } +}; + +class QDESIGNER_SHARED_EXPORT RemoveActionFromCommand : public ActionInsertionCommand +{ + +public: + explicit RemoveActionFromCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { removeAction(); } + virtual void undo() { insertAction(); } +}; + +class QDESIGNER_SHARED_EXPORT MenuActionCommand : public QDesignerFormWindowCommand +{ +public: + void init(QAction *action, QAction *actionBefore, QWidget *associatedWidget, QWidget *objectToSelect); + +protected: + MenuActionCommand(const QString &text, QDesignerFormWindowInterface *formWindow); + void insertMenu(); + void removeMenu(); + +private: + QAction *m_action; + QAction *m_actionBefore; + QWidget *m_menuParent; + QWidget *m_associatedWidget; + QWidget *m_objectToSelect; +}; + +class QDESIGNER_SHARED_EXPORT AddMenuActionCommand : public MenuActionCommand +{ + +public: + explicit AddMenuActionCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { insertMenu(); } + virtual void undo() { removeMenu(); } +}; + +class QDESIGNER_SHARED_EXPORT RemoveMenuActionCommand : public MenuActionCommand +{ + +public: + explicit RemoveMenuActionCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { removeMenu(); } + virtual void undo() { insertMenu(); } +}; + +class QDESIGNER_SHARED_EXPORT CreateSubmenuCommand : public QDesignerFormWindowCommand +{ + +public: + explicit CreateSubmenuCommand(QDesignerFormWindowInterface *formWindow); + void init(QDesignerMenu *menu, QAction *action, QObject *m_objectToSelect = 0); + virtual void redo(); + virtual void undo(); +private: + QAction *m_action; + QDesignerMenu *m_menu; + QObject *m_objectToSelect; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND_H diff --git a/designer/lib/shared/qdesigner_dnditem.cpp b/designer/lib/shared/qdesigner_dnditem.cpp new file mode 100644 index 0000000..1199be5 --- /dev/null +++ b/designer/lib/shared/qdesigner_dnditem.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_dnditem_p.h" +#include "formwindowbase_p.h" +#include "ui4_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerDnDItem::QDesignerDnDItem(DropType type, QWidget *source) : + m_source(source), + m_type(type), + m_dom_ui(0), + m_widget(0), + m_decoration(0) +{ +} + +void QDesignerDnDItem::init(DomUI *ui, QWidget *widget, QWidget *decoration, + const QPoint &global_mouse_pos) +{ + Q_ASSERT(widget != 0 || ui != 0); + Q_ASSERT(decoration != 0); + + m_dom_ui = ui; + m_widget = widget; + m_decoration = decoration; + + const QRect geometry = m_decoration->geometry(); + m_hot_spot = global_mouse_pos - m_decoration->geometry().topLeft(); +} + +QDesignerDnDItem::~QDesignerDnDItem() +{ + if (m_decoration != 0) + m_decoration->deleteLater(); + delete m_dom_ui; +} + +DomUI *QDesignerDnDItem::domUi() const +{ + return m_dom_ui; +} + +QWidget *QDesignerDnDItem::decoration() const +{ + return m_decoration; +} + +QPoint QDesignerDnDItem::hotSpot() const +{ + return m_hot_spot; +} + +QWidget *QDesignerDnDItem::widget() const +{ + return m_widget; +} + +QDesignerDnDItem::DropType QDesignerDnDItem::type() const +{ + return m_type; +} + +QWidget *QDesignerDnDItem::source() const +{ + return m_source; +} + +void QDesignerDnDItem::setDomUi(DomUI *dom_ui) +{ + delete m_dom_ui; + m_dom_ui = dom_ui; +} + +// ---------- QDesignerMimeData + +// Make pixmap transparent on Windows only. Mac is transparent by default, Unix usually does not work. +#ifdef Q_WS_WIN +# define TRANSPARENT_DRAG_PIXMAP +#endif + +QDesignerMimeData::QDesignerMimeData(const QDesignerDnDItems &items, QDrag *drag) : + m_items(items) +{ + enum { Alpha = 200 }; + QPoint decorationTopLeft; + switch (m_items.size()) { + case 0: + break; + case 1: { + QWidget *deco = m_items.first()->decoration(); + decorationTopLeft = deco->pos(); + const QPixmap widgetPixmap = QPixmap::grabWidget(deco); +#ifdef TRANSPARENT_DRAG_PIXMAP + QImage image(widgetPixmap.size(), QImage::Format_ARGB32); + image.fill(QColor(Qt::transparent).rgba()); + QPainter painter(&image); + painter.drawPixmap(QPoint(0, 0), widgetPixmap); + painter.end(); + setImageTransparency(image, Alpha); + drag->setPixmap(QPixmap::fromImage(image)); +#else + drag->setPixmap(widgetPixmap); +#endif + } + break; + default: { + // determine size of drag decoration by uniting all geometries + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + QDesignerDnDItems::const_iterator it =m_items.constBegin(); + QRect unitedGeometry = (*it)->decoration()->geometry(); + for (++it; it != cend; ++it ) + unitedGeometry = unitedGeometry .united((*it)->decoration()->geometry()); + + // paint with offset. At the same time, create a mask bitmap, containing widget rectangles. + QImage image(unitedGeometry.size(), QImage::Format_ARGB32); + image.fill(QColor(Qt::transparent).rgba()); + QBitmap mask(unitedGeometry.size()); + mask.clear(); + // paint with offset, determine action + QPainter painter(&image); + QPainter maskPainter(&mask); + decorationTopLeft = unitedGeometry.topLeft(); + for (it = m_items.constBegin() ; it != cend; ++it ) { + QWidget *w = (*it)->decoration(); + const QPixmap wp = QPixmap::grabWidget(w); + const QPoint pos = w->pos() - decorationTopLeft; + painter.drawPixmap(pos, wp); + maskPainter.fillRect(QRect(pos, wp.size()), Qt::color1); + } + painter.end(); + maskPainter.end(); +#ifdef TRANSPARENT_DRAG_PIXMAP + setImageTransparency(image, Alpha); +#endif + QPixmap pixmap = QPixmap::fromImage(image); + pixmap.setMask(mask); + drag->setPixmap(pixmap); + } + break; + } + // determine hot spot and reconstruct the exact starting position as form window + // introduces some offset when detecting DnD + m_globalStartPos = m_items.first()->decoration()->pos() + m_items.first()->hotSpot(); + m_hotSpot = m_globalStartPos - decorationTopLeft; + drag->setHotSpot(m_hotSpot); + + drag->setMimeData(this); +} + +QDesignerMimeData::~QDesignerMimeData() +{ + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + for (QDesignerDnDItems::const_iterator it = m_items.constBegin(); it != cend; ++it ) + delete *it; +} + +Qt::DropAction QDesignerMimeData::proposedDropAction() const +{ + return m_items.first()->type() == QDesignerDnDItemInterface::CopyDrop ? Qt::CopyAction : Qt::MoveAction; +} + +Qt::DropAction QDesignerMimeData::execDrag(const QDesignerDnDItems &items, QWidget * dragSource) +{ + if (items.empty()) + return Qt::IgnoreAction; + + QDrag *drag = new QDrag(dragSource); + QDesignerMimeData *mimeData = new QDesignerMimeData(items, drag); + + // Store pointers to widgets that are to be re-shown if a move operation is canceled + QWidgetList reshowWidgets; + const QDesignerDnDItems::const_iterator cend = items.constEnd(); + for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it ) + if (QWidget *w = (*it)->widget()) + if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop) + reshowWidgets.push_back(w); + + const Qt::DropAction executedAction = drag->exec(Qt::CopyAction|Qt::MoveAction, mimeData->proposedDropAction()); + + if (executedAction == Qt::IgnoreAction && !reshowWidgets.empty()) + foreach (QWidget *w, reshowWidgets) + w->show(); + + return executedAction; +} + + +void QDesignerMimeData::moveDecoration(const QPoint &globalPos) const +{ + const QPoint relativeDistance = globalPos - m_globalStartPos; + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + for (QDesignerDnDItems::const_iterator it =m_items.constBegin(); it != cend; ++it ) { + QWidget *w = (*it)->decoration(); + w->move(w->pos() + relativeDistance); + } +} + +void QDesignerMimeData::removeMovedWidgetsFromSourceForm(const QDesignerDnDItems &items) +{ + typedef QMultiMap FormWidgetMap; + FormWidgetMap formWidgetMap; + // Find moved widgets per form + const QDesignerDnDItems::const_iterator cend = items.constEnd(); + for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it ) + if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop) + if (QWidget *w = (*it)->widget()) + if (FormWindowBase *fb = qobject_cast((*it)->source())) + formWidgetMap.insert(fb, w); + if (formWidgetMap.empty()) + return; + + foreach (FormWindowBase * fb, formWidgetMap.keys()) + fb->deleteWidgetList(formWidgetMap.values(fb)); +} + +void QDesignerMimeData::acceptEventWithAction(Qt::DropAction desiredAction, QDropEvent *e) +{ + if (e->proposedAction() == desiredAction) { + e->acceptProposedAction(); + } else { + e->setDropAction(desiredAction); + e->accept(); + } +} + +void QDesignerMimeData::acceptEvent(QDropEvent *e) const +{ + acceptEventWithAction(proposedDropAction(), e); +} + +void QDesignerMimeData::setImageTransparency(QImage &image, int alpha) +{ + const int height = image.height(); + for (int l = 0; l < height; l++) { + QRgb *line = reinterpret_cast(image.scanLine(l)); + QRgb *lineEnd = line + image.width(); + for ( ; line < lineEnd; line++) { + const QRgb rgba = *line; + *line = qRgba(qRed(rgba), qGreen(rgba), qBlue(rgba), alpha); + } + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_dnditem_p.h b/designer/lib/shared/qdesigner_dnditem_p.h new file mode 100644 index 0000000..e394fe4 --- /dev/null +++ b/designer/lib/shared/qdesigner_dnditem_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_DNDITEM_H +#define QDESIGNER_DNDITEM_H + +#include "shared_global_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDrag; +class QImage; +class QDropEvent; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerDnDItem: public QDesignerDnDItemInterface +{ +public: + explicit QDesignerDnDItem(DropType type, QWidget *source = 0); + virtual ~QDesignerDnDItem(); + + virtual DomUI *domUi() const; + virtual QWidget *decoration() const; + virtual QWidget *widget() const; + virtual QPoint hotSpot() const; + virtual QWidget *source() const; + + virtual DropType type() const; + +protected: + void setDomUi(DomUI *dom_ui); + void init(DomUI *ui, QWidget *widget, QWidget *decoration, const QPoint &global_mouse_pos); + +private: + QWidget *m_source; + const DropType m_type; + const QPoint m_globalStartPos; + DomUI *m_dom_ui; + QWidget *m_widget; + QWidget *m_decoration; + QPoint m_hot_spot; + + Q_DISABLE_COPY(QDesignerDnDItem) +}; + +// Mime data for use with designer drag and drop operations. + +class QDESIGNER_SHARED_EXPORT QDesignerMimeData : public QMimeData { + Q_OBJECT + +public: + typedef QList QDesignerDnDItems; + + virtual ~QDesignerMimeData(); + + const QDesignerDnDItems &items() const { return m_items; } + + // Execute a drag and drop operation. + static Qt::DropAction execDrag(const QDesignerDnDItems &items, QWidget * dragSource); + + QPoint hotSpot() const { return m_hotSpot; } + + // Move the decoration. Required for drops over form windows as the position + // is derived from the decoration position. + void moveDecoration(const QPoint &globalPos) const; + + // For a move operation, create the undo command sequence to remove + // the widgets from the source form. + static void removeMovedWidgetsFromSourceForm(const QDesignerDnDItems &items); + + // Accept an event with the proper action. + void acceptEvent(QDropEvent *e) const; + + // Helper to accept an event with the desired action. + static void acceptEventWithAction(Qt::DropAction desiredAction, QDropEvent *e); + +private: + QDesignerMimeData(const QDesignerDnDItems &items, QDrag *drag); + Qt::DropAction proposedDropAction() const; + + static void setImageTransparency(QImage &image, int alpha); + + const QDesignerDnDItems m_items; + QPoint m_globalStartPos; + QPoint m_hotSpot; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_DNDITEM_H diff --git a/designer/lib/shared/qdesigner_dockwidget.cpp b/designer/lib/shared/qdesigner_dockwidget.cpp new file mode 100644 index 0000000..e9a55fc --- /dev/null +++ b/designer/lib/shared/qdesigner_dockwidget.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_dockwidget_p.h" +#include "layoutinfo_p.h" + +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QDesignerDockWidget::QDesignerDockWidget(QWidget *parent) + : QDockWidget(parent) +{ +} + +QDesignerDockWidget::~QDesignerDockWidget() +{ +} + +bool QDesignerDockWidget::docked() const +{ + return qobject_cast(parentWidget()) != 0; +} + +void QDesignerDockWidget::setDocked(bool b) +{ + if (QMainWindow *mainWindow = findMainWindow()) { + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension(core->extensionManager(), mainWindow); + if (b && !docked()) { + // Dock it + // ### undo/redo stack + setParent(0); + c->addWidget(this); + formWindow()->selectWidget(this, formWindow()->cursor()->isWidgetSelected(this)); + } else if (!b && docked()) { + // Undock it + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == this) { + c->remove(i); + break; + } + } + // #### restore the position + setParent(mainWindow->centralWidget()); + show(); + formWindow()->selectWidget(this, formWindow()->cursor()->isWidgetSelected(this)); + } + } +} + +Qt::DockWidgetArea QDesignerDockWidget::dockWidgetArea() const +{ + if (QMainWindow *mainWindow = qobject_cast(parentWidget())) + return mainWindow->dockWidgetArea(const_cast(this)); + + return Qt::LeftDockWidgetArea; +} + +void QDesignerDockWidget::setDockWidgetArea(Qt::DockWidgetArea dockWidgetArea) +{ + if (QMainWindow *mainWindow = qobject_cast(parentWidget())) { + if ((dockWidgetArea != Qt::NoDockWidgetArea) + && isAreaAllowed(dockWidgetArea)) { + mainWindow->addDockWidget(dockWidgetArea, this); + } + } +} + +bool QDesignerDockWidget::inMainWindow() const +{ + QMainWindow *mw = findMainWindow(); + if (mw && !mw->centralWidget()->layout()) { + if (mw == parentWidget()) + return true; + if (mw->centralWidget() == parentWidget()) + return true; + } + return false; +} + +QDesignerFormWindowInterface *QDesignerDockWidget::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast(this)); +} + +QMainWindow *QDesignerDockWidget::findMainWindow() const +{ + if (QDesignerFormWindowInterface *fw = formWindow()) + return qobject_cast(fw->mainContainer()); + return 0; +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_dockwidget_p.h b/designer/lib/shared/qdesigner_dockwidget_p.h new file mode 100644 index 0000000..6538500 --- /dev/null +++ b/designer/lib/shared/qdesigner_dockwidget_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_DOCKWIDGET_H +#define QDESIGNER_DOCKWIDGET_H + +#include "shared_global_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QDESIGNER_SHARED_EXPORT QDesignerDockWidget: public QDockWidget +{ + Q_OBJECT + Q_PROPERTY(Qt::DockWidgetArea dockWidgetArea READ dockWidgetArea WRITE setDockWidgetArea DESIGNABLE docked STORED false) + Q_PROPERTY(bool docked READ docked WRITE setDocked DESIGNABLE inMainWindow STORED false) +public: + QDesignerDockWidget(QWidget *parent = 0); + virtual ~QDesignerDockWidget(); + + bool docked() const; + void setDocked(bool b); + + Qt::DockWidgetArea dockWidgetArea() const; + void setDockWidgetArea(Qt::DockWidgetArea dockWidgetArea); + + bool inMainWindow() const; + +private: + QDesignerFormWindowInterface *formWindow() const; + QMainWindow *findMainWindow() const; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_DOCKWIDGET_H diff --git a/designer/lib/shared/qdesigner_formbuilder.cpp b/designer/lib/shared/qdesigner_formbuilder.cpp new file mode 100644 index 0000000..2ef50b0 --- /dev/null +++ b/designer/lib/shared/qdesigner_formbuilder.cpp @@ -0,0 +1,498 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_formbuilder_p.h" +#include "dynamicpropertysheet.h" +#include "qsimpleresource_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_introspection_p.h" + +#include +#include +// sdk +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// shared +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_FORMBUILDER_NO_SCRIPT +static QString summarizeScriptErrors(const QFormScriptRunner::Errors &errors) +{ + QString rc = QCoreApplication::translate("QDesignerFormBuilder", "Script errors occurred:"); + foreach (QFormScriptRunner::Error error, errors) { + rc += QLatin1Char('\n'); + rc += error.errorMessage; + } + return rc; +} +#endif + +namespace qdesigner_internal { + +QDesignerFormBuilder::QDesignerFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile) : + m_core(core), + m_mode(mode), + m_deviceProfile(deviceProfile), + m_pixmapCache(0), + m_iconCache(0), + m_ignoreCreateResources(false), + m_tempResourceSet(0), + m_mainWidget(true) +{ + Q_ASSERT(m_core); +#ifndef QT_FORMBUILDER_NO_SCRIPT + // Disable scripting in the editors. + QFormScriptRunner::Options options = formScriptRunner()->options(); + switch (m_mode) { + case DisableScripts: + options |= QFormScriptRunner::DisableScripts; + break; + case EnableScripts: + options |= QFormScriptRunner::DisableWarnings; + options &= ~QFormScriptRunner::DisableScripts; + break; + } + formScriptRunner()-> setOptions(options); +#endif +} + +QString QDesignerFormBuilder::systemStyle() const +{ + return m_deviceProfile.isEmpty() ? + QString::fromUtf8(QApplication::style()->metaObject()->className()) : + m_deviceProfile.style(); +} + +QWidget *QDesignerFormBuilder::createWidgetFromContents(const QString &contents, QWidget *parentWidget) +{ + QByteArray data = contents.toUtf8(); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + return load(&buffer, parentWidget); +} + +QWidget *QDesignerFormBuilder::create(DomUI *ui, QWidget *parentWidget) +{ + m_mainWidget = true; + QtResourceSet *resourceSet = core()->resourceModel()->currentResourceSet(); + + // reload resource properties; + createResources(ui->elementResources()); + core()->resourceModel()->setCurrentResourceSet(m_tempResourceSet); + + m_ignoreCreateResources = true; + DesignerPixmapCache pixmapCache; + DesignerIconCache iconCache(&pixmapCache); + m_pixmapCache = &pixmapCache; + m_iconCache = &iconCache; + + QWidget *widget = QFormBuilder::create(ui, parentWidget); + + core()->resourceModel()->setCurrentResourceSet(resourceSet); + core()->resourceModel()->removeResourceSet(m_tempResourceSet); + m_tempResourceSet = 0; + m_ignoreCreateResources = false; + m_pixmapCache = 0; + m_iconCache = 0; + + m_customWidgetsWithScript.clear(); + return widget; +} + +QWidget *QDesignerFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + QWidget *widget = 0; + + if (widgetName == QLatin1String("QToolBar")) { + widget = new QToolBar(parentWidget); + } else if (widgetName == QLatin1String("QMenu")) { + widget = new QMenu(parentWidget); + } else if (widgetName == QLatin1String("QMenuBar")) { + widget = new QMenuBar(parentWidget); + } else { + widget = core()->widgetFactory()->createWidget(widgetName, parentWidget); + } + + if (widget) { + widget->setObjectName(name); + if (QSimpleResource::hasCustomWidgetScript(m_core, widget)) + m_customWidgetsWithScript.insert(widget); + } + + if (m_mainWidget) { // We need to apply the DPI here to take effect on size hints, etc. + m_deviceProfile.apply(m_core, widget, DeviceProfile::ApplyPreview); + m_mainWidget = false; + } + return widget; +} + +bool QDesignerFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + // Use container extension or rely on scripts unless main window. + if (QFormBuilder::addItem(ui_widget, widget, parentWidget)) + return true; + + if (QDesignerContainerExtension *container = qt_extension(m_core->extensionManager(), parentWidget)) { + container->addWidget(widget); + return true; + } + return false; +} + +bool QDesignerFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + return QFormBuilder::addItem(ui_item, item, layout); +} + +QIcon QDesignerFormBuilder::nameToIcon(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QDesignerFormBuilder::nameToIcon() is obsoleted"; + return QIcon(); +} + +QPixmap QDesignerFormBuilder::nameToPixmap(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QDesignerFormBuilder::nameToPixmap() is obsoleted"; + return QPixmap(); +} + +/* If the property is a enum or flag value, retrieve + * the existing enum/flag type via property sheet and use it to convert */ + +static bool readDomEnumerationValue(const DomProperty *p, + const QDesignerPropertySheetExtension* sheet, + QVariant &v) +{ + switch (p->kind()) { + case DomProperty::Set: { + const int index = sheet->indexOf(p->attributeName()); + if (index == -1) + return false; + const QVariant sheetValue = sheet->property(index); + if (qVariantCanConvert(sheetValue)) { + const PropertySheetFlagValue f = qvariant_cast(sheetValue); + bool ok = false; + v = f.metaFlags.parseFlags(p->elementSet(), &ok); + if (!ok) + designerWarning(f.metaFlags.messageParseFailed(p->elementSet())); + return true; + } + } + break; + case DomProperty::Enum: { + const int index = sheet->indexOf(p->attributeName()); + if (index == -1) + return false; + const QVariant sheetValue = sheet->property(index); + if (qVariantCanConvert(sheetValue)) { + const PropertySheetEnumValue e = qvariant_cast(sheetValue); + bool ok = false; + v = e.metaEnum.parseEnum(p->elementEnum(), &ok); + if (!ok) + designerWarning(e.metaEnum.messageParseFailed(p->elementEnum())); + return true; + } + } + break; + default: + break; + } + return false; +} + +void QDesignerFormBuilder::applyProperties(QObject *o, const QList &properties) +{ + typedef QList DomPropertyList; + + if (properties.empty()) + return; + + QFormBuilderExtra *formBuilderExtra = QFormBuilderExtra::instance(this); + const QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), o); + const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core()->extensionManager(), o); + const bool changingMetaObject = WidgetFactory::classNameOf(core(), o) == QLatin1String("QAxWidget"); + const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(o); + const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed(); + + QDesignerPropertySheet *designerPropertySheet = qobject_cast( + core()->extensionManager()->extension(o, Q_TYPEID(QDesignerPropertySheetExtension))); + + if (designerPropertySheet) { + if (designerPropertySheet->pixmapCache()) + designerPropertySheet->setPixmapCache(m_pixmapCache); + if (designerPropertySheet->iconCache()) + designerPropertySheet->setIconCache(m_iconCache); + } + + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + DomProperty *p = *it; + QVariant v; + if (!readDomEnumerationValue(p, sheet, v)) + v = toVariant(o->metaObject(), p); + + if (v.isNull()) + continue; + + const QString attributeName = p->attributeName(); + if (formBuilderExtra->applyPropertyInternally(o, attributeName, v)) + continue; + + // refuse fake properties like current tab name (weak test) + if (!dynamicPropertiesAllowed) { + if (changingMetaObject) // Changes after setting control of QAxWidget + meta = core()->introspection()->metaObject(o); + if (meta->indexOfProperty(attributeName) == -1) + continue; + } + + QObject *obj = o; + QAbstractScrollArea *scroll = qobject_cast(o); + if (scroll && attributeName == QLatin1String("cursor") && scroll->viewport()) + obj = scroll->viewport(); + + // a real property + obj->setProperty(attributeName.toUtf8(), v); + } +} + +DomWidget *QDesignerFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) +{ + DomWidget *ui_widget = QFormBuilder::createDom(widget, ui_parentWidget, recursive); + QSimpleResource::addExtensionDataToDOM(this, m_core, ui_widget, widget); + return ui_widget; +} + +QWidget *QDesignerFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + QWidget *widget = QFormBuilder::create(ui_widget, parentWidget); + // Do not apply state if scripts are to be run in preview mode + QSimpleResource::applyExtensionDataFromDOM(this, m_core, ui_widget, widget, m_mode == DisableScripts); + return widget; +} + +void QDesignerFormBuilder::createResources(DomResources *resources) +{ + if (m_ignoreCreateResources) + return; + QStringList paths; + if (resources != 0) { + const QList dom_include = resources->elementInclude(); + foreach (DomResource *res, dom_include) { + QString path = QDir::cleanPath(workingDirectory().absoluteFilePath(res->attributeLocation())); + paths << path; + } + } + + m_tempResourceSet = core()->resourceModel()->addResourceSet(paths); +} + +QLayout *QDesignerFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + return QFormBuilder::create(ui_layout, layout, parentWidget); +} + +void QDesignerFormBuilder::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + QFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + const DeviceProfile &deviceProfile, + ScriptErrors *scriptErrors, + QString *errorMessage) +{ + scriptErrors->clear(); + + // load + QDesignerFormBuilder builder(fw->core(), EnableScripts, deviceProfile); + builder.setWorkingDirectory(fw->absoluteDir()); + + const bool warningsEnabled = QSimpleResource::setWarningsEnabled(false); + QByteArray bytes = fw->contents().toUtf8(); + QSimpleResource::setWarningsEnabled(warningsEnabled); + + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadOnly); + + QWidget *widget = builder.load(&buffer, 0); + if (!widget) { // Shouldn't happen + *errorMessage = QCoreApplication::translate("QDesignerFormBuilder", "The preview failed to build."); + return 0; + } + // Make sure palette is applied + const QString styleToUse = styleName.isEmpty() ? builder.deviceProfile().style() : styleName; + if (!styleToUse.isEmpty()) { + if (WidgetFactory *wf = qobject_cast(fw->core()->widgetFactory())) { + if (styleToUse != wf->styleName()) + WidgetFactory::applyStyleToTopLevel(wf->getStyle(styleToUse), widget); + } + } +#ifndef QT_FORMBUILDER_NO_SCRIPT + // Check for script errors + *scriptErrors = builder.formScriptRunner()->errors(); + if (!scriptErrors->empty()) { + *errorMessage = summarizeScriptErrors(*scriptErrors); + delete widget; + return 0; + } +#endif + // Fake application style sheet by prepending. (If this doesn't work, fake by nesting + // into parent widget). + if (!appStyleSheet.isEmpty()) { + QString styleSheet = appStyleSheet; + styleSheet += QLatin1Char('\n'); + styleSheet += widget->styleSheet(); + widget->setStyleSheet(styleSheet); + } + return widget; +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName) +{ + return createPreview(fw, styleName, QString()); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + const DeviceProfile &deviceProfile, + QString *errorMessage) +{ + ScriptErrors scriptErrors; + return createPreview(fw, styleName, appStyleSheet, deviceProfile, &scriptErrors, errorMessage); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + QString *errorMessage) +{ + ScriptErrors scriptErrors; + return createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, errorMessage); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) +{ + ScriptErrors scriptErrors; + QString errorMessage; + QWidget *widget = createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, &errorMessage); + if (!widget) { + // Display Script errors or message box + QWidget *dialogParent = fw->core()->topLevel(); + if (scriptErrors.empty()) { + fw->core()->dialogGui()->message(dialogParent, QDesignerDialogGuiInterface::PreviewFailureMessage, + QMessageBox::Warning, QCoreApplication::translate("QDesignerFormBuilder", "Designer"), errorMessage, QMessageBox::Ok); + } else { + ScriptErrorDialog scriptErrorDialog(scriptErrors, dialogParent); + scriptErrorDialog.exec(); + } + return 0; + } + return widget; +} + +QPixmap QDesignerFormBuilder::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) +{ + QWidget *widget = createPreview(fw, styleName, appStyleSheet); + if (!widget) + return QPixmap(); + + const QPixmap rc = QPixmap::grabWidget (widget); + widget->deleteLater(); + return rc; +} + +// ---------- NewFormWidgetFormBuilder + +NewFormWidgetFormBuilder::NewFormWidgetFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile) : + QDesignerFormBuilder(core, mode, deviceProfile) +{ +} + +void NewFormWidgetFormBuilder::createCustomWidgets(DomCustomWidgets *dc) +{ + QSimpleResource::handleDomCustomWidgets(core(), dc); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_formbuilder_p.h b/designer/lib/shared/qdesigner_formbuilder_p.h new file mode 100644 index 0000000..93e51a7 --- /dev/null +++ b/designer/lib/shared/qdesigner_formbuilder_p.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMBUILDER_H +#define QDESIGNER_FORMBUILDER_H + +#include "shared_global_p.h" +#include "deviceprofile_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QPixmap; +class QtResourceSet; + +namespace qdesigner_internal { + +class DesignerPixmapCache; +class DesignerIconCache; + +/* Form builder used for previewing forms and widget box. + * It applies the system settings to its toplevel window. */ + +class QDESIGNER_SHARED_EXPORT QDesignerFormBuilder: public QFormBuilder +{ +public: + enum Mode { + DisableScripts, + EnableScripts + }; + + QDesignerFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile = DeviceProfile()); + + QWidget *createWidgetFromContents(const QString &contents, QWidget *parentWidget = 0); + + virtual QWidget *createWidget(DomWidget *ui_widget, QWidget *parentWidget = 0) + { return QFormBuilder::create(ui_widget, parentWidget); } + + inline QDesignerFormEditorInterface *core() const + { return m_core; } + + QString systemStyle() const; + + typedef QFormScriptRunner::Errors ScriptErrors; + // Create a preview widget (for integrations) or return 0. The widget has to be embedded into a main window. + // Experimental, depending on script support. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName /* ="" */, + const QString &appStyleSheet /* ="" */, + const DeviceProfile &deviceProfile, + ScriptErrors *scriptErrors, QString *errorMessage); + // Convenience that pops up message boxes in case of failures. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName = QString()); + // Create a preview widget (for integrations) or return 0. The widget has to be embedded into a main window. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet, QString *errorMessage); + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet, const DeviceProfile &deviceProfile, QString *errorMessage); + // Convenience that pops up message boxes in case of failures. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet); + + // Create a preview image + static QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName = QString(), const QString &appStyleSheet = QString()); + +protected: + using QFormBuilder::createDom; + using QFormBuilder::create; + + virtual QWidget *create(DomUI *ui, QWidget *parentWidget); + virtual DomWidget *createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive = true); + virtual QWidget *create(DomWidget *ui_widget, QWidget *parentWidget); + virtual QLayout *create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget); + virtual void createResources(DomResources *resources); + + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + virtual bool addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout); + + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath); + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath); + + virtual void applyProperties(QObject *o, const QList &properties); + + virtual void loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + QtResourceSet *internalResourceSet() const { return m_tempResourceSet; } + + DeviceProfile deviceProfile() const { return m_deviceProfile; } + +private: + QDesignerFormEditorInterface *m_core; + const Mode m_mode; + + typedef QSet WidgetSet; + WidgetSet m_customWidgetsWithScript; + + const DeviceProfile m_deviceProfile; + + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + bool m_ignoreCreateResources; + QtResourceSet *m_tempResourceSet; + bool m_mainWidget; +}; + +// Form builder for a new form widget (preview). To allow for promoted +// widgets in the template, it implements the handling of custom widgets +// (adding of them to the widget database). + +class QDESIGNER_SHARED_EXPORT NewFormWidgetFormBuilder: public QDesignerFormBuilder { +public: + NewFormWidgetFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile = DeviceProfile()); + +protected: + virtual void createCustomWidgets(DomCustomWidgets *); +}; + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMBUILDER_H diff --git a/designer/lib/shared/qdesigner_formeditorcommand.cpp b/designer/lib/shared/qdesigner_formeditorcommand.cpp new file mode 100644 index 0000000..1631df9 --- /dev/null +++ b/designer/lib/shared/qdesigner_formeditorcommand.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qdesigner_formeditorcommand_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---- QDesignerFormEditorCommand ---- +QDesignerFormEditorCommand::QDesignerFormEditorCommand(const QString &description, QDesignerFormEditorInterface *core) + : QUndoCommand(description), + m_core(core) +{ +} + +QDesignerFormEditorInterface *QDesignerFormEditorCommand::core() const +{ + return m_core; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_formeditorcommand_p.h b/designer/lib/shared/qdesigner_formeditorcommand_p.h new file mode 100644 index 0000000..dfe172c --- /dev/null +++ b/designer/lib/shared/qdesigner_formeditorcommand_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMEDITORCOMMAND_H +#define QDESIGNER_FORMEDITORCOMMAND_H + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerFormEditorCommand: public QUndoCommand +{ + +public: + QDesignerFormEditorCommand(const QString &description, QDesignerFormEditorInterface *core); + +protected: + QDesignerFormEditorInterface *core() const; + +private: + QPointer m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMEDITORCOMMAND_H diff --git a/designer/lib/shared/qdesigner_formwindowcommand.cpp b/designer/lib/shared/qdesigner_formwindowcommand.cpp new file mode 100644 index 0000000..8e99a6f --- /dev/null +++ b/designer/lib/shared/qdesigner_formwindowcommand.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qdesigner_formwindowcommand_p.h" +#include "qdesigner_objectinspector_p.h" +#include "layout_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---- QDesignerFormWindowCommand ---- +QDesignerFormWindowCommand::QDesignerFormWindowCommand(const QString &description, + QDesignerFormWindowInterface *formWindow, + QUndoCommand *parent) + : QUndoCommand(description, parent), + m_formWindow(formWindow) +{ +} + +QDesignerFormWindowInterface *QDesignerFormWindowCommand::formWindow() const +{ + return m_formWindow; +} + +QDesignerFormEditorInterface *QDesignerFormWindowCommand::core() const +{ + if (QDesignerFormWindowInterface *fw = formWindow()) + return fw->core(); + + return 0; +} + +void QDesignerFormWindowCommand::undo() +{ + cheapUpdate(); +} + +void QDesignerFormWindowCommand::redo() +{ + cheapUpdate(); +} + +void QDesignerFormWindowCommand::cheapUpdate() +{ + if (core()->objectInspector()) + core()->objectInspector()->setFormWindow(formWindow()); + + if (core()->actionEditor()) + core()->actionEditor()->setFormWindow(formWindow()); +} + +QDesignerPropertySheetExtension* QDesignerFormWindowCommand::propertySheet(QObject *object) const +{ + return qt_extension(formWindow()->core()->extensionManager(), object); +} + +void QDesignerFormWindowCommand::updateBuddies(QDesignerFormWindowInterface *form, + const QString &old_name, + const QString &new_name) +{ + QExtensionManager* extensionManager = form->core()->extensionManager(); + + typedef QList LabelList; + + const LabelList label_list = qFindChildren(form); + if (label_list.empty()) + return; + + const QString buddyProperty = QLatin1String("buddy"); + const QByteArray oldNameU8 = old_name.toUtf8(); + const QByteArray newNameU8 = new_name.toUtf8(); + + const LabelList::const_iterator cend = label_list.constEnd(); + for (LabelList::const_iterator it = label_list.constBegin(); it != cend; ++it ) { + if (QDesignerPropertySheetExtension* sheet = qt_extension(extensionManager, *it)) { + const int idx = sheet->indexOf(buddyProperty); + if (idx != -1) { + const QByteArray oldBuddy = sheet->property(idx).toByteArray(); + if (oldBuddy == oldNameU8) + sheet->setProperty(idx, newNameU8); + } + } + } +} + +void QDesignerFormWindowCommand::selectUnmanagedObject(QObject *unmanagedObject) +{ + // Keep selection in sync + if (QDesignerObjectInspector *oi = qobject_cast(core()->objectInspector())) { + oi->clearSelection(); + oi->selectObject(unmanagedObject); + } + core()->propertyEditor()->setObject(unmanagedObject); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_formwindowcommand_p.h b/designer/lib/shared/qdesigner_formwindowcommand_p.h new file mode 100644 index 0000000..9f696f6 --- /dev/null +++ b/designer/lib/shared/qdesigner_formwindowcommand_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMWINDOWCOMMAND_H +#define QDESIGNER_FORMWINDOWCOMMAND_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerPropertySheetExtension; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand: public QUndoCommand +{ + +public: + QDesignerFormWindowCommand(const QString &description, + QDesignerFormWindowInterface *formWindow, + QUndoCommand *parent = 0); + + virtual void undo(); + virtual void redo(); + + static void updateBuddies(QDesignerFormWindowInterface *form, + const QString &old_name, const QString &new_name); +protected: + QDesignerFormWindowInterface *formWindow() const; + QDesignerFormEditorInterface *core() const; + QDesignerPropertySheetExtension* propertySheet(QObject *object) const; + + void cheapUpdate(); + + void selectUnmanagedObject(QObject *unmanagedObject); +private: + QPointer m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND_H diff --git a/designer/lib/shared/qdesigner_formwindowmanager.cpp b/designer/lib/shared/qdesigner_formwindowmanager.cpp new file mode 100644 index 0000000..b4c9325 --- /dev/null +++ b/designer/lib/shared/qdesigner_formwindowmanager.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_formwindowmanager_p.h" +#include "plugindialog_p.h" + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +/*! + \class QDesignerFormWindowManager + + Extends QDesignerFormWindowManagerInterface with methods to control + the preview and printing of forms. It provides a facade that simplifies + the complexity of the more general PreviewConfiguration & PreviewManager + interfaces. + + \since 4.5 + */ + + +QDesignerFormWindowManager::QDesignerFormWindowManager(QObject *parent) + : QDesignerFormWindowManagerInterface(parent), m_unused(0) +{ +} + +QDesignerFormWindowManager::~QDesignerFormWindowManager() +{ +} + +/*! + Allows you to intervene and control \QD's form "Preview" action. The + function returns the original action. + + \since 4.5 + */ +QAction *QDesignerFormWindowManager::actionDefaultPreview() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's form "Preview in" style action. The + function returns the original list of actions. + + The method calls PreviewManager::createStyleActionGroup() internally. + + \since 4.5 + */ +QActionGroup *QDesignerFormWindowManager::actionGroupPreviewInStyle() const +{ + return 0; +} + +/*! + \fn QPixmap QDesignerFormWindowManager::createPreviewPixmap(QString *errorMessage) + + Creates a pixmap representing the preview of the currently active form. + + The method calls PreviewManager::createPreviewPixmap() internally. + + \since 4.5 + */ + + +/*! + \fn QPixmap QDesignerFormWindowManager::closeAllPreviews() + + Closes all preview windows generated by actionDefaultPreview, actionGroupPreviewInStyle + and the corresponding methods in PreviewManager. + + \since 4.5 + */ + +/*! + \fn PreviewManager *QDesignerFormWindowManager::previewManager() + + Accesses the previewmanager implementation. + + \since 4.5 + \internal + */ + +/*! + \fn QAction *QDesignerFormWindowManager::actionShowFormWindowSettingsDialog() const; + + Allows you to intervene and control \QD's form "Form Settings" action. The + function returns the original action. + + \since 4.5 + \internal + */ + +QAction *QDesignerFormWindowManager::actionShowFormWindowSettingsDialog() const +{ + return 0; +} + +/*! + \fn void QDesignerFormWindowManager::aboutPlugins() + + Pops up an "About plugins" dialog. + + \since 4.5 + \internal + */ + +void QDesignerFormWindowManager::aboutPlugins() +{ + PluginDialog dlg(core(), core()->topLevel()); + dlg.exec(); +} + +/*! + \fn + void QDesignerFormWindowManager::formWindowSettingsChanged(QDesignerFormWindowInterface *fw); + + This signal is emitted when the form settings dialog was shown + and changes have been made to the form. + + \since 4.5 + \internal + */ + + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_formwindowmanager_p.h b/designer/lib/shared/qdesigner_formwindowmanager_p.h new file mode 100644 index 0000000..6c22cbe --- /dev/null +++ b/designer/lib/shared/qdesigner_formwindowmanager_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMWINDOMANAGER_H +#define QDESIGNER_FORMWINDOMANAGER_H + +#include "shared_global_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class PreviewManager; + +// +// Convenience methods to manage form previews (ultimately forwarded to PreviewManager). +// +class QDESIGNER_SHARED_EXPORT QDesignerFormWindowManager + : public QDesignerFormWindowManagerInterface +{ + Q_OBJECT +public: + explicit QDesignerFormWindowManager(QObject *parent = 0); + virtual ~QDesignerFormWindowManager(); + + virtual QAction *actionDefaultPreview() const; + virtual QActionGroup *actionGroupPreviewInStyle() const; + virtual QAction *actionShowFormWindowSettingsDialog() const; + + virtual QPixmap createPreviewPixmap(QString *errorMessage) = 0; + + virtual PreviewManager *previewManager() const = 0; + +Q_SIGNALS: + void formWindowSettingsChanged(QDesignerFormWindowInterface *fw); + +public Q_SLOTS: + virtual void closeAllPreviews() = 0; + void aboutPlugins(); + +private: + void *m_unused; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMWINDOMANAGER_H diff --git a/designer/lib/shared/qdesigner_integration.cpp b/designer/lib/shared/qdesigner_integration.cpp new file mode 100644 index 0000000..d574db6 --- /dev/null +++ b/designer/lib/shared/qdesigner_integration.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_integration_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_objectinspector_p.h" +#include "widgetdatabase_p.h" +#include "pluginmanager_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_widgetbox_p.h" +#include "qtgradientmanager.h" +#include "qtgradientutils.h" +#include "qtresourcemodel_p.h" + +// sdk +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---------------- DesignerIntegrationPrivate +class QDesignerIntegrationPrivate { +public: + QDesignerIntegrationPrivate() + : m_gradientManager(0), + m_fileWatcherBehaviour(QDesignerIntegration::PromptAndReload), + m_resourceEditingEnabled(true), + m_slotNavigationEnabled(false) + {} + + QString m_gradientsPath; + QtGradientManager *m_gradientManager; + QDesignerIntegration::ResourceFileWatcherBehaviour m_fileWatcherBehaviour; + bool m_resourceEditingEnabled; + bool m_slotNavigationEnabled; +}; + +// -------------- QDesignerIntegration +// As of 4.4, the header will be distributed with the Eclipse plugin. + +QDesignerIntegration::QDesignerIntegration(QDesignerFormEditorInterface *core, QObject *parent) : + QDesignerIntegrationInterface(core, parent), + m_d(new QDesignerIntegrationPrivate) +{ + initialize(); +} + +QDesignerIntegration::~QDesignerIntegration() +{ + QFile f(m_d->m_gradientsPath); + if (f.open(QIODevice::WriteOnly)) { + f.write(QtGradientUtils::saveState(m_d->m_gradientManager).toUtf8()); + f.close(); + } + delete m_d; +} + +void QDesignerIntegration::initialize() +{ + // + // integrate the `Form Editor component' + // + + // Extensions + if (QDesignerPropertyEditor *designerPropertyEditor= qobject_cast(core()->propertyEditor())) { + connect(designerPropertyEditor, SIGNAL(propertyValueChanged(QString,QVariant,bool)), this, SLOT(updateProperty(QString,QVariant,bool))); + connect(designerPropertyEditor, SIGNAL(resetProperty(QString)), this, SLOT(resetProperty(QString))); + connect(designerPropertyEditor, SIGNAL(addDynamicProperty(QString,QVariant)), + this, SLOT(addDynamicProperty(QString,QVariant))); + connect(designerPropertyEditor, SIGNAL(removeDynamicProperty(QString)), + this, SLOT(removeDynamicProperty(QString))); + } else { + connect(core()->propertyEditor(), SIGNAL(propertyChanged(QString,QVariant)), + this, SLOT(updatePropertyPrivate(QString,QVariant))); + } + + connect(core()->formWindowManager(), SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), + this, SLOT(setupFormWindow(QDesignerFormWindowInterface*))); + + connect(core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(updateActiveFormWindow(QDesignerFormWindowInterface*))); + + m_d->m_gradientManager = new QtGradientManager(this); + core()->setGradientManager(m_d->m_gradientManager); + + QString designerFolder = QDir::homePath(); + designerFolder += QDir::separator(); + designerFolder += QLatin1String(".designer"); + m_d->m_gradientsPath = designerFolder; + m_d->m_gradientsPath += QDir::separator(); + m_d->m_gradientsPath += QLatin1String("gradients.xml"); + + QFile f(m_d->m_gradientsPath); + if (f.open(QIODevice::ReadOnly)) { + QtGradientUtils::restoreState(m_d->m_gradientManager, QString::fromAscii(f.readAll())); + f.close(); + } else { + QFile defaultGradients(QLatin1String(":/trolltech/designer/defaultgradients.xml")); + if (defaultGradients.open(QIODevice::ReadOnly)) { + QtGradientUtils::restoreState(m_d->m_gradientManager, QString::fromAscii(defaultGradients.readAll())); + defaultGradients.close(); + } + } + + if (WidgetDataBase *widgetDataBase = qobject_cast(core()->widgetDataBase())) + widgetDataBase->grabStandardWidgetBoxIcons(); +} + +void QDesignerIntegration::updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + SetPropertyCommand *cmd = new SetPropertyCommand(formWindow); + // find a reference object to compare to and to find the right group + if (cmd->init(selection.selection(), name, value, propertyEditorObject(), enableSubPropertyHandling)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "Unable to set property " << name << '.'; + } + + emit propertyChanged(formWindow, name, value); +} + +void QDesignerIntegration::updatePropertyPrivate(const QString &name, const QVariant &value) +{ + updateProperty(name, value, true); +} + +void QDesignerIntegration::resetProperty(const QString &name) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + + ResetPropertyCommand *cmd = new ResetPropertyCommand(formWindow); + // find a reference object to find the right group + if (cmd->init(selection.selection(), name, propertyEditorObject())) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to reset property " << name << '.'; + } +} + +void QDesignerIntegration::addDynamicProperty(const QString &name, const QVariant &value) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + AddDynamicPropertyCommand *cmd = new AddDynamicPropertyCommand(formWindow); + if (cmd->init(selection.selection(), propertyEditorObject(), name, value)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to add dynamic property " << name << '.'; + } +} + +void QDesignerIntegration::removeDynamicProperty(const QString &name) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + RemoveDynamicPropertyCommand *cmd = new RemoveDynamicPropertyCommand(formWindow); + if (cmd->init(selection.selection(), propertyEditorObject(), name)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to remove dynamic property " << name << '.'; + } + +} + + +void QDesignerIntegration::updateActiveFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_UNUSED(formWindow); + updateSelection(); +} + +void QDesignerIntegration::setupFormWindow(QDesignerFormWindowInterface *formWindow) +{ + connect(formWindow, SIGNAL(selectionChanged()), this, SLOT(updateSelection())); + connect(formWindow, SIGNAL(activated(QWidget*)), this, SLOT(activateWidget(QWidget*))); +} + +void QDesignerIntegration::updateGeometry() +{ +} + +void QDesignerIntegration::updateSelection() +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + QWidget *selection = 0; + + if (formWindow) { + selection = formWindow->cursor()->current(); + } + + if (QDesignerActionEditorInterface *actionEditor = core()->actionEditor()) + actionEditor->setFormWindow(formWindow); + + if (QDesignerPropertyEditorInterface *propertyEditor = core()->propertyEditor()) + propertyEditor->setObject(selection); + + if (QDesignerObjectInspectorInterface *objectInspector = core()->objectInspector()) + objectInspector->setFormWindow(formWindow); + +} + +void QDesignerIntegration::activateWidget(QWidget *widget) +{ + Q_UNUSED(widget); +} + +QWidget *QDesignerIntegration::containerWindow(QWidget *widget) const +{ + // Find the parent window to apply a geometry to. + while (widget) { + if (widget->isWindow()) + break; + if (!qstrcmp(widget->metaObject()->className(), "QMdiSubWindow")) + break; + + widget = widget->parentWidget(); + } + + return widget; +} + +void QDesignerIntegration::getSelection(Selection &s) +{ + // Get multiselection from object inspector + if (QDesignerObjectInspector *designerObjectInspector = qobject_cast(core()->objectInspector())) { + designerObjectInspector->getSelection(s); + // Action editor puts actions that are not on the form yet + // into the property editor only. + if (s.empty()) + if (QObject *object = core()->propertyEditor()->object()) + s.objects.push_back(object); + + } else { + // Just in case someone plugs in an old-style object inspector: Emulate selection + s.clear(); + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + QObject *object = core()->propertyEditor()->object(); + if (object->isWidgetType()) { + QWidget *widget = static_cast(object); + QDesignerFormWindowCursorInterface *cursor = formWindow->cursor(); + if (cursor->isWidgetSelected(widget)) { + s.managed.push_back(widget); + } else { + s.unmanaged.push_back(widget); + } + } else { + s.objects.push_back(object); + } + } +} + +QObject *QDesignerIntegration::propertyEditorObject() +{ + QDesignerPropertyEditorInterface *propertyEditor = core()->propertyEditor(); + if (!propertyEditor) + return 0; + return propertyEditor->object(); +} + +// Load plugins into widget database and factory. +void QDesignerIntegration::initializePlugins(QDesignerFormEditorInterface *formEditor) +{ + // load the plugins + WidgetDataBase *widgetDataBase = qobject_cast(formEditor->widgetDataBase()); + if (widgetDataBase) { + widgetDataBase->loadPlugins(); + } + + if (WidgetFactory *widgetFactory = qobject_cast(formEditor->widgetFactory())) { + widgetFactory->loadPlugins(); + } + + if (widgetDataBase) { + widgetDataBase->grabDefaultPropertyValues(); + } +} + +void QDesignerIntegration::updateCustomWidgetPlugins() +{ + QDesignerFormEditorInterface *formEditor = core(); + if (QDesignerPluginManager *pm = formEditor->pluginManager()) + pm->registerNewPlugins(); + + initializePlugins(formEditor); + + // Do not just reload the last file as the WidgetBox merges the compiled-in resources + // and $HOME/.designer/widgetbox.xml. This would also double the scratchpad. + if (QDesignerWidgetBox *wb = qobject_cast(formEditor->widgetBox())) { + const QDesignerWidgetBox::LoadMode oldLoadMode = wb->loadMode(); + wb->setLoadMode(QDesignerWidgetBox::LoadCustomWidgetsOnly); + wb->load(); + wb->setLoadMode(oldLoadMode); + } +} + +void QDesignerIntegration::emitObjectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName) +{ + emit objectNameChanged(formWindow, object, newName, oldName); +} + +void QDesignerIntegration::emitNavigateToSlot(const QString &objectName, + const QString &signalSignature, + const QStringList ¶meterNames) +{ + emit navigateToSlot(objectName, signalSignature, parameterNames); +} + +void QDesignerIntegration::emitNavigateToSlot(const QString &slotSignature) +{ + emit navigateToSlot(slotSignature); +} + +void QDesignerIntegration::requestHelp(const QDesignerFormEditorInterface *core, const QString &manual, const QString &document) +{ + if (QDesignerIntegration *di = qobject_cast(core->integration())) + emit di->helpRequested(manual, document); +} + +QDesignerResourceBrowserInterface *QDesignerIntegration::createResourceBrowser(QWidget *) +{ + return 0; +} + +void QDesignerIntegration::setResourceFileWatcherBehaviour(ResourceFileWatcherBehaviour behaviour) +{ + m_d->m_fileWatcherBehaviour = behaviour; + core()->resourceModel()->setWatcherEnabled(behaviour != QDesignerIntegration::NoWatcher); +} + +QDesignerIntegration::ResourceFileWatcherBehaviour QDesignerIntegration::resourceFileWatcherBehaviour() const +{ + return m_d->m_fileWatcherBehaviour; +} + +void QDesignerIntegration::setResourceEditingEnabled(bool enable) +{ + m_d->m_resourceEditingEnabled = enable; +} + +bool QDesignerIntegration::isResourceEditingEnabled() const +{ + return m_d->m_resourceEditingEnabled; +} + +void QDesignerIntegration::setSlotNavigationEnabled(bool enable) +{ + m_d->m_slotNavigationEnabled = enable; +} + +bool QDesignerIntegration::isSlotNavigationEnabled() const +{ + return m_d->m_slotNavigationEnabled; +} + +static QString fixHelpClassName(const QString &className) +{ + // ### generalize using the Widget Data Base + if (className == QLatin1String("Line")) + return QLatin1String("QFrame"); + if (className == QLatin1String("Spacer")) + return QLatin1String("QSpacerItem"); + if (className == QLatin1String("QLayoutWidget")) + return QLatin1String("QLayout"); + return className; +} + +// Return class in which the property is defined +static QString classForProperty(QDesignerFormEditorInterface *core, + QObject *object, + const QString &property) +{ + if (const QDesignerPropertySheetExtension *ps = qt_extension(core->extensionManager(), object)) { + const int index = ps->indexOf(property); + if (index >= 0) + return ps->propertyGroup(index); + } + return QString(); +} + +QString QDesignerIntegration::contextHelpId() const +{ + QObject *currentObject = core()->propertyEditor()->object(); + if (!currentObject) + return QString(); + // Return a help index id consisting of "class::property" + QString className; + QString currentPropertyName = core()->propertyEditor()->currentPropertyName(); + if (!currentPropertyName.isEmpty()) + className = classForProperty(core(), currentObject, currentPropertyName); + if (className.isEmpty()) { + currentPropertyName.clear(); // We hit on some fake property. + className = WidgetFactory::classNameOf(core(), currentObject); + } + QString helpId = fixHelpClassName(className); + if (!currentPropertyName.isEmpty()) { + helpId += QLatin1String("::"); + helpId += currentPropertyName; + } + return helpId; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_integration_p.h b/designer/lib/shared/qdesigner_integration_p.h new file mode 100644 index 0000000..219da5d --- /dev/null +++ b/designer/lib/shared/qdesigner_integration_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_INTEGRATION_H +#define QDESIGNER_INTEGRATION_H + +#include "shared_global_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerResourceBrowserInterface; + +class QVariant; +class QWidget; + +namespace qdesigner_internal { + +struct Selection; +class QDesignerIntegrationPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerIntegration: public QDesignerIntegrationInterface +{ + Q_OBJECT +public: + explicit QDesignerIntegration(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~QDesignerIntegration(); + + static void requestHelp(const QDesignerFormEditorInterface *core, const QString &manual, const QString &document); + + virtual QWidget *containerWindow(QWidget *widget) const; + + // Load plugins into widget database and factory. + static void initializePlugins(QDesignerFormEditorInterface *formEditor); + void emitObjectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, + const QString &newName, const QString &oldName); + void emitNavigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList ¶meterNames); + void emitNavigateToSlot(const QString &slotSignature); + + // Create a resource browser specific to integration. Language integration takes precedence + virtual QDesignerResourceBrowserInterface *createResourceBrowser(QWidget *parent = 0); + + enum ResourceFileWatcherBehaviour { + NoWatcher, + ReloadSilently, + PromptAndReload + }; + + ResourceFileWatcherBehaviour resourceFileWatcherBehaviour() const; + bool isResourceEditingEnabled() const; + bool isSlotNavigationEnabled() const; + + QString contextHelpId() const; + +protected: + + void setResourceFileWatcherBehaviour(ResourceFileWatcherBehaviour behaviour); // PromptAndReload by default + void setResourceEditingEnabled(bool enable); // true by default + void setSlotNavigationEnabled(bool enable); // false by default + +signals: + void propertyChanged(QDesignerFormWindowInterface *formWindow, const QString &name, const QVariant &value); + void objectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName); + void helpRequested(const QString &manual, const QString &document); + + void navigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList ¶meterNames); + void navigateToSlot(const QString &slotSignature); + +public slots: + virtual void updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling); + // Additional signals of designer property editor + virtual void resetProperty(const QString &name); + virtual void addDynamicProperty(const QString &name, const QVariant &value); + virtual void removeDynamicProperty(const QString &name); + + virtual void updateActiveFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void setupFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void updateSelection(); + virtual void updateGeometry(); + virtual void activateWidget(QWidget *widget); + + void updateCustomWidgetPlugins(); + +private slots: + void updatePropertyPrivate(const QString &name, const QVariant &value); + +private: + void initialize(); + void getSelection(Selection &s); + QObject *propertyEditorObject(); + + QDesignerIntegrationPrivate *m_d; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_INTEGRATION_H diff --git a/designer/lib/shared/qdesigner_introspection.cpp b/designer/lib/shared/qdesigner_introspection.cpp new file mode 100644 index 0000000..83aa8ee --- /dev/null +++ b/designer/lib/shared/qdesigner_introspection.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_introspection_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Qt Implementation +static QStringList byteArrayListToStringList(const QList &l) +{ + if (l.empty()) + return QStringList(); + QStringList rc; + const QList::const_iterator cend = l.constEnd(); + for (QList::const_iterator it = l.constBegin(); it != cend; ++it) + rc += QString::fromUtf8(*it); + return rc; +} + +static inline QString charToQString(const char *c) +{ + if (!c) + return QString(); + return QString::fromUtf8(c); +} + +namespace { + // ------- QDesignerMetaEnum + class QDesignerMetaEnum : public QDesignerMetaEnumInterface { + public: + QDesignerMetaEnum(const QMetaEnum &qEnum); + virtual bool isFlag() const { return m_enum.isFlag(); } + virtual QString key(int index) const { return charToQString(m_enum.key(index)); } + virtual int keyCount() const { return m_enum.keyCount(); } + virtual int keyToValue(const QString &key) const { return m_enum.keyToValue(key.toUtf8()); } + virtual int keysToValue(const QString &keys) const { return m_enum.keysToValue(keys.toUtf8()); } + virtual QString name() const { return m_name; } + virtual QString scope() const { return m_scope; } + virtual QString separator() const; + virtual int value(int index) const { return m_enum.value(index); } + virtual QString valueToKey(int value) const { return charToQString(m_enum.valueToKey(value)); } + virtual QString valueToKeys(int value) const { return charToQString(m_enum.valueToKeys(value)); } + + private: + const QMetaEnum m_enum; + const QString m_name; + const QString m_scope; + }; + + QDesignerMetaEnum::QDesignerMetaEnum(const QMetaEnum &qEnum) : + m_enum(qEnum), + m_name(charToQString(m_enum.name())), + m_scope(charToQString(m_enum.scope())) + { + } + + QString QDesignerMetaEnum::separator() const + { + static const QString rc = QLatin1String("::"); + return rc; + } + + // ------- QDesignerMetaProperty + class QDesignerMetaProperty : public QDesignerMetaPropertyInterface { + public: + QDesignerMetaProperty(const QMetaProperty &property); + virtual ~QDesignerMetaProperty(); + + virtual const QDesignerMetaEnumInterface *enumerator() const { return m_enumerator; } + + virtual Kind kind() const { return m_kind; } + + virtual AccessFlags accessFlags() const { return m_access; } + virtual Attributes attributes(const QObject *object = 0) const; + + virtual QVariant::Type type() const { return m_property.type(); } + virtual QString name() const { return m_name; } + virtual QString typeName() const { return m_typeName; } + virtual int userType() const { return m_property.userType(); } + virtual bool hasSetter() const { return m_property.hasStdCppSet(); } + + virtual QVariant read(const QObject *object) const { return m_property.read(object); } + virtual bool reset(QObject *object) const { return m_property.reset(object); } + virtual bool write(QObject *object, const QVariant &value) const { return m_property.write(object, value); } + + private: + const QMetaProperty m_property; + const QString m_name; + const QString m_typeName; + Kind m_kind; + AccessFlags m_access; + Attributes m_defaultAttributes; + QDesignerMetaEnumInterface *m_enumerator; + }; + + QDesignerMetaProperty::QDesignerMetaProperty(const QMetaProperty &property) : + m_property(property), + m_name(charToQString(m_property.name())), + m_typeName(charToQString(m_property.typeName())), + m_kind(OtherKind), + m_enumerator(0) + { + if (m_property.isFlagType() || m_property.isEnumType()) { + const QMetaEnum metaEnum = m_property.enumerator(); + Q_ASSERT(metaEnum.isValid()); + m_enumerator = new QDesignerMetaEnum(metaEnum); + } + // kind + if (m_property.isFlagType()) + m_kind = FlagKind; + else + if (m_property.isEnumType()) + m_kind = EnumKind; + // flags and attributes + if (m_property.isReadable()) + m_access |= ReadAccess; + if (m_property.isWritable()) + m_access |= WriteAccess; + if (m_property.isResettable()) + m_access |= ResetAccess; + + if (m_property.isDesignable()) + m_defaultAttributes |= DesignableAttribute; + if (m_property.isScriptable()) + m_defaultAttributes |= ScriptableAttribute; + if (m_property.isStored()) + m_defaultAttributes |= StoredAttribute; + if (m_property.isUser()) + m_defaultAttributes |= UserAttribute; + } + + QDesignerMetaProperty::~QDesignerMetaProperty() + { + delete m_enumerator; + } + + QDesignerMetaProperty::Attributes QDesignerMetaProperty::attributes(const QObject *object) const + { + if (!object) + return m_defaultAttributes; + Attributes rc; + if (m_property.isDesignable(object)) + rc |= DesignableAttribute; + if (m_property.isScriptable(object)) + rc |= ScriptableAttribute; + if (m_property.isStored(object)) + rc |= StoredAttribute; + if (m_property.isUser(object)) + rc |= UserAttribute; + return rc; + } + + // -------------- QDesignerMetaMethod + + class QDesignerMetaMethod : public QDesignerMetaMethodInterface { + public: + QDesignerMetaMethod(const QMetaMethod &method); + + virtual Access access() const { return m_access; } + virtual MethodType methodType() const { return m_methodType; } + virtual QStringList parameterNames() const { return m_parameterNames; } + virtual QStringList parameterTypes() const { return m_parameterTypes; } + virtual QString signature() const { return m_signature; } + virtual QString normalizedSignature() const { return m_normalizedSignature; } + virtual QString tag() const { return m_tag; } + virtual QString typeName() const { return m_typeName; } + + private: + Access m_access; + MethodType m_methodType; + const QStringList m_parameterNames; + const QStringList m_parameterTypes; + const QString m_signature; + const QString m_normalizedSignature; + const QString m_tag; + const QString m_typeName; + }; + + QDesignerMetaMethod::QDesignerMetaMethod(const QMetaMethod &method) : + m_parameterNames(byteArrayListToStringList(method.parameterNames())), + m_parameterTypes(byteArrayListToStringList(method.parameterTypes())), + m_signature(charToQString(method.signature())), + m_normalizedSignature(charToQString(QMetaObject::normalizedSignature(method.signature()))), + m_tag(charToQString(method.tag())), + m_typeName(charToQString(method.typeName())) + { + switch (method.access()) { + case QMetaMethod::Public: + m_access = Public; + break; + case QMetaMethod::Protected: + m_access = Protected; + break; + case QMetaMethod::Private: + m_access = Private; + break; + + } + switch (method.methodType()) { + case QMetaMethod::Constructor: + m_methodType = Constructor; + break; + case QMetaMethod::Method: + m_methodType = Method; + break; + case QMetaMethod::Signal: + m_methodType = Signal; + break; + + case QMetaMethod::Slot: + m_methodType = Slot; + break; + } + } + + // ------------- QDesignerMetaObject + class QDesignerMetaObject : public QDesignerMetaObjectInterface { + public: + QDesignerMetaObject(const qdesigner_internal::QDesignerIntrospection *introspection, const QMetaObject *metaObject); + virtual ~QDesignerMetaObject(); + + virtual QString className() const { return m_className; } + virtual const QDesignerMetaEnumInterface *enumerator(int index) const { return m_enumerators[index]; } + virtual int enumeratorCount() const { return m_enumerators.size(); } + virtual int enumeratorOffset() const { return m_metaObject->enumeratorOffset(); } + + virtual int indexOfEnumerator(const QString &name) const { return m_metaObject->indexOfEnumerator(name.toUtf8()); } + virtual int indexOfMethod(const QString &method) const { return m_metaObject->indexOfMethod(method.toUtf8()); } + virtual int indexOfProperty(const QString &name) const { return m_metaObject->indexOfProperty(name.toUtf8()); } + virtual int indexOfSignal(const QString &signal) const { return m_metaObject->indexOfSignal(signal.toUtf8()); } + virtual int indexOfSlot(const QString &slot) const { return m_metaObject->indexOfSlot(slot.toUtf8()); } + + virtual const QDesignerMetaMethodInterface *method(int index) const { return m_methods[index]; } + virtual int methodCount() const { return m_methods.size(); } + virtual int methodOffset() const { return m_metaObject->methodOffset(); } + + virtual const QDesignerMetaPropertyInterface *property(int index) const { return m_properties[index]; } + virtual int propertyCount() const { return m_properties.size(); } + virtual int propertyOffset() const { return m_metaObject->propertyOffset(); } + + const QDesignerMetaObjectInterface *superClass() const; + virtual const QDesignerMetaPropertyInterface *userProperty() const { return m_userProperty; } + + private: + const QString m_className; + const qdesigner_internal::QDesignerIntrospection *m_introspection; + const QMetaObject *m_metaObject; + + typedef QVector Enumerators; + Enumerators m_enumerators; + + typedef QVector Methods; + Methods m_methods; + + typedef QVector Properties; + Properties m_properties; + + QDesignerMetaPropertyInterface *m_userProperty; + }; + + QDesignerMetaObject::QDesignerMetaObject(const qdesigner_internal::QDesignerIntrospection *introspection, const QMetaObject *metaObject) : + m_className(charToQString(metaObject->className())), + m_introspection(introspection), + m_metaObject(metaObject), + m_userProperty(0) + { + const int numEnumerators = metaObject->enumeratorCount(); + m_enumerators.reserve(numEnumerators); + for (int i = 0; i < numEnumerators; i++) + m_enumerators.push_back(new QDesignerMetaEnum(metaObject->enumerator(i))); + const int numMethods = metaObject->methodCount(); + m_methods.reserve(numMethods); + for (int i = 0; i < numMethods; i++) + m_methods.push_back(new QDesignerMetaMethod(metaObject->method(i))); + + const int numProperties = metaObject->propertyCount(); + m_properties.reserve(numProperties); + for (int i = 0; i < numProperties; i++) + m_properties.push_back(new QDesignerMetaProperty(metaObject->property(i))); + + const QMetaProperty userProperty = metaObject->userProperty(); + if (userProperty.isValid()) + m_userProperty = new QDesignerMetaProperty(userProperty); + } + + QDesignerMetaObject::~QDesignerMetaObject() + { + qDeleteAll(m_enumerators); + qDeleteAll(m_methods); + qDeleteAll(m_properties); + delete m_userProperty; + } + + const QDesignerMetaObjectInterface *QDesignerMetaObject::superClass() const + { + const QMetaObject *qSuperClass = m_metaObject->superClass(); + if (!qSuperClass) + return 0; + return m_introspection->metaObjectForQMetaObject(qSuperClass); + } + +} + +namespace qdesigner_internal { + + QDesignerIntrospection::QDesignerIntrospection() + { + } + + QDesignerIntrospection::~QDesignerIntrospection() + { + qDeleteAll(m_metaObjectMap.values()); + } + + const QDesignerMetaObjectInterface* QDesignerIntrospection::metaObject(const QObject *object) const + { + return metaObjectForQMetaObject(object->metaObject()); + } + + const QDesignerMetaObjectInterface* QDesignerIntrospection::metaObjectForQMetaObject(const QMetaObject *metaObject) const + { + MetaObjectMap::iterator it = m_metaObjectMap.find(metaObject); + if (it == m_metaObjectMap.end()) + it = m_metaObjectMap.insert(metaObject, new QDesignerMetaObject(this, metaObject)); + return it.value(); + } +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_introspection_p.h b/designer/lib/shared/qdesigner_introspection_p.h new file mode 100644 index 0000000..a824dc5 --- /dev/null +++ b/designer/lib/shared/qdesigner_introspection_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNERINTROSPECTION +#define DESIGNERINTROSPECTION + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +struct QMetaObject; +class QWidget; + +namespace qdesigner_internal { + // Qt C++ introspection with helpers to find core and meta object for an object + class QDESIGNER_SHARED_EXPORT QDesignerIntrospection : public QDesignerIntrospectionInterface { + public: + QDesignerIntrospection(); + virtual ~QDesignerIntrospection(); + + virtual const QDesignerMetaObjectInterface* metaObject(const QObject *object) const; + + const QDesignerMetaObjectInterface* metaObjectForQMetaObject(const QMetaObject *metaObject) const; + private: + typedef QMap MetaObjectMap; + mutable MetaObjectMap m_metaObjectMap; + + }; +} + +QT_END_NAMESPACE + +#endif // DESIGNERINTROSPECTION diff --git a/designer/lib/shared/qdesigner_membersheet.cpp b/designer/lib/shared/qdesigner_membersheet.cpp new file mode 100644 index 0000000..f134029 --- /dev/null +++ b/designer/lib/shared/qdesigner_membersheet.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_membersheet_p.h" + +#include +#include + +#include + +namespace { + +class Qt3Members + { + public: + static Qt3Members *instance(); + QMap getSignals() const { return m_classNameToSignals; } + QMap getSlots() const { return m_classNameToSlots; } + private: + Qt3Members(); + static Qt3Members *m_instance; + QMap m_classNameToSignals; + QMap m_classNameToSlots; + }; + +Qt3Members *Qt3Members::m_instance = 0; + +Qt3Members::Qt3Members() +{ + m_classNameToSignals[QLatin1String("QTextEdit")].append(QLatin1String("currentFontChanged(QFont)")); + m_classNameToSignals[QLatin1String("QTextEdit")].append(QLatin1String("currentColorChanged(QColor)")); + m_classNameToSignals[QLatin1String("QTabWidget")].append(QLatin1String("currentChanged(QWidget*)")); + m_classNameToSignals[QLatin1String("QTabWidget")].append(QLatin1String("selected(QString)")); + m_classNameToSignals[QLatin1String("QTabBar")].append(QLatin1String("selected(int)")); + m_classNameToSignals[QLatin1String("QMenuBar")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QMenuBar")].append(QLatin1String("highlighted(int)")); + m_classNameToSignals[QLatin1String("QMenu")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QMenu")].append(QLatin1String("highlighted(int)")); + m_classNameToSignals[QLatin1String("QLineEdit")].append(QLatin1String("lostFocus()")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialPressed()")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialMoved(int)")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialReleased()")); + m_classNameToSignals[QLatin1String("QComboBox")].append(QLatin1String("textChanged(QString)")); + m_classNameToSignals[QLatin1String("QActionGroup")].append(QLatin1String("selected(QAction*)")); + m_classNameToSignals[QLatin1String("QAction")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QAbstractSocket")].append(QLatin1String("connectionClosed()")); + m_classNameToSignals[QLatin1String("QAbstractSocket")].append(QLatin1String("delayedCloseFinished()")); + + m_classNameToSlots[QLatin1String("QWidget")].append(QLatin1String("setShown(bool)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setTextPosition(QToolButton::TextPosition)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setUsesBigPixmap(bool)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setUsesTextLabel(bool)")); + m_classNameToSlots[QLatin1String("QTextEdit")].append(QLatin1String("setModified(bool)")); + m_classNameToSlots[QLatin1String("QTextEdit")].append(QLatin1String("setColor(QColor)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("setCurrentPage(int)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("showPage(QWidget*)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("removePage(QWidget*)")); + m_classNameToSlots[QLatin1String("QTabBar")].append(QLatin1String("setCurrentTab(int)")); + m_classNameToSlots[QLatin1String("QStatusBar")].append(QLatin1String("message(QString,int)")); + m_classNameToSlots[QLatin1String("QStatusBar")].append(QLatin1String("clear()")); + m_classNameToSlots[QLatin1String("QSplashScreen")].append(QLatin1String("message(QString,int)")); + m_classNameToSlots[QLatin1String("QSplashScreen")].append(QLatin1String("clear()")); + m_classNameToSlots[QLatin1String("QSlider")].append(QLatin1String("addStep()")); + m_classNameToSlots[QLatin1String("QSlider")].append(QLatin1String("subtractStep()")); + m_classNameToSlots[QLatin1String("QAbstractButton")].append(QLatin1String("setOn(bool)")); + m_classNameToSlots[QLatin1String("QAction")].append(QLatin1String("setOn(bool)")); + m_classNameToSlots[QLatin1String("QErrorMessage")].append(QLatin1String("message(QString)")); + m_classNameToSlots[QLatin1String("QTimer")].append(QLatin1String("changeInterval(int)")); + m_classNameToSlots[QLatin1String("QTimer")].append(QLatin1String("start(int,bool)")); +} + +Qt3Members *Qt3Members::instance() +{ + if (!m_instance) + m_instance = new Qt3Members(); + return m_instance; +} +} + +QT_BEGIN_NAMESPACE + +static QList stringListToByteArray(const QStringList &l) +{ + if (l.empty()) + return QList(); + QList rc; + const QStringList::const_iterator cend = l.constEnd(); + for (QStringList::const_iterator it = l.constBegin(); it != cend; ++it) + rc += it->toUtf8(); + return rc; +} + +// Find the form editor in the hierarchy. +// We know that the parent of the sheet is the extension manager +// whose parent is the core. + +static QDesignerFormEditorInterface *formEditorForObject(QObject *o) { + do { + if (QDesignerFormEditorInterface* core = qobject_cast(o)) + return core; + o = o->parent(); + } while(o); + Q_ASSERT(o); + return 0; +} + +// ------------ QDesignerMemberSheetPrivate +class QDesignerMemberSheetPrivate { +public: + explicit QDesignerMemberSheetPrivate(QObject *object, QObject *sheetParent); + + QDesignerFormEditorInterface *m_core; + const QDesignerMetaObjectInterface *m_meta; + + class Info { + public: + inline Info() : visible(true) {} + + QString group; + bool visible; + }; + + typedef QHash InfoHash; + + Info &ensureInfo(int index); + + InfoHash m_info; +}; + +QDesignerMemberSheetPrivate::QDesignerMemberSheetPrivate(QObject *object, QObject *sheetParent) : + m_core(formEditorForObject(sheetParent)), + m_meta(m_core->introspection()->metaObject(object)) +{ +} + +QDesignerMemberSheetPrivate::Info &QDesignerMemberSheetPrivate::ensureInfo(int index) +{ + InfoHash::iterator it = m_info.find(index); + if (it == m_info.end()) { + it = m_info.insert(index, Info()); + } + return it.value(); +} + +// --------- QDesignerMemberSheet + +QDesignerMemberSheet::QDesignerMemberSheet(QObject *object, QObject *parent) : + QObject(parent), + d(new QDesignerMemberSheetPrivate(object, parent)) +{ +} + +QDesignerMemberSheet::~QDesignerMemberSheet() +{ + delete d; +} + +int QDesignerMemberSheet::count() const +{ + return d->m_meta->methodCount(); +} + +int QDesignerMemberSheet::indexOf(const QString &name) const +{ + return d->m_meta->indexOfMethod(name); +} + +QString QDesignerMemberSheet::memberName(int index) const +{ + return d->m_meta->method(index)->tag(); +} + +QString QDesignerMemberSheet::declaredInClass(int index) const +{ + const QString member = d->m_meta->method(index)->signature(); + + // Find class whose superclass does not contain the method. + const QDesignerMetaObjectInterface *meta_obj = d->m_meta; + + for (;;) { + const QDesignerMetaObjectInterface *tmp = meta_obj->superClass(); + if (tmp == 0) + break; + if (tmp->indexOfMethod(member) == -1) + break; + meta_obj = tmp; + } + return meta_obj->className(); +} + +QString QDesignerMemberSheet::memberGroup(int index) const +{ + return d->m_info.value(index).group; +} + +void QDesignerMemberSheet::setMemberGroup(int index, const QString &group) +{ + d->ensureInfo(index).group = group; +} + +QString QDesignerMemberSheet::signature(int index) const +{ + return d->m_meta->method(index)->normalizedSignature(); +} + +bool QDesignerMemberSheet::isVisible(int index) const +{ + typedef QDesignerMemberSheetPrivate::InfoHash InfoHash; + const InfoHash::const_iterator it = d->m_info.constFind(index); + if (it != d->m_info.constEnd()) + return it.value().visible; + + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Signal + || d->m_meta->method(index)->access() == QDesignerMetaMethodInterface::Public; +} + +void QDesignerMemberSheet::setVisible(int index, bool visible) +{ + d->ensureInfo(index).visible = visible; +} + +bool QDesignerMemberSheet::isSignal(int index) const +{ + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Signal; +} + +bool QDesignerMemberSheet::isSlot(int index) const +{ + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Slot; +} + +bool QDesignerMemberSheet::inheritedFromWidget(int index) const +{ + const QString name = d->m_meta->method(index)->signature(); + return declaredInClass(index) == QLatin1String("QWidget") || declaredInClass(index) == QLatin1String("QObject"); +} + + +QList QDesignerMemberSheet::parameterTypes(int index) const +{ + return stringListToByteArray(d->m_meta->method(index)->parameterTypes()); +} + +QList QDesignerMemberSheet::parameterNames(int index) const +{ + return stringListToByteArray(d->m_meta->method(index)->parameterNames()); +} + +bool QDesignerMemberSheet::signalMatchesSlot(const QString &signal, const QString &slot) +{ + bool result = true; + + do { + int signal_idx = signal.indexOf(QLatin1Char('(')); + int slot_idx = slot.indexOf(QLatin1Char('(')); + if (signal_idx == -1 || slot_idx == -1) + break; + + ++signal_idx; ++slot_idx; + + if (slot.at(slot_idx) == QLatin1Char(')')) + break; + + while (signal_idx < signal.size() && slot_idx < slot.size()) { + const QChar signal_c = signal.at(signal_idx); + const QChar slot_c = slot.at(slot_idx); + + if (signal_c == QLatin1Char(',') && slot_c == QLatin1Char(')')) + break; + + if (signal_c == QLatin1Char(')') && slot_c == QLatin1Char(')')) + break; + + if (signal_c != slot_c) { + result = false; + break; + } + + ++signal_idx; ++slot_idx; + } + } while (false); + + return result; +} + +bool QDesignerMemberSheet::isQt3Signal(int index) const +{ + if (!isSignal(index)) + return false; + + const QString className = declaredInClass(index); + const QString signalSignature = signature(index); + + QMap qt3signals = Qt3Members::instance()->getSignals(); + QMap::const_iterator it = qt3signals.constFind(className); + if (it != qt3signals.constEnd() && (*it).contains(signalSignature)) + return true; + + return false; +} + +bool QDesignerMemberSheet::isQt3Slot(int index) const +{ + if (!isSlot(index)) + return false; + + const QString className = declaredInClass(index); + const QString slotSignature = signature(index); + + QMap qt3slots = Qt3Members::instance()->getSlots(); + QMap::const_iterator it = qt3slots.constFind(className); + if (it != qt3slots.constEnd() && (*it).contains(slotSignature)) + return true; + return false; +} + +// ------------ QDesignerMemberSheetFactory + +QDesignerMemberSheetFactory::QDesignerMemberSheetFactory(QExtensionManager *parent) + : QExtensionFactory(parent) +{ +} + +QObject *QDesignerMemberSheetFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const +{ + if (iid == Q_TYPEID(QDesignerMemberSheetExtension)) { + return new QDesignerMemberSheet(object, parent); + } + + return 0; +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_membersheet_p.h b/designer/lib/shared/qdesigner_membersheet_p.h new file mode 100644 index 0000000..832393c --- /dev/null +++ b/designer/lib/shared/qdesigner_membersheet_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MEMBERSHEET_H +#define QDESIGNER_MEMBERSHEET_H + +#include "shared_global_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerMemberSheetPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerMemberSheet: public QObject, public QDesignerMemberSheetExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerMemberSheetExtension) + +public: + explicit QDesignerMemberSheet(QObject *object, QObject *parent = 0); + virtual ~QDesignerMemberSheet(); + + virtual int indexOf(const QString &name) const; + + virtual int count() const; + virtual QString memberName(int index) const; + + virtual QString memberGroup(int index) const; + virtual void setMemberGroup(int index, const QString &group); + + virtual bool isVisible(int index) const; + virtual void setVisible(int index, bool b); + + virtual bool isSignal(int index) const; + virtual bool isSlot(int index) const; + + virtual bool isQt3Signal(int index) const; + virtual bool isQt3Slot(int index) const; + + virtual bool inheritedFromWidget(int index) const; + + static bool signalMatchesSlot(const QString &signal, const QString &slot); + + virtual QString declaredInClass(int index) const; + + virtual QString signature(int index) const; + virtual QList parameterTypes(int index) const; + virtual QList parameterNames(int index) const; + +private: + QDesignerMemberSheetPrivate *d; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerMemberSheetFactory: public QExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) + +public: + QDesignerMemberSheetFactory(QExtensionManager *parent = 0); + +protected: + virtual QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MEMBERSHEET_H diff --git a/designer/lib/shared/qdesigner_menu.cpp b/designer/lib/shared/qdesigner_menu.cpp new file mode 100644 index 0000000..d988b52 --- /dev/null +++ b/designer/lib/shared/qdesigner_menu.cpp @@ -0,0 +1,1390 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "actioneditor_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +// give the user a little more space to click on the sub menu rectangle +static inline void extendClickableArea(QRect *subMenuRect, Qt::LayoutDirection dir) +{ + switch (dir) { + case Qt::LayoutDirectionAuto: // Should never happen + case Qt::LeftToRight: + subMenuRect->setLeft(subMenuRect->left() - 20); + break; + case Qt::RightToLeft: + subMenuRect->setRight(subMenuRect->right() + 20); + break; + } +} + +QDesignerMenu::QDesignerMenu(QWidget *parent) : + QMenu(parent), + m_subMenuPixmap(QPixmap(QLatin1String(":/trolltech/formeditor/images/submenu.png"))), + m_currentIndex(0), + m_addItem(new SpecialMenuAction(this)), + m_addSeparator(new SpecialMenuAction(this)), + m_showSubMenuTimer(new QTimer(this)), + m_deactivateWindowTimer(new QTimer(this)), + m_adjustSizeTimer(new QTimer(this)), + m_editor(new QLineEdit(this)), + m_dragging(false), + m_lastSubMenuIndex(-1) +{ + setContextMenuPolicy(Qt::DefaultContextMenu); + setAcceptDrops(true); // ### fake + setSeparatorsCollapsible(false); + + connect(m_adjustSizeTimer, SIGNAL(timeout()), this, SLOT(slotAdjustSizeNow())); + m_addItem->setText(tr("Type Here")); + addAction(m_addItem); + + m_addSeparator->setText(tr("Add Separator")); + addAction(m_addSeparator); + + connect(m_showSubMenuTimer, SIGNAL(timeout()), this, SLOT(slotShowSubMenuNow())); + + connect(m_deactivateWindowTimer, SIGNAL(timeout()), this, SLOT(slotDeactivateNow())); + + m_editor->setObjectName(QLatin1String("__qt__passive_editor")); + m_editor->hide(); + + m_editor->installEventFilter(this); + installEventFilter(this); +} + +QDesignerMenu::~QDesignerMenu() +{ +} + +void QDesignerMenu::slotAdjustSizeNow() +{ + // Not using a single-shot, since we want to compress the timers if many items are being + // adjusted + m_adjustSizeTimer->stop(); + adjustSize(); +} + +bool QDesignerMenu::handleEvent(QWidget *widget, QEvent *event) +{ + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) { + update(); + + if (widget == m_editor) + return false; + } + + switch (event->type()) { + default: break; + + case QEvent::MouseButtonPress: + return handleMousePressEvent(widget, static_cast(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(widget, static_cast(event)); + case QEvent::MouseButtonDblClick: + return handleMouseDoubleClickEvent(widget, static_cast(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(widget, static_cast(event)); + case QEvent::ContextMenu: + return handleContextMenuEvent(widget, static_cast(event)); + case QEvent::KeyPress: + return handleKeyPressEvent(widget, static_cast(event)); + } + + return true; +} + +void QDesignerMenu::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers) +{ + const int index = findAction(pos); + if (index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + QDesignerFormWindowInterface *fw = formWindow(); + const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction; + if (dropAction == Qt::MoveAction) { + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, actions().at(index + 1)); + fw->commandHistory()->push(cmd); + } + + QDrag *drag = new QDrag(this); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action)); + drag->setMimeData(new ActionRepositoryMimeData(action, dropAction)); + + const int old_index = m_currentIndex; + m_currentIndex = -1; + + if (drag->start(dropAction) == Qt::IgnoreAction) { + if (dropAction == Qt::MoveAction) { + QAction *previous = safeActionAt(index); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, previous); + fw->commandHistory()->push(cmd); + } + + m_currentIndex = old_index; + } +} + +bool QDesignerMenu::handleKeyPressEvent(QWidget * /*widget*/, QKeyEvent *e) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isHidden() && hasFocus()) { // In navigation mode + switch (e->key()) { + + case Qt::Key_Delete: + if (m_currentIndex == -1 || m_currentIndex >= realActionCount()) + break; + hideSubMenu(); + deleteAction(); + break; + + case Qt::Key_Left: + e->accept(); + moveLeft(); + return true; + + case Qt::Key_Right: + e->accept(); + moveRight(); + return true; // no update + + case Qt::Key_Up: + e->accept(); + moveUp(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_Down: + e->accept(); + moveDown(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_PageUp: + m_currentIndex = 0; + break; + + case Qt::Key_PageDown: + m_currentIndex = actions().count() - 1; + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_F2: + e->accept(); + enterEditMode(); + return true; // no update + + case Qt::Key_Escape: + e->ignore(); + setFocus(); + hide(); + closeMenuChain(); + return true; + + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + e->ignore(); + setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed + return true; // no update + + default: { + QAction *action = currentAction(); + if (!action || action->isSeparator() || action == m_addSeparator) { + e->ignore(); + return true; + } else if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) { + showLineEdit(); + QApplication::sendEvent(m_editor, e); + e->accept(); + } else { + e->ignore(); + } + } + return true; + } + } else if (m_editor->hasFocus()) { // In edit mode + switch (e->key()) { + default: + e->ignore(); + return false; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_editor->text().isEmpty()) { + leaveEditMode(ForceAccept); + m_editor->hide(); + setFocus(); + moveDown(false); + break; + } + // fall through + + case Qt::Key_Escape: + m_editor->hide(); + setFocus(); + break; + } + } + + e->accept(); + update(); + + return true; +} + +static void sendMouseEventTo(QWidget *target, const QPoint &targetPoint, const QMouseEvent *event) +{ + QMouseEvent e(event->type(), targetPoint, event->globalPos(), event->button(), event->buttons(), event->modifiers()); + QApplication::sendEvent(target, &e); +} + +bool QDesignerMenu::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + // special case for menubar + QWidget *target = QApplication::widgetAt(event->globalPos()); + QMenuBar *mb = qobject_cast(target); + QDesignerMenu *menu = qobject_cast(target); + if (mb != 0 || menu != 0) { + const QPoint pt = target->mapFromGlobal(event->globalPos()); + QAction *action = mb == 0 ? menu->actionAt(pt) : mb->actionAt(pt); + if (action) + sendMouseEventTo(target, pt, event); + } + return true; + } + + m_currentIndex = findAction(event->pos()); + QAction *action = safeActionAt(m_currentIndex); + + QRect pm_rect; + if (action->menu() || hasSubMenuPixmap(action)) { + pm_rect = subMenuPixmapRect(action); + extendClickableArea(&pm_rect, layoutDirection()); + } + + if (!pm_rect.contains(event->pos()) && m_currentIndex != -1) + enterEditMode(); + + return true; +} + +bool QDesignerMenu::handleMousePressEvent(QWidget * /*widget*/, QMouseEvent *event) +{ + if (!rect().contains(event->pos())) { + QWidget *clickedWidget = QApplication::widgetAt(event->globalPos()); + if (QMenuBar *mb = qobject_cast(clickedWidget)) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + if (QAction *action = mb->actionAt(pt)) { + QMenu * menu = action->menu(); + if (menu == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + } + } + + if (QDesignerMenu *m = qobject_cast(clickedWidget)) { + m->hideSubMenu(); + sendMouseEventTo(m, m->mapFromGlobal(event->globalPos()), event); + } else { + QDesignerMenu *root = findRootMenu(); + root->hide(); + root->hideSubMenu(); + } + if (clickedWidget) { + if (QWidget *focusProxy = clickedWidget->focusProxy()) + clickedWidget = focusProxy; + if (clickedWidget->focusPolicy() != Qt::NoFocus) + clickedWidget->setFocus(Qt::OtherFocusReason); + } + return true; + } + + m_showSubMenuTimer->stop(); + m_startPosition = QPoint(); + event->accept(); + + if (event->button() != Qt::LeftButton) + return true; + + m_startPosition = mapFromGlobal(event->globalPos()); + + const int index = findAction(m_startPosition); + + QAction *action = safeActionAt(index); + QRect pm_rect = subMenuPixmapRect(action); + extendClickableArea(&pm_rect, layoutDirection()); + + const int old_index = m_currentIndex; + m_currentIndex = index; + if ((hasSubMenuPixmap(action) || action->menu() != 0) + && pm_rect.contains(m_startPosition)) { + if (m_currentIndex == m_lastSubMenuIndex) { + hideSubMenu(); + } else + slotShowSubMenuNow(); + } else { + if (index == old_index) { + if (m_currentIndex == m_lastSubMenuIndex) + hideSubMenu(); + } else { + hideSubMenu(); + } + } + + update(); + if (index != old_index) + selectCurrentAction(); + + return true; +} + +bool QDesignerMenu::handleMouseReleaseEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleMouseMoveEvent(QWidget *, QMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + + if (QMenuBar *mb = qobject_cast(QApplication::widgetAt(event->globalPos()))) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + QAction *action = mb->actionAt(pt); + if (action && action->menu() == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + // hide the popup Qt will replay the event + slotDeactivateNow(); + } + return true; + } + + if (m_startPosition.isNull()) + return true; + + event->accept(); + + const QPoint pos = mapFromGlobal(event->globalPos()); + + if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance()) + return true; + + startDrag(m_startPosition, event->modifiers()); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleContextMenuEvent(QWidget *, QContextMenuEvent *event) +{ + event->accept(); + + const int index = findAction(mapFromGlobal(event->globalPos())); + QAction *action = safeActionAt(index); + if (qobject_cast(action)) + return true; + + QMenu menu; + QVariant itemData; + qVariantSetValue(itemData, action); + + QAction *addSeparatorAction = menu.addAction(tr("Insert separator")); + addSeparatorAction->setData(itemData); + + QAction *removeAction = 0; + if (action->isSeparator()) + removeAction = menu.addAction(tr("Remove separator")); + else + removeAction = menu.addAction(tr("Remove action '%1'").arg(action->objectName())); + removeAction->setData(itemData); + + connect(addSeparatorAction, SIGNAL(triggered(bool)), this, SLOT(slotAddSeparator())); + connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(slotRemoveSelectedAction())); + menu.exec(event->globalPos()); + + return true; +} + +void QDesignerMenu::slotAddSeparator() +{ + QAction *action = qobject_cast(sender()); + if (!action) + return; + + QAction *a = qvariant_cast(action->data()); + Q_ASSERT(a != 0); + + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos); + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, action_before); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); +} + +void QDesignerMenu::slotRemoveSelectedAction() +{ + if (QAction *action = qobject_cast(sender())) + if (QAction *a = qvariant_cast(action->data())) + deleteAction(a); +} + +void QDesignerMenu::deleteAction(QAction *a) +{ + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, a, action_before); + fw->commandHistory()->push(cmd); +} + +QRect QDesignerMenu::subMenuPixmapRect(QAction *action) const +{ + const QRect g = actionGeometry(action); + const int x = layoutDirection() == Qt::LeftToRight ? (g.right() - m_subMenuPixmap.width() - 2) : 2; + const int y = g.top() + (g.height() - m_subMenuPixmap.height())/2 + 1; + return QRect(x, y, m_subMenuPixmap.width(), m_subMenuPixmap.height()); +} + +bool QDesignerMenu::hasSubMenuPixmap(QAction *action) const +{ + return action != 0 + && qobject_cast(action) == 0 + && !action->isSeparator() + && !action->menu() + && canCreateSubMenu(action); +} + +void QDesignerMenu::showEvent ( QShowEvent * event ) +{ + selectCurrentAction(); + QMenu::showEvent (event); +} + +void QDesignerMenu::paintEvent(QPaintEvent *event) +{ + QMenu::paintEvent(event); + + QPainter p(this); + + QAction *current = currentAction(); + + foreach (QAction *a, actions()) { + const QRect g = actionGeometry(a); + + if (qobject_cast(a)) { + QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom()); + lg.setColorAt(0.0, Qt::transparent); + lg.setColorAt(0.7, QColor(0, 0, 0, 32)); + lg.setColorAt(1.0, Qt::transparent); + + p.fillRect(g, lg); + } else if (hasSubMenuPixmap(a)) { + p.drawPixmap(subMenuPixmapRect(a).topLeft(), m_subMenuPixmap); + } + } + + if (!hasFocus() || !current || m_dragging) + return; + + if (QDesignerMenu *menu = parentMenu()) { + if (menu->dragging()) + return; + } + + if (QDesignerMenuBar *menubar = qobject_cast(parentWidget())) { + if (menubar->dragging()) + return; + } + + const QRect g = actionGeometry(current); + drawSelection(&p, g.adjusted(1, 1, -3, -3)); +} + +bool QDesignerMenu::dragging() const +{ + return m_dragging; +} + +QDesignerMenu *QDesignerMenu::findRootMenu() const +{ + if (parentMenu()) + return parentMenu()->findRootMenu(); + + return const_cast(this); +} + +QDesignerMenu *QDesignerMenu::findActivatedMenu() const +{ + QList candidates; + candidates.append(const_cast(this)); + candidates += qFindChildren(this); + + foreach (QDesignerMenu *m, candidates) { + if (m == qApp->activeWindow()) + return m; + } + + return 0; +} + +bool QDesignerMenu::eventFilter(QObject *object, QEvent *event) +{ + if (object != this && object != m_editor) { + return false; + } + + if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) { + leaveEditMode(Default); + m_editor->hide(); + update(); + return false; + } + + bool dispatch = true; + + switch (event->type()) { + default: break; + + case QEvent::WindowDeactivate: + deactivateMenu(); + break; + case QEvent::ContextMenu: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + + while (QApplication::activePopupWidget() && !qobject_cast(QApplication::activePopupWidget())) { + QApplication::activePopupWidget()->close(); + } + + // fall through + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::MouseMove: + dispatch = (object != m_editor); + // no break + + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + if (dispatch) + if (QWidget *widget = qobject_cast(object)) + if (widget == this || isAncestorOf(widget)) + return handleEvent(widget, event); + break; + } + + return false; +}; + +int QDesignerMenu::findAction(const QPoint &pos) const +{ + const int index = actionIndexAt(this, pos, Qt::Vertical); + if (index == -1) + return realActionCount(); + + return index; +} + +void QDesignerMenu::adjustIndicator(const QPoint &pos) +{ + if (QDesignerActionProviderExtension *a = actionProvider()) { + a->adjustIndicator(pos); + } +} + +QDesignerMenu::ActionDragCheck QDesignerMenu::checkAction(QAction *action) const +{ + if (!action || (action->menu() && action->menu()->parentWidget() != const_cast(this))) + return NoActionDrag; // menu action!! nothing to do + + if (!Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) + return NoActionDrag; // the action belongs to another form window + + if (actions().contains(action)) + return ActionDragOnSubMenu; // we already have the action in the menu + + return AcceptActionDrag; +} + +void QDesignerMenu::dragEnterEvent(QDragEnterEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + d->accept(event); + m_dragging = true; + break; + case AcceptActionDrag: + d->accept(event); + m_dragging = true; + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenu::dragMoveEvent(QDragMoveEvent *event) +{ + if (actionGeometry(m_addSeparator).contains(event->pos())) { + event->ignore(); + adjustIndicator(QPoint(-1, -1)); + return; + } + + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + const ActionDragCheck dc = checkAction(action); + switch (dc) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + case AcceptActionDrag: { // Do not pop up submenu of action being dragged + const int newIndex = findAction(event->pos()); + if (safeActionAt(newIndex) != action) { + m_currentIndex = newIndex; + if (m_lastSubMenuIndex != m_currentIndex) + m_showSubMenuTimer->start(300); + } + if (dc == AcceptActionDrag) { + adjustIndicator(event->pos()); + d->accept(event); + } else { + event->ignore(); + } + } + break; + } +} + +void QDesignerMenu::dragLeaveEvent(QDragLeaveEvent *) +{ + m_dragging = false; + adjustIndicator(QPoint(-1, -1)); + m_showSubMenuTimer->stop(); +} + +void QDesignerMenu::dropEvent(QDropEvent *event) +{ + m_showSubMenuTimer->stop(); + hideSubMenu(); + m_dragging = false; + + QDesignerFormWindowInterface *fw = formWindow(); + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + QAction *action = d->actionList().first(); + if (action && checkAction(action) == AcceptActionDrag) { + event->acceptProposedAction(); + int index = findAction(event->pos()); + index = qMin(index, actions().count() - 1); + + fw->beginCommand(tr("Insert action")); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = index; + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + update(); + fw->endCommand(); + } else { + event->ignore(); + } + adjustIndicator(QPoint(-1, -1)); +} + +void QDesignerMenu::actionEvent(QActionEvent *event) +{ + QMenu::actionEvent(event); + m_adjustSizeTimer->start(0); +} + +QDesignerFormWindowInterface *QDesignerMenu::formWindow() const +{ + if (parentMenu()) + return parentMenu()->formWindow(); + + return QDesignerFormWindowInterface::findFormWindow(parentWidget()); +} + +QDesignerActionProviderExtension *QDesignerMenu::actionProvider() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + return qt_extension(core->extensionManager(), this); + } + + return 0; +} + +void QDesignerMenu::closeMenuChain() +{ + m_showSubMenuTimer->stop(); + + QWidget *w = this; + while (w && qobject_cast(w)) + w = w->parentWidget(); + + if (w) { + foreach (QMenu *subMenu, qFindChildren(w)) { + subMenu->hide(); + } + } + + m_lastSubMenuIndex = -1; +} + +// Close submenu using the left/right keys according to layoutDirection(). +// Return false to indicate the event must be propagated to the menu bar. +bool QDesignerMenu::hideSubMenuOnCursorKey() +{ + if (parentMenu()) { + hide(); + return true; + } + closeMenuChain(); + update(); + if (parentMenuBar()) + return false; + return true; +} + +// Open a submenu using the left/right keys according to layoutDirection(). +// Return false to indicate the event must be propagated to the menu bar. +bool QDesignerMenu::showSubMenuOnCursorKey() +{ + const QAction *action = currentAction(); + + if (qobject_cast(action) || action->isSeparator()) { + closeMenuChain(); + if (parentMenuBar()) + return false; + return true; + } + m_lastSubMenuIndex = -1; // force a refresh + slotShowSubMenuNow(); + return true; +} + +void QDesignerMenu::moveLeft() +{ + const bool handled = layoutDirection() == Qt::LeftToRight ? + hideSubMenuOnCursorKey() : showSubMenuOnCursorKey(); + if (!handled) + parentMenuBar()->moveLeft(); +} + +void QDesignerMenu::moveRight() +{ + const bool handled = layoutDirection() == Qt::LeftToRight ? + showSubMenuOnCursorKey() : hideSubMenuOnCursorKey(); + if (!handled) + parentMenuBar()->moveRight(); +} + +void QDesignerMenu::moveUp(bool ctrl) +{ + if (m_currentIndex == 0) { + hide(); + return; + } + + if (ctrl) + (void) swap(m_currentIndex, m_currentIndex - 1); + --m_currentIndex; + m_currentIndex = qMax(0, m_currentIndex); + // Always re-select, swapping destroys order + update(); + selectCurrentAction(); +} + +void QDesignerMenu::moveDown(bool ctrl) +{ + if (m_currentIndex == actions().count() - 1) { + return; + } + + if (ctrl) + (void) swap(m_currentIndex + 1, m_currentIndex); + + ++m_currentIndex; + m_currentIndex = qMin(actions().count() - 1, m_currentIndex); + update(); + if (!ctrl) + selectCurrentAction(); +} + +QAction *QDesignerMenu::currentAction() const +{ + if (m_currentIndex < 0 || m_currentIndex >= actions().count()) + return 0; + + return safeActionAt(m_currentIndex); +} + +int QDesignerMenu::realActionCount() const +{ + return actions().count() - 2; // 2 fake actions +} + +void QDesignerMenu::selectCurrentAction() +{ + QAction *action = currentAction(); + if (!action || action == m_addSeparator || action == m_addItem) + return; + + QDesignerObjectInspector *oi = 0; + if (QDesignerFormWindowInterface *fw = formWindow()) + oi = qobject_cast(fw->core()->objectInspector()); + + if (!oi) + return; + + oi->clearSelection(); + if (QMenu *menu = action->menu()) + oi->selectObject(menu); + else + oi->selectObject(action); +} + +void QDesignerMenu::createRealMenuAction(QAction *action) +{ + if (action->menu()) + return; // nothing to do + + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = formWindow()->core(); + + QDesignerMenu *menu = findOrCreateSubMenu(action); + m_subMenus.remove(action); + + action->setMenu(menu); + menu->setTitle(action->text()); + + Q_ASSERT(fw); + + core->widgetFactory()->initialize(menu); + + const QString niceObjectName = ActionEditor::actionTextToName(menu->title(), QLatin1String("menu")); + menu->setObjectName(niceObjectName); + + core->metaDataBase()->add(menu); + fw->ensureUniqueObjectName(menu); + + QAction *menuAction = menu->menuAction(); + core->metaDataBase()->add(menuAction); +} + +void QDesignerMenu::removeRealMenu(QAction *action) +{ + QDesignerMenu *menu = qobject_cast(action->menu()); + if (menu == 0) + return; + action->setMenu(0); + m_subMenus.insert(action, menu); + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->remove(menu); +} + +QDesignerMenu *QDesignerMenu::findOrCreateSubMenu(QAction *action) +{ + if (action->menu()) + return qobject_cast(action->menu()); + + QDesignerMenu *menu = m_subMenus.value(action); + if (!menu) { + menu = new QDesignerMenu(this); + m_subMenus.insert(action, menu); + } + + return menu; +} + +bool QDesignerMenu::canCreateSubMenu(QAction *action) const // ### improve it's a bit too slow +{ + foreach (const QWidget *aw, action->associatedWidgets()) + if (aw != this) { + if (const QMenu *m = qobject_cast(aw)) { + if (m->actions().contains(action)) + return false; // sorry + } else { + if (const QToolBar *tb = qobject_cast(aw)) + if (tb->actions().contains(action)) + return false; // sorry + } + } + return true; +} + +void QDesignerMenu::slotShowSubMenuNow() +{ + m_showSubMenuTimer->stop(); + + if (m_lastSubMenuIndex == m_currentIndex) + return; + + if (m_lastSubMenuIndex != -1) + hideSubMenu(); + + if (m_currentIndex >= realActionCount()) + return; + + QAction *action = currentAction(); + + if (action->isSeparator() || !canCreateSubMenu(action)) + return; + + if (QMenu *menu = findOrCreateSubMenu(action)) { + if (!menu->isVisible()) { + if ((menu->windowFlags() & Qt::Popup) != Qt::Popup) + menu->setWindowFlags(Qt::Popup); + const QRect g = actionGeometry(action); + if (layoutDirection() == Qt::LeftToRight) { + menu->move(mapToGlobal(g.topRight())); + } else { + // The position is not initially correct due to the unknown width, + // causing it to overlap a bit the first time it is invoked. + const QSize menuSize = menu->size(); + QPoint point = g.topLeft() - QPoint(menu->width() + 10, 0); + menu->move(mapToGlobal(point)); + } + menu->show(); + menu->setFocus(); + } else { + menu->raise(); + } + menu->setFocus(); + + m_lastSubMenuIndex = m_currentIndex; + } +} + +void QDesignerMenu::showSubMenu(QAction *action) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isVisible() || !action || qobject_cast(action) + || action->isSeparator() || !isVisible()) + return; + + m_showSubMenuTimer->start(300); +} + +QDesignerMenu *QDesignerMenu::parentMenu() const +{ + return qobject_cast(parentWidget()); +} + +QDesignerMenuBar *QDesignerMenu::parentMenuBar() const +{ + if (QDesignerMenuBar *mb = qobject_cast(parentWidget())) { + return mb; + } else if (QDesignerMenu *m = parentMenu()) { + return m->parentMenuBar(); + } + + return 0; +} + +void QDesignerMenu::setVisible(bool visible) +{ + if (visible) + m_currentIndex = 0; + else + m_lastSubMenuIndex = -1; + + QMenu::setVisible(visible); + +} + +void QDesignerMenu::adjustSpecialActions() +{ + removeAction(m_addItem); + removeAction(m_addSeparator); + addAction(m_addItem); + addAction(m_addSeparator); +} + +bool QDesignerMenu::interactive(bool i) +{ + const bool old = m_interactive; + m_interactive = i; + return old; +} + +void QDesignerMenu::enterEditMode() +{ + if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) { + showLineEdit(); + } else { + hideSubMenu(); + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, safeActionAt(realActionCount())); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); + + m_currentIndex = actions().indexOf(m_addItem); + update(); + } +} + +void QDesignerMenu::leaveEditMode(LeaveEditMode mode) +{ + if (mode == Default) + return; + + QAction *action = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + if (m_currentIndex < realActionCount()) { + action = safeActionAt(m_currentIndex); + fw->beginCommand(QApplication::translate("Command", "Set action text")); + } else { + Q_ASSERT(fw != 0); + fw->beginCommand(QApplication::translate("Command", "Insert action")); + action = createAction(ActionEditor::actionTextToName(m_editor->text())); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, currentAction()); + fw->commandHistory()->push(cmd); + } + + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, QLatin1String("text"), m_editor->text()); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + + update(); + fw->endCommand(); +} + +QAction *QDesignerMenu::safeMenuAction(QDesignerMenu *menu) const +{ + QAction *action = menu->menuAction(); + + if (!action) + action = m_subMenus.key(menu); + + return action; +} + +void QDesignerMenu::showLineEdit() +{ + m_showSubMenuTimer->stop(); + + QAction *action = 0; + + if (m_currentIndex < realActionCount()) + action = safeActionAt(m_currentIndex); + else + action = m_addItem; + + if (action->isSeparator()) + return; + + hideSubMenu(); + + // open edit field for item name + setFocus(); + + const QString text = action != m_addItem ? action->text() : QString(); + m_editor->setText(text); + m_editor->selectAll(); + m_editor->setGeometry(actionGeometry(action).adjusted(1, 1, -2, -2)); + m_editor->show(); + m_editor->setFocus(); +} + +QAction *QDesignerMenu::createAction(const QString &objectName, bool separator) +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + return ToolBarEventFilter::createAction(fw, objectName, separator); +} + +// ### share with QDesignerMenu::swap +bool QDesignerMenu::swap(int a, int b) +{ + const int left = qMin(a, b); + int right = qMax(a, b); + + QAction *action_a = safeActionAt(left); + QAction *action_b = safeActionAt(right); + + if (action_a == action_b + || !action_a + || !action_b + || qobject_cast(action_a) + || qobject_cast(action_b)) + return false; // nothing to do + + right = qMin(right, realActionCount()); + if (right < 0) + return false; // nothing to do + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(QApplication::translate("Command", "Move action")); + + QAction *action_b_before = safeActionAt(right + 1); + + RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw); + cmd1->init(this, action_b, action_b_before, false); + fw->commandHistory()->push(cmd1); + + QAction *action_a_before = safeActionAt(left + 1); + + InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw); + cmd2->init(this, action_b, action_a_before, false); + fw->commandHistory()->push(cmd2); + + RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw); + cmd3->init(this, action_a, action_b, false); + fw->commandHistory()->push(cmd3); + + InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw); + cmd4->init(this, action_a, action_b_before, true); + fw->commandHistory()->push(cmd4); + + fw->endCommand(); + + return true; +} + +QAction *QDesignerMenu::safeActionAt(int index) const +{ + if (index < 0 || index >= actions().count()) + return 0; + + return actions().at(index); +} + +void QDesignerMenu::hideSubMenu() +{ + m_lastSubMenuIndex = -1; + foreach (QMenu *subMenu, qFindChildren(this)) { + subMenu->hide(); + } +} + +void QDesignerMenu::deleteAction() +{ + QAction *action = currentAction(); + const int pos = actions().indexOf(action); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, action_before); + fw->commandHistory()->push(cmd); + + update(); +} + +void QDesignerMenu::deactivateMenu() +{ + m_deactivateWindowTimer->start(10); +} + +void QDesignerMenu::slotDeactivateNow() +{ + m_deactivateWindowTimer->stop(); + + if (m_dragging) + return; + + QDesignerMenu *root = findRootMenu(); + + if (! root->findActivatedMenu()) { + root->hide(); + root->hideSubMenu(); + } +} + +void QDesignerMenu::drawSelection(QPainter *p, const QRect &r) +{ + p->save(); + + QColor c = Qt::blue; + p->setPen(QPen(c, 1)); + c.setAlpha(32); + p->setBrush(c); + p->drawRect(r); + + p->restore(); +} + +void QDesignerMenu::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenu::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_menu_p.h b/designer/lib/shared/qdesigner_menu_p.h new file mode 100644 index 0000000..a0e1932 --- /dev/null +++ b/designer/lib/shared/qdesigner_menu_p.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MENU_H +#define QDESIGNER_MENU_H + +#include "shared_global_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QTimer; +class QLineEdit; + +class QDesignerFormWindowInterface; +class QDesignerActionProviderExtension; +class QDesignerMenu; +class QDesignerMenuBar; +class QPainter; +class QMimeData; + +namespace qdesigner_internal { + class CreateSubmenuCommand; + class ActionInsertionCommand; +} + +class QDESIGNER_SHARED_EXPORT QDesignerMenu: public QMenu +{ + Q_OBJECT +public: + QDesignerMenu(QWidget *parent = 0); + virtual ~QDesignerMenu(); + + bool eventFilter(QObject *object, QEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + QDesignerActionProviderExtension *actionProvider(); + + QDesignerMenu *parentMenu() const; + QDesignerMenuBar *parentMenuBar() const; + + virtual void setVisible(bool visible); + + void adjustSpecialActions(); + + bool interactive(bool i); + void createRealMenuAction(QAction *action); + void removeRealMenu(QAction *action); + + static void drawSelection(QPainter *p, const QRect &r); + + bool dragging() const; + + void closeMenuChain(); + + void moveLeft(); + void moveRight(); + void moveUp(bool ctrl); + void moveDown(bool ctrl); + + // Helper for MenuTaskMenu extension + void deleteAction(QAction *a); + +private slots: + void slotAddSeparator(); + void slotRemoveSelectedAction(); + void slotShowSubMenuNow(); + void slotDeactivateNow(); + void slotAdjustSizeNow(); + +protected: + virtual void actionEvent(QActionEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void showEvent(QShowEvent *event); + + bool handleEvent(QWidget *widget, QEvent *event); + bool handleMouseDoubleClickEvent(QWidget *widget, QMouseEvent *event); + bool handleMousePressEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseReleaseEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseMoveEvent(QWidget *widget, QMouseEvent *event); + bool handleContextMenuEvent(QWidget *widget, QContextMenuEvent *event); + bool handleKeyPressEvent(QWidget *widget, QKeyEvent *event); + + void startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers); + + void adjustIndicator(const QPoint &pos); + int findAction(const QPoint &pos) const; + + QAction *currentAction() const; + int realActionCount() const; + enum ActionDragCheck { NoActionDrag, ActionDragOnSubMenu, AcceptActionDrag }; + ActionDragCheck checkAction(QAction *action) const; + + void showSubMenu(QAction *action); + + enum LeaveEditMode { + Default = 0, + ForceAccept + }; + + void enterEditMode(); + void leaveEditMode(LeaveEditMode mode); + void showLineEdit(); + + QAction *createAction(const QString &text, bool separator = false); + QDesignerMenu *findOrCreateSubMenu(QAction *action); + + QAction *safeActionAt(int index) const; + QAction *safeMenuAction(QDesignerMenu *menu) const; + bool swap(int a, int b); + + void hideSubMenu(); + void deleteAction(); + void deactivateMenu(); + + bool canCreateSubMenu(QAction *action) const; + QDesignerMenu *findRootMenu() const; + QDesignerMenu *findActivatedMenu() const; + + QRect subMenuPixmapRect(QAction *action) const; + bool hasSubMenuPixmap(QAction *action) const; + + void selectCurrentAction(); + +private: + bool hideSubMenuOnCursorKey(); + bool showSubMenuOnCursorKey(); + const QPixmap m_subMenuPixmap; + + QPoint m_startPosition; + int m_currentIndex; + QAction *m_addItem; + QAction *m_addSeparator; + QHash m_subMenus; + QTimer *m_showSubMenuTimer; + QTimer *m_deactivateWindowTimer; + QTimer *m_adjustSizeTimer; + bool m_interactive; + QLineEdit *m_editor; + bool m_dragging; + int m_lastSubMenuIndex; + + friend class qdesigner_internal::CreateSubmenuCommand; + friend class qdesigner_internal::ActionInsertionCommand; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MENU_H diff --git a/designer/lib/shared/qdesigner_menubar.cpp b/designer/lib/shared/qdesigner_menubar.cpp new file mode 100644 index 0000000..7eb6907 --- /dev/null +++ b/designer/lib/shared/qdesigner_menubar.cpp @@ -0,0 +1,979 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_menubar_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "actioneditor_p.h" +#include "qdesigner_utils_p.h" +#include "promotiontaskmenu_p.h" +#include "qdesigner_objectinspector_p.h" + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +typedef QList ActionList; + +using namespace qdesigner_internal; + +namespace qdesigner_internal +{ + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +SpecialMenuAction::SpecialMenuAction(QObject *parent) + : QAction(parent) +{ +} + +SpecialMenuAction::~SpecialMenuAction() +{ +} + + +} // namespace qdesigner_internal + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +QDesignerMenuBar::QDesignerMenuBar(QWidget *parent) : + QMenuBar(parent), + m_addMenu(new SpecialMenuAction(this)), + m_currentIndex(0), + m_interactive(true), + m_editor(new QLineEdit(this)), + m_dragging(false), + m_lastMenuActionIndex( -1), + m_promotionTaskMenu(new PromotionTaskMenu(this, PromotionTaskMenu::ModeSingleWidget, this)) +{ + setContextMenuPolicy(Qt::DefaultContextMenu); + + setAcceptDrops(true); // ### fake + // Fake property: Keep the menu bar editable in the form even if a native menu bar is used. + setNativeMenuBar(false); + + m_addMenu->setText(tr("Type Here")); + addAction(m_addMenu); + + QFont italic; + italic.setItalic(true); + m_addMenu->setFont(italic); + + m_editor->setObjectName(QLatin1String("__qt__passive_editor")); + m_editor->hide(); + m_editor->installEventFilter(this); + installEventFilter(this); +} + +QDesignerMenuBar::~QDesignerMenuBar() +{ +} + +void QDesignerMenuBar::paintEvent(QPaintEvent *event) +{ + QMenuBar::paintEvent(event); + + QPainter p(this); + + foreach (QAction *a, actions()) { + if (qobject_cast(a)) { + const QRect g = actionGeometry(a); + QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom()); + lg.setColorAt(0.0, Qt::transparent); + lg.setColorAt(0.7, QColor(0, 0, 0, 32)); + lg.setColorAt(1.0, Qt::transparent); + + p.fillRect(g, lg); + } + } + + QAction *action = currentAction(); + + if (m_dragging || !action) + return; + + if (hasFocus()) { + const QRect g = actionGeometry(action); + QDesignerMenu::drawSelection(&p, g.adjusted(1, 1, -1, -1)); + } else if (action->menu() && action->menu()->isVisible()) { + const QRect g = actionGeometry(action); + p.drawRect(g.adjusted(1, 1, -1, -1)); + } +} + +bool QDesignerMenuBar::handleEvent(QWidget *widget, QEvent *event) +{ + if (!formWindow()) + return false; + + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) + update(); + + switch (event->type()) { + default: break; + + case QEvent::MouseButtonDblClick: + return handleMouseDoubleClickEvent(widget, static_cast(event)); + case QEvent::MouseButtonPress: + return handleMousePressEvent(widget, static_cast(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(widget, static_cast(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(widget, static_cast(event)); + case QEvent::ContextMenu: + return handleContextMenuEvent(widget, static_cast(event)); + case QEvent::KeyPress: + return handleKeyPressEvent(widget, static_cast(event)); + case QEvent::FocusIn: + case QEvent::FocusOut: + return widget != m_editor; + } + + return true; +} + +bool QDesignerMenuBar::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event) +{ + if (!rect().contains(event->pos())) + return true; + + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + event->accept(); + + m_startPosition = QPoint(); + + m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal); + if (m_currentIndex != -1) { + showLineEdit(); + } + + return true; +} + +bool QDesignerMenuBar::handleKeyPressEvent(QWidget *, QKeyEvent *e) +{ + if (m_editor->isHidden()) { // In navigation mode + switch (e->key()) { + + case Qt::Key_Delete: + if (m_currentIndex == -1 || m_currentIndex >= realActionCount()) + break; + hideMenu(); + deleteMenu(); + break; + + case Qt::Key_Left: + e->accept(); + moveLeft(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_Right: + e->accept(); + moveRight(e->modifiers() & Qt::ControlModifier); + return true; // no update + + case Qt::Key_Up: + e->accept(); + moveUp(); + return true; + + case Qt::Key_Down: + e->accept(); + moveDown(); + return true; + + case Qt::Key_PageUp: + m_currentIndex = 0; + break; + + case Qt::Key_PageDown: + m_currentIndex = actions().count() - 1; + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + e->accept(); + enterEditMode(); + return true; // no update + + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Escape: + e->ignore(); + setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed + return true; // no update + + default: + if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) { + showLineEdit(); + QApplication::sendEvent(m_editor, e); + e->accept(); + } else { + e->ignore(); + } + return true; + } + } else { // In edit mode + switch (e->key()) { + default: + return false; + + case Qt::Key_Control: + e->ignore(); + return true; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_editor->text().isEmpty()) { + leaveEditMode(ForceAccept); + if (m_lastFocusWidget) + m_lastFocusWidget->setFocus(); + + m_editor->hide(); + showMenu(); + break; + } + // fall through + + case Qt::Key_Escape: + update(); + setFocus(); + break; + } + } + + e->accept(); + update(); + + return true; +} + +void QDesignerMenuBar::startDrag(const QPoint &pos) +{ + const int index = findAction(pos); + if (m_currentIndex == -1 || index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, actions().at(index + 1)); + fw->commandHistory()->push(cmd); + + adjustSize(); + + hideMenu(index); + + QDrag *drag = new QDrag(this); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action)); + drag->setMimeData(new ActionRepositoryMimeData(action, Qt::MoveAction)); + + const int old_index = m_currentIndex; + m_currentIndex = -1; + + if (drag->start(Qt::MoveAction) == Qt::IgnoreAction) { + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = old_index; + adjustSize(); + } +} + +bool QDesignerMenuBar::handleMousePressEvent(QWidget *, QMouseEvent *event) +{ + m_startPosition = QPoint(); + event->accept(); + + if (event->button() != Qt::LeftButton) + return true; + + m_startPosition = event->pos(); + const int newIndex = actionIndexAt(this, m_startPosition, Qt::Horizontal); + const bool changed = newIndex != m_currentIndex; + m_currentIndex = newIndex; + updateCurrentAction(changed); + + return true; +} + +bool QDesignerMenuBar::handleMouseReleaseEvent(QWidget *, QMouseEvent *event) +{ + m_startPosition = QPoint(); + + if (event->button() != Qt::LeftButton) + return true; + + event->accept(); + m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal); + if (!m_editor->isVisible() && m_currentIndex != -1 && m_currentIndex < realActionCount()) + showMenu(); + + return true; +} + +bool QDesignerMenuBar::handleMouseMoveEvent(QWidget *, QMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (m_startPosition.isNull()) + return true; + + const QPoint pos = mapFromGlobal(event->globalPos()); + + if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance()) + return true; + + const int index = actionIndexAt(this, m_startPosition, Qt::Horizontal); + if (index < actions().count()) { + hideMenu(index); + update(); + } + + startDrag(m_startPosition); + m_startPosition = QPoint(); + + return true; +} + +ActionList QDesignerMenuBar::contextMenuActions() +{ + ActionList rc; + if (QAction *action = safeActionAt(m_currentIndex)) { + if (!qobject_cast(action)) { + QVariant itemData; + qVariantSetValue(itemData, action); + + QAction *remove_action = new QAction(tr("Remove Menu '%1'").arg(action->menu()->objectName()), 0); + remove_action->setData(itemData); + connect(remove_action, SIGNAL(triggered()), this, SLOT(deleteMenu())); + rc.push_back(remove_action); + QAction *sep = new QAction(0); + sep->setSeparator(true); + rc.push_back(sep); + } + } + + m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::TrailingSeparator, rc); + + QAction *remove_menubar = new QAction(tr("Remove Menu Bar"), 0); + connect(remove_menubar, SIGNAL(triggered()), this, SLOT(slotRemoveMenuBar())); + rc.push_back(remove_menubar); + return rc; +} + +bool QDesignerMenuBar::handleContextMenuEvent(QWidget *, QContextMenuEvent *event) +{ + event->accept(); + + m_currentIndex = actionIndexAt(this, mapFromGlobal(event->globalPos()), Qt::Horizontal); + + update(); + + QMenu menu; + const ActionList al = contextMenuActions(); + const ActionList::const_iterator acend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it) + menu.addAction(*it); + menu.exec(event->globalPos()); + return true; +} + +void QDesignerMenuBar::slotRemoveMenuBar() +{ + Q_ASSERT(formWindow() != 0); + + QDesignerFormWindowInterface *fw = formWindow(); + + DeleteMenuBarCommand *cmd = new DeleteMenuBarCommand(fw); + cmd->init(this); + fw->commandHistory()->push(cmd); +} + +void QDesignerMenuBar::focusOutEvent(QFocusEvent *event) +{ + QMenuBar::focusOutEvent(event); +} + +void QDesignerMenuBar::enterEditMode() +{ + if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) { + showLineEdit(); + } +} + +void QDesignerMenuBar::leaveEditMode(LeaveEditMode mode) +{ + m_editor->releaseKeyboard(); + + if (mode == Default) + return; + + if (m_editor->text().isEmpty()) + return; + + QAction *action = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + + if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) { + action = safeActionAt(m_currentIndex); + fw->beginCommand(QApplication::translate("Command", "Change Title")); + } else { + fw->beginCommand(QApplication::translate("Command", "Insert Menu")); + const QString niceObjectName = ActionEditor::actionTextToName(m_editor->text(), QLatin1String("menu")); + QMenu *menu = qobject_cast(fw->core()->widgetFactory()->createWidget(QLatin1String("QMenu"), this)); + fw->core()->widgetFactory()->initialize(menu); + menu->setObjectName(niceObjectName); + menu->setTitle(tr("Menu")); + fw->ensureUniqueObjectName(menu); + action = menu->menuAction(); + AddMenuActionCommand *cmd = new AddMenuActionCommand(fw); + cmd->init(action, m_addMenu, this, this); + fw->commandHistory()->push(cmd); + } + + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, QLatin1String("text"), m_editor->text()); + fw->commandHistory()->push(cmd); + fw->endCommand(); +} + +void QDesignerMenuBar::showLineEdit() +{ + QAction *action = 0; + + if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) + action = safeActionAt(m_currentIndex); + else + action = m_addMenu; + + if (action->isSeparator()) + return; + + // hideMenu(); + + m_lastFocusWidget = qApp->focusWidget(); + + // open edit field for item name + const QString text = action != m_addMenu ? action->text() : QString(); + + m_editor->setText(text); + m_editor->selectAll(); + m_editor->setGeometry(actionGeometry(action)); + m_editor->show(); + qApp->setActiveWindow(m_editor); + m_editor->setFocus(); + m_editor->grabKeyboard(); +} + +bool QDesignerMenuBar::eventFilter(QObject *object, QEvent *event) +{ + if (object != this && object != m_editor) + return false; + + if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) { + leaveEditMode(Default); + m_editor->hide(); + update(); + return true; + } + + bool dispatch = true; + + switch (event->type()) { + default: break; + + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::ContextMenu: + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + dispatch = (object != m_editor); + // no break + + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + { + QWidget *widget = qobject_cast(object); + + if (dispatch && widget && (widget == this || isAncestorOf(widget))) + return handleEvent(widget, event); + } break; + + case QEvent::Shortcut: + event->accept(); + return true; + } + + return false; +}; + +int QDesignerMenuBar::findAction(const QPoint &pos) const +{ + const int index = actionIndexAt(this, pos, Qt::Horizontal); + if (index == -1) + return realActionCount(); + + return index; +} + +void QDesignerMenuBar::adjustIndicator(const QPoint &pos) +{ + const int index = findAction(pos); + QAction *action = safeActionAt(index); + Q_ASSERT(action != 0); + + if (pos != QPoint(-1, -1)) { + QDesignerMenu *m = qobject_cast(action->menu()); + if (!m || m->parentMenu()) { + m_currentIndex = index; + showMenu(index); + } + } + + if (QDesignerActionProviderExtension *a = actionProvider()) { + a->adjustIndicator(pos); + } +} + +QDesignerMenuBar::ActionDragCheck QDesignerMenuBar::checkAction(QAction *action) const +{ + // action belongs to another form + if (!action || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) + return NoActionDrag; + + if (!action->menu()) + return ActionDragOnSubMenu; // simple action only on sub menus + + QDesignerMenu *m = qobject_cast(action->menu()); + if (m && m->parentMenu()) + return ActionDragOnSubMenu; // it looks like a submenu + + if (actions().contains(action)) + return ActionDragOnSubMenu; // we already have the action in the menubar + + return AcceptActionDrag; +} + +void QDesignerMenuBar::dragEnterEvent(QDragEnterEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + m_dragging = true; + d->accept(event); + break; + case AcceptActionDrag: + m_dragging = true; + d->accept(event); + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenuBar::dragMoveEvent(QDragMoveEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + QAction *action = d->actionList().first(); + + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + event->ignore(); + showMenu(findAction(event->pos())); + break; + case AcceptActionDrag: + d->accept(event); + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenuBar::dragLeaveEvent(QDragLeaveEvent *) +{ + m_dragging = false; + + adjustIndicator(QPoint(-1, -1)); +} + +void QDesignerMenuBar::dropEvent(QDropEvent *event) +{ + m_dragging = false; + + if (const ActionRepositoryMimeData *d = qobject_cast(event->mimeData())) { + + QAction *action = d->actionList().first(); + if (checkAction(action) == AcceptActionDrag) { + event->acceptProposedAction(); + int index = findAction(event->pos()); + index = qMin(index, actions().count() - 1); + + QDesignerFormWindowInterface *fw = formWindow(); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = index; + update(); + adjustIndicator(QPoint(-1, -1)); + return; + } + } + event->ignore(); +} + +void QDesignerMenuBar::actionEvent(QActionEvent *event) +{ + QMenuBar::actionEvent(event); +} + +QDesignerFormWindowInterface *QDesignerMenuBar::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast(this)); +} + +QDesignerActionProviderExtension *QDesignerMenuBar::actionProvider() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + return qt_extension(core->extensionManager(), this); + } + + return 0; +} + +QAction *QDesignerMenuBar::currentAction() const +{ + if (m_currentIndex < 0 || m_currentIndex >= actions().count()) + return 0; + + return safeActionAt(m_currentIndex); +} + +int QDesignerMenuBar::realActionCount() const +{ + return actions().count() - 1; // 1 fake actions +} + +bool QDesignerMenuBar::dragging() const +{ + return m_dragging; +} + +void QDesignerMenuBar::moveLeft(bool ctrl) +{ + if (layoutDirection() == Qt::LeftToRight) { + movePrevious(ctrl); + } else { + moveNext(ctrl); + } +} + +void QDesignerMenuBar::moveRight(bool ctrl) +{ + if (layoutDirection() == Qt::LeftToRight) { + moveNext(ctrl); + } else { + movePrevious(ctrl); + } +} + +void QDesignerMenuBar::movePrevious(bool ctrl) +{ + const bool swapped = ctrl && swapActions(m_currentIndex, m_currentIndex - 1); + const int newIndex = qMax(0, m_currentIndex - 1); + // Always re-select, swapping destroys order + if (swapped || newIndex != m_currentIndex) { + m_currentIndex = newIndex; + updateCurrentAction(true); + } +} + +void QDesignerMenuBar::moveNext(bool ctrl) +{ + const bool swapped = ctrl && swapActions(m_currentIndex + 1, m_currentIndex); + const int newIndex = qMin(actions().count() - 1, m_currentIndex + 1); + if (swapped || newIndex != m_currentIndex) { + m_currentIndex = newIndex; + updateCurrentAction(!ctrl); + } +} + +void QDesignerMenuBar::moveUp() +{ + update(); +} + +void QDesignerMenuBar::moveDown() +{ + showMenu(); +} + +void QDesignerMenuBar::adjustSpecialActions() +{ + removeAction(m_addMenu); + addAction(m_addMenu); +} + +bool QDesignerMenuBar::interactive(bool i) +{ + const bool old = m_interactive; + m_interactive = i; + return old; +} + +void QDesignerMenuBar::hideMenu(int index) +{ + if (index < 0 && m_currentIndex >= 0) + index = m_currentIndex; + + if (index < 0 || index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + if (action && action->menu()) { + action->menu()->hide(); + + if (QDesignerMenu *menu = qobject_cast(action->menu())) { + menu->closeMenuChain(); + } + } +} + +void QDesignerMenuBar::deleteMenu() +{ + deleteMenuAction(currentAction()); +} + +void QDesignerMenuBar::deleteMenuAction(QAction *action) +{ + if (action && !qobject_cast(action)) { + const int pos = actions().indexOf(action); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveMenuActionCommand *cmd = new RemoveMenuActionCommand(fw); + cmd->init(action, action_before, this, this); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerMenuBar::showMenu(int index) +{ + if (index < 0 && m_currentIndex >= 0) + index = m_currentIndex; + + if (index < 0 || index >= realActionCount()) + return; + + m_currentIndex = index; + QAction *action = currentAction(); + + if (action && action->menu()) { + if (m_lastMenuActionIndex != -1 && m_lastMenuActionIndex != index) { + hideMenu(m_lastMenuActionIndex); + } + + m_lastMenuActionIndex = index; + QMenu *menu = action->menu(); + const QRect g = actionGeometry(action); + + if (!menu->isVisible()) { + if ((menu->windowFlags() & Qt::Popup) != Qt::Popup) + menu->setWindowFlags(Qt::Popup); + menu->adjustSize(); + if (layoutDirection() == Qt::LeftToRight) { + menu->move(mapToGlobal(g.bottomLeft())); + } else { + // The position is not initially correct due to the unknown width, + // causing it to overlap a bit the first time it is invoked. + const QSize menuSize = menu->size(); + QPoint point = g.bottomRight() - QPoint(menu->width(), 0); + menu->move(mapToGlobal(point)); + } + menu->setFocus(Qt::MouseFocusReason); + menu->raise(); + menu->show(); + } else { + menu->raise(); + } + } +} + +QAction *QDesignerMenuBar::safeActionAt(int index) const +{ + if (index < 0 || index >= actions().count()) + return 0; + + return actions().at(index); +} + +bool QDesignerMenuBar::swapActions(int a, int b) +{ + const int left = qMin(a, b); + int right = qMax(a, b); + + QAction *action_a = safeActionAt(left); + QAction *action_b = safeActionAt(right); + + if (action_a == action_b + || !action_a + || !action_b + || qobject_cast(action_a) + || qobject_cast(action_b)) + return false; // nothing to do + + right = qMin(right, realActionCount()); + if (right < 0) + return false; // nothing to do + + formWindow()->beginCommand(QApplication::translate("Command", "Move action")); + + QAction *action_b_before = safeActionAt(right + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw); + cmd1->init(this, action_b, action_b_before, false); + fw->commandHistory()->push(cmd1); + + QAction *action_a_before = safeActionAt(left + 1); + + InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw); + cmd2->init(this, action_b, action_a_before, false); + fw->commandHistory()->push(cmd2); + + RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw); + cmd3->init(this, action_a, action_b, false); + fw->commandHistory()->push(cmd3); + + InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw); + cmd4->init(this, action_a, action_b_before, true); + fw->commandHistory()->push(cmd4); + + fw->endCommand(); + + return true; +} + +void QDesignerMenuBar::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenuBar::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenuBar::updateCurrentAction(bool selectAction) +{ + update(); + + if (!selectAction) + return; + + QAction *action = currentAction(); + if (!action || action == m_addMenu) + return; + + QMenu *menu = action->menu(); + if (!menu) + return; + + QDesignerObjectInspector *oi = 0; + if (QDesignerFormWindowInterface *fw = formWindow()) + oi = qobject_cast(fw->core()->objectInspector()); + + if (!oi) + return; + + oi->clearSelection(); + oi->selectObject(menu); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_menubar_p.h b/designer/lib/shared/qdesigner_menubar_p.h new file mode 100644 index 0000000..b872041 --- /dev/null +++ b/designer/lib/shared/qdesigner_menubar_p.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MENUBAR_H +#define QDESIGNER_MENUBAR_H + +#include "shared_global_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerActionProviderExtension; + +class QLineEdit; +class QMimeData; + +namespace qdesigner_internal { +class PromotionTaskMenu; + +class SpecialMenuAction: public QAction +{ + Q_OBJECT +public: + SpecialMenuAction(QObject *parent = 0); + virtual ~SpecialMenuAction(); +}; + +} // namespace qdesigner_internal + +class QDESIGNER_SHARED_EXPORT QDesignerMenuBar: public QMenuBar +{ + Q_OBJECT +public: + QDesignerMenuBar(QWidget *parent = 0); + virtual ~QDesignerMenuBar(); + + bool eventFilter(QObject *object, QEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + QDesignerActionProviderExtension *actionProvider(); + + void adjustSpecialActions(); + bool interactive(bool i); + bool dragging() const; + + void moveLeft(bool ctrl = false); + void moveRight(bool ctrl = false); + void moveUp(); + void moveDown(); + + // Helpers for MenuTaskMenu/MenuBarTaskMenu extensions + QList contextMenuActions(); + void deleteMenuAction(QAction *action); + +private slots: + void deleteMenu(); + void slotRemoveMenuBar(); + +protected: + virtual void actionEvent(QActionEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + + bool handleEvent(QWidget *widget, QEvent *event); + bool handleMouseDoubleClickEvent(QWidget *widget, QMouseEvent *event); + bool handleMousePressEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseReleaseEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseMoveEvent(QWidget *widget, QMouseEvent *event); + bool handleContextMenuEvent(QWidget *widget, QContextMenuEvent *event); + bool handleKeyPressEvent(QWidget *widget, QKeyEvent *event); + + void startDrag(const QPoint &pos); + + enum ActionDragCheck { NoActionDrag, ActionDragOnSubMenu, AcceptActionDrag }; + ActionDragCheck checkAction(QAction *action) const; + + void adjustIndicator(const QPoint &pos); + int findAction(const QPoint &pos) const; + + QAction *currentAction() const; + int realActionCount() const; + + enum LeaveEditMode { + Default = 0, + ForceAccept + }; + + void enterEditMode(); + void leaveEditMode(LeaveEditMode mode); + void showLineEdit(); + + void showMenu(int index = -1); + void hideMenu(int index = -1); + + QAction *safeActionAt(int index) const; + + bool swapActions(int a, int b); + +private: + void updateCurrentAction(bool selectAction); + void movePrevious(bool ctrl); + void moveNext(bool ctrl); + + QAction *m_addMenu; + QPointer m_activeMenu; + QPoint m_startPosition; + int m_currentIndex; + bool m_interactive; + QLineEdit *m_editor; + bool m_dragging; + int m_lastMenuActionIndex; + QPointer m_lastFocusWidget; + qdesigner_internal::PromotionTaskMenu* m_promotionTaskMenu; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MENUBAR_H diff --git a/designer/lib/shared/qdesigner_objectinspector.cpp b/designer/lib/shared/qdesigner_objectinspector.cpp new file mode 100644 index 0000000..863caba --- /dev/null +++ b/designer/lib/shared/qdesigner_objectinspector.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_objectinspector_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerObjectInspector::QDesignerObjectInspector(QWidget *parent, Qt::WindowFlags flags) : + QDesignerObjectInspectorInterface(parent, flags) +{ +} + +void QDesignerObjectInspector::mainContainerChanged() +{ +} + +void Selection::clear() +{ + managed.clear(); + unmanaged.clear(); + objects.clear(); +} + +bool Selection::empty() const +{ + return managed.empty() && unmanaged.empty() && objects.empty(); +} + +QObjectList Selection::selection() const +{ + QObjectList rc(objects); + foreach (QObject* o, managed) + rc.push_back(o); + foreach (QObject* o, unmanaged) + rc.push_back(o); + return rc; +} +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_objectinspector_p.h b/designer/lib/shared/qdesigner_objectinspector_p.h new file mode 100644 index 0000000..ee7d6fe --- /dev/null +++ b/designer/lib/shared/qdesigner_objectinspector_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNEROBJECTINSPECTOR_H +#define DESIGNEROBJECTINSPECTOR_H + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerDnDItemInterface; + +namespace qdesigner_internal { + +struct QDESIGNER_SHARED_EXPORT Selection { + bool empty() const; + void clear(); + + // Merge all lists + QObjectList selection() const; + + // Selection in cursor (managed widgets) + QWidgetList managed; + // Unmanaged widgets + QWidgetList unmanaged; + // Remaining selected objects (non-widgets) + QObjectList objects; +}; + +// Extends the QDesignerObjectInspectorInterface by functionality +// to access the selection + +class QDESIGNER_SHARED_EXPORT QDesignerObjectInspector: public QDesignerObjectInspectorInterface +{ + Q_OBJECT +public: + explicit QDesignerObjectInspector(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + // Select a qobject unmanaged by form window + virtual bool selectObject(QObject *o) = 0; + virtual void getSelection(Selection &s) const = 0; + virtual void clearSelection() = 0; + +public slots: + virtual void mainContainerChanged(); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DESIGNEROBJECTINSPECTOR_H diff --git a/designer/lib/shared/qdesigner_promotion.cpp b/designer/lib/shared/qdesigner_promotion.cpp new file mode 100644 index 0000000..0e05bac --- /dev/null +++ b/designer/lib/shared/qdesigner_promotion.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_promotion_p.h" +#include "widgetdatabase_p.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + // Return a set of on-promotable classes + const QSet &nonPromotableClasses() { + static QSet rc; + if (rc.empty()) { + rc.insert(QLatin1String("Line")); + rc.insert(QLatin1String("QAction")); + rc.insert(QLatin1String("Spacer")); + rc.insert(QLatin1String("QMainWindow")); + rc.insert(QLatin1String("QDialog")); + rc.insert(QLatin1String("QWorkspace")); + rc.insert(QLatin1String("QMdiArea")); + rc.insert(QLatin1String("QMdiSubWindow")); + } + return rc; + } + + // Return widget database index of a promoted class or -1 with error message + int promotedWidgetDataBaseIndex(const QDesignerWidgetDataBaseInterface *widgetDataBase, + const QString &className, + QString *errorMessage) { + const int index = widgetDataBase->indexOfClassName(className); + if (index == -1 || !widgetDataBase->item(index)->isPromoted()) { + *errorMessage = QCoreApplication::tr("%1 is not a promoted class.").arg(className); + return -1; + } + return index; + } + + // Return widget database item of a promoted class or 0 with error message + QDesignerWidgetDataBaseItemInterface *promotedWidgetDataBaseItem(const QDesignerWidgetDataBaseInterface *widgetDataBase, + const QString &className, + QString *errorMessage) { + + const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage); + if (index == -1) + return 0; + return widgetDataBase->item(index); + } + + // extract class name from xml "". Quite a hack. + QString classNameFromXml(QString xml) { + static const QString tag = QLatin1String("class=\""); + const int pos = xml.indexOf(tag); + if (pos == -1) + return QString(); + xml.remove(0, pos + tag.size()); + const int closingPos = xml.indexOf(QLatin1Char('"')); + if (closingPos == -1) + return QString(); + xml.remove(closingPos, xml.size() - closingPos); + return xml; + } + + // return a list of class names in the scratch pad + QStringList getScratchPadClasses(const QDesignerWidgetBoxInterface *wb) { + QStringList rc; + const int catCount = wb->categoryCount(); + for (int c = 0; c < catCount; c++) { + const QDesignerWidgetBoxInterface::Category category = wb->category(c); + if (category.type() == QDesignerWidgetBoxInterface::Category::Scratchpad) { + const int widgetCount = category.widgetCount(); + for (int w = 0; w < widgetCount; w++) { + const QString className = classNameFromXml( category.widget(w).domXml()); + if (!className.isEmpty()) + rc += className; + } + } + } + return rc; + } +} + +namespace qdesigner_internal { + + QDesignerPromotion::QDesignerPromotion(QDesignerFormEditorInterface *core) : + m_core(core) { + } + + bool QDesignerPromotion::addPromotedClass(const QString &baseClass, + const QString &className, + const QString &includeFile, + QString *errorMessage) + { + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + const int baseClassIndex = widgetDataBase->indexOfClassName(baseClass); + + if (baseClassIndex == -1) { + *errorMessage = QCoreApplication::tr("The base class %1 is invalid.").arg(baseClass); + return false; + } + + const int existingClassIndex = widgetDataBase->indexOfClassName(className); + + if (existingClassIndex != -1) { + *errorMessage = QCoreApplication::tr("The class %1 already exists.").arg(className); + return false; + } + // Clone derived item. + QDesignerWidgetDataBaseItemInterface *promotedItem = WidgetDataBaseItem::clone(widgetDataBase->item(baseClassIndex)); + // Also inherit the container flag in case of QWidget-derived classes + // as it is most likely intended for stacked pages. + // set new props + promotedItem->setName(className); + promotedItem->setGroup(QCoreApplication::tr("Promoted Widgets")); + promotedItem->setCustom(true); + promotedItem->setPromoted(true); + promotedItem->setExtends(baseClass); + promotedItem->setIncludeFile(includeFile); + widgetDataBase->append(promotedItem); + return true; + } + + QList QDesignerPromotion::promotionBaseClasses() const + { + typedef QMap SortedDatabaseItemMap; + SortedDatabaseItemMap sortedDatabaseItemMap; + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + + const int cnt = widgetDataBase->count(); + for (int i = 0; i < cnt; i++) { + QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i); + if (canBePromoted(dbItem)) { + sortedDatabaseItemMap.insert(dbItem->name(), dbItem); + } + } + + return sortedDatabaseItemMap.values(); + } + + + bool QDesignerPromotion::canBePromoted(const QDesignerWidgetDataBaseItemInterface *dbItem) const + { + if (dbItem->isPromoted() || !dbItem->extends().isEmpty()) + return false; + + const QString name = dbItem->name(); + + if (nonPromotableClasses().contains(name)) + return false; + + if (name.startsWith(QLatin1String("QDesigner")) || + name.startsWith(QLatin1String("QLayout"))) + return false; + + return true; + } + + QDesignerPromotion::PromotedClasses QDesignerPromotion::promotedClasses() const + { + typedef QMap ClassNameItemMap; + // A map containing base classes and their promoted classes. + typedef QMap BaseClassPromotedMap; + + BaseClassPromotedMap baseClassPromotedMap; + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + // Look for promoted classes and insert into map according to base class. + const int cnt = widgetDataBase->count(); + for (int i = 0; i < cnt; i++) { + QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i); + if (dbItem->isPromoted()) { + const QString baseClassName = dbItem->extends(); + BaseClassPromotedMap::iterator it = baseClassPromotedMap.find(baseClassName); + if (it == baseClassPromotedMap.end()) { + it = baseClassPromotedMap.insert(baseClassName, ClassNameItemMap()); + } + it.value().insert(dbItem->name(), dbItem); + } + } + // convert map into list. + PromotedClasses rc; + + if (baseClassPromotedMap.empty()) + return rc; + + const BaseClassPromotedMap::const_iterator bcend = baseClassPromotedMap.constEnd(); + for (BaseClassPromotedMap::const_iterator bit = baseClassPromotedMap.constBegin(); bit != bcend; ++bit) { + const int baseIndex = widgetDataBase->indexOfClassName(bit.key()); + Q_ASSERT(baseIndex >= 0); + QDesignerWidgetDataBaseItemInterface *baseItem = widgetDataBase->item(baseIndex); + // promoted + const ClassNameItemMap::const_iterator pcend = bit.value().constEnd(); + for (ClassNameItemMap::const_iterator pit = bit.value().constBegin(); pit != pcend; ++pit) { + PromotedClass item; + item.baseItem = baseItem; + item.promotedItem = pit.value(); + rc.push_back(item); + } + } + + return rc; + } + + QSet QDesignerPromotion::referencedPromotedClassNames() const { + QSet rc; + const MetaDataBase *metaDataBase = qobject_cast(m_core->metaDataBase()); + if (!metaDataBase) + return rc; + + const QList objs = metaDataBase->objects(); + const QList::const_iterator cend = objs.constEnd(); + for ( QList::const_iterator it = objs.constBegin(); it != cend; ++it) { + const QString customClass = metaDataBase->metaDataBaseItem(*it)->customClassName(); + if (!customClass.isEmpty()) + rc.insert(customClass); + + } + // check the scratchpad of the widget box + if (QDesignerWidgetBoxInterface *widgetBox = m_core->widgetBox()) { + const QStringList scratchPadClasses = getScratchPadClasses(widgetBox); + if (!scratchPadClasses.empty()) { + // Check whether these are actually promoted + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QStringList::const_iterator cend = scratchPadClasses.constEnd(); + for (QStringList::const_iterator it = scratchPadClasses.constBegin(); it != cend; ++it ) { + const int index = widgetDataBase->indexOfClassName(*it); + if (index != -1 && widgetDataBase->item(index)->isPromoted()) + rc += *it; + } + } + } + return rc; + } + + bool QDesignerPromotion::removePromotedClass(const QString &className, QString *errorMessage) { + // check if it exists and is promoted + WidgetDataBase *widgetDataBase = qobject_cast(m_core->widgetDataBase()); + if (!widgetDataBase) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be removed").arg(className); + return false; + } + + const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage); + if (index == -1) + return false; + + if (referencedPromotedClassNames().contains(className)) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be removed because it is still referenced.").arg(className); + return false; + } + widgetDataBase->remove(index); + return true; + } + + bool QDesignerPromotion::changePromotedClassName(const QString &oldclassName, const QString &newClassName, QString *errorMessage) { + const MetaDataBase *metaDataBase = qobject_cast(m_core->metaDataBase()); + if (!metaDataBase) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed").arg(oldclassName); + return false; + } + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + + // check the new name + if (newClassName.isEmpty()) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed to an empty name.").arg(oldclassName); + return false; + } + const int existingIndex = widgetDataBase->indexOfClassName(newClassName); + if (existingIndex != -1) { + *errorMessage = QCoreApplication::tr("There is already a class named %1.").arg(newClassName); + return false; + } + // Check old class + QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, oldclassName, errorMessage); + if (!dbItem) + return false; + + // Change the name in the data base and change all referencing objects in the meta database + dbItem->setName(newClassName); + bool foundReferences = false; + foreach (QObject* object, metaDataBase->objects()) { + MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object); + Q_ASSERT(item); + if (item->customClassName() == oldclassName) { + item->setCustomClassName(newClassName); + foundReferences = true; + } + } + // set state + if (foundReferences) + refreshObjectInspector(); + + return true; + } + + bool QDesignerPromotion::setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage) { + // check file + if (includeFile.isEmpty()) { + *errorMessage = QCoreApplication::tr("Cannot set an empty include file."); + return false; + } + // check item + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, className, errorMessage); + if (!dbItem) + return false; + + dbItem->setIncludeFile(includeFile); + return true; + } + + void QDesignerPromotion::refreshObjectInspector() { + if (QDesignerFormWindowManagerInterface *fwm = m_core->formWindowManager()) { + if (QDesignerFormWindowInterface *fw = fwm->activeFormWindow()) + if ( QDesignerObjectInspectorInterface *oi = m_core->objectInspector()) { + oi->setFormWindow(fw); + } + } + } +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_promotion_p.h b/designer/lib/shared/qdesigner_promotion_p.h new file mode 100644 index 0000000..ccea1d3 --- /dev/null +++ b/designer/lib/shared/qdesigner_promotion_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNERPROMOTION_H +#define QDESIGNERPROMOTION_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + + class QDESIGNER_SHARED_EXPORT QDesignerPromotion : public QDesignerPromotionInterface + { + public: + explicit QDesignerPromotion(QDesignerFormEditorInterface *core); + + virtual PromotedClasses promotedClasses() const; + + virtual QSet referencedPromotedClassNames() const; + + virtual bool addPromotedClass(const QString &baseClass, + const QString &className, + const QString &includeFile, + QString *errorMessage); + + virtual bool removePromotedClass(const QString &className, QString *errorMessage); + + virtual bool changePromotedClassName(const QString &oldclassName, const QString &newClassName, QString *errorMessage); + + virtual bool setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage); + + virtual QList promotionBaseClasses() const; + + private: + bool canBePromoted(const QDesignerWidgetDataBaseItemInterface *) const; + void refreshObjectInspector(); + + QDesignerFormEditorInterface *m_core; + }; +} + +QT_END_NAMESPACE + +#endif // QDESIGNERPROMOTION_H diff --git a/designer/lib/shared/qdesigner_promotiondialog.cpp b/designer/lib/shared/qdesigner_promotiondialog.cpp new file mode 100644 index 0000000..9bc1803 --- /dev/null +++ b/designer/lib/shared/qdesigner_promotiondialog.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_promotiondialog_p.h" +#include "promotionmodel_p.h" +#include "iconloader_p.h" +#include "widgetdatabase_p.h" +#include "signalslotdialog_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + // PromotionParameters + struct PromotionParameters { + QString m_baseClass; + QString m_className; + QString m_includeFile; + }; + + // NewPromotedClassPanel + NewPromotedClassPanel::NewPromotedClassPanel(const QStringList &baseClasses, + int selectedBaseClass, + QWidget *parent) : + QGroupBox(parent), + m_baseClassCombo(new QComboBox), + m_classNameEdit(new QLineEdit), + m_includeFileEdit(new QLineEdit), + m_globalIncludeCheckBox(new QCheckBox), + m_addButton(new QPushButton(tr("Add"))) + { + setTitle(tr("New Promoted Class")); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); + QHBoxLayout *hboxLayout = new QHBoxLayout(this); + + m_classNameEdit->setValidator(new QRegExpValidator(QRegExp(QLatin1String("[_a-zA-Z:][:_a-zA-Z0-9]*")), m_classNameEdit)); + connect(m_classNameEdit, SIGNAL(textChanged(QString)), this, SLOT(slotNameChanged(QString))); + connect(m_includeFileEdit, SIGNAL(textChanged(QString)), this, SLOT(slotIncludeFileChanged(QString))); + + m_baseClassCombo->setEditable(false); + m_baseClassCombo->addItems(baseClasses); + if (selectedBaseClass != -1) + m_baseClassCombo->setCurrentIndex(selectedBaseClass); + + // Grid + QFormLayout *formLayout = new QFormLayout(); + formLayout->addRow(tr("Base class name:"), m_baseClassCombo); + formLayout->addRow(tr("Promoted class name:"), m_classNameEdit); + formLayout->addRow(tr("Header file:"), m_includeFileEdit); + formLayout->addRow(tr("Global include"), m_globalIncludeCheckBox); + hboxLayout->addLayout(formLayout); + hboxLayout->addItem(new QSpacerItem(15, 0, QSizePolicy::Fixed, QSizePolicy::Ignored)); + // Button box + QVBoxLayout *buttonLayout = new QVBoxLayout(); + + m_addButton->setAutoDefault(false); + connect(m_addButton, SIGNAL(clicked()), this, SLOT(slotAdd())); + m_addButton->setEnabled(false); + buttonLayout->addWidget(m_addButton); + + QPushButton *resetButton = new QPushButton(tr("Reset")); + resetButton->setAutoDefault(false); + connect(resetButton, SIGNAL(clicked()), this, SLOT(slotReset())); + + buttonLayout->addWidget(resetButton); + buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding)); + hboxLayout->addLayout(buttonLayout); + + enableButtons(); + } + + void NewPromotedClassPanel::slotAdd() { + bool ok = false; + emit newPromotedClass(promotionParameters(), &ok); + if (ok) + slotReset(); + } + + void NewPromotedClassPanel::slotReset() { + const QString empty; + m_classNameEdit->setText(empty); + m_includeFileEdit->setText(empty); + m_globalIncludeCheckBox->setCheckState(Qt::Unchecked); + } + + void NewPromotedClassPanel::grabFocus() { + m_classNameEdit->setFocus(Qt::OtherFocusReason); + } + + void NewPromotedClassPanel::slotNameChanged(const QString &className) { + // Suggest a name + if (!className.isEmpty()) { + QString suggestedHeader = className.toLower().replace(QLatin1String("::"), QString(QLatin1Char('_'))); + suggestedHeader += QLatin1String(".h"); + + const bool blocked = m_includeFileEdit->blockSignals(true); + m_includeFileEdit->setText(suggestedHeader); + m_includeFileEdit->blockSignals(blocked); + } + enableButtons(); + } + + void NewPromotedClassPanel::slotIncludeFileChanged(const QString &){ + enableButtons(); + } + + void NewPromotedClassPanel::enableButtons() { + const bool enabled = !m_classNameEdit->text().isEmpty() && !m_includeFileEdit->text().isEmpty(); + m_addButton->setEnabled(enabled); + m_addButton->setDefault(enabled); + } + + PromotionParameters NewPromotedClassPanel::promotionParameters() const { + PromotionParameters rc; + rc.m_baseClass = m_baseClassCombo->currentText(); + rc.m_className = m_classNameEdit->text(); + rc.m_includeFile = buildIncludeFile(m_includeFileEdit->text(), + m_globalIncludeCheckBox->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal); + return rc; + } + + void NewPromotedClassPanel::chooseBaseClass(const QString &baseClass) { + const int index = m_baseClassCombo->findText (baseClass); + if (index != -1) + m_baseClassCombo->setCurrentIndex (index); + } + + // --------------- QDesignerPromotionDialog + QDesignerPromotionDialog::QDesignerPromotionDialog(QDesignerFormEditorInterface *core, + QWidget *parent, + const QString &promotableWidgetClassName, + QString *promoteTo) : + QDialog(parent), + m_mode(promotableWidgetClassName.isEmpty() || promoteTo == 0 ? ModeEdit : ModeEditChooseClass), + m_promotableWidgetClassName(promotableWidgetClassName), + m_core(core), + m_promoteTo(promoteTo), + m_promotion(core->promotion()), + m_model(new PromotionModel(core)), + m_treeView(new QTreeView), + m_buttonBox(0), + m_removeButton(new QPushButton(createIconSet(QString::fromUtf8("minus.png")), QString())) + { + m_buttonBox = createButtonBox(); + setModal(true); + setWindowTitle(tr("Promoted Widgets")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + // tree view group + QGroupBox *treeViewGroup = new QGroupBox(); + treeViewGroup->setTitle(tr("Promoted Classes")); + QVBoxLayout *treeViewVBoxLayout = new QVBoxLayout(treeViewGroup); + // tree view + m_treeView->setModel (m_model); + m_treeView->setMinimumWidth(450); + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + + connect(m_treeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotTreeViewContextMenu(QPoint))); + + QHeaderView *headerView = m_treeView->header(); + headerView->setResizeMode(QHeaderView::ResizeToContents); + treeViewVBoxLayout->addWidget(m_treeView); + // remove button + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_removeButton->setAutoDefault(false); + connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemove())); + m_removeButton->setEnabled(false); + hboxLayout->addWidget(m_removeButton); + treeViewVBoxLayout->addLayout(hboxLayout); + vboxLayout->addWidget(treeViewGroup); + // Create new panel: Try to be smart and preselect a base class. Default to QFrame + const QStringList &baseClassNameList = baseClassNames(m_promotion); + int preselectedBaseClass = -1; + if (m_mode == ModeEditChooseClass) { + preselectedBaseClass = baseClassNameList.indexOf(m_promotableWidgetClassName); + } + if (preselectedBaseClass == -1) + preselectedBaseClass = baseClassNameList.indexOf(QLatin1String("QFrame")); + + NewPromotedClassPanel *newPromotedClassPanel = new NewPromotedClassPanel(baseClassNameList, preselectedBaseClass); + connect(newPromotedClassPanel, SIGNAL(newPromotedClass(PromotionParameters,bool*)), this, SLOT(slotNewPromotedClass(PromotionParameters,bool*))); + connect(this, SIGNAL(selectedBaseClassChanged(QString)), + newPromotedClassPanel, SLOT(chooseBaseClass(QString))); + vboxLayout->addWidget(newPromotedClassPanel); + // button box + vboxLayout->addWidget(m_buttonBox); + // connect model + connect(m_model, SIGNAL(includeFileChanged(QDesignerWidgetDataBaseItemInterface*,QString)), + this, SLOT(slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface*,QString))); + + connect(m_model, SIGNAL(classNameChanged(QDesignerWidgetDataBaseItemInterface*,QString)), + this, SLOT(slotClassNameChanged(QDesignerWidgetDataBaseItemInterface*,QString))); + + // focus + if (m_mode == ModeEditChooseClass) + newPromotedClassPanel->grabFocus(); + + slotUpdateFromWidgetDatabase(); + } + + QDialogButtonBox *QDesignerPromotionDialog::createButtonBox() { + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Close); + + connect(buttonBox , SIGNAL(accepted()), this, SLOT(slotAcceptPromoteTo())); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Promote")); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + return buttonBox; + } + + void QDesignerPromotionDialog::slotUpdateFromWidgetDatabase() { + m_model->updateFromWidgetDatabase(); + m_treeView->expandAll(); + m_removeButton->setEnabled(false); + } + + void QDesignerPromotionDialog::delayedUpdateFromWidgetDatabase() { + QTimer::singleShot(0, this, SLOT(slotUpdateFromWidgetDatabase())); + } + + const QStringList &QDesignerPromotionDialog::baseClassNames(const QDesignerPromotionInterface *promotion) { + typedef QList WidgetDataBaseItemList; + static QStringList rc; + if (rc.empty()) { + // Convert the item list into a string list. + const WidgetDataBaseItemList dbItems = promotion->promotionBaseClasses(); + const WidgetDataBaseItemList::const_iterator cend = dbItems.constEnd(); + for (WidgetDataBaseItemList::const_iterator it = dbItems.constBegin() ; it != cend; ++it) { + rc.push_back( (*it)->name()); + } + } + return rc; + } + + void QDesignerPromotionDialog::slotAcceptPromoteTo() { + Q_ASSERT(m_mode == ModeEditChooseClass); + unsigned flags; + // Ok pressed: Promote to selected class + if (QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags)) { + if (flags & CanPromote) { + *m_promoteTo = dbItem ->name(); + accept(); + } + } + } + + void QDesignerPromotionDialog::slotRemove() { + unsigned flags; + QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem || (flags & Referenced)) + return; + + QString errorMessage; + if (m_promotion->removePromotedClass(dbItem->name(), &errorMessage)) { + slotUpdateFromWidgetDatabase(); + } else { + displayError(errorMessage); + } + } + + void QDesignerPromotionDialog::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) { + // Enable deleting non-referenced items + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(selected, flags); + m_removeButton->setEnabled(dbItem && !(flags & Referenced)); + // In choose mode, can we promote to the class? + if (m_mode == ModeEditChooseClass) { + const bool enablePromoted = flags & CanPromote; + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enablePromoted); + m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(enablePromoted); + } + // different base? + if (dbItem) { + const QString baseClass = dbItem->extends(); + if (baseClass != m_lastSelectedBaseClass) { + m_lastSelectedBaseClass = baseClass; + emit selectedBaseClassChanged(m_lastSelectedBaseClass); + } + } + } + + QDesignerWidgetDataBaseItemInterface *QDesignerPromotionDialog::databaseItemAt(const QItemSelection &selected, unsigned &flags) const { + flags = 0; + const QModelIndexList indexes = selected.indexes(); + if (indexes.empty()) + return 0; + + bool referenced; + QDesignerWidgetDataBaseItemInterface *dbItem = m_model->databaseItemAt(indexes.front(), &referenced); + + if (dbItem) { + if (referenced) + flags |= Referenced; + // In choose mode, can we promote to the class? + if (m_mode == ModeEditChooseClass && dbItem && dbItem->isPromoted() && dbItem->extends() == m_promotableWidgetClassName) + flags |= CanPromote; + + } + return dbItem; + } + + void QDesignerPromotionDialog::slotNewPromotedClass(const PromotionParameters &p, bool *ok) { + QString errorMessage; + *ok = m_promotion->addPromotedClass(p.m_baseClass, p.m_className, p.m_includeFile, &errorMessage); + if (*ok) { + // update and select + slotUpdateFromWidgetDatabase(); + const QModelIndex newClassIndex = m_model->indexOfClass(p.m_className); + if (newClassIndex.isValid()) { + m_treeView->selectionModel()->select(newClassIndex, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); + } + } else { + displayError(errorMessage); + } + } + + void QDesignerPromotionDialog::slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface *dbItem, const QString &includeFile) { + if (includeFile.isEmpty()) { + delayedUpdateFromWidgetDatabase(); + return; + } + + if (dbItem->includeFile() == includeFile) + return; + + QString errorMessage; + if (!m_promotion->setPromotedClassIncludeFile(dbItem->name(), includeFile, &errorMessage)) { + displayError(errorMessage); + delayedUpdateFromWidgetDatabase(); + } + } + + void QDesignerPromotionDialog::slotClassNameChanged(QDesignerWidgetDataBaseItemInterface *dbItem, const QString &newName) { + if (newName.isEmpty()) { + delayedUpdateFromWidgetDatabase(); + return; + } + const QString oldName = dbItem->name(); + if (newName == oldName) + return; + + QString errorMessage; + if (!m_promotion->changePromotedClassName(oldName , newName, &errorMessage)) { + displayError(errorMessage); + delayedUpdateFromWidgetDatabase(); + } + } + + void QDesignerPromotionDialog::slotTreeViewContextMenu(const QPoint &pos) { + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem) + return; + + QMenu menu; + QAction *signalSlotAction = menu.addAction(tr("Change signals/slots...")); + connect(signalSlotAction, SIGNAL(triggered()), this, SLOT(slotEditSignalsSlots())); + + menu.exec(m_treeView->viewport()->mapToGlobal(pos)); + } + + void QDesignerPromotionDialog::slotEditSignalsSlots() { + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem) + return; + + SignalSlotDialog::editPromotedClass(m_core, dbItem->name(), this); + } + + void QDesignerPromotionDialog::displayError(const QString &message) { + m_core->dialogGui()->message(this, QDesignerDialogGuiInterface::PromotionErrorMessage, QMessageBox::Warning, + tr("%1 - Error").arg(windowTitle()), message, QMessageBox::Close); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_promotiondialog_p.h b/designer/lib/shared/qdesigner_promotiondialog_p.h new file mode 100644 index 0000000..ceff66c --- /dev/null +++ b/designer/lib/shared/qdesigner_promotiondialog_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONEDITORDIALOG_H +#define PROMOTIONEDITORDIALOG_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerPromotionInterface; +class QDesignerWidgetDataBaseItemInterface; + +class QTreeView; +class QPushButton; +class QItemSelection; +class QDialogButtonBox; +class QComboBox; +class QLineEdit; +class QCheckBox; + +namespace qdesigner_internal { + struct PromotionParameters; + class PromotionModel; + + + // Panel for adding a new promoted class. Separate class for code cleanliness. + class NewPromotedClassPanel : public QGroupBox { + Q_OBJECT + public: + explicit NewPromotedClassPanel(const QStringList &baseClasses, + int selectedBaseClass = -1, + QWidget *parent = 0); + + signals: + void newPromotedClass(const PromotionParameters &, bool *ok); + + public slots: + void grabFocus(); + void chooseBaseClass(const QString &); + private slots: + void slotNameChanged(const QString &); + void slotIncludeFileChanged(const QString &); + void slotAdd(); + void slotReset(); + + private: + PromotionParameters promotionParameters() const; + void enableButtons(); + + QComboBox *m_baseClassCombo; + QLineEdit *m_classNameEdit; + QLineEdit *m_includeFileEdit; + QCheckBox *m_globalIncludeCheckBox; + QPushButton *m_addButton; + }; + + // Dialog for editing promoted classes. + class QDesignerPromotionDialog : public QDialog { + Q_OBJECT + + public: + enum Mode { ModeEdit, ModeEditChooseClass }; + + explicit QDesignerPromotionDialog(QDesignerFormEditorInterface *core, + QWidget *parent = 0, + const QString &promotableWidgetClassName = QString(), + QString *promoteTo = 0); + // Return an alphabetically ordered list of base class names for adding new classes. + static const QStringList &baseClassNames(const QDesignerPromotionInterface *promotion); + + signals: + void selectedBaseClassChanged(const QString &); + private slots: + void slotRemove(); + void slotAcceptPromoteTo(); + void slotSelectionChanged(const QItemSelection &, const QItemSelection &); + void slotNewPromotedClass(const PromotionParameters &, bool *ok); + + void slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface *, const QString &includeFile); + void slotClassNameChanged(QDesignerWidgetDataBaseItemInterface *, const QString &newName); + void slotUpdateFromWidgetDatabase(); + void slotTreeViewContextMenu(const QPoint &); + void slotEditSignalsSlots(); + + private: + QDialogButtonBox *createButtonBox(); + void delayedUpdateFromWidgetDatabase(); + // Return item at model index and a combination of flags or 0. + enum { Referenced = 1, CanPromote = 2 }; + QDesignerWidgetDataBaseItemInterface *databaseItemAt(const QItemSelection &, unsigned &flags) const; + void displayError(const QString &message); + + const Mode m_mode; + const QString m_promotableWidgetClassName; + QDesignerFormEditorInterface *m_core; + QString *m_promoteTo; + QDesignerPromotionInterface *m_promotion; + PromotionModel *m_model; + QTreeView *m_treeView; + QDialogButtonBox *m_buttonBox; + QPushButton *m_removeButton; + QString m_lastSelectedBaseClass; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONEDITORDIALOG_H diff --git a/designer/lib/shared/qdesigner_propertycommand.cpp b/designer/lib/shared/qdesigner_propertycommand.cpp new file mode 100644 index 0000000..910aa5b --- /dev/null +++ b/designer/lib/shared/qdesigner_propertycommand.cpp @@ -0,0 +1,1503 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "dynamicpropertysheet.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_integration_p.h" +#include "spacer_widget_p.h" +#include "qdesigner_propertysheet_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { +enum { debugPropertyCommands = 0 }; + +// Debug resolve mask of font +QString fontMask(unsigned m) +{ + QString rc; + if (m & QFont::FamilyResolved) + rc += QLatin1String("Family"); + if (m & QFont::SizeResolved) + rc += QLatin1String("Size "); + if (m & QFont::WeightResolved) + rc += QLatin1String("Bold "); + if (m & QFont::StyleResolved) + rc += QLatin1String("Style "); + if (m & QFont::UnderlineResolved) + rc += QLatin1String("Underline "); + if (m & QFont::StrikeOutResolved) + rc += QLatin1String("StrikeOut "); + if (m & QFont::KerningResolved) + rc += QLatin1String("Kerning "); + if (m & QFont::StyleStrategyResolved) + rc += QLatin1String("StyleStrategy"); + return rc; +} + +// Debug font +QString fontString(const QFont &f) +{ + QString rc; { + const QChar comma = QLatin1Char(','); + QTextStream str(&rc); + str << QLatin1String("QFont(\"") << f.family() << comma << + f.pointSize(); + if (f.bold()) + str << comma << QLatin1String("bold"); + if (f.italic()) + str << comma << QLatin1String("italic"); + if (f.underline()) + str << comma << QLatin1String("underline"); + if (f.strikeOut()) + str << comma << QLatin1String("strikeOut"); + if (f.kerning()) + str << comma << QLatin1String("kerning"); + str << comma << f.styleStrategy() << QLatin1String(" resolve: ") + << fontMask(f.resolve()) << QLatin1Char(')'); + } + return rc; +} +QSize checkSize(const QSize &size) +{ + return size.boundedTo(QSize(0xFFFFFF, 0xFFFFFF)); +} + +QSize diffSize(QDesignerFormWindowInterface *fw) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return QSize(); + + const QSize diff = container->size() - fw->size(); // decoration offset of container window + return diff; +} + +void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + const QSize diff = diffSize(fw); // decoration offset of container window + + QSize newFormSize = checkSize(size).expandedTo(fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint + QSize newContainerSize = newFormSize + diff; + + newContainerSize = newContainerSize.expandedTo(container->minimumSizeHint()); + newContainerSize = newContainerSize.expandedTo(container->minimumSize()); + + newFormSize = newContainerSize - diff; + + newContainerSize = checkSize(newContainerSize); + + if (formSize) + *formSize = newFormSize; + if (containerSize) + *containerSize = newContainerSize; +} + +/* SubProperties: When applying a changed property to a multiselection, it sometimes makes + * sense to apply only parts (subproperties) of the property. + * For example, if someone changes the x-value of a geometry in the property editor + * and applies it to a multi-selection, y should not be applied as this would cause all + * the widgets to overlap. + * The following routines can be used to find out the changed subproperties of a property, + * which are represented as a mask, and to apply them while leaving the others intact. */ + +enum RectSubPropertyMask { SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 }; +enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 }; +enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 }; +enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2, SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8 }; +enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2, SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8 }; + +enum CommonSubPropertyMask { SubPropertyAll = 0xFFFFFFFF }; + +// Set the mask flag in mask if the properties do not match. +#define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag) if (object1.getter() != object2.getter()) mask |= maskFlag; + +// find changed subproperties of a rectangle +unsigned compareSubProperties(const QRect & r1, const QRect & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, x, rc, SubPropertyX) + COMPARE_SUBPROPERTY(r1, r2, y, rc, SubPropertyY) + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} + +// find changed subproperties of a QSize +unsigned compareSubProperties(const QSize & r1, const QSize & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} +// find changed subproperties of a QSizePolicy +unsigned compareSubProperties(const QSizePolicy & sp1, const QSizePolicy & sp2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(sp1, sp2, horizontalPolicy, rc, SubPropertyHSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, horizontalStretch, rc, SubPropertyHStretch) + COMPARE_SUBPROPERTY(sp1, sp2, verticalPolicy, rc, SubPropertyVSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, verticalStretch, rc, SubPropertyVStretch) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetStringValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1, const qdesigner_internal::PropertySheetStringValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyStringValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyStringComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyStringTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringDisambiguation) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetKeySequenceValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1, const qdesigner_internal::PropertySheetKeySequenceValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyKeySequenceValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyKeySequenceComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyKeySequenceTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Compare font-subproperties taking the [undocumented] +// resolve flag into account +template +void compareFontSubProperty(const QFont & f1, + const QFont & f2, + Property (QFont::*getter) () const, + unsigned maskBit, + unsigned &mask) +{ + const bool f1Changed = f1.resolve() & maskBit; + const bool f2Changed = f2.resolve() & maskBit; + // Role has been set/reset in editor + if (f1Changed != f2Changed) { + mask |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (f1Changed && f2Changed && (f1.*getter)() != (f2.*getter)()) + mask |= maskBit; + } +} +// find changed subproperties of a QFont +unsigned compareSubProperties(const QFont & f1, const QFont & f2) +{ + unsigned rc = 0; + compareFontSubProperty(f1, f2, &QFont::family, QFont::FamilyResolved, rc); + compareFontSubProperty(f1, f2, &QFont::pointSize, QFont::SizeResolved, rc); + compareFontSubProperty(f1, f2, &QFont::bold, QFont::WeightResolved, rc); + compareFontSubProperty(f1, f2, &QFont::italic, QFont::StyleResolved, rc); + compareFontSubProperty(f1, f2, &QFont::underline, QFont::UnderlineResolved, rc); + compareFontSubProperty(f1, f2, &QFont::strikeOut, QFont::StrikeOutResolved, rc); + compareFontSubProperty(f1, f2, &QFont::kerning, QFont::KerningResolved, rc); + compareFontSubProperty(f1, f2, &QFont::styleStrategy, QFont::StyleStrategyResolved, rc); + if (debugPropertyCommands) + qDebug() << "compareSubProperties " << fontString(f1) << fontString(f2) << "\n\treturns " << fontMask(rc); + return rc; +} + +// Compare colors of a role +bool roleColorChanged(const QPalette & p1, const QPalette & p2, QPalette::ColorRole role) +{ + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast(group); + if (p1.color(pgroup, role) != p2.color(pgroup, role)) + return true; + } + return false; +} +// find changed subproperties of a QPalette taking the [undocumented] resolve flags into account +unsigned compareSubProperties(const QPalette & p1, const QPalette & p2) +{ + unsigned rc = 0; + unsigned maskBit = 1u; + // generate a mask for each role + const unsigned p1Changed = p1.resolve(); + const unsigned p2Changed = p2.resolve(); + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + const bool p1RoleChanged = p1Changed & maskBit; + const bool p2RoleChanged = p2Changed & maskBit; + // Role has been set/reset in editor + if (p1RoleChanged != p2RoleChanged) { + rc |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (p1RoleChanged && p2RoleChanged && roleColorChanged(p1, p2, static_cast(role))) + rc |= maskBit; + } + } + return rc; +} + +// find changed subproperties of a QAlignment which is a flag combination of vertical and horizontal + +unsigned compareSubProperties(Qt::Alignment a1, Qt::Alignment a2) +{ + unsigned rc = 0; + if ((a1 & Qt::AlignHorizontal_Mask) != (a2 & Qt::AlignHorizontal_Mask)) + rc |= SubPropertyHorizontalAlignment; + if ((a1 & Qt::AlignVertical_Mask) != (a2 & Qt::AlignVertical_Mask)) + rc |= SubPropertyVerticalAlignment; + return rc; +} + +Qt::Alignment variantToAlignment(const QVariant & q) +{ + return Qt::Alignment(qdesigner_internal::Utils::valueOf(q)); +} +// find changed subproperties of a variant +unsigned compareSubProperties(const QVariant & q1, const QVariant & q2, qdesigner_internal::SpecialProperty specialProperty) +{ + // Do not clobber new value in the comparison function in + // case someone sets a QString on a PropertySheetStringValue. + if (q1.type() != q2.type()) + return SubPropertyAll; + switch (q1.type()) { + case QVariant::Rect: + return compareSubProperties(q1.toRect(), q2.toRect()); + case QVariant::Size: + return compareSubProperties(q1.toSize(), q2.toSize()); + case QVariant::SizePolicy: + return compareSubProperties(qvariant_cast(q1), qvariant_cast(q2)); + case QVariant::Font: + return compareSubProperties(qvariant_cast(q1), qvariant_cast(q2)); + case QVariant::Palette: + return compareSubProperties(qvariant_cast(q1), qvariant_cast(q2)); + default: + if (q1.userType() == qMetaTypeId()) + return qvariant_cast(q1).compare(qvariant_cast(q2)); + else if (q1.userType() == qMetaTypeId()) + return compareSubProperties(qvariant_cast(q1), qvariant_cast(q2)); + else if (q1.userType() == qMetaTypeId()) + return compareSubProperties(qvariant_cast(q1), qvariant_cast(q2)); + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: + return compareSubProperties(variantToAlignment(q1), variantToAlignment(q2)); + default: + break; + } + break; + } + return SubPropertyAll; +} + +// Apply the sub property if mask flag is set in mask +#define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag) if (mask & maskFlag) rc.setter(newValue.getter()); + +// apply changed subproperties to a rectangle +QRect applyRectSubProperty(const QRect &oldValue, const QRect &newValue, unsigned mask) +{ + QRect rc = oldValue; + SET_SUBPROPERTY(rc, newValue, x, moveLeft, mask, SubPropertyX) + SET_SUBPROPERTY(rc, newValue, y, moveTop, mask, SubPropertyY) + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a rectangle QSize +QSize applySizeSubProperty(const QSize &oldValue, const QSize &newValue, unsigned mask) +{ + QSize rc = oldValue; + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a SizePolicy +QSizePolicy applySizePolicySubProperty(const QSizePolicy &oldValue, const QSizePolicy &newValue, unsigned mask) +{ + QSizePolicy rc = oldValue; + SET_SUBPROPERTY(rc, newValue, horizontalPolicy, setHorizontalPolicy, mask, SubPropertyHSizePolicy) + SET_SUBPROPERTY(rc, newValue, horizontalStretch, setHorizontalStretch, mask, SubPropertyHStretch) + SET_SUBPROPERTY(rc, newValue, verticalPolicy, setVerticalPolicy, mask, SubPropertyVSizePolicy) + SET_SUBPROPERTY(rc, newValue, verticalStretch, setVerticalStretch, mask, SubPropertyVStretch) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetStringValue +qdesigner_internal::PropertySheetStringValue applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue &oldValue, + const qdesigner_internal::PropertySheetStringValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetStringValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringDisambiguation) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetKeySequenceValue +qdesigner_internal::PropertySheetKeySequenceValue applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue &oldValue, + const qdesigner_internal::PropertySheetKeySequenceValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetKeySequenceValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyKeySequenceValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyKeySequenceComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyKeySequenceTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Apply the font-subproperties keeping the [undocumented] +// resolve flag in sync (note that PropertySetterType might be something like const T&). +template +inline void setFontSubProperty(unsigned mask, + const QFont &newValue, + unsigned maskBit, + PropertyReturnType (QFont::*getter) () const, + void (QFont::*setter) (PropertySetterType), + QFont &value) +{ + if (mask & maskBit) { + (value.*setter)((newValue.*getter)()); + // Set the resolve bit from NewValue in return value + uint r = value.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + value.resolve(r); + if (debugPropertyCommands) + qDebug() << "setFontSubProperty " << fontMask(maskBit) << " resolve=" << origFlag; + } +} +// apply changed subproperties to a QFont +QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask) +{ + QFont rc = oldValue; + setFontSubProperty(mask, newValue, QFont::FamilyResolved, &QFont::family, &QFont::setFamily, rc); + setFontSubProperty(mask, newValue, QFont::SizeResolved, &QFont::pointSize, &QFont::setPointSize, rc); + setFontSubProperty(mask, newValue, QFont::WeightResolved, &QFont::bold, &QFont::setBold, rc); + setFontSubProperty(mask, newValue, QFont::StyleResolved, &QFont::italic, &QFont::setItalic, rc); + setFontSubProperty(mask, newValue, QFont::UnderlineResolved, &QFont::underline, &QFont::setUnderline, rc); + setFontSubProperty(mask, newValue, QFont::StrikeOutResolved, &QFont::strikeOut, &QFont::setStrikeOut, rc); + setFontSubProperty(mask, newValue, QFont::KerningResolved, &QFont::kerning, &QFont::setKerning, rc); + setFontSubProperty(mask, newValue, QFont::StyleStrategyResolved, &QFont::styleStrategy, &QFont::setStyleStrategy, rc); + if (debugPropertyCommands) + qDebug() << "applyFontSubProperty old " << fontMask(oldValue.resolve()) << " new " << fontMask(newValue.resolve()) << " return: " << fontMask(rc.resolve()); + return rc; +} + +// apply changed subproperties to a QPalette +QPalette applyPaletteSubProperty(const QPalette &oldValue, const QPalette &newValue, unsigned mask) +{ + QPalette rc = oldValue; + // apply a mask for each role + unsigned maskBit = 1u; + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + if (mask & maskBit) { + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast(group); + const QPalette::ColorRole prole = static_cast(role); + rc.setColor(pgroup, prole, newValue.color(pgroup, prole)); + } + // Set the resolve bit from NewValue in return value + uint r = rc.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + rc.resolve(r); + } + } + return rc; +} + +// apply changed subproperties to a QAlignment which is a flag combination of vertical and horizontal +Qt::Alignment applyAlignmentSubProperty(Qt::Alignment oldValue, Qt::Alignment newValue, unsigned mask) +{ + // easy: both changed. + if (mask == (SubPropertyHorizontalAlignment|SubPropertyVerticalAlignment)) + return newValue; + // Change subprop + const Qt::Alignment changeMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask; + const Qt::Alignment takeOverMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignVertical_Mask : Qt::AlignHorizontal_Mask; + return (oldValue & takeOverMask) | (newValue & changeMask); +} + +} + +namespace qdesigner_internal { + +// apply changed subproperties to a variant +PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue, qdesigner_internal::SpecialProperty specialProperty, unsigned mask, bool changed) +{ + if (mask == SubPropertyAll) + return PropertyHelper::Value(newValue, changed); + + switch (oldValue.type()) { + case QVariant::Rect: + return PropertyHelper::Value(applyRectSubProperty(oldValue.toRect(), newValue.toRect(), mask), changed); + case QVariant::Size: + return PropertyHelper::Value(applySizeSubProperty(oldValue.toSize(), newValue.toSize(), mask), changed); + case QVariant::SizePolicy: + return PropertyHelper::Value(qVariantFromValue(applySizePolicySubProperty(qvariant_cast(oldValue), qvariant_cast(newValue), mask)), changed); + case QVariant::Font: { + // Changed flag in case of font and palette depends on resolve mask only, not on the passed "changed" value. + + // The first case: the user changed bold subproperty and then pressed reset button for this subproperty (not for + // the whole font property). We instantiate SetPropertyCommand passing changed=true. But in this case no + // subproperty is changed and the whole property should be marked an unchanged. + + // The second case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset next to bold subproperty. In result the 2nd widget should have the whole + // font property marked as unchanged and the 1st widget should have the font property + // marked as changed and only italic subproperty should be marked as changed (the bold should be reset). + + // The third case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset button for the whole font property. In result whole font properties for both + // widgets should be marked as unchanged. + QFont font = applyFontSubProperty(qvariant_cast(oldValue), qvariant_cast(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(font), font.resolve()); + } + case QVariant::Palette: { + QPalette palette = applyPaletteSubProperty(qvariant_cast(oldValue), qvariant_cast(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(palette), palette.resolve()); + } + default: + if (oldValue.userType() == qMetaTypeId()) { + PropertySheetIconValue icon = qvariant_cast(oldValue); + icon.assign(qvariant_cast(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(icon), icon.mask()); + } else if (oldValue.userType() == qMetaTypeId()) { + qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty( + qvariant_cast(oldValue), + qvariant_cast(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(str), changed); + } else if (oldValue.userType() == qMetaTypeId()) { + qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty( + qvariant_cast(oldValue), + qvariant_cast(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(key), changed); + } + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: { + qdesigner_internal::PropertySheetFlagValue f = qvariant_cast(oldValue); + f.value = applyAlignmentSubProperty(variantToAlignment(oldValue), variantToAlignment(newValue), mask); + QVariant v; + qVariantSetValue(v, f); + return PropertyHelper::Value(v, changed); + } + default: + break; + } + break; + } + return PropertyHelper::Value(newValue, changed); + +} +// figure out special property +enum SpecialProperty getSpecialProperty(const QString& propertyName) +{ + if (propertyName == QLatin1String("objectName")) + return SP_ObjectName; + if (propertyName == QLatin1String("layoutName")) + return SP_LayoutName; + if (propertyName == QLatin1String("spacerName")) + return SP_SpacerName; + if (propertyName == QLatin1String("icon")) + return SP_Icon; + if (propertyName == QLatin1String("currentTabName")) + return SP_CurrentTabName; + if (propertyName == QLatin1String("currentItemName")) + return SP_CurrentItemName; + if (propertyName == QLatin1String("currentPageName")) + return SP_CurrentPageName; + if (propertyName == QLatin1String("geometry")) + return SP_Geometry; + if (propertyName == QLatin1String("windowTitle")) + return SP_WindowTitle; + if (propertyName == QLatin1String("minimumSize")) + return SP_MinimumSize; + if (propertyName == QLatin1String("maximumSize")) + return SP_MaximumSize; + if (propertyName == QLatin1String("alignment")) + return SP_Alignment; + if (propertyName == QLatin1String("autoDefault")) + return SP_AutoDefault; + if (propertyName == QLatin1String("shortcut")) + return SP_Shortcut; + if (propertyName == QLatin1String("orientation")) + return SP_Orientation; + return SP_None; +} + + +PropertyHelper::PropertyHelper(QObject* object, + SpecialProperty specialProperty, + QDesignerPropertySheetExtension *sheet, + int index) : + m_specialProperty(specialProperty), + m_object(object), + m_objectType(OT_Object), + m_propertySheet(sheet), m_index(index), + m_oldValue(m_propertySheet->property(m_index), m_propertySheet->isChanged(m_index)) +{ + if (object->isWidgetType()) { + m_parentWidget = (qobject_cast(object))->parentWidget(); + m_objectType = OT_Widget; + } else { + if (const QAction *action = qobject_cast(m_object)) + m_objectType = action->associatedWidgets().empty() ? OT_FreeAction : OT_AssociatedAction; + } + + if(debugPropertyCommands) + qDebug() << "PropertyHelper on " << m_object->objectName() << " index= " << m_index << " type = " << m_objectType; +} + +QDesignerIntegration *PropertyHelper::integration(QDesignerFormWindowInterface *fw) const +{ + return qobject_cast(fw->core()->integration()); +} + +// Set widget value, apply corrections and checks in case of main window. +void PropertyHelper::checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w, + SpecialProperty specialProperty, QVariant &value) +{ + + bool isMainContainer = false; + if (QDesignerFormWindowCursorInterface *cursor = fw->cursor()) { + if (cursor->isWidgetSelected(w)) { + if (cursor->isWidgetSelected(fw->mainContainer())) { + isMainContainer = true; + } + } + } + if (!isMainContainer) + return; + + QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + + switch (specialProperty) { + case SP_MinimumSize: { + const QSize size = checkSize(value.toSize()); + qVariantSetValue(value, size); + } + + break; + case SP_MaximumSize: { + QSize fs, cs; + checkSizes(fw, value.toSize(), &fs, &cs); + container->setMaximumSize(cs); + fw->mainContainer()->setMaximumSize(fs); + qVariantSetValue(value, fs); + + } + break; + case SP_Geometry: { + QRect r = value.toRect(); + QSize fs, cs; + checkSizes(fw, r.size(), &fs, &cs); + container->resize(cs); + r.setSize(fs); + qVariantSetValue(value, r); + } + break; + default: + break; + } +} + +unsigned PropertyHelper::updateMask() const +{ + unsigned rc = 0; + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + case SP_CurrentTabName: + case SP_CurrentItemName: + case SP_CurrentPageName: + if (m_objectType != OT_FreeAction) + rc |= UpdateObjectInspector; + break; + case SP_Icon: + if (m_objectType == OT_AssociatedAction) + rc |= UpdateObjectInspector; + break; + case SP_Orientation: // for updating splitter icon + rc |= UpdateObjectInspector; + break; + default: + break; + + } + return rc; +} + + +bool PropertyHelper::canMerge(const PropertyHelper &other) const +{ + return m_object == other.m_object && m_index == other.m_index; +} + +void PropertyHelper::triggerActionChanged(QAction *a) +{ + a->setData(QVariant(true)); // this triggers signal "changed" in QAction + a->setData(QVariant(false)); +} + +// Update the object to reflect the changes +void PropertyHelper::updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::updateObject(" << m_object->objectName() << ") " << oldValue << " -> " << newValue; + } + switch (m_objectType) { + case OT_Widget: { + switch (m_specialProperty) { + case SP_ObjectName: { + const QString oldName = qVariantValue(oldValue).value(); + const QString newName = qVariantValue(newValue).value(); + QDesignerFormWindowCommand::updateBuddies(fw, oldName, newName); + } + break; + default: + break; + } + } break; + case OT_AssociatedAction: + case OT_FreeAction: + // SP_Shortcut is a fake property, so, QAction::changed does not trigger. + if (m_specialProperty == SP_ObjectName || m_specialProperty == SP_Shortcut) + triggerActionChanged(qobject_cast(m_object)); + break; + default: + break; + } + + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + if (QDesignerIntegration *integr = integration(fw)) { + const QString oldName = qVariantValue(oldValue).value(); + const QString newName = qVariantValue(newValue).value(); + integr->emitObjectNameChanged(fw, m_object, newName, oldName); + } + break; + default: + break; + } +} + +void PropertyHelper::ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const +{ + switch (m_specialProperty) { + case SP_SpacerName: + if (object->isWidgetType()) { + if (Spacer *sp = qobject_cast(object)) { + fw->ensureUniqueObjectName(sp); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_LayoutName: // Layout name is invoked on the parent widget. + if (object->isWidgetType()) { + const QWidget * w = qobject_cast(object); + if (QLayout *wlayout = w->layout()) { + fw->ensureUniqueObjectName(wlayout); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_ObjectName: + fw->ensureUniqueObjectName(object); + break; + default: + break; + } +} + +PropertyHelper::Value PropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask) +{ + // Set new whole value + if (subPropertyMask == SubPropertyAll) + return applyValue(fw, m_oldValue.first, Value(value, changed)); + + // apply subproperties + const PropertyHelper::Value maskedNewValue = applySubProperty(m_oldValue.first, value, m_specialProperty, subPropertyMask, changed); + return applyValue(fw, m_oldValue.first, maskedNewValue); +} + +// Apply the value and update. Returns corrected value +PropertyHelper::Value PropertyHelper::applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::applyValue(" << m_object << ") " << oldValue << " -> " << newValue.first << " changed=" << newValue.second; + } + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast(m_object), m_specialProperty, newValue.first); + } + + m_propertySheet->setProperty(m_index, newValue.first); + m_propertySheet->setChanged(m_index, newValue.second); + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + newValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, oldValue, newValue.first); + return newValue; +} + +PropertyHelper::Value PropertyHelper::restoreOldValue(QDesignerFormWindowInterface *fw) +{ + return applyValue(fw, m_propertySheet->property(m_index), m_oldValue); +} + +// find the default value in widget DB in case PropertySheet::reset fails +QVariant PropertyHelper::findDefaultValue(QDesignerFormWindowInterface *fw) const +{ + if (m_specialProperty == SP_AutoDefault && qobject_cast(m_object)) { + // AutoDefault defaults to true on dialogs + const bool isDialog = qobject_cast(fw->mainContainer()); + return QVariant(isDialog); + } + + const int item_idx = fw->core()->widgetDataBase()->indexOfObject(m_object); + if (item_idx == -1) + return m_oldValue.first; // We simply don't know the value in this case + + const QDesignerWidgetDataBaseItemInterface *item = fw->core()->widgetDataBase()->item(item_idx); + const QList default_prop_values = item->defaultPropertyValues(); + if (m_index < default_prop_values.size()) + return default_prop_values.at(m_index); + + if (m_oldValue.first.type() == QVariant::Color) + return QColor(); + + return m_oldValue.first; // Again, we just don't know +} + +PropertyHelper::Value PropertyHelper::restoreDefaultValue(QDesignerFormWindowInterface *fw) +{ + + Value defaultValue = qMakePair(QVariant(), false); + const QVariant currentValue = m_propertySheet->property(m_index); + // try to reset sheet, else try to find default + if (m_propertySheet->reset(m_index)) { + defaultValue.first = m_propertySheet->property(m_index); + } else { + defaultValue.first = findDefaultValue(fw); + m_propertySheet->setProperty(m_index, defaultValue.first); + } + + m_propertySheet->setChanged(m_index, defaultValue.second); + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast(m_object), m_specialProperty, defaultValue.first); + } + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + defaultValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, currentValue, defaultValue.first); + return defaultValue; +} + +// ---- PropertyListCommand::PropertyDescription( + + +PropertyListCommand::PropertyDescription::PropertyDescription(const QString &propertyName, + QDesignerPropertySheetExtension *propertySheet, + int index) : + m_propertyName(propertyName), + m_propertyGroup(propertySheet->propertyGroup(index)), + m_propertyType(propertySheet->property(index).type()), + m_specialProperty(getSpecialProperty(propertyName)) +{ +} + +PropertyListCommand::PropertyDescription::PropertyDescription() : + m_propertyType(QVariant::Invalid), + m_specialProperty(SP_None) +{ +} + +void PropertyListCommand::PropertyDescription::debug() const +{ + qDebug() << m_propertyName << m_propertyGroup << m_propertyType << m_specialProperty; +} + +bool PropertyListCommand::PropertyDescription::equals(const PropertyDescription &p) const +{ + return m_propertyType == p.m_propertyType && m_specialProperty == p.m_specialProperty && + m_propertyName == p.m_propertyName && m_propertyGroup == p.m_propertyGroup; +} + + +// ---- PropertyListCommand +PropertyListCommand::PropertyListCommand(QDesignerFormWindowInterface *formWindow, + QUndoCommand *parent) : + QDesignerFormWindowCommand(QString(), formWindow, parent) +{ +} + +const QString PropertyListCommand::propertyName() const +{ + return m_propertyDescription.m_propertyName; +} + +SpecialProperty PropertyListCommand::specialProperty() const +{ + return m_propertyDescription.m_specialProperty; +} + +// add an object +bool PropertyListCommand::add(QObject *object, const QString &propertyName) +{ + QDesignerPropertySheetExtension* sheet = propertySheet(object); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName); + if (index == -1) + return false; + + if (QDesignerPropertySheet *exSheet = qobject_cast(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension)))) + if (!exSheet->isEnabled(index)) + return false; + + const PropertyDescription description(propertyName, sheet, index); + + if (m_propertyHelperList.empty()) { + // first entry + m_propertyDescription = description; + } else { + // checks: mismatch or only one object in case of name + const bool match = m_propertyDescription.equals(description); + if (!match || m_propertyDescription.m_specialProperty == SP_ObjectName) + return false; + } + + const PropertyHelperPtr ph(createPropertyHelper(object, m_propertyDescription.m_specialProperty, sheet, index)); + m_propertyHelperList.push_back(ph); + return true; +} + +PropertyHelper *PropertyListCommand::createPropertyHelper(QObject *object, SpecialProperty sp, + QDesignerPropertySheetExtension *sheet, int sheetIndex) const +{ + return new PropertyHelper(object, sp, sheet, sheetIndex); +} + +// Init from a list and make sure referenceObject is added first to obtain the right property group +bool PropertyListCommand::initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + propertyHelperList().clear(); + + // Ensure the referenceObject (property editor) is first, so the right property group is chosen. + if (referenceObject) { + if (!add(referenceObject, apropertyName)) + return false; + } + foreach (QObject *o, list) { + if (o != referenceObject) + add(o, apropertyName); + } + + return !propertyHelperList().empty(); +} + + +QObject* PropertyListCommand::object(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList.at(index)->object(); +} + +QVariant PropertyListCommand::oldValue(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList.at(index)->oldValue(); +} + +void PropertyListCommand::setOldValue(const QVariant &oldValue, int index) +{ + Q_ASSERT(index < m_propertyHelperList.size()); + m_propertyHelperList.at(index)->setOldValue(oldValue); +} +// ----- SetValueFunction: Set a new value when applied to a PropertyHelper. +class SetValueFunction { +public: + SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask); + + PropertyHelper::Value operator()(PropertyHelper&); +private: + QDesignerFormWindowInterface *m_formWindow; + const PropertyHelper::Value m_newValue; + const unsigned m_subPropertyMask; +}; + + +SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask) : + m_formWindow(formWindow), + m_newValue(newValue), + m_subPropertyMask(subPropertyMask) +{ +} + +PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) { + return ph.setValue(m_formWindow, m_newValue.first, m_newValue.second, m_subPropertyMask); +} + +// ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper. +class UndoSetValueFunction { +public: + UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreOldValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper. +class RestoreDefaultFunction { +public: + RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreDefaultValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- changePropertyList: Iterates over a sequence of PropertyHelpers and +// applies a function to them. +// The function returns the corrected value which is then set in the property editor. +// Returns a combination of update flags. +template + unsigned changePropertyList(QDesignerFormEditorInterface *core, + const QString &propertyName, + PropertyListIterator begin, + PropertyListIterator end, + Function function) +{ + unsigned updateMask = 0; + QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor(); + bool updatedPropertyEditor = false; + + for (PropertyListIterator it = begin; it != end; ++it) { + PropertyHelper *ph = it->data(); + if (QObject* object = ph->object()) { // Might have been deleted in the meantime + const PropertyHelper::Value newValue = function( *ph ); + updateMask |= ph->updateMask(); + // Update property editor if it is the current object + if (!updatedPropertyEditor && propertyEditor && object == propertyEditor->object()) { + propertyEditor->setPropertyValue(propertyName, newValue.first, newValue.second); + updatedPropertyEditor = true; + } + } + } + if (!updatedPropertyEditor) updateMask |= PropertyHelper::UpdatePropertyEditor; + return updateMask; +} + + +// set a new value, return update mask +unsigned PropertyListCommand::setValue(QVariant value, bool changed, unsigned subPropertyMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::setValue(" << value + << changed << subPropertyMask << ')'; + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, + m_propertyHelperList.begin(), m_propertyHelperList.end(), + SetValueFunction(formWindow(), PropertyHelper::Value(value, changed), subPropertyMask)); +} + +// restore old value, return update mask +unsigned PropertyListCommand::restoreOldValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreOldValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + UndoSetValueFunction(formWindow())); +} +// set default value, return update mask +unsigned PropertyListCommand::restoreDefaultValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreDefaultValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + RestoreDefaultFunction(formWindow())); +} + +// update +void PropertyListCommand::update(unsigned updateMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::update(" << updateMask << ')'; + + if (updateMask & PropertyHelper::UpdateObjectInspector) { + if (QDesignerObjectInspectorInterface *oi = formWindow()->core()->objectInspector()) + oi->setFormWindow(formWindow()); + } + + if (updateMask & PropertyHelper::UpdatePropertyEditor) { + // this is needed when f.ex. undo, changes parent's palette, but + // the child is the active widget, + // TODO: current object? + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + propertyEditor->setObject(propertyEditor->object()); + } + } +} + +void PropertyListCommand::undo() +{ + update(restoreOldValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +// check if lists are aequivalent for command merging (same widgets and props) +bool PropertyListCommand::canMergeLists(const PropertyHelperList& other) const +{ + if (m_propertyHelperList.size() != other.size()) + return false; + for (int i = 0; i < m_propertyHelperList.size(); i++) { + if (!m_propertyHelperList.at(i)->canMerge(*other.at(i))) + return false; + } + return true; +} + +// ---- SetPropertyCommand ---- +SetPropertyCommand::SetPropertyCommand(QDesignerFormWindowInterface *formWindow, + QUndoCommand *parent) + : PropertyListCommand(formWindow, parent), + m_subPropertyMask(SubPropertyAll) +{ +} + +bool SetPropertyCommand::init(QObject *object, const QString &apropertyName, const QVariant &newValue) +{ + Q_ASSERT(object); + + m_newValue = newValue; + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool SetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, const QVariant &newValue, + QObject *referenceObject, bool enableSubPropertyHandling) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + m_newValue = newValue; + + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size() << " reference " << referenceObject; + + setDescription(); + + if (enableSubPropertyHandling) + m_subPropertyMask = subPropertyMask(newValue, referenceObject); + return true; +} + +unsigned SetPropertyCommand::subPropertyMask(const QVariant &newValue, QObject *referenceObject) +{ + // figure out the mask of changed sub properties when comparing newValue to the current value of the reference object. + if (!referenceObject) + return SubPropertyAll; + + QDesignerPropertySheetExtension* sheet = propertySheet(referenceObject); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName()); + if (index == -1 || !sheet->isVisible(index)) + return SubPropertyAll; + + return compareSubProperties(sheet->property(index), newValue, specialProperty()); +} + +void SetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Changed '%1' of '%2'").arg(propertyName()).arg(propertyHelperList().at(0)->object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Changed '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void SetPropertyCommand::redo() +{ + update(setValue(m_newValue, true, m_subPropertyMask)); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + + +int SetPropertyCommand::id() const +{ + return 1976; +} + +QVariant SetPropertyCommand::mergeValue(const QVariant &newValue) +{ + return newValue; +} + +bool SetPropertyCommand::mergeWith(const QUndoCommand *other) +{ + if (id() != other->id() || !formWindow()->isDirty()) + return false; + + // Merging: When for example when the user types ahead in an inplace-editor, + // it makes sense to merge all the generated commands containing the one-character changes. + // In the case of subproperties, if the user changes the font size from 10 to 30 via 20 + // and then changes to bold, it makes sense to merge the font size commands only. + // This is why the m_subPropertyMask is checked. + + const SetPropertyCommand *cmd = static_cast(other); + if (!propertyDescription().equals(cmd->propertyDescription()) || + m_subPropertyMask != cmd->m_subPropertyMask || + !canMergeLists(cmd->propertyHelperList())) + return false; + + const QVariant newValue = mergeValue(cmd->newValue()); + if (!newValue.isValid()) + return false; + m_newValue = newValue; + m_subPropertyMask |= cmd->m_subPropertyMask; + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::mergeWith() succeeded " << propertyName(); + + return true; +} + +// ---- ResetPropertyCommand ---- +ResetPropertyCommand::ResetPropertyCommand(QDesignerFormWindowInterface *formWindow) + : PropertyListCommand(formWindow) +{ +} + +bool ResetPropertyCommand::init(QObject *object, const QString &apropertyName) +{ + Q_ASSERT(object); + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool ResetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + if(debugPropertyCommands) + qDebug() << "ResetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size(); + + setDescription(); + return true; +} + +void ResetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Reset '%1' of '%2'").arg(propertyName()).arg(propertyHelperList().at(0)->object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Reset '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void ResetPropertyCommand::redo() +{ + update(restoreDefaultValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +AddDynamicPropertyCommand::AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool AddDynamicPropertyCommand::init(const QList &selection, QObject *current, + const QString &propertyName, const QVariant &value) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_selection.clear(); + + if (!value.isValid()) + return false; + + if (!dynamicSheet->canAddDynamicProperty(m_propertyName)) + return false; + + m_selection.append(current); + + m_value = value; + + QListIterator it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_selection.contains(obj)) + continue; + dynamicSheet = qt_extension(core->extensionManager(), obj); + Q_ASSERT(dynamicSheet); + if (dynamicSheet->canAddDynamicProperty(m_propertyName)) + m_selection.append(obj); + } + + setDescription(); + return true; +} + +void AddDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), obj); + dynamicSheet->addDynamicProperty(m_propertyName, m_value); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::setDescription() +{ + if (m_selection.size() == 1) { + setText(QApplication::translate("Command", "Add dynamic property '%1' to '%2'").arg(m_propertyName).arg(m_selection.first()->objectName())); + } else { + int count = m_selection.size(); + setText(QApplication::translate("Command", "Add dynamic property '%1' to %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +RemoveDynamicPropertyCommand::RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool RemoveDynamicPropertyCommand::init(const QList &selection, QObject *current, + const QString &propertyName) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerPropertySheetExtension *propertySheet = qt_extension(core->extensionManager(), current); + Q_ASSERT(propertySheet); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_objectToValueAndChanged.clear(); + + const int index = propertySheet->indexOf(m_propertyName); + if (!dynamicSheet->isDynamicProperty(index)) + return false; + + m_objectToValueAndChanged[current] = qMakePair(propertySheet->property(index), propertySheet->isChanged(index)); + + QListIterator it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_objectToValueAndChanged.contains(obj)) + continue; + + propertySheet = qt_extension(core->extensionManager(), obj); + dynamicSheet = qt_extension(core->extensionManager(), obj); + const int idx = propertySheet->indexOf(m_propertyName); + if (dynamicSheet->isDynamicProperty(idx)) + m_objectToValueAndChanged[obj] = qMakePair(propertySheet->property(idx), propertySheet->isChanged(idx)); + } + + setDescription(); + return true; +} + +void RemoveDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), obj); + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerPropertySheetExtension *propertySheet = qt_extension(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core->extensionManager(), obj); + const int index = dynamicSheet->addDynamicProperty(m_propertyName, it.value().first); + propertySheet->setChanged(index, it.value().second); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::setDescription() +{ + if (m_objectToValueAndChanged.size() == 1) { + setText(QApplication::translate("Command", "Remove dynamic property '%1' from '%2'").arg(m_propertyName).arg(m_objectToValueAndChanged.constBegin().key()->objectName())); + } else { + int count = m_objectToValueAndChanged.size(); + setText(QApplication::translate("Command", "Remove dynamic property '%1' from %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_propertycommand_p.h b/designer/lib/shared/qdesigner_propertycommand_p.h new file mode 100644 index 0000000..6ca0678 --- /dev/null +++ b/designer/lib/shared/qdesigner_propertycommand_p.h @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_PROPERTYCOMMAND_H +#define QDESIGNER_PROPERTYCOMMAND_H + +#include "qdesigner_formwindowcommand_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerPropertySheetExtension; + +namespace qdesigner_internal { + +class QDesignerIntegration; + +enum SpecialProperty { + SP_None, SP_ObjectName, SP_LayoutName, SP_SpacerName,SP_WindowTitle, + SP_MinimumSize, SP_MaximumSize, SP_Geometry, SP_Icon, SP_CurrentTabName, SP_CurrentItemName, SP_CurrentPageName, + SP_AutoDefault, SP_Alignment, SP_Shortcut, SP_Orientation +}; + +//Determine special property +enum SpecialProperty getSpecialProperty(const QString& propertyName); + +// A helper class for applying properties to objects. +// Can be used for Set commands (setValue(), restoreOldValue()) or +// Reset Commands restoreDefaultValue(), restoreOldValue()). +// +class QDESIGNER_SHARED_EXPORT PropertyHelper { + Q_DISABLE_COPY(PropertyHelper) +public: + // A pair of Value and changed flag + typedef QPair Value; + + enum ObjectType {OT_Object, OT_FreeAction, OT_AssociatedAction, OT_Widget}; + + PropertyHelper(QObject* object, + SpecialProperty specialProperty, + QDesignerPropertySheetExtension *sheet, + int index); + virtual ~PropertyHelper() {} + + QObject *object() const { return m_object; } + SpecialProperty specialProperty() const { return m_specialProperty; } + // set a new value. Can be overwritten to perform a transformation (see + // handling of Arrow key move in FormWindow class). + virtual Value setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask); + + // restore old value + Value restoreOldValue(QDesignerFormWindowInterface *fw); + // set default value + Value restoreDefaultValue(QDesignerFormWindowInterface *fw); + + inline QVariant oldValue() const + { return m_oldValue.first; } + + inline void setOldValue(const QVariant &oldValue) + { m_oldValue.first = oldValue; } + + // required updates for this property (bit mask) + enum UpdateMask { UpdatePropertyEditor=1, UpdateObjectInspector=2 }; + unsigned updateMask() const; + + // can be merged into one command (that is, object and name match) + bool canMerge(const PropertyHelper &other) const; + QDesignerIntegration *integration(QDesignerFormWindowInterface *fw) const; + + static void triggerActionChanged(QAction *a); + +private: + // Apply the value and update. Returns corrected value + Value applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue); + + static void checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w, + SpecialProperty specialProperty, QVariant &v); + + void updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue); + QVariant findDefaultValue(QDesignerFormWindowInterface *fw) const; + void ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const; + SpecialProperty m_specialProperty; + + QPointer m_object; + ObjectType m_objectType; + QPointer m_parentWidget; + + QDesignerPropertySheetExtension *m_propertySheet; + int m_index; + + Value m_oldValue; +}; + +// Base class for commands that can be applied to several widgets + +class QDESIGNER_SHARED_EXPORT PropertyListCommand : public QDesignerFormWindowCommand { +public: + typedef QList ObjectList; + + explicit PropertyListCommand(QDesignerFormWindowInterface *formWindow, QUndoCommand *parent = 0); + + QObject* object(int index = 0) const; + + QVariant oldValue(int index = 0) const; + + void setOldValue(const QVariant &oldValue, int index = 0); + + // Calls restoreDefaultValue() and update() + virtual void undo(); + +protected: + typedef QSharedPointer PropertyHelperPtr; + typedef QList PropertyHelperList; + + // add an object + bool add(QObject *object, const QString &propertyName); + + // Init from a list and make sure referenceObject is added first to obtain the right property group + bool initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject = 0); + + // set a new value, return update mask + unsigned setValue(QVariant value, bool changed, unsigned subPropertyMask); + + // restore old value, return update mask + unsigned restoreOldValue(); + // set default value, return update mask + unsigned restoreDefaultValue(); + + // update designer + void update(unsigned updateMask); + + // check if lists are aequivalent for command merging (same widgets and props) + bool canMergeLists(const PropertyHelperList& other) const; + + PropertyHelperList& propertyHelperList() { return m_propertyHelperList; } + const PropertyHelperList& propertyHelperList() const { return m_propertyHelperList; } + + const QString propertyName() const; + SpecialProperty specialProperty() const; + + // Helper struct describing a property used for checking whether + // properties of different widgets are equivalent + struct PropertyDescription { + public: + PropertyDescription(); + PropertyDescription(const QString &propertyName, QDesignerPropertySheetExtension *propertySheet, int index); + bool equals(const PropertyDescription &p) const; + void debug() const; + + QString m_propertyName; + QString m_propertyGroup; + QVariant::Type m_propertyType; + SpecialProperty m_specialProperty; + }; + const PropertyDescription &propertyDescription() const { return m_propertyDescription; } + +protected: + virtual PropertyHelper *createPropertyHelper(QObject *o, SpecialProperty sp, + QDesignerPropertySheetExtension *sheet, int sheetIndex) const; + +private: + PropertyDescription m_propertyDescription; + PropertyHelperList m_propertyHelperList; +}; + +class QDESIGNER_SHARED_EXPORT SetPropertyCommand: public PropertyListCommand +{ + +public: + typedef QList ObjectList; + + explicit SetPropertyCommand(QDesignerFormWindowInterface *formWindow, QUndoCommand *parent = 0); + + bool init(QObject *object, const QString &propertyName, const QVariant &newValue); + bool init(const ObjectList &list, const QString &propertyName, const QVariant &newValue, + QObject *referenceObject = 0, bool enableSubPropertyHandling = true); + + + inline QVariant newValue() const + { return m_newValue; } + + inline void setNewValue(const QVariant &newValue) + { m_newValue = newValue; } + + int id() const; + bool mergeWith(const QUndoCommand *other); + + virtual void redo(); + +protected: + virtual QVariant mergeValue(const QVariant &newValue); + +private: + unsigned subPropertyMask(const QVariant &newValue, QObject *referenceObject); + void setDescription(); + QVariant m_newValue; + unsigned m_subPropertyMask; +}; + +class QDESIGNER_SHARED_EXPORT ResetPropertyCommand: public PropertyListCommand +{ + +public: + typedef QList ObjectList; + + explicit ResetPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(QObject *object, const QString &propertyName); + bool init(const ObjectList &list, const QString &propertyName, QObject *referenceObject = 0); + + virtual void redo(); + +protected: + virtual bool mergeWith(const QUndoCommand *) { return false; } + +private: + void setDescription(); + QString m_propertyName; +}; + + +class QDESIGNER_SHARED_EXPORT AddDynamicPropertyCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(const QList &selection, QObject *current, const QString &propertyName, const QVariant &value); + + virtual void redo(); + virtual void undo(); +private: + void setDescription(); + QString m_propertyName; + QList m_selection; + QVariant m_value; +}; + +class QDESIGNER_SHARED_EXPORT RemoveDynamicPropertyCommand: public QDesignerFormWindowCommand +{ + +public: + explicit RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(const QList &selection, QObject *current, const QString &propertyName); + + virtual void redo(); + virtual void undo(); +private: + void setDescription(); + QString m_propertyName; + QMap > m_objectToValueAndChanged; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_PROPERTYCOMMAND_H diff --git a/designer/lib/shared/qdesigner_propertyeditor.cpp b/designer/lib/shared/qdesigner_propertyeditor.cpp new file mode 100644 index 0000000..e550308 --- /dev/null +++ b/designer/lib/shared/qdesigner_propertyeditor.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertyeditor_p.h" +#include "pluginmanager_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +typedef QDesignerPropertyEditor::StringPropertyParameters StringPropertyParameters; +// A map of property name to type +typedef QHash PropertyNameTypeMap; + +// Compile a map of hard-coded string property types +static const PropertyNameTypeMap &stringPropertyTypes() +{ + static PropertyNameTypeMap propertyNameTypeMap; + if (propertyNameTypeMap.empty()) { + const StringPropertyParameters richtext(ValidationRichText, true); + // Accessibility. Both are texts the narrator reads + propertyNameTypeMap.insert(QLatin1String("accessibleDescription"), richtext); + propertyNameTypeMap.insert(QLatin1String("accessibleName"), richtext); + // object names + const StringPropertyParameters objectName(ValidationObjectName, false); + propertyNameTypeMap.insert(QLatin1String("buddy"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentItemName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentPageName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentTabName"), objectName); + propertyNameTypeMap.insert(QLatin1String("layoutName"), objectName); + propertyNameTypeMap.insert(QLatin1String("spacerName"), objectName); + // Style sheet + propertyNameTypeMap.insert(QLatin1String("styleSheet"), StringPropertyParameters(ValidationStyleSheet, false)); + // Buttons/ QCommandLinkButton + const StringPropertyParameters multiline(ValidationMultiLine, true); + propertyNameTypeMap.insert(QLatin1String("description"), multiline); + propertyNameTypeMap.insert(QLatin1String("iconText"), multiline); + // Tooltips, etc. + propertyNameTypeMap.insert(QLatin1String("toolTip"), richtext); + propertyNameTypeMap.insert(QLatin1String("whatsThis"), richtext); + propertyNameTypeMap.insert(QLatin1String("windowIconText"), richtext); + propertyNameTypeMap.insert(QLatin1String("html"), richtext); + // A QWizard page id + propertyNameTypeMap.insert(QLatin1String("pageId"), StringPropertyParameters(ValidationSingleLine, false)); + // QPlainTextEdit + propertyNameTypeMap.insert(QLatin1String("plainText"), StringPropertyParameters(ValidationMultiLine, true)); + } + return propertyNameTypeMap; +} + +QDesignerPropertyEditor::QDesignerPropertyEditor(QWidget *parent, Qt::WindowFlags flags) : + QDesignerPropertyEditorInterface(parent, flags), + m_propertyChangedForwardingBlocked(false) +{ + // Make old signal work for compatibility + connect(this, SIGNAL(propertyChanged(QString,QVariant)), this, SLOT(slotPropertyChanged(QString,QVariant))); +} + +QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textPropertyValidationMode( + QDesignerFormEditorInterface *core, const QObject *object, + const QString &propertyName, bool isMainContainer) +{ + // object name - no comment + if (propertyName == QLatin1String("objectName")) { + const TextPropertyValidationMode vm = isMainContainer ? ValidationObjectNameScope : ValidationObjectName; + return StringPropertyParameters(vm, false); + } + + // Check custom widgets by class. + const QString className = WidgetFactory::classNameOf(core, object); + const QDesignerCustomWidgetData customData = core->pluginManager()->customWidgetData(className); + if (!customData.isNull()) { + StringPropertyParameters customType; + if (customData.xmlStringPropertyType(propertyName, &customType)) + return customType; + } + + // Check hardcoded property ames + const PropertyNameTypeMap::const_iterator hit = stringPropertyTypes().constFind(propertyName); + if (hit != stringPropertyTypes().constEnd()) + return hit.value(); + + // text: Check according to widget type. + if (propertyName == QLatin1String("text")) { + if (qobject_cast(object) || qobject_cast(object)) + return StringPropertyParameters(ValidationSingleLine, true); + if (qobject_cast(object)) + return StringPropertyParameters(ValidationMultiLine, true); + return StringPropertyParameters(ValidationRichText, true); + } + + // Fuzzy matching + if (propertyName.endsWith(QLatin1String("Name"))) + return StringPropertyParameters(ValidationSingleLine, true); + + if (propertyName.endsWith(QLatin1String("ToolTip"))) + return StringPropertyParameters(ValidationRichText, true); + +#ifdef Q_OS_WIN // No translation for the active X "control" property + if (propertyName == QLatin1String("control") && className == QLatin1String("QAxWidget")) + return StringPropertyParameters(ValidationSingleLine, false); +#endif + + // default to single + return StringPropertyParameters(ValidationSingleLine, true); +} + +void QDesignerPropertyEditor::emitPropertyValueChanged(const QString &name, const QVariant &value, bool enableSubPropertyHandling) +{ + // Avoid duplicate signal emission - see below + m_propertyChangedForwardingBlocked = true; + emit propertyValueChanged(name, value, enableSubPropertyHandling); + emit propertyChanged(name, value); + m_propertyChangedForwardingBlocked = false; +} + +void QDesignerPropertyEditor::slotPropertyChanged(const QString &name, const QVariant &value) +{ + // Forward signal from Integration using the old interfaces. + if (!m_propertyChangedForwardingBlocked) + emit propertyValueChanged(name, value, true); +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_propertyeditor_p.h b/designer/lib/shared/qdesigner_propertyeditor_p.h new file mode 100644 index 0000000..27092fa --- /dev/null +++ b/designer/lib/shared/qdesigner_propertyeditor_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef DESIGNERPROPERTYEDITOR_H +#define DESIGNERPROPERTYEDITOR_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Extends the QDesignerPropertyEditorInterface by property comment handling and +// a signal for resetproperty. + +class QDESIGNER_SHARED_EXPORT QDesignerPropertyEditor: public QDesignerPropertyEditorInterface +{ + Q_OBJECT +public: + explicit QDesignerPropertyEditor(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + // A pair . + typedef QPair StringPropertyParameters; + + // Return a pair of validation mode and flag indicating whether property is translatable + // for textual properties. + static StringPropertyParameters textPropertyValidationMode(QDesignerFormEditorInterface *core, + const QObject *object, const QString &propertyName, bool isMainContainer); + +Q_SIGNALS: + void propertyValueChanged(const QString &name, const QVariant &value, bool enableSubPropertyHandling); + void resetProperty(const QString &name); + void addDynamicProperty(const QString &name, const QVariant &value); + void removeDynamicProperty(const QString &name); + void editorOpened(); + void editorClosed(); + +public Q_SLOTS: + /* Quick update that assumes the actual count of properties has not changed + * (as opposed to setObject()). N/A when for example executing a + * layout command and margin properties appear. */ + virtual void updatePropertySheet() = 0; + virtual void reloadResourceProperties() = 0; + +private Q_SLOTS: + void slotPropertyChanged(const QString &name, const QVariant &value); + +protected: + void emitPropertyValueChanged(const QString &name, const QVariant &value, bool enableSubPropertyHandling); + +private: + bool m_propertyChangedForwardingBlocked; + +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DESIGNERPROPERTYEDITOR_H diff --git a/designer/lib/shared/qdesigner_propertysheet.cpp b/designer/lib/shared/qdesigner_propertysheet.cpp new file mode 100644 index 0000000..76301c7 --- /dev/null +++ b/designer/lib/shared/qdesigner_propertysheet.cpp @@ -0,0 +1,1648 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" +#include "formwindowbase_p.h" +#include "layoutinfo_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_introspection_p.h" + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define USE_LAYOUT_SIZE_CONSTRAINT + +static const QDesignerMetaObjectInterface *propertyIntroducedBy(const QDesignerMetaObjectInterface *meta, int index) +{ + if (index >= meta->propertyOffset()) + return meta; + + if (meta->superClass()) + return propertyIntroducedBy(meta->superClass(), index); + + return 0; +} + +// Layout fake properties (prefixed by 'layout' to distinguish them from other 'margins' +// that might be around. These are forwarded to the layout sheet (after name transformation). +// +// 'layoutObjectName' is new for 4.4. It is the name of the actual layout. +// Up to 4.3, QLayoutWidget's name was displayed in the objectinspector. +// This changes with 4.4; the layout name is displayed. This means that for +// old forms, QLayoutWidget will show up as ''; however, the uic code will +// still use 'verticalLayout' (in case someone accesses it). New Layouts get autogenerated names, +// legacy forms will keep their empty names (unless someone types in a new name). +static const char *layoutObjectNameC = "layoutName"; +static const char *layoutLeftMarginC = "layoutLeftMargin"; +static const char *layoutTopMarginC = "layoutTopMargin"; +static const char *layoutRightMarginC = "layoutRightMargin"; +static const char *layoutBottomMarginC = "layoutBottomMargin"; +static const char *layoutSpacingC = "layoutSpacing"; +static const char *layoutHorizontalSpacingC = "layoutHorizontalSpacing"; +static const char *layoutVerticalSpacingC = "layoutVerticalSpacing"; +static const char *layoutSizeConstraintC = "layoutSizeConstraint"; +// form layout +static const char *layoutFieldGrowthPolicyC = "layoutFieldGrowthPolicy"; +static const char *layoutRowWrapPolicyC = "layoutRowWrapPolicy"; +static const char *layoutLabelAlignmentC = "layoutLabelAlignment"; +static const char *layoutFormAlignmentC = "layoutFormAlignment"; +// stretches +static const char *layoutboxStretchPropertyC = "layoutStretch"; +static const char *layoutGridRowStretchPropertyC = "layoutRowStretch"; +static const char *layoutGridColumnStretchPropertyC = "layoutColumnStretch"; +static const char *layoutGridRowMinimumHeightC = "layoutRowMinimumHeight"; +static const char *layoutGridColumnMinimumWidthC = "layoutColumnMinimumWidth"; + +// Find the form editor in the hierarchy. +// We know that the parent of the sheet is the extension manager +// whose parent is the core. + +static QDesignerFormEditorInterface *formEditorForObject(QObject *o) { + do { + if (QDesignerFormEditorInterface* core = qobject_cast(o)) + return core; + o = o->parent(); + } while(o); + Q_ASSERT(o); + return 0; +} + +static bool hasLayoutAttributes(QDesignerFormEditorInterface *core, QObject *object) +{ + if (!object->isWidgetType()) + return false; + + QWidget *w = qobject_cast(object); + if (const QDesignerWidgetDataBaseInterface *db = core->widgetDataBase()) { + if (db->isContainer(w)) + return true; + } + return false; +} + +// Cache DesignerMetaEnum by scope/name of a QMetaEnum +static const qdesigner_internal::DesignerMetaEnum &designerMetaEnumFor(const QDesignerMetaEnumInterface *me) +{ + typedef QPair ScopeNameKey; + typedef QMap DesignerMetaEnumCache; + static DesignerMetaEnumCache cache; + + const QString name = me->name(); + const QString scope = me->scope(); + + const ScopeNameKey key = ScopeNameKey(scope, name); + DesignerMetaEnumCache::iterator it = cache.find(key); + if (it == cache.end()) { + qdesigner_internal::DesignerMetaEnum dme = qdesigner_internal::DesignerMetaEnum(name, scope, me->separator()); + const int keyCount = me->keyCount(); + for (int i=0; i < keyCount; ++i) + dme.addKey(me->value(i), me->key(i)); + it = cache.insert(key, dme); + } + return it.value(); +} + +// Cache DesignerMetaFlags by scope/name of a QMetaEnum +static const qdesigner_internal::DesignerMetaFlags &designerMetaFlagsFor(const QDesignerMetaEnumInterface *me) +{ + typedef QPair ScopeNameKey; + typedef QMap DesignerMetaFlagsCache; + static DesignerMetaFlagsCache cache; + + const QString name = me->name(); + const QString scope = me->scope(); + + const ScopeNameKey key = ScopeNameKey(scope, name); + DesignerMetaFlagsCache::iterator it = cache.find(key); + if (it == cache.end()) { + qdesigner_internal::DesignerMetaFlags dme = qdesigner_internal::DesignerMetaFlags(name, scope, me->separator()); + const int keyCount = me->keyCount(); + for (int i=0; i < keyCount; ++i) + dme.addKey(me->value(i), me->key(i)); + it = cache.insert(key, dme); + } + return it.value(); +} + +// ------------ QDesignerMemberSheetPrivate +class QDesignerPropertySheetPrivate { +public: + typedef QDesignerPropertySheet::PropertyType PropertyType; + typedef QDesignerPropertySheet::ObjectType ObjectType; + + explicit QDesignerPropertySheetPrivate(QDesignerPropertySheet *sheetPublic, QObject *object, QObject *sheetParent); + + bool invalidIndex(const char *functionName, int index) const; + inline int count() const { return m_meta->propertyCount() + m_addProperties.count(); } + + PropertyType propertyType(int index) const; + QString transformLayoutPropertyName(int index) const; + QLayout* layout(QDesignerPropertySheetExtension **layoutPropertySheet = 0) const; + static ObjectType objectType(const QObject *o); + + bool isReloadableProperty(int index) const; + bool isResourceProperty(int index) const; + void addResourceProperty(int index, QVariant::Type type); + QVariant resourceProperty(int index) const; + void setResourceProperty(int index, const QVariant &value); + QVariant emptyResourceProperty(int index) const; // of type PropertySheetPixmapValue / PropertySheetIconValue + QVariant defaultResourceProperty(int index) const; // of type QPixmap / QIcon (maybe it can be generalized for all types, not resource only) + + bool isStringProperty(int index) const; + void addStringProperty(int index); + qdesigner_internal::PropertySheetStringValue stringProperty(int index) const; + void setStringProperty(int index, const qdesigner_internal::PropertySheetStringValue &value); + + bool isKeySequenceProperty(int index) const; + void addKeySequenceProperty(int index); + qdesigner_internal::PropertySheetKeySequenceValue keySequenceProperty(int index) const; + void setKeySequenceProperty(int index, const qdesigner_internal::PropertySheetKeySequenceValue &value); + + enum PropertyKind { NormalProperty, FakeProperty, DynamicProperty, DefaultDynamicProperty }; + class Info { + public: + Info(); + + QString group; + QVariant defaultValue; + bool changed; + bool visible; + bool attribute; + bool reset; + PropertyType propertyType; + PropertyKind kind; + }; + + Info &ensureInfo(int index); + + QDesignerPropertySheet *q; + QDesignerFormEditorInterface *m_core; + const QDesignerMetaObjectInterface *m_meta; + const ObjectType m_objectType; + + typedef QHash InfoHash; + InfoHash m_info; + QHash m_fakeProperties; + QHash m_addProperties; + QHash m_addIndex; + QHash m_resourceProperties; // only PropertySheetPixmapValue snd PropertySheetIconValue here + QHash m_stringProperties; // only PropertySheetStringValue + QHash m_keySequenceProperties; // only PropertySheetKeySequenceValue + + const bool m_canHaveLayoutAttributes; + + // Variables used for caching the layout, access via layout(). + QPointer m_object; + mutable QPointer m_lastLayout; + mutable QDesignerPropertySheetExtension *m_lastLayoutPropertySheet; + mutable bool m_LastLayoutByDesigner; + + qdesigner_internal::DesignerPixmapCache *m_pixmapCache; + qdesigner_internal::DesignerIconCache *m_iconCache; + QPointer m_fwb; + + // Enable Qt's internal properties starting with prefix "_q_" + static bool m_internalDynamicPropertiesEnabled; +}; + +bool QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled = false; + +/* + The property is reloadable if its contents depends on resource. +*/ +bool QDesignerPropertySheetPrivate::isReloadableProperty(int index) const +{ + return isResourceProperty(index) + || propertyType(index) == QDesignerPropertySheet::PropertyStyleSheet + || propertyType(index) == QDesignerPropertySheet::PropertyText + || q->property(index).type() == QVariant::Url; +} + +/* + Resource properties are those which: + 1) are reloadable + 2) their state is associated with a file which can be taken from resources + 3) we don't store them in Qt meta object system (because designer keeps different data structure for them) +*/ + +bool QDesignerPropertySheetPrivate::isResourceProperty(int index) const +{ + return m_resourceProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addResourceProperty(int index, QVariant::Type type) +{ + if (type == QVariant::Pixmap) + m_resourceProperties.insert(index, qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue())); + else if (type == QVariant::Icon) + m_resourceProperties.insert(index, qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); +} + +QVariant QDesignerPropertySheetPrivate::emptyResourceProperty(int index) const +{ + QVariant v = m_resourceProperties.value(index); + if (qVariantCanConvert(v)) + return qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue()); + if (qVariantCanConvert(v)) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + return v; +} + +QVariant QDesignerPropertySheetPrivate::defaultResourceProperty(int index) const +{ + return m_info.value(index).defaultValue; +} + +QVariant QDesignerPropertySheetPrivate::resourceProperty(int index) const +{ + return m_resourceProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setResourceProperty(int index, const QVariant &value) +{ + Q_ASSERT(isResourceProperty(index)); + + QVariant &v = m_resourceProperties[index]; + if ((qVariantCanConvert(value) && qVariantCanConvert(v)) + || (qVariantCanConvert(value) && qVariantCanConvert(v))) + v = value; +} + +bool QDesignerPropertySheetPrivate::isStringProperty(int index) const +{ + return m_stringProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addStringProperty(int index) +{ + m_stringProperties.insert(index, qdesigner_internal::PropertySheetStringValue()); +} + +qdesigner_internal::PropertySheetStringValue QDesignerPropertySheetPrivate::stringProperty(int index) const +{ + return m_stringProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setStringProperty(int index, const qdesigner_internal::PropertySheetStringValue &value) +{ + Q_ASSERT(isStringProperty(index)); + + m_stringProperties[index] = value; +} + +bool QDesignerPropertySheetPrivate::isKeySequenceProperty(int index) const +{ + return m_keySequenceProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addKeySequenceProperty(int index) +{ + m_keySequenceProperties.insert(index, qdesigner_internal::PropertySheetKeySequenceValue()); +} + +qdesigner_internal::PropertySheetKeySequenceValue QDesignerPropertySheetPrivate::keySequenceProperty(int index) const +{ + return m_keySequenceProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setKeySequenceProperty(int index, const qdesigner_internal::PropertySheetKeySequenceValue &value) +{ + Q_ASSERT(isKeySequenceProperty(index)); + + m_keySequenceProperties[index] = value; +} + +QDesignerPropertySheetPrivate::Info::Info() : + changed(false), + visible(true), + attribute(false), + reset(true), + propertyType(QDesignerPropertySheet::PropertyNone), + kind(NormalProperty) +{ +} + +QDesignerPropertySheetPrivate::QDesignerPropertySheetPrivate(QDesignerPropertySheet *sheetPublic, QObject *object, QObject *sheetParent) : + q(sheetPublic), + m_core(formEditorForObject(sheetParent)), + m_meta(m_core->introspection()->metaObject(object)), + m_objectType(QDesignerPropertySheet::objectTypeFromObject(object)), + m_canHaveLayoutAttributes(hasLayoutAttributes(m_core, object)), + m_object(object), + m_lastLayout(0), + m_lastLayoutPropertySheet(0), + m_LastLayoutByDesigner(false), + m_pixmapCache(0), + m_iconCache(0) +{ +} + +qdesigner_internal::FormWindowBase *QDesignerPropertySheet::formWindowBase() const +{ + return d->m_fwb; +} + +bool QDesignerPropertySheetPrivate::invalidIndex(const char *functionName, int index) const +{ + if (index < 0 || index >= count()) { + qWarning() << "** WARNING " << functionName << " invoked for " << m_object->objectName() << " was passed an invalid index " << index << '.'; + return true; + } + return false; +} + +QLayout* QDesignerPropertySheetPrivate::layout(QDesignerPropertySheetExtension **layoutPropertySheet) const +{ + // Return the layout and its property sheet + // only if it is managed by designer and not one created on a custom widget. + // (attempt to cache the value as this requires some hoops). + if (layoutPropertySheet) + *layoutPropertySheet = 0; + + if (!m_object->isWidgetType() || !m_canHaveLayoutAttributes) + return 0; + + QWidget *widget = qobject_cast(m_object); + QLayout *widgetLayout = qdesigner_internal::LayoutInfo::internalLayout(widget); + if (!widgetLayout) { + m_lastLayout = 0; + m_lastLayoutPropertySheet = 0; + return 0; + } + // Smart logic to avoid retrieving the meta DB from the widget every time. + if (widgetLayout != m_lastLayout) { + m_lastLayout = widgetLayout; + m_LastLayoutByDesigner = false; + m_lastLayoutPropertySheet = 0; + // Is this a layout managed by designer or some layout on a custom widget? + if (qdesigner_internal::LayoutInfo::managedLayout(m_core ,widgetLayout)) { + m_LastLayoutByDesigner = true; + m_lastLayoutPropertySheet = qt_extension(m_core->extensionManager(), m_lastLayout); + } + } + if (!m_LastLayoutByDesigner) + return 0; + + if (layoutPropertySheet) + *layoutPropertySheet = m_lastLayoutPropertySheet; + + return m_lastLayout; +} + +QDesignerPropertySheetPrivate::Info &QDesignerPropertySheetPrivate::ensureInfo(int index) +{ + InfoHash::iterator it = m_info.find(index); + if (it == m_info.end()) + it = m_info.insert(index, Info()); + return it.value(); +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheetPrivate::propertyType(int index) const +{ + const InfoHash::const_iterator it = m_info.constFind(index); + if (it == m_info.constEnd()) + return QDesignerPropertySheet::PropertyNone; + return it.value().propertyType; +} + +QString QDesignerPropertySheetPrivate::transformLayoutPropertyName(int index) const +{ + typedef QMap TypeNameMap; + static TypeNameMap typeNameMap; + if (typeNameMap.empty()) { + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutObjectName, QLatin1String("objectName")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutLeftMargin, QLatin1String("leftMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutTopMargin, QLatin1String("topMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutRightMargin, QLatin1String("rightMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutBottomMargin, QLatin1String("bottomMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutSpacing, QLatin1String("spacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutHorizontalSpacing, QLatin1String("horizontalSpacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutVerticalSpacing, QLatin1String("verticalSpacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutSizeConstraint, QLatin1String("sizeConstraint")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutFieldGrowthPolicy, QLatin1String("fieldGrowthPolicy")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutRowWrapPolicy, QLatin1String("rowWrapPolicy")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutLabelAlignment, QLatin1String("labelAlignment")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutFormAlignment, QLatin1String("formAlignment")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutBoxStretch, QLatin1String("stretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridRowStretch, QLatin1String("rowStretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridColumnStretch, QLatin1String("columnStretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridRowMinimumHeight, QLatin1String("rowMinimumHeight")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridColumnMinimumWidth, QLatin1String("columnMinimumWidth")); + } + const TypeNameMap::const_iterator it = typeNameMap.constFind(propertyType(index)); + if (it != typeNameMap.constEnd()) + return it.value(); + return QString(); +} + +// ----------- QDesignerPropertySheet + +QDesignerPropertySheet::ObjectType QDesignerPropertySheet::objectTypeFromObject(const QObject *o) +{ + if (qobject_cast(o)) + return ObjectLayout; + + if (!o->isWidgetType()) + return ObjectNone; + + if (qobject_cast(o)) + return ObjectLayoutWidget; + + if (qobject_cast(o)) + return ObjectLabel; + + if (o->inherits("Q3GroupBox")) + return ObjectQ3GroupBox; + + return ObjectNone; +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheet::propertyTypeFromName(const QString &name) +{ + typedef QHash PropertyTypeHash; + static PropertyTypeHash propertyTypeHash; + if (propertyTypeHash.empty()) { + propertyTypeHash.insert(QLatin1String(layoutObjectNameC), PropertyLayoutObjectName); + propertyTypeHash.insert(QLatin1String(layoutLeftMarginC), PropertyLayoutLeftMargin); + propertyTypeHash.insert(QLatin1String(layoutTopMarginC), PropertyLayoutTopMargin); + propertyTypeHash.insert(QLatin1String(layoutRightMarginC), PropertyLayoutRightMargin); + propertyTypeHash.insert(QLatin1String(layoutBottomMarginC), PropertyLayoutBottomMargin); + propertyTypeHash.insert(QLatin1String(layoutSpacingC), PropertyLayoutSpacing); + propertyTypeHash.insert(QLatin1String(layoutHorizontalSpacingC), PropertyLayoutHorizontalSpacing); + propertyTypeHash.insert(QLatin1String(layoutVerticalSpacingC), PropertyLayoutVerticalSpacing); + propertyTypeHash.insert(QLatin1String(layoutSizeConstraintC), PropertyLayoutSizeConstraint); + propertyTypeHash.insert(QLatin1String(layoutFieldGrowthPolicyC), PropertyLayoutFieldGrowthPolicy); + propertyTypeHash.insert(QLatin1String(layoutRowWrapPolicyC), PropertyLayoutRowWrapPolicy); + propertyTypeHash.insert(QLatin1String(layoutLabelAlignmentC), PropertyLayoutLabelAlignment); + propertyTypeHash.insert(QLatin1String(layoutFormAlignmentC), PropertyLayoutFormAlignment); + propertyTypeHash.insert(QLatin1String(layoutboxStretchPropertyC), PropertyLayoutBoxStretch); + propertyTypeHash.insert(QLatin1String(layoutGridRowStretchPropertyC), PropertyLayoutGridRowStretch); + propertyTypeHash.insert(QLatin1String(layoutGridColumnStretchPropertyC), PropertyLayoutGridColumnStretch); + propertyTypeHash.insert(QLatin1String(layoutGridRowMinimumHeightC), PropertyLayoutGridRowMinimumHeight); + propertyTypeHash.insert(QLatin1String(layoutGridColumnMinimumWidthC), PropertyLayoutGridColumnMinimumWidth); + propertyTypeHash.insert(QLatin1String("buddy"), PropertyBuddy); + propertyTypeHash.insert(QLatin1String("geometry"), PropertyGeometry); + propertyTypeHash.insert(QLatin1String("checkable"), PropertyCheckable); + propertyTypeHash.insert(QLatin1String("accessibleName"), PropertyAccessibility); + propertyTypeHash.insert(QLatin1String("accessibleDescription"), PropertyAccessibility); + propertyTypeHash.insert(QLatin1String("windowTitle"), PropertyWindowTitle); + propertyTypeHash.insert(QLatin1String("windowIcon"), PropertyWindowIcon); + propertyTypeHash.insert(QLatin1String("windowFilePath"), PropertyWindowFilePath); + propertyTypeHash.insert(QLatin1String("windowOpacity"), PropertyWindowOpacity); + propertyTypeHash.insert(QLatin1String("windowIconText"), PropertyWindowIconText); + propertyTypeHash.insert(QLatin1String("windowModality"), PropertyWindowModality); + propertyTypeHash.insert(QLatin1String("windowModified"), PropertyWindowModified); + propertyTypeHash.insert(QLatin1String("styleSheet"), PropertyStyleSheet); + propertyTypeHash.insert(QLatin1String("text"), PropertyText); + } + return propertyTypeHash.value(name, PropertyNone); +} + +QDesignerPropertySheet::QDesignerPropertySheet(QObject *object, QObject *parent) : + QObject(parent), + d(new QDesignerPropertySheetPrivate(this, object, parent)) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + const QDesignerMetaObjectInterface *baseMeta = d->m_meta; + + while (baseMeta &&baseMeta->className().startsWith(QLatin1String("QDesigner"))) { + baseMeta = baseMeta->superClass(); + } + Q_ASSERT(baseMeta != 0); + + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(d->m_object); + d->m_fwb = qobject_cast(formWindow); + if (d->m_fwb) { + d->m_pixmapCache = d->m_fwb->pixmapCache(); + d->m_iconCache = d->m_fwb->iconCache(); + d->m_fwb->addReloadablePropertySheet(this, object); + } + + for (int index=0; indexm_meta->property(index); + const QString name = p->name(); + if (p->type() == QVariant::KeySequence) { + createFakeProperty(name); + } else { + setVisible(index, false); // use the default for `real' properties + } + + QString pgroup = baseMeta->className(); + + if (const QDesignerMetaObjectInterface *pmeta = propertyIntroducedBy(baseMeta, index)) { + pgroup = pmeta->className(); + } + + Info &info = d->ensureInfo(index); + info.group = pgroup; + info.propertyType = propertyTypeFromName(name); + + if (p->type() == QVariant::Cursor || p->type() == QVariant::Icon || p->type() == QVariant::Pixmap) { + info.defaultValue = p->read(d->m_object); + if (p->type() == QVariant::Icon || p->type() == QVariant::Pixmap) + d->addResourceProperty(index, p->type()); + } else if (p->type() == QVariant::String) { + d->addStringProperty(index); + } else if (p->type() == QVariant::KeySequence) { + d->addKeySequenceProperty(index); + } + } + + if (object->isWidgetType()) { + createFakeProperty(QLatin1String("focusPolicy")); + createFakeProperty(QLatin1String("cursor")); + createFakeProperty(QLatin1String("toolTip")); + createFakeProperty(QLatin1String("whatsThis")); + createFakeProperty(QLatin1String("acceptDrops")); + createFakeProperty(QLatin1String("dragEnabled")); + // windowModality/Opacity is visible only for the main container, in which case the form windows enables it on loading + setVisible(createFakeProperty(QLatin1String("windowModality")), false); + setVisible(createFakeProperty(QLatin1String("windowOpacity"), double(1.0)), false); + if (qobject_cast(d->m_object)) { // prevent toolbars from being dragged off + createFakeProperty(QLatin1String("floatable"), QVariant(true)); + } else { + if (qobject_cast(d->m_object)) { + // Keep the menu bar editable in the form even if a native menu bar is used. + const bool nativeMenuBarDefault = !qApp->testAttribute(Qt::AA_DontUseNativeMenuBar); + createFakeProperty(QLatin1String("nativeMenuBar"), QVariant(nativeMenuBarDefault)); + } + } + if (d->m_canHaveLayoutAttributes) { + static const QString layoutGroup = QLatin1String("Layout"); + const char* fakeLayoutProperties[] = { + layoutObjectNameC, layoutLeftMarginC, layoutTopMarginC, layoutRightMarginC, layoutBottomMarginC, layoutSpacingC, layoutHorizontalSpacingC, layoutVerticalSpacingC, + layoutFieldGrowthPolicyC, layoutRowWrapPolicyC, layoutLabelAlignmentC, layoutFormAlignmentC, + layoutboxStretchPropertyC, layoutGridRowStretchPropertyC, layoutGridColumnStretchPropertyC, + layoutGridRowMinimumHeightC, layoutGridColumnMinimumWidthC +#ifdef USE_LAYOUT_SIZE_CONSTRAINT + , layoutSizeConstraintC +#endif + }; + const int fakeLayoutPropertyCount = sizeof(fakeLayoutProperties)/sizeof(const char*); + const int size = count(); + for (int i = 0; i < fakeLayoutPropertyCount; i++) { + createFakeProperty(QLatin1String(fakeLayoutProperties[i]), 0); + setAttribute(size + i, true); + setPropertyGroup(size + i, layoutGroup); + } + } + + if (d->m_objectType == ObjectLabel) + createFakeProperty(QLatin1String("buddy"), QVariant(QByteArray())); + /* We need to create a fake property since the property does not work + * for non-toplevel windows or on other systems than Mac and only if + * it is above a certain Mac OS version. */ + if (qobject_cast(d->m_object)) + createFakeProperty(QLatin1String("unifiedTitleAndToolBarOnMac"), false); + } + + if (qobject_cast(object)) { + createFakeProperty(QLatin1String("modal")); + } + if (qobject_cast(object)) { + createFakeProperty(QLatin1String("floating")); + } + + typedef QList ByteArrayList; + const ByteArrayList names = object->dynamicPropertyNames(); + if (!names.empty()) { + const ByteArrayList::const_iterator cend = names.constEnd(); + for (ByteArrayList::const_iterator it = names.constBegin(); it != cend; ++it) { + const char* cName = it->constData(); + const QString name = QString::fromLatin1(cName); + const int idx = addDynamicProperty(name, object->property(cName)); + if (idx != -1) + d->ensureInfo(idx).kind = QDesignerPropertySheetPrivate::DefaultDynamicProperty; + } + } +} + +QDesignerPropertySheet::~QDesignerPropertySheet() +{ + if (d->m_fwb) + d->m_fwb->removeReloadablePropertySheet(this); + delete d; +} + +QObject *QDesignerPropertySheet::object() const +{ + return d->m_object; +} + +bool QDesignerPropertySheet::dynamicPropertiesAllowed() const +{ + return true; +} + +bool QDesignerPropertySheet::canAddDynamicProperty(const QString &propName) const +{ + // used internally + if (propName == QLatin1String("database") || + propName == QLatin1String("buttonGroupId")) + return false; + const int index = d->m_meta->indexOfProperty(propName); + if (index != -1) + return false; // property already exists and is not a dynamic one + if (d->m_addIndex.contains(propName)) { + const int idx = d->m_addIndex.value(propName); + if (isVisible(idx)) + return false; // dynamic property already exists + else + return true; + } + if (!QDesignerPropertySheet::internalDynamicPropertiesEnabled() && propName.startsWith(QLatin1String("_q_"))) + return false; + return true; +} + +int QDesignerPropertySheet::addDynamicProperty(const QString &propName, const QVariant &value) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + if (!value.isValid()) + return -1; // property has invalid type + if (!canAddDynamicProperty(propName)) + return -1; + + QVariant v = value; + if (value.type() == QVariant::Icon) + v = qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + else if (value.type() == QVariant::Pixmap) + v = qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue()); + else if (value.type() == QVariant::String) + v = qVariantFromValue(qdesigner_internal::PropertySheetStringValue(value.toString())); + else if (value.type() == QVariant::KeySequence) { + const QKeySequence keySequence = qVariantValue(value); + v = qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue(keySequence)); + } + + if (d->m_addIndex.contains(propName)) { + const int idx = d->m_addIndex.value(propName); + // have to be invisible, this was checked in canAddDynamicProperty() method + setVisible(idx, true); + d->m_addProperties.insert(idx, v); + setChanged(idx, false); + const int index = d->m_meta->indexOfProperty(propName); + Info &info = d->ensureInfo(index); + info.defaultValue = value; + info.kind = QDesignerPropertySheetPrivate::DynamicProperty; + if (value.type() == QVariant::Icon || value.type() == QVariant::Pixmap) + d->addResourceProperty(idx, value.type()); + else if (value.type() == QVariant::String) + d->addStringProperty(idx); + else if (value.type() == QVariant::KeySequence) + d->addKeySequenceProperty(idx); + return idx; + } + + const int index = count(); + d->m_addIndex.insert(propName, index); + d->m_addProperties.insert(index, v); + Info &info = d->ensureInfo(index); + info.visible = true; + info.changed = false; + info.defaultValue = value; + info.kind = QDesignerPropertySheetPrivate::DynamicProperty; + setPropertyGroup(index, tr("Dynamic Properties")); + if (value.type() == QVariant::Icon || value.type() == QVariant::Pixmap) + d->addResourceProperty(index, value.type()); + else if (value.type() == QVariant::String) + d->addStringProperty(index); + else if (value.type() == QVariant::KeySequence) + d->addKeySequenceProperty(index); + return index; +} + +bool QDesignerPropertySheet::removeDynamicProperty(int index) +{ + if (!d->m_addIndex.contains(propertyName(index))) + return false; + + setVisible(index, false); + return true; +} + +bool QDesignerPropertySheet::isDynamic(int index) const +{ + if (!d->m_addProperties.contains(index)) + return false; + + switch (propertyType(index)) { + case PropertyBuddy: + if (d->m_objectType == ObjectLabel) + return false; + break; + case PropertyLayoutLeftMargin: + case PropertyLayoutTopMargin: + case PropertyLayoutRightMargin: + case PropertyLayoutBottomMargin: + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + case PropertyLayoutObjectName: + case PropertyLayoutSizeConstraint: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + if (d->m_object->isWidgetType() && d->m_canHaveLayoutAttributes) + return false; + default: + break; + } + return true; +} + +bool QDesignerPropertySheet::isDynamicProperty(int index) const +{ + // Do not complain here, as an invalid index might be encountered + // if someone implements a property sheet only, omitting the dynamic sheet. + if (index < 0 || index >= count()) + return false; + return d->m_info.value(index).kind == QDesignerPropertySheetPrivate::DynamicProperty; +} + +bool QDesignerPropertySheet::isDefaultDynamicProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + return d->m_info.value(index).kind == QDesignerPropertySheetPrivate::DefaultDynamicProperty; +} + +bool QDesignerPropertySheet::isResourceProperty(int index) const +{ + return d->isResourceProperty(index); +} + +QVariant QDesignerPropertySheet::defaultResourceProperty(int index) const +{ + return d->defaultResourceProperty(index); +} + +qdesigner_internal::DesignerPixmapCache *QDesignerPropertySheet::pixmapCache() const +{ + return d->m_pixmapCache; +} + +void QDesignerPropertySheet::setPixmapCache(qdesigner_internal::DesignerPixmapCache *cache) +{ + d->m_pixmapCache = cache; +} + +qdesigner_internal::DesignerIconCache *QDesignerPropertySheet::iconCache() const +{ + return d->m_iconCache; +} + +void QDesignerPropertySheet::setIconCache(qdesigner_internal::DesignerIconCache *cache) +{ + d->m_iconCache = cache; +} + +int QDesignerPropertySheet::createFakeProperty(const QString &propertyName, const QVariant &value) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + // fake properties + const int index = d->m_meta->indexOfProperty(propertyName); + if (index != -1) { + if (!(d->m_meta->property(index)->attributes() & QDesignerMetaPropertyInterface::DesignableAttribute)) + return -1; + Info &info = d->ensureInfo(index); + info.visible = false; + info.kind = QDesignerPropertySheetPrivate::FakeProperty; + QVariant v = value.isValid() ? value : metaProperty(index); + if (v.type() == QVariant::String) + v = qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (v.type() == QVariant::KeySequence) + v = qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue()); + d->m_fakeProperties.insert(index, v); + return index; + } + if (!value.isValid()) + return -1; + + const int newIndex = count(); + d->m_addIndex.insert(propertyName, newIndex); + d->m_addProperties.insert(newIndex, value); + Info &info = d->ensureInfo(newIndex); + info.propertyType = propertyTypeFromName(propertyName); + info.kind = QDesignerPropertySheetPrivate::FakeProperty; + return newIndex; +} + +bool QDesignerPropertySheet::isAdditionalProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + return d->m_addProperties.contains(index); +} + +bool QDesignerPropertySheet::isFakeProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + // additional properties must be fake + return (d->m_fakeProperties.contains(index) || isAdditionalProperty(index)); +} + +int QDesignerPropertySheet::count() const +{ + return d->count(); +} + +int QDesignerPropertySheet::indexOf(const QString &name) const +{ + int index = d->m_meta->indexOfProperty(name); + + if (index == -1) + index = d->m_addIndex.value(name, -1); + + return index; +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheet::propertyType(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return PropertyNone; + return d->propertyType(index); +} + +QDesignerPropertySheet::ObjectType QDesignerPropertySheet::objectType() const +{ + return d->m_objectType; +} + +QString QDesignerPropertySheet::propertyName(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QString(); + if (isAdditionalProperty(index)) + return d->m_addIndex.key(index); + + return d->m_meta->property(index)->name(); +} + +QString QDesignerPropertySheet::propertyGroup(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QString(); + const QString g = d->m_info.value(index).group; + + if (!g.isEmpty()) + return g; + + if (propertyType(index) == PropertyAccessibility) + return QString::fromUtf8("Accessibility"); + + if (isAdditionalProperty(index)) + return d->m_meta->className(); + + return g; +} + +void QDesignerPropertySheet::setPropertyGroup(int index, const QString &group) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).group = group; +} + +QVariant QDesignerPropertySheet::property(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QVariant(); + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + return layoutPropertySheet->property(newIndex); + return QVariant(); + } + } + } + return d->m_addProperties.value(index); + } + + if (isFakeProperty(index)) { + return d->m_fakeProperties.value(index); + } + + if (d->isResourceProperty(index)) + return d->resourceProperty(index); + + if (d->isStringProperty(index)) { + QString strValue = metaProperty(index).toString(); + qdesigner_internal::PropertySheetStringValue value = d->stringProperty(index); + if (strValue != value.value()) { + value.setValue(strValue); + d->setStringProperty(index, value); // cache it + } + return qVariantFromValue(value); + } + + if (d->isKeySequenceProperty(index)) { + QKeySequence keyValue = qVariantValue(metaProperty(index)); + qdesigner_internal::PropertySheetKeySequenceValue value = d->keySequenceProperty(index); + if (keyValue != value.value()) { + value.setValue(keyValue); + d->setKeySequenceProperty(index, value); // cache it + } + return qVariantFromValue(value); + } + + return metaProperty(index); +} + +QVariant QDesignerPropertySheet::metaProperty(int index) const +{ + Q_ASSERT(!isFakeProperty(index)); + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + QVariant v = p->read(d->m_object); + switch (p->kind()) { + case QDesignerMetaPropertyInterface::FlagKind: { + qdesigner_internal::PropertySheetFlagValue psflags = qdesigner_internal::PropertySheetFlagValue(v.toInt(), designerMetaFlagsFor(p->enumerator())); + qVariantSetValue(v, psflags); + } + break; + case QDesignerMetaPropertyInterface::EnumKind: { + qdesigner_internal::PropertySheetEnumValue pse = qdesigner_internal::PropertySheetEnumValue(v.toInt(), designerMetaEnumFor(p->enumerator())); + qVariantSetValue(v, pse); + } + break; + case QDesignerMetaPropertyInterface::OtherKind: + break; + } + return v; +} + +QVariant QDesignerPropertySheet::resolvePropertyValue(int index, const QVariant &value) const +{ + if (qVariantCanConvert(value)) + return qvariant_cast(value).value; + + if (qVariantCanConvert(value)) + return qvariant_cast(value).value; + + if (qVariantCanConvert(value)) + return qVariantValue(value).value(); + + if (qVariantCanConvert(value)) + return qVariantValue(value).value(); + + if (qVariantCanConvert(value)) { + const QString path = qVariantValue(value).path(); + if (path.isEmpty()) + return defaultResourceProperty(index); + if (d->m_pixmapCache) { + return d->m_pixmapCache->pixmap(qvariant_cast(value)); + } + } + + if (qVariantCanConvert(value)) { + const int pathCount = qVariantValue(value).paths().count(); + if (pathCount == 0) + return defaultResourceProperty(index); + if (d->m_iconCache) + return d->m_iconCache->icon(qvariant_cast(value)); + } + + return value; +} + +void QDesignerPropertySheet::setFakeProperty(int index, const QVariant &value) +{ + Q_ASSERT(isFakeProperty(index)); + + QVariant &v = d->m_fakeProperties[index]; + + // set resource properties also (if we are going to have fake resource properties) + if (qVariantCanConvert(value) || qVariantCanConvert(value)) { + v = value; + } else if (qVariantCanConvert(v)) { + qdesigner_internal::PropertySheetFlagValue f = qvariant_cast(v); + f.value = value.toInt(); + qVariantSetValue(v, f); + Q_ASSERT(value.type() == QVariant::Int); + } else if (qVariantCanConvert(v)) { + qdesigner_internal::PropertySheetEnumValue e = qvariant_cast(v); + e.value = value.toInt(); + qVariantSetValue(v, e); + Q_ASSERT(value.type() == QVariant::Int); + } else { + v = value; + } +} + +void QDesignerPropertySheet::clearFakeProperties() +{ + d->m_fakeProperties.clear(); +} + +// Buddy needs to be byte array, else uic won't work +static QVariant toByteArray(const QVariant &value) { + if (value.type() == QVariant::ByteArray) + return value; + const QByteArray ba = value.toString().toUtf8(); + return QVariant(ba); +} + +void QDesignerPropertySheet::setProperty(int index, const QVariant &value) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + if (isAdditionalProperty(index)) { + if (d->m_objectType == ObjectLabel && propertyType(index) == PropertyBuddy) { + QFormBuilderExtra::applyBuddy(value.toString(), QFormBuilderExtra::BuddyApplyVisibleOnly, qobject_cast(d->m_object)); + d->m_addProperties[index] = toByteArray(value); + return; + } + + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + layoutPropertySheet->setProperty(newIndex, value); + } + } + } + + if (isDynamicProperty(index) || isDefaultDynamicProperty(index)) { + if (d->isResourceProperty(index)) + d->setResourceProperty(index, value); + if (d->isStringProperty(index)) + d->setStringProperty(index, qVariantValue(value)); + if (d->isKeySequenceProperty(index)) + d->setKeySequenceProperty(index, qVariantValue(value)); + d->m_object->setProperty(propertyName(index).toUtf8(), resolvePropertyValue(index, value)); + if (d->m_object->isWidgetType()) { + QWidget *w = qobject_cast(d->m_object); + w->setStyleSheet(w->styleSheet()); + } + } + d->m_addProperties[index] = value; + } else if (isFakeProperty(index)) { + setFakeProperty(index, value); + } else { + if (d->isResourceProperty(index)) + d->setResourceProperty(index, value); + if (d->isStringProperty(index)) + d->setStringProperty(index, qVariantValue(value)); + if (d->isKeySequenceProperty(index)) + d->setKeySequenceProperty(index, qVariantValue(value)); + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + p->write(d->m_object, resolvePropertyValue(index, value)); + if (qobject_cast(d->m_object) && propertyType(index) == PropertyCheckable) { + const int idx = indexOf(QLatin1String("focusPolicy")); + if (!isChanged(idx)) { + qdesigner_internal::PropertySheetEnumValue e = qVariantValue(property(idx)); + if (value.toBool()) { + const QDesignerMetaPropertyInterface *p = d->m_meta->property(idx); + p->write(d->m_object, Qt::NoFocus); + e.value = Qt::StrongFocus; + QVariant v; + qVariantSetValue(v, e); + setFakeProperty(idx, v); + } else { + e.value = Qt::NoFocus; + QVariant v; + qVariantSetValue(v, e); + setFakeProperty(idx, v); + } + } + } + } +} + +bool QDesignerPropertySheet::hasReset(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return d->m_info.value(index).reset; + return true; +} + +bool QDesignerPropertySheet::reset(int index) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (d->isStringProperty(index)) + setProperty(index, qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + if (d->isKeySequenceProperty(index)) + setProperty(index, qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue())); + if (d->isResourceProperty(index)) { + setProperty(index, d->emptyResourceProperty(index)); + return true; + } else if (isDynamic(index)) { + const QString propName = propertyName(index); + const QVariant oldValue = d->m_addProperties.value(index); + const QVariant defaultValue = d->m_info.value(index).defaultValue; + QVariant newValue = defaultValue; + if (d->isStringProperty(index)) { + newValue = qVariantFromValue(qdesigner_internal::PropertySheetStringValue(newValue.toString())); + } else if (d->isKeySequenceProperty(index)) { + const QKeySequence keySequence = qVariantValue(newValue); + newValue = qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue(keySequence)); + } + if (oldValue == newValue) + return true; + d->m_object->setProperty(propName.toUtf8(), defaultValue); + d->m_addProperties[index] = newValue; + return true; + } else if (!d->m_info.value(index).defaultValue.isNull()) { + setProperty(index, d->m_info.value(index).defaultValue); + return true; + } + if (isAdditionalProperty(index)) { + const PropertyType pType = propertyType(index); + if (d->m_objectType == ObjectLabel && pType == PropertyBuddy) { + setProperty(index, QVariant(QByteArray())); + return true; + } + if (isFakeLayoutProperty(index)) { + // special properties + switch (pType) { + case PropertyLayoutObjectName: + setProperty(index, QString()); + return true; + case PropertyLayoutSizeConstraint: + setProperty(index, QVariant(QLayout::SetDefaultConstraint)); + return true; + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) + return layoutPropertySheet->reset(layoutPropertySheet->indexOf(d->transformLayoutPropertyName(index))); + } + break; + default: + break; + } + // special margins + int value = -1; + switch (d->m_objectType) { + case ObjectQ3GroupBox: { + const QWidget *w = qobject_cast(d->m_object); + switch (pType) { + case PropertyLayoutLeftMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin); + break; + case PropertyLayoutTopMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutTopMargin); + break; + case PropertyLayoutRightMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutRightMargin); + break; + case PropertyLayoutBottomMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin); + break; + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + value = -1; + break; + default: + break; + } + } + break; + case ObjectLayoutWidget: + if (pType == PropertyLayoutLeftMargin || + pType == PropertyLayoutTopMargin || + pType == PropertyLayoutRightMargin || + pType == PropertyLayoutBottomMargin) + value = 0; + break; + default: + break; + } + setProperty(index, value); + return true; + } + return false; + } else if (isFakeProperty(index)) { + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + const bool result = p->reset(d->m_object); + d->m_fakeProperties[index] = p->read(d->m_object); + return result; + } else if (propertyType(index) == PropertyGeometry && d->m_object->isWidgetType()) { + if (QWidget *w = qobject_cast(d->m_object)) { + QWidget *widget = w; + if (qdesigner_internal::Utils::isCentralWidget(d->m_fwb, widget) && d->m_fwb->parentWidget()) + widget = d->m_fwb->parentWidget(); + + if (widget != w && widget->parentWidget()) { + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + widget->parentWidget()->adjustSize(); + } + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + widget->adjustSize(); + return true; + } + } + // ### TODO: reset for fake properties. + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + return p->reset(d->m_object); +} + +bool QDesignerPropertySheet::isChanged(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + return layoutPropertySheet->isChanged(newIndex); + return false; + } + } + } + } + return d->m_info.value(index).changed; +} + +void QDesignerPropertySheet::setChanged(int index, bool changed) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + layoutPropertySheet->setChanged(newIndex, changed); + } + } + } + } + if (d->isReloadableProperty(index)) { + if (d->m_fwb) { + if (changed) + d->m_fwb->addReloadableProperty(this, index); + else + d->m_fwb->removeReloadableProperty(this, index); + } + } + d->ensureInfo(index).changed = changed; +} + +bool QDesignerPropertySheet::isFakeLayoutProperty(int index) const +{ + if (!isAdditionalProperty(index)) + return false; + + switch (propertyType(index)) { + case PropertyLayoutObjectName: + case PropertyLayoutSizeConstraint: + return true; + case PropertyLayoutLeftMargin: + case PropertyLayoutTopMargin: + case PropertyLayoutRightMargin: + case PropertyLayoutBottomMargin: + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + return d->m_canHaveLayoutAttributes; + default: + break; + } + return false; +} + +// Determine the "designable" state of a property. Properties, which have +// a per-object boolean test function that returns false are shown in +// disabled state ("checked" depending on "checkable", etc.) +// Properties, which are generally not designable independent +// of the object are not shown at all. +enum DesignableState { PropertyIsDesignable, + // Object has a Designable test function that returns false. + PropertyOfObjectNotDesignable, + PropertyNotDesignable }; + +static inline DesignableState designableState(const QDesignerMetaPropertyInterface *p, const QObject *object) +{ + if (p->attributes(object) & QDesignerMetaPropertyInterface::DesignableAttribute) + return PropertyIsDesignable; + return (p->attributes() & QDesignerMetaPropertyInterface::DesignableAttribute) ? + PropertyOfObjectNotDesignable : PropertyNotDesignable; +} + +bool QDesignerPropertySheet::isVisible(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + + const PropertyType type = propertyType(index); + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index) && d->m_object->isWidgetType()) { + const QLayout *currentLayout = d->layout(); + if (!currentLayout) + return false; + const int visibleMask = qdesigner_internal::LayoutProperties::visibleProperties(currentLayout); + switch (type) { + case PropertyLayoutSpacing: + return visibleMask & qdesigner_internal::LayoutProperties::SpacingProperty; + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + return visibleMask & qdesigner_internal::LayoutProperties::HorizSpacingProperty; + case PropertyLayoutFieldGrowthPolicy: + return visibleMask & qdesigner_internal::LayoutProperties::FieldGrowthPolicyProperty; + case PropertyLayoutRowWrapPolicy: + return visibleMask & qdesigner_internal::LayoutProperties::RowWrapPolicyProperty; + case PropertyLayoutLabelAlignment: + return visibleMask & qdesigner_internal::LayoutProperties::LabelAlignmentProperty; + case PropertyLayoutFormAlignment: + return visibleMask & qdesigner_internal::LayoutProperties::FormAlignmentProperty; + case PropertyLayoutBoxStretch: + return visibleMask & qdesigner_internal::LayoutProperties::BoxStretchProperty; + case PropertyLayoutGridRowStretch: + return visibleMask & qdesigner_internal::LayoutProperties::GridRowStretchProperty; + case PropertyLayoutGridColumnStretch: + return visibleMask & qdesigner_internal::LayoutProperties::GridColumnStretchProperty; + case PropertyLayoutGridRowMinimumHeight: + return visibleMask & qdesigner_internal::LayoutProperties::GridRowMinimumHeightProperty; + case PropertyLayoutGridColumnMinimumWidth: + return visibleMask & qdesigner_internal::LayoutProperties::GridColumnMinimumWidthProperty; + default: + break; + } + return true; + } + return d->m_info.value(index).visible; + } + + if (isFakeProperty(index)) { + switch (type) { + case PropertyWindowModality: // Hidden for child widgets + case PropertyWindowOpacity: + return d->m_info.value(index).visible; + default: + break; + } + return true; + } + + const bool visible = d->m_info.value(index).visible; + switch (type) { + case PropertyWindowTitle: + case PropertyWindowIcon: + case PropertyWindowFilePath: + case PropertyWindowOpacity: + case PropertyWindowIconText: + case PropertyWindowModified: + return visible; + default: + if (visible) + return true; + break; + } + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + if (!(p->accessFlags() & QDesignerMetaPropertyInterface::WriteAccess)) + return false; + + // Enabled handling: Hide only statically not designable properties + return designableState(p, d->m_object) != PropertyNotDesignable; +} + +void QDesignerPropertySheet::setVisible(int index, bool visible) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).visible = visible; +} + +bool QDesignerPropertySheet::isEnabled(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return true; + + if (isFakeProperty(index)) + return true; + + // Grey out geometry of laid-out widgets (including splitter) + if (propertyType(index) == PropertyGeometry && d->m_object->isWidgetType()) { + bool isManaged; + const qdesigner_internal::LayoutInfo::Type lt = qdesigner_internal::LayoutInfo::laidoutWidgetType(d->m_core, qobject_cast(d->m_object), &isManaged); + return !isManaged || lt == qdesigner_internal::LayoutInfo::NoLayout; + } + + if (d->m_info.value(index).visible == true) // Sun CC 5.5 oddity, wants true + return true; + + // Enable setting of properties for statically non-designable properties + // as this might be done via TaskMenu/Cursor::setProperty. Note that those + // properties are not visible. + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + return (p->accessFlags() & QDesignerMetaPropertyInterface::WriteAccess) && + designableState(p, d->m_object) != PropertyOfObjectNotDesignable; +} + +bool QDesignerPropertySheet::isAttribute(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return d->m_info.value(index).attribute; + + if (isFakeProperty(index)) + return false; + + return d->m_info.value(index).attribute; +} + +void QDesignerPropertySheet::setAttribute(int index, bool attribute) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).attribute = attribute; +} + +QDesignerFormEditorInterface *QDesignerPropertySheet::core() const +{ + return d->m_core; +} + +bool QDesignerPropertySheet::internalDynamicPropertiesEnabled() +{ + return QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled; +} + +void QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(bool v) +{ + QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled = v; +} + +// ---------- QDesignerAbstractPropertySheetFactory + +struct QDesignerAbstractPropertySheetFactory::PropertySheetFactoryPrivate { + PropertySheetFactoryPrivate(); + const QString m_propertySheetId; + const QString m_dynamicPropertySheetId; + + typedef QMap ExtensionMap; + ExtensionMap m_extensions; + typedef QHash ExtendedSet; + ExtendedSet m_extended; +}; + +QDesignerAbstractPropertySheetFactory::PropertySheetFactoryPrivate::PropertySheetFactoryPrivate() : + m_propertySheetId(Q_TYPEID(QDesignerPropertySheetExtension)), + m_dynamicPropertySheetId(Q_TYPEID(QDesignerDynamicPropertySheetExtension)) +{ +} + +// ---------- QDesignerAbstractPropertySheetFactory + + +QDesignerAbstractPropertySheetFactory::QDesignerAbstractPropertySheetFactory(QExtensionManager *parent) : + QExtensionFactory(parent), + m_impl(new PropertySheetFactoryPrivate) +{ +} + +QDesignerAbstractPropertySheetFactory::~QDesignerAbstractPropertySheetFactory() +{ + delete m_impl; +} + +QObject *QDesignerAbstractPropertySheetFactory::extension(QObject *object, const QString &iid) const +{ + typedef PropertySheetFactoryPrivate::ExtensionMap ExtensionMap; + if (!object) + return 0; + + if (iid != m_impl->m_propertySheetId && iid != m_impl->m_dynamicPropertySheetId) + return 0; + + ExtensionMap::iterator it = m_impl->m_extensions.find(object); + if (it == m_impl->m_extensions.end()) { + if (QObject *ext = createPropertySheet(object, const_cast(this))) { + connect(ext, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + it = m_impl->m_extensions.insert(object, ext); + } + } + + if (!m_impl->m_extended.contains(object)) { + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + m_impl->m_extended.insert(object, true); + } + + if (it == m_impl->m_extensions.end()) + return 0; + + return it.value(); +} + +void QDesignerAbstractPropertySheetFactory::objectDestroyed(QObject *object) +{ + QMutableMapIterator it(m_impl->m_extensions); + while (it.hasNext()) { + it.next(); + + QObject *o = it.key(); + if (o == object || object == it.value()) { + it.remove(); + } + } + + m_impl->m_extended.remove(object); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_propertysheet_p.h b/designer/lib/shared/qdesigner_propertysheet_p.h new file mode 100644 index 0000000..b6ab37c --- /dev/null +++ b/designer/lib/shared/qdesigner_propertysheet_p.h @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_PROPERTYSHEET_H +#define QDESIGNER_PROPERTYSHEET_H + +#include "shared_global_p.h" +#include "dynamicpropertysheet.h" +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QLayout; +class QDesignerFormEditorInterface; +class QDesignerPropertySheetPrivate; + +namespace qdesigner_internal +{ + class DesignerPixmapCache; + class DesignerIconCache; + class FormWindowBase; +} + +class QDESIGNER_SHARED_EXPORT QDesignerPropertySheet: public QObject, public QDesignerPropertySheetExtension, public QDesignerDynamicPropertySheetExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension QDesignerDynamicPropertySheetExtension) +public: + explicit QDesignerPropertySheet(QObject *object, QObject *parent = 0); + virtual ~QDesignerPropertySheet(); + + virtual int indexOf(const QString &name) const; + + virtual int count() const; + virtual QString propertyName(int index) const; + + virtual QString propertyGroup(int index) const; + virtual void setPropertyGroup(int index, const QString &group); + + virtual bool hasReset(int index) const; + virtual bool reset(int index); + + virtual bool isAttribute(int index) const; + virtual void setAttribute(int index, bool b); + + virtual bool isVisible(int index) const; + virtual void setVisible(int index, bool b); + + virtual QVariant property(int index) const; + virtual void setProperty(int index, const QVariant &value); + + virtual bool isChanged(int index) const; + + virtual void setChanged(int index, bool changed); + + virtual bool dynamicPropertiesAllowed() const; + virtual int addDynamicProperty(const QString &propertyName, const QVariant &value); + virtual bool removeDynamicProperty(int index); + virtual bool isDynamicProperty(int index) const; + virtual bool canAddDynamicProperty(const QString &propertyName) const; + + bool isDefaultDynamicProperty(int index) const; + + bool isResourceProperty(int index) const; + QVariant defaultResourceProperty(int index) const; + + qdesigner_internal::DesignerPixmapCache *pixmapCache() const; + void setPixmapCache(qdesigner_internal::DesignerPixmapCache *cache); + qdesigner_internal::DesignerIconCache *iconCache() const; + void setIconCache(qdesigner_internal::DesignerIconCache *cache); + int createFakeProperty(const QString &propertyName, const QVariant &value = QVariant()); + + virtual bool isEnabled(int index) const; + QObject *object() const; + + static bool internalDynamicPropertiesEnabled(); + static void setInternalDynamicPropertiesEnabled(bool v); + +protected: + bool isAdditionalProperty(int index) const; + bool isFakeProperty(int index) const; + QVariant resolvePropertyValue(int index, const QVariant &value) const; + QVariant metaProperty(int index) const; + void setFakeProperty(int index, const QVariant &value); + void clearFakeProperties(); + + bool isFakeLayoutProperty(int index) const; + bool isDynamic(int index) const; + qdesigner_internal::FormWindowBase *formWindowBase() const; + QDesignerFormEditorInterface *core() const; + +public: + enum PropertyType { PropertyNone, + PropertyLayoutObjectName, + PropertyLayoutLeftMargin, + PropertyLayoutTopMargin, + PropertyLayoutRightMargin, + PropertyLayoutBottomMargin, + PropertyLayoutSpacing, + PropertyLayoutHorizontalSpacing, + PropertyLayoutVerticalSpacing, + PropertyLayoutSizeConstraint, + PropertyLayoutFieldGrowthPolicy, + PropertyLayoutRowWrapPolicy, + PropertyLayoutLabelAlignment, + PropertyLayoutFormAlignment, + PropertyLayoutBoxStretch, + PropertyLayoutGridRowStretch, + PropertyLayoutGridColumnStretch, + PropertyLayoutGridRowMinimumHeight, + PropertyLayoutGridColumnMinimumWidth, + PropertyBuddy, + PropertyAccessibility, + PropertyGeometry, + PropertyCheckable, + PropertyWindowTitle, + PropertyWindowIcon, + PropertyWindowFilePath, + PropertyWindowOpacity, + PropertyWindowIconText, + PropertyWindowModality, + PropertyWindowModified, + PropertyStyleSheet, + PropertyText + }; + + enum ObjectType { ObjectNone, ObjectLabel, ObjectLayout, ObjectLayoutWidget, ObjectQ3GroupBox }; + + static ObjectType objectTypeFromObject(const QObject *o); + static PropertyType propertyTypeFromName(const QString &name); + +protected: + PropertyType propertyType(int index) const; + ObjectType objectType() const; + +private: + QDesignerPropertySheetPrivate *d; +}; + +/* Abstract base class for factories that register a property sheet that implements + * both QDesignerPropertySheetExtension and QDesignerDynamicPropertySheetExtension + * by multiple inheritance. The factory maintains ownership of + * the extension and returns it for both id's. */ + +class QDESIGNER_SHARED_EXPORT QDesignerAbstractPropertySheetFactory: public QExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) +public: + explicit QDesignerAbstractPropertySheetFactory(QExtensionManager *parent = 0); + virtual ~QDesignerAbstractPropertySheetFactory(); + + QObject *extension(QObject *object, const QString &iid) const; + +private slots: + void objectDestroyed(QObject *object); + +private: + virtual QObject *createPropertySheet(QObject *qObject, QObject *parent) const = 0; + + struct PropertySheetFactoryPrivate; + PropertySheetFactoryPrivate *m_impl; +}; + +/* Convenience factory template for property sheets that implement + * QDesignerPropertySheetExtension and QDesignerDynamicPropertySheetExtension + * by multiple inheritance. */ + +template +class QDesignerPropertySheetFactory : public QDesignerAbstractPropertySheetFactory { +public: + explicit QDesignerPropertySheetFactory(QExtensionManager *parent = 0); + + static void registerExtension(QExtensionManager *mgr); + +private: + // Does a qobject_cast on the object. + virtual QObject *createPropertySheet(QObject *qObject, QObject *parent) const; +}; + +template +QDesignerPropertySheetFactory::QDesignerPropertySheetFactory(QExtensionManager *parent) : + QDesignerAbstractPropertySheetFactory(parent) +{ +} + +template +QObject *QDesignerPropertySheetFactory::createPropertySheet(QObject *qObject, QObject *parent) const +{ + Object *object = qobject_cast(qObject); + if (!object) + return 0; + return new PropertySheet(object, parent); +} + +template +void QDesignerPropertySheetFactory::registerExtension(QExtensionManager *mgr) +{ + QDesignerPropertySheetFactory *factory = new QDesignerPropertySheetFactory(mgr); + mgr->registerExtensions(factory, Q_TYPEID(QDesignerPropertySheetExtension)); + mgr->registerExtensions(factory, Q_TYPEID(QDesignerDynamicPropertySheetExtension)); +} + + +// Standard property sheet +typedef QDesignerPropertySheetFactory QDesignerDefaultPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_PROPERTYSHEET_H diff --git a/designer/lib/shared/qdesigner_qsettings.cpp b/designer/lib/shared/qdesigner_qsettings.cpp new file mode 100644 index 0000000..2bf2920 --- /dev/null +++ b/designer/lib/shared/qdesigner_qsettings.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_qsettings_p.h" + +#include +#include +#include +#include +#include + +/*! + \class QDesignerSettingsSimple + + \brief Implements QDesignerSettingsInterface by calls to QSettings + */ + +QDesignerQSettings::QDesignerQSettings() : + m_settings(qApp->organizationName(), settingsApplicationName()) +{ +} + +QString QDesignerQSettings::settingsApplicationName() +{ + return qApp->applicationName(); +} + +void QDesignerQSettings::beginGroup(const QString &prefix) +{ + m_settings.beginGroup(prefix); +} + +void QDesignerQSettings::endGroup() +{ + m_settings.endGroup(); +} + +bool QDesignerQSettings::contains(const QString &key) const +{ + return m_settings.contains(key); +} + +void QDesignerQSettings::setValue(const QString &key, const QVariant &value) +{ + m_settings.setValue(key, value); +} + +QVariant QDesignerQSettings::value(const QString &key, const QVariant &defaultValue) const +{ + return m_settings.value(key, defaultValue); +} + +void QDesignerQSettings::remove(const QString &key) +{ + m_settings.remove(key); +} diff --git a/designer/lib/shared/qdesigner_qsettings_p.h b/designer/lib/shared/qdesigner_qsettings_p.h new file mode 100644 index 0000000..221110d --- /dev/null +++ b/designer/lib/shared/qdesigner_qsettings_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_QSETTINGS_H +#define QDESIGNER_QSETTINGS_H + +#include "abstractsettings_p.h" +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +// Implements QDesignerSettingsInterface by calls to QSettings +class QDESIGNER_SHARED_EXPORT QDesignerQSettings : public QDesignerSettingsInterface +{ +public: + QDesignerQSettings(); + + virtual void beginGroup(const QString &prefix); + virtual void endGroup(); + + virtual bool contains(const QString &key) const; + virtual void setValue(const QString &key, const QVariant &value); + virtual QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + virtual void remove(const QString &key); + + // The application name to be used for settings. Allows for including + // the Qt version to prevent settings of different Qt versions from + // interfering. + static QString settingsApplicationName(); + +private: + QSettings m_settings; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_QSETTINGS_H diff --git a/designer/lib/shared/qdesigner_stackedbox.cpp b/designer/lib/shared/qdesigner_stackedbox.cpp new file mode 100644 index 0000000..5e9527b --- /dev/null +++ b/designer/lib/shared/qdesigner_stackedbox.cpp @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "orderdialog_p.h" +#include "promotiontaskmenu_p.h" +#include "widgetfactory_p.h" + +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QToolButton *createToolButton(QWidget *parent, Qt::ArrowType at, const QString &name) { + QToolButton *rc = new QToolButton(); + rc->setAttribute(Qt::WA_NoChildEventsForParent, true); + rc->setParent(parent); + rc->setObjectName(name); + rc->setArrowType(at); + rc->setAutoRaise(true); + rc->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + rc->setFixedSize(QSize(15, 15)); + return rc; +} + +// --------------- QStackedWidgetPreviewEventFilter +QStackedWidgetPreviewEventFilter::QStackedWidgetPreviewEventFilter(QStackedWidget *parent) : + QObject(parent), + m_buttonToolTipEnabled(false), // Not on preview + m_stackedWidget(parent), + m_prev(createToolButton(m_stackedWidget, Qt::LeftArrow, QLatin1String("__qt__passive_prev"))), + m_next(createToolButton(m_stackedWidget, Qt::RightArrow, QLatin1String("__qt__passive_next"))) +{ + connect(m_prev, SIGNAL(clicked()), this, SLOT(prevPage())); + connect(m_next, SIGNAL(clicked()), this, SLOT(nextPage())); + + updateButtons(); + m_stackedWidget->installEventFilter(this); + m_prev->installEventFilter(this); + m_next->installEventFilter(this); +} + +void QStackedWidgetPreviewEventFilter::install(QStackedWidget *stackedWidget) +{ + new QStackedWidgetPreviewEventFilter(stackedWidget); +} + +void QStackedWidgetPreviewEventFilter::updateButtons() +{ + m_prev->move(m_stackedWidget->width() - 31, 1); + m_prev->show(); + m_prev->raise(); + + m_next->move(m_stackedWidget->width() - 16, 1); + m_next->show(); + m_next->raise(); +} + +void QStackedWidgetPreviewEventFilter::prevPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + fw->clearSelection(); + fw->selectWidget(stackedWidget(), true); + } + const int count = m_stackedWidget->count(); + if (count > 1) { + int newIndex = m_stackedWidget->currentIndex() - 1; + if (newIndex < 0) + newIndex = count - 1; + gotoPage(newIndex); + } +} + +void QStackedWidgetPreviewEventFilter::nextPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + fw->clearSelection(); + fw->selectWidget(stackedWidget(), true); + } + const int count = m_stackedWidget->count(); + if (count > 1) + gotoPage((m_stackedWidget->currentIndex() + 1) % count); +} + +bool QStackedWidgetPreviewEventFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (watched->isWidgetType()) { + if (watched == m_stackedWidget) { + switch (event->type()) { + case QEvent::LayoutRequest: + updateButtons(); + break; + case QEvent::ChildAdded: + case QEvent::ChildRemoved: + case QEvent::Resize: + case QEvent::Show: + updateButtons(); + break; + default: + break; + } + } + if (m_buttonToolTipEnabled && (watched == m_next || watched == m_prev)) { + switch (event->type()) { + case QEvent::ToolTip: + updateButtonToolTip(watched); // Tooltip includes page number, so, refresh on demand + break; + default: + break; + } + } + } + return QObject::eventFilter(watched, event); +} + +void QStackedWidgetPreviewEventFilter::gotoPage(int page) +{ + m_stackedWidget->setCurrentIndex(page); + updateButtons(); +} + +static inline QString stackedClassName(QStackedWidget *w) +{ + if (const QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w)) + return qdesigner_internal::WidgetFactory::classNameOf(fw->core(), w); + return QLatin1String("Stacked widget"); +} + +void QStackedWidgetPreviewEventFilter::updateButtonToolTip(QObject *o) +{ + QString className = QLatin1String("Stacked widget"); + if (const QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_stackedWidget)) + className = qdesigner_internal::WidgetFactory::classNameOf(fw->core(), m_stackedWidget); + if (o == m_prev) { + const QString msg = tr("Go to previous page of %1 '%2' (%3/%4).").arg(stackedClassName(m_stackedWidget)).arg(m_stackedWidget->objectName()).arg(m_stackedWidget->currentIndex() + 1).arg(m_stackedWidget->count()); + m_prev->setToolTip(msg); + } else { + if (o == m_next) { + const QString msg = tr("Go to next page of %1 '%2' (%3/%4).").arg(stackedClassName(m_stackedWidget)).arg(m_stackedWidget->objectName()).arg(m_stackedWidget->currentIndex() + 1).arg(m_stackedWidget->count()); + m_next->setToolTip(msg); + } + } +} + +// --------------- QStackedWidgetEventFilter +QStackedWidgetEventFilter::QStackedWidgetEventFilter(QStackedWidget *parent) : + QStackedWidgetPreviewEventFilter(parent), + m_actionPreviousPage(new QAction(tr("Previous Page"), this)), + m_actionNextPage(new QAction(tr("Next Page"), this)), + m_actionDeletePage(new QAction(tr("Delete"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_actionChangePageOrder(new QAction(tr("Change Page Order..."), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + setButtonToolTipEnabled(true); + connect(m_actionPreviousPage, SIGNAL(triggered()), this, SLOT(prevPage())); + connect(m_actionNextPage, SIGNAL(triggered()), this, SLOT(nextPage())); + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionChangePageOrder, SIGNAL(triggered()), this, SLOT(changeOrder())); +} + +void QStackedWidgetEventFilter::install(QStackedWidget *stackedWidget) +{ + new QStackedWidgetEventFilter(stackedWidget); +} + +QStackedWidgetEventFilter *QStackedWidgetEventFilter::eventFilterOf(const QStackedWidget *stackedWidget) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = stackedWidget->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QStackedWidgetEventFilter *ef = qobject_cast(o)) + return ef; + } + return 0; +} + +QMenu *QStackedWidgetEventFilter::addStackedWidgetContextMenuActions(const QStackedWidget *stackedWidget, QMenu *popup) +{ + QStackedWidgetEventFilter *filter = eventFilterOf(stackedWidget); + if (!filter) + return 0; + return filter->addContextMenuActions(popup); +} + +void QStackedWidgetEventFilter::removeCurrentPage() +{ + if (stackedWidget()->currentIndex() == -1) + return; + + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::DeleteStackedWidgetPageCommand *cmd = new qdesigner_internal::DeleteStackedWidgetPageCommand(fw); + cmd->init(stackedWidget()); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::changeOrder() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget()); + + if (!fw) + return; + + const QWidgetList oldPages = qdesigner_internal::OrderDialog::pagesOfContainer(fw->core(), stackedWidget()); + const int pageCount = oldPages.size(); + if (pageCount < 2) + return; + + qdesigner_internal::OrderDialog dlg(fw); + dlg.setPageList(oldPages); + if (dlg.exec() == QDialog::Rejected) + return; + + const QWidgetList newPages = dlg.pageList(); + if (newPages == oldPages) + return; + + fw->beginCommand(tr("Change Page Order")); + for(int i=0; i < pageCount; ++i) { + if (newPages.at(i) == stackedWidget()->widget(i)) + continue; + qdesigner_internal::MoveStackedWidgetCommand *cmd = new qdesigner_internal::MoveStackedWidgetCommand(fw); + cmd->init(stackedWidget(), newPages.at(i), i); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void QStackedWidgetEventFilter::addPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::AddStackedWidgetPageCommand *cmd = new qdesigner_internal::AddStackedWidgetPageCommand(fw); + cmd->init(stackedWidget(), qdesigner_internal::AddStackedWidgetPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::AddStackedWidgetPageCommand *cmd = new qdesigner_internal::AddStackedWidgetPageCommand(fw); + cmd->init(stackedWidget(), qdesigner_internal::AddStackedWidgetPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::gotoPage(int page) { + // Are we on a form or in a preview? + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::SetPropertyCommand *cmd = new qdesigner_internal::SetPropertyCommand(fw); + cmd->init(stackedWidget(), QLatin1String("currentIndex"), page); + fw->commandHistory()->push(cmd); + fw->emitSelectionChanged(); // Magically prevent an endless loop triggered by auto-repeat. + updateButtons(); + } else { + QStackedWidgetPreviewEventFilter::gotoPage(page); + } +} + +QMenu *QStackedWidgetEventFilter::addContextMenuActions(QMenu *popup) +{ + QMenu *pageMenu = 0; + const int count = stackedWidget()->count(); + const bool hasSeveralPages = count > 1; + m_actionDeletePage->setEnabled(count); + if (count) { + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(stackedWidget()->currentIndex() + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = stackedWidget()->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(stackedWidget()), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + } else { + QAction *insertPageAction = popup->addAction(tr("Insert Page")); + connect(insertPageAction, SIGNAL(triggered()), this, SLOT(addPage())); + } + popup->addAction(m_actionNextPage); + m_actionNextPage->setEnabled(hasSeveralPages); + popup->addAction(m_actionPreviousPage); + m_actionPreviousPage->setEnabled(hasSeveralPages); + popup->addAction(m_actionChangePageOrder); + m_actionChangePageOrder->setEnabled(hasSeveralPages); + popup->addSeparator(); + return pageMenu; +} + +// -------- QStackedWidgetPropertySheet + +static const char *pagePropertyName = "currentPageName"; + +QStackedWidgetPropertySheet::QStackedWidgetPropertySheet(QStackedWidget *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_stackedWidget(object) +{ + createFakeProperty(QLatin1String(pagePropertyName), QString()); +} + +bool QStackedWidgetPropertySheet::isEnabled(int index) const +{ + if (propertyName(index) != QLatin1String(pagePropertyName)) + return QDesignerPropertySheet::isEnabled(index); + return m_stackedWidget->currentWidget() != 0; +} + +void QStackedWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + if (QWidget *w = m_stackedWidget->currentWidget()) + w->setObjectName(value.toString()); + } else { + QDesignerPropertySheet::setProperty(index, value); + } +} + +QVariant QStackedWidgetPropertySheet::property(int index) const +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + if (const QWidget *w = m_stackedWidget->currentWidget()) + return w->objectName(); + return QString(); + } + return QDesignerPropertySheet::property(index); +} + +bool QStackedWidgetPropertySheet::reset(int index) +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + setProperty(index, QString()); + return true; + } + return QDesignerPropertySheet::reset(index); +} + +bool QStackedWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + return propertyName != QLatin1String(pagePropertyName); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_stackedbox_p.h b/designer/lib/shared/qdesigner_stackedbox_p.h new file mode 100644 index 0000000..31be9e1 --- /dev/null +++ b/designer/lib/shared/qdesigner_stackedbox_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_STACKEDBOX_H +#define QDESIGNER_STACKEDBOX_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" + +QT_BEGIN_NAMESPACE + +class QStackedWidget; +class QWidget; +class QAction; +class QMenu; +class QToolButton; + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +// Event filter to be installed on a QStackedWidget in preview mode. +// Create two buttons to switch pages. + +class QDESIGNER_SHARED_EXPORT QStackedWidgetPreviewEventFilter : public QObject +{ + Q_OBJECT +public: + explicit QStackedWidgetPreviewEventFilter(QStackedWidget *parent); + + // Install helper on QStackedWidget + static void install(QStackedWidget *stackedWidget); + bool eventFilter(QObject *watched, QEvent *event); + + void setButtonToolTipEnabled(bool v) { m_buttonToolTipEnabled = v; } + bool buttonToolTipEnabled() const { return m_buttonToolTipEnabled; } + +public slots: + void updateButtons(); + void prevPage(); + void nextPage(); + +protected: + QStackedWidget *stackedWidget() const { return m_stackedWidget; } + virtual void gotoPage(int page); + +private: + void updateButtonToolTip(QObject *o); + + bool m_buttonToolTipEnabled; + QStackedWidget *m_stackedWidget; + QToolButton *m_prev; + QToolButton *m_next; +}; + +// Event filter to be installed on a QStackedWidget in editing mode. +// In addition to the browse buttons, handles context menu and everything + +class QDESIGNER_SHARED_EXPORT QStackedWidgetEventFilter : public QStackedWidgetPreviewEventFilter +{ + Q_OBJECT +public: + explicit QStackedWidgetEventFilter(QStackedWidget *parent); + + // Install helper on QStackedWidget + static void install(QStackedWidget *stackedWidget); + static QStackedWidgetEventFilter *eventFilterOf(const QStackedWidget *stackedWidget); + // Convenience to add a menu on a tackedWidget + static QMenu *addStackedWidgetContextMenuActions(const QStackedWidget *stackedWidget, QMenu *popup); + + // Add context menu and return page submenu or 0. + QMenu *addContextMenuActions(QMenu *popup); + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + void changeOrder(); + +protected: + virtual void gotoPage(int page); + +private: + QAction *m_actionPreviousPage; + QAction *m_actionNextPage; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + QAction *m_actionChangePageOrder; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the "currentPageName" property +class QDESIGNER_SHARED_EXPORT QStackedWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QStackedWidgetPropertySheet(QStackedWidget *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + QStackedWidget *m_stackedWidget; +}; + +typedef QDesignerPropertySheetFactory QStackedWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_STACKEDBOX_H diff --git a/designer/lib/shared/qdesigner_tabwidget.cpp b/designer/lib/shared/qdesigner_tabwidget.cpp new file mode 100644 index 0000000..73cc152 --- /dev/null +++ b/designer/lib/shared/qdesigner_tabwidget.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "promotiontaskmenu_p.h" +#include "formwindowbase_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// Store tab widget as drag source +class MyMimeData : public QMimeData +{ + Q_OBJECT +public: + MyMimeData(const QTabWidget *tab) : m_tab(tab) {} + static bool fromMyTab(const QMimeData *mimeData, const QTabWidget *tab) { + if (!mimeData) + return false; + const MyMimeData *m = qobject_cast(mimeData); + return m && m->m_tab == tab; + } +private: + const QTabWidget *m_tab; +}; + +} // namespace qdesigner_internal + +// ------------- QTabWidgetEventFilter + +QTabWidgetEventFilter::QTabWidgetEventFilter(QTabWidget *parent) : + QObject(parent), + m_tabWidget(parent), + m_dropIndicator(0), + m_dragPage(0), + m_mousePressed(false), + m_actionDeletePage(new QAction(tr("Delete"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + tabBar()->setAcceptDrops(true); + tabBar()->installEventFilter(this); + + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); +} + +QTabWidgetEventFilter::~QTabWidgetEventFilter() +{ +} + +void QTabWidgetEventFilter::install(QTabWidget *tabWidget) +{ + new QTabWidgetEventFilter(tabWidget); +} + +QTabWidgetEventFilter *QTabWidgetEventFilter::eventFilterOf(const QTabWidget *tabWidget) +{ + // Look for 1st order children only..otherwise, we might get filters of nested tab widgets + const QObjectList children = tabWidget->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QTabWidgetEventFilter *ef = qobject_cast(o)) + return ef; + } + return 0; +} + +QMenu *QTabWidgetEventFilter::addTabWidgetContextMenuActions(const QTabWidget *tabWidget, QMenu *popup) +{ + QTabWidgetEventFilter *filter = eventFilterOf(tabWidget); + if (!filter) + return 0; + return filter->addContextMenuActions(popup); +} + +QTabBar *QTabWidgetEventFilter::tabBar() const +{ + // QTabWidget::tabBar() accessor is protected, grmbl... + if (!m_cachedTabBar) { + const QList tabBars = qFindChildren(m_tabWidget); + Q_ASSERT(tabBars.size() == 1); + m_cachedTabBar = tabBars.front(); + } + return m_cachedTabBar; + +} + +static bool canMove(const QPoint &pressPoint, const QMouseEvent *e) +{ + const QPoint pt = pressPoint - e->pos(); + return pt.manhattanLength() > QApplication::startDragDistance(); +} + +bool QTabWidgetEventFilter::eventFilter(QObject *o, QEvent *e) +{ + const QEvent::Type type = e->type(); + // Do not try to locate tab bar and form window, etc. for uninteresting events and + // avoid asserts about missing tab bars when being destroyed + switch (type) { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::DragLeave: + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::Drop: + break; + default: + return false; + } + + if (o != tabBar()) + return false; + + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return false; + + switch (type) { + case QEvent::MouseButtonDblClick: + break; + case QEvent::MouseButtonPress: { + QMouseEvent *mev = static_cast(e); + if (QDesignerFormWindowInterface *fw = formWindow()) { + fw->clearSelection(); + fw->selectWidget(m_tabWidget, true); + } + if (mev->button() & Qt::LeftButton) { + m_mousePressed = true; + m_pressPoint = mev->pos(); + + QTabBar *tabbar = tabBar(); + const int count = tabbar->count(); + for (int i = 0; i < count; ++i) { + if (tabbar->tabRect(i).contains(m_pressPoint)) { + if (i != tabbar->currentIndex()) { + qdesigner_internal::SetPropertyCommand *cmd = new qdesigner_internal::SetPropertyCommand(fw); + cmd->init(m_tabWidget, QLatin1String("currentIndex"), i); + fw->commandHistory()->push(cmd); + } + break; + } + } + } + } break; + + case QEvent::MouseButtonRelease: + m_mousePressed = false; + break; + + case QEvent::MouseMove: { + QMouseEvent *mouseEvent = static_cast(e); + if (m_mousePressed && canMove(m_pressPoint, mouseEvent)) { + const int index = m_tabWidget->currentIndex(); + if (index == -1) + break; + + m_mousePressed = false; + QDrag *drg = new QDrag(m_tabWidget); + drg->setMimeData(new qdesigner_internal::MyMimeData(m_tabWidget)); + + m_dragIndex = index; + m_dragPage = m_tabWidget->currentWidget(); + m_dragLabel = m_tabWidget->tabText(index); + m_dragIcon = m_tabWidget->tabIcon(index); + if (m_dragIcon.isNull()) { + QLabel *label = new QLabel(m_dragLabel); + label->adjustSize(); + drg->setPixmap(QPixmap::grabWidget(label)); + label->deleteLater(); + } else { + drg->setPixmap(m_dragIcon.pixmap(22, 22)); + } + + m_tabWidget->removeTab(m_dragIndex); + + const Qt::DropActions dropAction = drg->start(Qt::MoveAction); + + if (dropAction == Qt::IgnoreAction) { + // abort + m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel); + m_tabWidget->setCurrentIndex(m_dragIndex); + } + + if (m_dropIndicator) + m_dropIndicator->hide(); + } + } break; + + case QEvent::DragLeave: { + if (m_dropIndicator) + m_dropIndicator->hide(); + } break; + + case QEvent::DragEnter: + case QEvent::DragMove: { + QDragMoveEvent *de = static_cast(e); + if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget)) + return false; + + if (de->proposedAction() == Qt::MoveAction) + de->acceptProposedAction(); + else { + de->setDropAction(Qt::MoveAction); + de->accept(); + } + + QRect rect; + const int index = pageFromPosition(de->pos(), rect); + + if (!m_dropIndicator) { + m_dropIndicator = new QWidget(m_tabWidget); + QPalette p = m_dropIndicator->palette(); + p.setColor(m_tabWidget->backgroundRole(), Qt::red); + m_dropIndicator->setPalette(p); + } + + QPoint pos; + if (index == m_tabWidget->count()) + pos = tabBar()->mapToParent(QPoint(rect.x() + rect.width(), rect.y())); + else + pos = tabBar()->mapToParent(QPoint(rect.x(), rect.y())); + + m_dropIndicator->setGeometry(pos.x(), pos.y() , 3, rect.height()); + m_dropIndicator->show(); + } break; + + case QEvent::Drop: { + QDropEvent *de = static_cast(e); + if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget)) + return false; + de->acceptProposedAction(); + de->accept(); + + QRect rect; + const int newIndex = pageFromPosition(de->pos(), rect); + + qdesigner_internal::MoveTabPageCommand *cmd = new qdesigner_internal::MoveTabPageCommand(fw); + m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel); + cmd->init(m_tabWidget, m_dragPage, m_dragIcon, m_dragLabel, m_dragIndex, newIndex); + fw->commandHistory()->push(cmd); + } break; + + default: + break; + } + + return false; +} + +void QTabWidgetEventFilter::removeCurrentPage() +{ + if (!m_tabWidget->currentWidget()) + return; + + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::DeleteTabPageCommand *cmd = new qdesigner_internal::DeleteTabPageCommand(fw); + cmd->init(m_tabWidget); + fw->commandHistory()->push(cmd); + } +} + +void QTabWidgetEventFilter::addPage() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw); + cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QTabWidgetEventFilter::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw); + cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +QDesignerFormWindowInterface *QTabWidgetEventFilter::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast(m_tabWidget)); +} + +// Get page from mouse position. Default to new page if in right half of last page? +int QTabWidgetEventFilter::pageFromPosition(const QPoint &pos, QRect &rect) const +{ + int index = 0; + const QTabBar *tabbar = tabBar(); + const int count = m_tabWidget->count(); + for (; index < count; index++) { + const QRect rc = tabbar->tabRect(index); + if (rc.contains(pos)) { + rect = rc; + break; + } + } + + if (index == count -1) { + QRect rect2 = rect; + rect2.setLeft(rect2.left() + rect2.width() / 2); + if (rect2.contains(pos)) + index++; + } + return index; +} + +QMenu *QTabWidgetEventFilter::addContextMenuActions(QMenu *popup) +{ + QMenu *pageMenu = 0; + const int count = m_tabWidget->count(); + m_actionDeletePage->setEnabled(count); + if (count) { + const int currentIndex = m_tabWidget->currentIndex(); + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(currentIndex + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = m_tabWidget->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(m_tabWidget), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + } else { + QAction *insertPageAction = popup->addAction(tr("Insert Page")); + connect(insertPageAction, SIGNAL(triggered()), this, SLOT(addPage())); + } + popup->addSeparator(); + return pageMenu; +} + +// ----------- QTabWidgetPropertySheet + +static const char *currentTabTextKey = "currentTabText"; +static const char *currentTabNameKey = "currentTabName"; +static const char *currentTabIconKey = "currentTabIcon"; +static const char *currentTabToolTipKey = "currentTabToolTip"; +static const char *currentTabWhatsThisKey = "currentTabWhatsThis"; +static const char *tabMovableKey = "movable"; + +QTabWidgetPropertySheet::QTabWidgetPropertySheet(QTabWidget *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_tabWidget(object) +{ + createFakeProperty(QLatin1String(currentTabTextKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentTabNameKey), QString()); + createFakeProperty(QLatin1String(currentTabIconKey), qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); + if (formWindowBase()) + formWindowBase()->addReloadableProperty(this, indexOf(QLatin1String(currentTabIconKey))); + createFakeProperty(QLatin1String(currentTabToolTipKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentTabWhatsThisKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + // Prevent the tab widget's drag and drop handling from interfering with Designer's + createFakeProperty(QLatin1String(tabMovableKey), QVariant(false)); +} + +QTabWidgetPropertySheet::TabWidgetProperty QTabWidgetPropertySheet::tabWidgetPropertyFromName(const QString &name) +{ + typedef QHash TabWidgetPropertyHash; + static TabWidgetPropertyHash tabWidgetPropertyHash; + if (tabWidgetPropertyHash.empty()) { + tabWidgetPropertyHash.insert(QLatin1String(currentTabTextKey), PropertyCurrentTabText); + tabWidgetPropertyHash.insert(QLatin1String(currentTabNameKey), PropertyCurrentTabName); + tabWidgetPropertyHash.insert(QLatin1String(currentTabIconKey), PropertyCurrentTabIcon); + tabWidgetPropertyHash.insert(QLatin1String(currentTabToolTipKey), PropertyCurrentTabToolTip); + tabWidgetPropertyHash.insert(QLatin1String(currentTabWhatsThisKey), PropertyCurrentTabWhatsThis); + } + return tabWidgetPropertyHash.value(name, PropertyTabWidgetNone); +} + +void QTabWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) { + QDesignerPropertySheet::setProperty(index, value); + return; + } + + // index-dependent + const int currentIndex = m_tabWidget->currentIndex(); + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) + return; + + switch (tabWidgetProperty) { + case PropertyCurrentTabText: + m_tabWidget->setTabText(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].text = qVariantValue(value); + break; + case PropertyCurrentTabName: + currentWidget->setObjectName(value.toString()); + break; + case PropertyCurrentTabIcon: + m_tabWidget->setTabIcon(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].icon = qVariantValue(value); + break; + case PropertyCurrentTabToolTip: + m_tabWidget->setTabToolTip(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].tooltip = qVariantValue(value); + break; + case PropertyCurrentTabWhatsThis: + m_tabWidget->setTabWhatsThis(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].whatsthis = qVariantValue(value); + break; + case PropertyTabWidgetNone: + break; + } +} + +bool QTabWidgetPropertySheet::isEnabled(int index) const +{ + if (tabWidgetPropertyFromName(propertyName(index)) == PropertyTabWidgetNone) + return QDesignerPropertySheet::isEnabled(index); + return m_tabWidget->currentIndex() != -1; +} + +QVariant QTabWidgetPropertySheet::property(int index) const +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) + return QDesignerPropertySheet::property(index); + + // index-dependent + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) { + if (tabWidgetProperty == PropertyCurrentTabIcon) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + if (tabWidgetProperty == PropertyCurrentTabText) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (tabWidgetProperty == PropertyCurrentTabToolTip) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (tabWidgetProperty == PropertyCurrentTabWhatsThis) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + return QVariant(QString()); + } + + // index-dependent + switch (tabWidgetProperty) { + case PropertyCurrentTabText: + return qVariantFromValue(m_pageToData.value(currentWidget).text); + case PropertyCurrentTabName: + return currentWidget->objectName(); + case PropertyCurrentTabIcon: + return qVariantFromValue(m_pageToData.value(currentWidget).icon); + case PropertyCurrentTabToolTip: + return qVariantFromValue(m_pageToData.value(currentWidget).tooltip); + case PropertyCurrentTabWhatsThis: + return qVariantFromValue(m_pageToData.value(currentWidget).whatsthis); + case PropertyTabWidgetNone: + break; + } + return QVariant(); +} + +bool QTabWidgetPropertySheet::reset(int index) +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) + return QDesignerPropertySheet::reset(index); + + // index-dependent + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) + return false; + + // index-dependent + switch (tabWidgetProperty) { + case PropertyCurrentTabName: + setProperty(index, QString()); + break; + case PropertyCurrentTabToolTip: + m_pageToData[currentWidget].tooltip = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabWhatsThis: + m_pageToData[currentWidget].whatsthis = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabText: + m_pageToData[currentWidget].text = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabIcon: + m_pageToData[currentWidget].icon = qdesigner_internal::PropertySheetIconValue(); + setProperty(index, QIcon()); + break; + case PropertyTabWidgetNone: + break; + } + return true; +} + +bool QTabWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + switch (tabWidgetPropertyFromName(propertyName)) { + case PropertyCurrentTabText: + case PropertyCurrentTabName: + case PropertyCurrentTabToolTip: + case PropertyCurrentTabWhatsThis: + case PropertyCurrentTabIcon: + return false; + default: + break; + } + return true; +} + +QT_END_NAMESPACE + +#include "qdesigner_tabwidget.moc" // required for MyMimeData diff --git a/designer/lib/shared/qdesigner_tabwidget_p.h b/designer/lib/shared/qdesigner_tabwidget_p.h new file mode 100644 index 0000000..178f151 --- /dev/null +++ b/designer/lib/shared/qdesigner_tabwidget_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TABWIDGET_H +#define QDESIGNER_TABWIDGET_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QTabWidget; +class QTabBar; +class QMenu; +class QAction; + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +class QDESIGNER_SHARED_EXPORT QTabWidgetEventFilter : public QObject +{ + Q_OBJECT +public: + explicit QTabWidgetEventFilter(QTabWidget *parent); + ~QTabWidgetEventFilter(); + + // Install helper on QTabWidget + static void install(QTabWidget *tabWidget); + static QTabWidgetEventFilter *eventFilterOf(const QTabWidget *tabWidget); + // Convenience to add a menu on a tackedWidget + static QMenu *addTabWidgetContextMenuActions(const QTabWidget *tabWidget, QMenu *popup); + + // Add context menu and return page submenu or 0. + QMenu *addContextMenuActions(QMenu *popup); + + virtual bool eventFilter(QObject *o, QEvent *e); + + QDesignerFormWindowInterface *formWindow() const; + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + +private: + int pageFromPosition(const QPoint &pos, QRect &rect) const; + QTabBar *tabBar() const; + + QTabWidget *m_tabWidget; + mutable QPointer m_cachedTabBar; + QPoint m_pressPoint; + QWidget *m_dropIndicator; + int m_dragIndex; + QWidget *m_dragPage; + QString m_dragLabel; + QIcon m_dragIcon; + bool m_mousePressed; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the page properties +class QDESIGNER_SHARED_EXPORT QTabWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QTabWidgetPropertySheet(QTabWidget *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + enum TabWidgetProperty { PropertyCurrentTabText, PropertyCurrentTabName, PropertyCurrentTabIcon, + PropertyCurrentTabToolTip, PropertyCurrentTabWhatsThis, PropertyTabWidgetNone }; + + static TabWidgetProperty tabWidgetPropertyFromName(const QString &name); + QTabWidget *m_tabWidget; + struct PageData + { + qdesigner_internal::PropertySheetStringValue text; + qdesigner_internal::PropertySheetStringValue tooltip; + qdesigner_internal::PropertySheetStringValue whatsthis; + qdesigner_internal::PropertySheetIconValue icon; + }; + QMap m_pageToData; +}; + +typedef QDesignerPropertySheetFactory QTabWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_TABWIDGET_H diff --git a/designer/lib/shared/qdesigner_taskmenu.cpp b/designer/lib/shared/qdesigner_taskmenu.cpp new file mode 100644 index 0000000..0278482 --- /dev/null +++ b/designer/lib/shared/qdesigner_taskmenu.cpp @@ -0,0 +1,777 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_taskmenu_p.h" +#include "qdesigner_command_p.h" +#include "richtexteditor_p.h" +#include "plaintexteditor_p.h" +#include "stylesheeteditor_p.h" +#include "qlayout_widget_p.h" +#include "layout_p.h" +#include "spacer_widget_p.h" +#include "textpropertyeditor_p.h" +#include "promotiontaskmenu_p.h" +#include "metadatabase_p.h" +#include "scriptdialog_p.h" +#include "scriptcommand_p.h" +#include "signalslotdialog_p.h" +#include "qdesigner_membersheet_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" +#include "morphmenu_p.h" +#include "qdesigner_integration_p.h" +#include "formlayoutmenu_p.h" +#include "ui_selectsignaldialog.h" +#include "widgetfactory_p.h" +#include "abstractintrospection_p.h" +#include "widgetdatabase_p.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QMenuBar *findMenuBar(const QWidget *widget) +{ + const QList children = widget->children(); + foreach (QObject *obj, widget->children()) { + if (QMenuBar *mb = qobject_cast(obj)) { + return mb; + } + } + + return 0; +} + +static QStatusBar *findStatusBar(const QWidget *widget) +{ + const QList children = widget->children(); + foreach (QObject *obj, widget->children()) { + if (QStatusBar *sb = qobject_cast(obj)) { + return sb; + } + } + + return 0; +} + +static inline QAction *createSeparatorHelper(QObject *parent) { + QAction *rc = new QAction(parent); + rc->setSeparator(true); + return rc; +} + +static inline qdesigner_internal::QDesignerIntegration *integration(const QDesignerFormEditorInterface *core) { + return qobject_cast(core->integration()); +} + +static QString objName(const QDesignerFormEditorInterface *core, QObject *object) { + QDesignerPropertySheetExtension *sheet + = qt_extension(core->extensionManager(), object); + Q_ASSERT(sheet != 0); + + const QString objectNameProperty = QLatin1String("objectName"); + const int index = sheet->indexOf(objectNameProperty); + const qdesigner_internal::PropertySheetStringValue objectNameValue + = qVariantValue(sheet->property(index)); + return objectNameValue.value(); +} + +enum { ApplyMinimumWidth = 0x1, ApplyMinimumHeight = 0x2, ApplyMaximumWidth = 0x4, ApplyMaximumHeight = 0x8 }; + +namespace { +// --------------- ObjectNameDialog +class ObjectNameDialog : public QDialog +{ + public: + ObjectNameDialog(QWidget *parent, const QString &oldName); + QString newObjectName() const; + + private: + qdesigner_internal::TextPropertyEditor *m_editor; +}; + +ObjectNameDialog::ObjectNameDialog(QWidget *parent, const QString &oldName) + : QDialog(parent), + m_editor( new qdesigner_internal::TextPropertyEditor(this, qdesigner_internal::TextPropertyEditor::EmbeddingNone, + qdesigner_internal::ValidationObjectName)) +{ + setWindowTitle(QCoreApplication::translate("ObjectNameDialog", "Change Object Name")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + vboxLayout->addWidget(new QLabel(QCoreApplication::translate("ObjectNameDialog", "Object Name"))); + + m_editor->setText(oldName); + m_editor->selectAll(); + m_editor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + vboxLayout->addWidget(m_editor); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + vboxLayout->addWidget(buttonBox); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +QString ObjectNameDialog::newObjectName() const +{ + return m_editor->text(); +} + +} + +namespace qdesigner_internal { +// -------------- QDesignerTaskMenuPrivate +class QDesignerTaskMenuPrivate { +public: + QDesignerTaskMenuPrivate(QWidget *widget, QObject *parent); + + QDesignerTaskMenu *m_q; + QPointer m_widget; + QAction *m_separator; + QAction *m_separator2; + QAction *m_separator3; + QAction *m_separator4; + QAction *m_separator5; + QAction *m_separator6; + QAction *m_separator7; + QAction *m_changeObjectNameAction; + QAction *m_changeToolTip; + QAction *m_changeWhatsThis; + QAction *m_changeStyleSheet; + MorphMenu *m_morphMenu; + FormLayoutMenu *m_formLayoutMenu; + + QAction *m_addMenuBar; + QAction *m_addToolBar; + QAction *m_addStatusBar; + QAction *m_removeStatusBar; + QAction *m_changeScript; + QAction *m_containerFakeMethods; + QAction *m_navigateToSlot; + PromotionTaskMenu* m_promotionTaskMenu; + QActionGroup *m_sizeActionGroup; + QAction *m_sizeActionsSubMenu; +}; + +QDesignerTaskMenuPrivate::QDesignerTaskMenuPrivate(QWidget *widget, QObject *parent) : + m_q(0), + m_widget(widget), + m_separator(createSeparatorHelper(parent)), + m_separator2(createSeparatorHelper(parent)), + m_separator3(createSeparatorHelper(parent)), + m_separator4(createSeparatorHelper(parent)), + m_separator5(createSeparatorHelper(parent)), + m_separator6(createSeparatorHelper(parent)), + m_separator7(createSeparatorHelper(parent)), + m_changeObjectNameAction(new QAction(QDesignerTaskMenu::tr("Change objectName..."), parent)), + m_changeToolTip(new QAction(QDesignerTaskMenu::tr("Change toolTip..."), parent)), + m_changeWhatsThis(new QAction(QDesignerTaskMenu::tr("Change whatsThis..."), parent)), + m_changeStyleSheet(new QAction(QDesignerTaskMenu::tr("Change styleSheet..."), parent)), + m_morphMenu(new MorphMenu(parent)), + m_formLayoutMenu(new FormLayoutMenu(parent)), + m_addMenuBar(new QAction(QDesignerTaskMenu::tr("Create Menu Bar"), parent)), + m_addToolBar(new QAction(QDesignerTaskMenu::tr("Add Tool Bar"), parent)), + m_addStatusBar(new QAction(QDesignerTaskMenu::tr("Create Status Bar"), parent)), + m_removeStatusBar(new QAction(QDesignerTaskMenu::tr("Remove Status Bar"), parent)), + m_changeScript(new QAction(QDesignerTaskMenu::tr("Change script..."), parent)), + m_containerFakeMethods(new QAction(QDesignerTaskMenu::tr("Change signals/slots..."), parent)), + m_navigateToSlot(new QAction(QDesignerTaskMenu::tr("Go to slot..."), parent)), + m_promotionTaskMenu(new PromotionTaskMenu(widget, PromotionTaskMenu::ModeManagedMultiSelection, parent)), + m_sizeActionGroup(new QActionGroup(parent)), + m_sizeActionsSubMenu(new QAction(QDesignerTaskMenu::tr("Size Constraints"), parent)) +{ + QMenu *sizeMenu = new QMenu; + m_sizeActionsSubMenu->setMenu(sizeMenu); + QAction *sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Width")); + sizeAction->setData(ApplyMinimumWidth); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Height")); + sizeAction->setData(ApplyMinimumHeight); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Size")); + sizeAction->setData(ApplyMinimumWidth|ApplyMinimumHeight); + sizeMenu->addAction(sizeAction); + + sizeMenu->addSeparator(); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Width")); + sizeAction->setData(ApplyMaximumWidth); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Height")); + sizeAction->setData(ApplyMaximumHeight); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Size")); + sizeAction->setData(ApplyMaximumWidth|ApplyMaximumHeight); + sizeMenu->addAction(sizeAction); +} + +// --------- QDesignerTaskMenu +QDesignerTaskMenu::QDesignerTaskMenu(QWidget *widget, QObject *parent) : + QObject(parent), + d(new QDesignerTaskMenuPrivate(widget, parent)) +{ + d->m_q = this; + Q_ASSERT(qobject_cast(widget) == 0); + + connect(d->m_changeObjectNameAction, SIGNAL(triggered()), this, SLOT(changeObjectName())); + connect(d->m_changeToolTip, SIGNAL(triggered()), this, SLOT(changeToolTip())); + connect(d->m_changeWhatsThis, SIGNAL(triggered()), this, SLOT(changeWhatsThis())); + connect(d->m_changeStyleSheet, SIGNAL(triggered()), this, SLOT(changeStyleSheet())); + connect(d->m_addMenuBar, SIGNAL(triggered()), this, SLOT(createMenuBar())); + connect(d->m_addToolBar, SIGNAL(triggered()), this, SLOT(addToolBar())); + connect(d->m_addStatusBar, SIGNAL(triggered()), this, SLOT(createStatusBar())); + connect(d->m_removeStatusBar, SIGNAL(triggered()), this, SLOT(removeStatusBar())); + connect(d->m_changeScript, SIGNAL(triggered()), this, SLOT(changeScript())); + connect(d->m_containerFakeMethods, SIGNAL(triggered()), this, SLOT(containerFakeMethods())); + connect(d->m_navigateToSlot, SIGNAL(triggered()), this, SLOT(slotNavigateToSlot())); + connect(d->m_sizeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(applySize(QAction*))); +} + +QDesignerTaskMenu::~QDesignerTaskMenu() +{ + delete d; +} + +QAction *QDesignerTaskMenu::createSeparator() +{ + return createSeparatorHelper(this); +} + +QWidget *QDesignerTaskMenu::widget() const +{ + return d->m_widget; +} + +QDesignerFormWindowInterface *QDesignerTaskMenu::formWindow() const +{ + QDesignerFormWindowInterface *result = QDesignerFormWindowInterface::findFormWindow(widget()); + Q_ASSERT(result != 0); + return result; +} + +void QDesignerTaskMenu::createMenuBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + CreateMenuBarCommand *cmd = new CreateMenuBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::addToolBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + AddToolBarCommand *cmd = new AddToolBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::createStatusBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + CreateStatusBarCommand *cmd = new CreateStatusBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::removeStatusBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + DeleteStatusBarCommand *cmd = new DeleteStatusBarCommand(fw); + cmd->init(findStatusBar(mw)); + fw->commandHistory()->push(cmd); + } +} + +QList QDesignerTaskMenu::taskActions() const +{ + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(widget()); + Q_ASSERT(formWindow); + + const bool isMainContainer = formWindow->mainContainer() == widget(); + + QList actions; + + if (const QMainWindow *mw = qobject_cast(formWindow->mainContainer())) { + if (isMainContainer || mw->centralWidget() == widget()) { + if (!findMenuBar(mw)) { + actions.append(d->m_addMenuBar); + } + + actions.append(d->m_addToolBar); + // ### create the status bar + if (!findStatusBar(mw)) + actions.append(d->m_addStatusBar); + else + actions.append(d->m_removeStatusBar); + actions.append(d->m_separator); + } + } + actions.append(d->m_changeObjectNameAction); + d->m_morphMenu->populate(d->m_widget, formWindow, actions); + d->m_formLayoutMenu->populate(d->m_widget, formWindow, actions); + actions.append(d->m_separator2); + actions.append(d->m_changeToolTip); + actions.append(d->m_changeWhatsThis); + actions.append(d->m_changeStyleSheet); + actions.append(d->m_separator6); + actions.append(d->m_sizeActionsSubMenu); + d->m_promotionTaskMenu->setMode(formWindow->isManaged(d->m_widget) ? + PromotionTaskMenu::ModeManagedMultiSelection : PromotionTaskMenu::ModeUnmanagedMultiSelection); + d->m_promotionTaskMenu->addActions(formWindow, PromotionTaskMenu::LeadingSeparator, actions); + +#ifdef WANT_SCRIPT_OPTION + if (!isMainContainer) { + actions.append(d->m_separator4); + actions.append(d->m_changeScript); + } +#endif + if (isMainContainer && !qt_extension(formWindow->core()->extensionManager(), formWindow->core())) { + actions.append(d->m_separator5); + actions.append(d->m_containerFakeMethods); + } + + if (isSlotNavigationEnabled(formWindow->core())) { + actions.append(d->m_separator7); + actions.append(d->m_navigateToSlot); + } + + return actions; +} + +void QDesignerTaskMenu::changeObjectName() +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw != 0); + + const QString oldObjectName = objName(fw->core(), widget()); + + ObjectNameDialog dialog(fw, oldObjectName); + if (dialog.exec() == QDialog::Accepted) { + const QString newObjectName = dialog.newObjectName(); + if (!newObjectName.isEmpty() && newObjectName != oldObjectName ) { + const QString objectNameProperty = QLatin1String("objectName"); + PropertySheetStringValue objectNameValue; + objectNameValue.setValue(newObjectName); + setProperty(fw, CurrentWidgetMode, objectNameProperty, qVariantFromValue(objectNameValue)); + } + } +} + +void QDesignerTaskMenu::changeTextProperty(const QString &propertyName, const QString &windowTitle, PropertyMode pm, Qt::TextFormat desiredFormat) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + Q_ASSERT(d->m_widget->parentWidget() != 0); + + const QDesignerPropertySheetExtension *sheet = qt_extension(fw->core()->extensionManager(), d->m_widget); + const int index = sheet->indexOf(propertyName); + if (index == -1) { + qDebug() << "** WARNING Invalid property" << propertyName << " passed to changeTextProperty!"; + return; + } + PropertySheetStringValue textValue = qVariantValue(sheet->property(index)); + const QString oldText = textValue.value(); + // Pop up respective dialog + bool accepted = false; + QString newText; + switch (desiredFormat) { + case Qt::PlainText: { + PlainTextEditorDialog dlg(fw->core(), fw); + if (!windowTitle.isEmpty()) + dlg.setWindowTitle(windowTitle); + dlg.setDefaultFont(d->m_widget->font()); + dlg.setText(oldText); + accepted = dlg.showDialog() == QDialog::Accepted; + newText = dlg.text(); + } + break; + default: { + RichTextEditorDialog dlg(fw->core(), fw); + if (!windowTitle.isEmpty()) + dlg.setWindowTitle(windowTitle); + dlg.setDefaultFont(d->m_widget->font()); + dlg.setText(oldText); + accepted = dlg.showDialog() == QDialog::Accepted; + newText = dlg.text(desiredFormat); + } + break; + } + // change property + if (!accepted || oldText == newText) + return; + + + textValue.setValue(newText); + setProperty(fw, pm, propertyName, qVariantFromValue(textValue)); +} + +void QDesignerTaskMenu::changeToolTip() +{ + changeTextProperty(QLatin1String("toolTip"), tr("Edit ToolTip"), MultiSelectionMode, Qt::AutoText); +} + +void QDesignerTaskMenu::changeWhatsThis() +{ + changeTextProperty(QLatin1String("whatsThis"), tr("Edit WhatsThis"), MultiSelectionMode, Qt::AutoText); +} + +void QDesignerTaskMenu::changeStyleSheet() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + StyleSheetPropertyEditorDialog dlg(fw, fw, d->m_widget); + dlg.exec(); + } +} + +void QDesignerTaskMenu::changeScript() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + MetaDataBase *metaDataBase = qobject_cast(fw->core()->metaDataBase()); + if (!metaDataBase) + return; + + const MetaDataBaseItem* item = metaDataBase->metaDataBaseItem(d->m_widget); + if (!item) + return; + + const QString oldScript = item->script(); + QString newScript = oldScript; + + ScriptDialog scriptDialog(fw->core()->dialogGui(), fw); + if (!scriptDialog.editScript(newScript)) + return; + + // compile list of selected objects + ScriptCommand *scriptCommand = new ScriptCommand(fw); + if (!scriptCommand->init(applicableObjects(fw, MultiSelectionMode), newScript)) { + delete scriptCommand; + return; + } + + fw->commandHistory()->push(scriptCommand); +} + +void QDesignerTaskMenu::containerFakeMethods() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + SignalSlotDialog::editMetaDataBase(fw, d->m_widget, fw); +} + +static QString declaredInClass(const QDesignerMetaObjectInterface *metaObject, const QString &member) +{ + // Find class whose superclass does not contain the method. + const QDesignerMetaObjectInterface *meta = metaObject; + + for (;;) { + const QDesignerMetaObjectInterface *tmpMeta = meta->superClass(); + if (tmpMeta == 0) + break; + if (tmpMeta->indexOfMethod(member) == -1) + break; + meta = tmpMeta; + } + return meta->className(); +} + +bool QDesignerTaskMenu::isSlotNavigationEnabled(const QDesignerFormEditorInterface *core) +{ + if (QDesignerIntegration *integr = integration(core)) + return integr->isSlotNavigationEnabled(); + return false; +} + +void QDesignerTaskMenu::slotNavigateToSlot() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + Q_ASSERT(core); + navigateToSlot(core, widget()); +} + +void QDesignerTaskMenu::navigateToSlot(QDesignerFormEditorInterface *core, + QObject *object, + const QString &defaultSignal) +{ + const QString objectName = objName(core, object); + QMap > classToSignalList; + + QDesignerIntegration *integr = integration(core); + + // "real" signals + if (const QDesignerMetaObjectInterface *metaObject = core->introspection()->metaObject(object)) { + const int methodCount = metaObject->methodCount(); + for (int i = 0; i < methodCount; ++i) { + const QDesignerMetaMethodInterface *metaMethod = metaObject->method(i); + if (metaMethod->methodType() == QDesignerMetaMethodInterface::Signal) { + const QString signature = metaMethod->signature(); + const QStringList parameterNames = metaMethod->parameterNames(); + classToSignalList[declaredInClass(metaObject, signature)][signature] = parameterNames; + } + } + } + + // fake signals + if (qdesigner_internal::MetaDataBase *metaDataBase + = qobject_cast(core->metaDataBase())) { + qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object); + Q_ASSERT(item); + const QStringList fakeSignals = item->fakeSignals(); + foreach (const QString &fakeSignal, fakeSignals) + classToSignalList[item->customClassName()][fakeSignal] = QStringList(); + } + + if (object->isWidgetType()) { + QWidget *widget = static_cast(object); + if (WidgetDataBase *db = qobject_cast(core->widgetDataBase())) { + const QString promotedClassName = promotedCustomClassName(core, widget); + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index >= 0) { + WidgetDataBaseItem* item = static_cast(db->item(index)); + const QStringList fakeSignals = item->fakeSignals(); + foreach (const QString &fakeSignal, fakeSignals) + classToSignalList[promotedClassName][fakeSignal] = QStringList(); + } + } + } + + Ui::SelectSignalDialog dialogUi; + QDialog selectSignalDialog(0, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + dialogUi.setupUi(&selectSignalDialog); + + QMap >::const_iterator iter(classToSignalList.constBegin()); + for (; iter != classToSignalList.constEnd(); ++iter) { + const QString className = iter.key(); + QMap signalNames = iter.value(); + + QMap::const_iterator itSignal(signalNames.constBegin()); + for (; itSignal != signalNames.constEnd(); ++itSignal) { + const QString signalName = itSignal.key(); + QTreeWidgetItem *row = new QTreeWidgetItem(QStringList() << signalName << className); + row->setData(0, Qt::UserRole, itSignal.value()); + dialogUi.signalList->addTopLevelItem(row); + } + } + if (dialogUi.signalList->topLevelItemCount() == 0) { + QTreeWidgetItem *row = new QTreeWidgetItem(QStringList() << tr("no signals available")); + dialogUi.signalList->addTopLevelItem(row); + dialogUi.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } else { + connect(dialogUi.signalList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), + &selectSignalDialog, SLOT(accept())); + } + + if (defaultSignal.isEmpty()) { + dialogUi.signalList->setCurrentItem(dialogUi.signalList->topLevelItem(0)); + } else { + const QList items = dialogUi.signalList->findItems (defaultSignal, Qt::MatchExactly, 0); + if (!items.empty()) + dialogUi.signalList->setCurrentItem(items.front()); + } + + dialogUi.signalList->resizeColumnToContents(0); + + if (selectSignalDialog.exec() == QDialog::Accepted) { + QTreeWidgetItem *selectedItem = dialogUi.signalList->selectedItems().first(); + const QString signalSignature = selectedItem->text(0); + const QStringList parameterNames = qVariantValue(selectedItem->data(0, Qt::UserRole)); + + // TODO: Check whether signal is connected to slot + integr->emitNavigateToSlot(objectName, signalSignature, parameterNames); + } +} + +// Add a command that takes over the value of the current geometry as +// minimum/maximum size according to the flags. +static void createSizeCommand(QDesignerFormWindowInterface *fw, QWidget *w, int flags) +{ + const QSize size = w->size(); + if (flags & (ApplyMinimumWidth|ApplyMinimumHeight)) { + QSize minimumSize = w-> minimumSize(); + if (flags & ApplyMinimumWidth) + minimumSize.setWidth(size.width()); + if (flags & ApplyMinimumHeight) + minimumSize.setHeight(size.height()); + SetPropertyCommand* cmd = new SetPropertyCommand(fw); + cmd->init(w, QLatin1String("minimumSize"), minimumSize); + fw->commandHistory()->push(cmd); + } + if (flags & (ApplyMaximumWidth|ApplyMaximumHeight)) { + QSize maximumSize = w-> maximumSize(); + if (flags & ApplyMaximumWidth) + maximumSize.setWidth(size.width()); + if (flags & ApplyMaximumHeight) + maximumSize.setHeight(size.height()); + SetPropertyCommand* cmd = new SetPropertyCommand(fw); + cmd->init(w, QLatin1String("maximumSize"), maximumSize); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::applySize(QAction *a) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const QWidgetList selection = applicableWidgets(fw, MultiSelectionMode); + if (selection.isEmpty()) + return; + + const int mask = a->data().toInt(); + const int size = selection.size(); + fw->commandHistory()->beginMacro(tr("Set size constraint on %n widget(s)", 0, size)); + for (int i = 0; i < size; i++) + createSizeCommand(fw, selection.at(i), mask); + fw->commandHistory()->endMacro(); +} + +template + static void getApplicableObjects(const QDesignerFormWindowInterface *fw, QWidget *current, + QDesignerTaskMenu::PropertyMode pm, Container *c) +{ + // Current is always first + c->push_back(current); + if (pm == QDesignerTaskMenu::CurrentWidgetMode) + return; + QDesignerObjectInspector *designerObjectInspector = qobject_cast(fw->core()->objectInspector()); + if (!designerObjectInspector) + return; // Ooops, someone plugged an old-style Object Inspector + // Add managed or unmanaged selection according to current type, make current first + Selection s; + designerObjectInspector->getSelection(s); + const QWidgetList &source = fw->isManaged(current) ? s.managed : s.unmanaged; + const QWidgetList::const_iterator cend = source.constEnd(); + for ( QWidgetList::const_iterator it = source.constBegin(); it != cend; ++it) + if (*it != current) // was first + c->push_back(*it); +} + +QObjectList QDesignerTaskMenu::applicableObjects(const QDesignerFormWindowInterface *fw, PropertyMode pm) const +{ + QObjectList rc; + getApplicableObjects(fw, d->m_widget, pm, &rc); + return rc; +} + +QWidgetList QDesignerTaskMenu::applicableWidgets(const QDesignerFormWindowInterface *fw, PropertyMode pm) const +{ + QWidgetList rc; + getApplicableObjects(fw, d->m_widget, pm, &rc); + return rc; +} + +void QDesignerTaskMenu::setProperty(QDesignerFormWindowInterface *fw, PropertyMode pm, const QString &name, const QVariant &newValue) +{ + SetPropertyCommand* setPropertyCommand = new SetPropertyCommand(fw); + if (setPropertyCommand->init(applicableObjects(fw, pm), name, newValue, d->m_widget)) { + fw->commandHistory()->push(setPropertyCommand); + } else { + delete setPropertyCommand; + qDebug() << "Unable to set property " << name << '.'; + } +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_taskmenu_p.h b/designer/lib/shared/qdesigner_taskmenu_p.h new file mode 100644 index 0000000..8ee77cf --- /dev/null +++ b/designer/lib/shared/qdesigner_taskmenu_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TASKMENU_H +#define QDESIGNER_TASKMENU_H + +#include "shared_global_p.h" +#include "extensionfactory_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QWidget; +class QSignalMapper; + +namespace qdesigner_internal { +class QDesignerTaskMenuPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerTaskMenu: public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + QDesignerTaskMenu(QWidget *widget, QObject *parent); + virtual ~QDesignerTaskMenu(); + + QWidget *widget() const; + + virtual QList taskActions() const; + + enum PropertyMode { CurrentWidgetMode, MultiSelectionMode }; + + static bool isSlotNavigationEnabled(const QDesignerFormEditorInterface *core); + static void navigateToSlot(QDesignerFormEditorInterface *core, QObject *o, + const QString &defaultSignal = QString()); + +protected: + + QDesignerFormWindowInterface *formWindow() const; + void changeTextProperty(const QString &propertyName, const QString &windowTitle, PropertyMode pm, Qt::TextFormat desiredFormat); + + QAction *createSeparator(); + + /* Retrieve the list of objects the task menu is supposed to act on. Note that a task menu can be invoked for + * an unmanaged widget [as of 4.5], in which case it must not use the cursor selection, + * but the unmanaged selection of the object inspector. */ + QObjectList applicableObjects(const QDesignerFormWindowInterface *fw, PropertyMode pm) const; + QList applicableWidgets(const QDesignerFormWindowInterface *fw, PropertyMode pm) const; + + void setProperty(QDesignerFormWindowInterface *fw, PropertyMode pm, const QString &name, const QVariant &newValue); + +private slots: + void changeObjectName(); + void changeToolTip(); + void changeWhatsThis(); + void changeStyleSheet(); + void createMenuBar(); + void addToolBar(); + void createStatusBar(); + void removeStatusBar(); + void changeScript(); + void containerFakeMethods(); + void slotNavigateToSlot(); + void applySize(QAction *a); + +private: + QDesignerTaskMenuPrivate *d; +}; + +typedef ExtensionFactory QDesignerTaskMenuFactory; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_TASKMENU_H diff --git a/designer/lib/shared/qdesigner_toolbar.cpp b/designer/lib/shared/qdesigner_toolbar.cpp new file mode 100644 index 0000000..6df5baa --- /dev/null +++ b/designer/lib/shared/qdesigner_toolbar.cpp @@ -0,0 +1,488 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_toolbar_p.h" +#include "qdesigner_command_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" +#include "promotiontaskmenu_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +typedef QList ActionList; + +namespace qdesigner_internal { +// ------------------- ToolBarEventFilter +void ToolBarEventFilter::install(QToolBar *tb) +{ + ToolBarEventFilter *tf = new ToolBarEventFilter(tb); + tb->installEventFilter(tf); + tb->setAcceptDrops(true); // ### fake +} + +ToolBarEventFilter::ToolBarEventFilter(QToolBar *tb) : + QObject(tb), + m_toolBar(tb), + m_promotionTaskMenu(0) +{ +} + +ToolBarEventFilter *ToolBarEventFilter::eventFilterOf(const QToolBar *tb) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = tb->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (ToolBarEventFilter *ef = qobject_cast(o)) + return ef; + } + return 0; +} + +bool ToolBarEventFilter::eventFilter (QObject *watched, QEvent *event) +{ + if (watched != m_toolBar) + return QObject::eventFilter (watched, event); + + switch (event->type()) { + case QEvent::ChildAdded: { + // Children should not interact with the mouse + const QChildEvent *ce = static_cast(event); + if (QWidget *w = qobject_cast(ce->child())) { + w->setAttribute(Qt::WA_TransparentForMouseEvents, true); + w->setFocusPolicy(Qt::NoFocus); + } + } + break; + case QEvent::ContextMenu: + return handleContextMenuEvent(static_cast(event)); + case QEvent::DragEnter: + case QEvent::DragMove: + return handleDragEnterMoveEvent(static_cast(event)); + case QEvent::DragLeave: + return handleDragLeaveEvent(static_cast(event)); + case QEvent::Drop: + return handleDropEvent(static_cast(event)); + case QEvent::MouseButtonPress: + return handleMousePressEvent(static_cast(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(static_cast(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(static_cast(event)); + default: + break; + } + return QObject::eventFilter (watched, event); +} + +ActionList ToolBarEventFilter::contextMenuActions(const QPoint &globalPos) +{ + ActionList rc; + const int index = actionIndexAt(m_toolBar, m_toolBar->mapFromGlobal(globalPos), m_toolBar->orientation()); + const ActionList actions = m_toolBar->actions(); + QAction *action = index != -1 ?actions.at(index) : 0; + QVariant itemData; + + // Insert before + if (action && index != 0 && !action->isSeparator()) { + QAction *newSeperatorAct = new QAction(tr("Insert Separator before '%1'").arg(action->objectName()), 0); + qVariantSetValue(itemData, action); + newSeperatorAct->setData(itemData); + connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator())); + rc.push_back(newSeperatorAct); + } + + // Append separator + if (actions.empty() || !actions.back()->isSeparator()) { + QAction *newSeperatorAct = new QAction(tr("Append Separator"), 0); + qVariantSetValue(itemData, static_cast(0)); + newSeperatorAct->setData(itemData); + connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator())); + rc.push_back(newSeperatorAct); + } + // Promotion + if (!m_promotionTaskMenu) + m_promotionTaskMenu = new PromotionTaskMenu(m_toolBar, PromotionTaskMenu::ModeSingleWidget, this); + m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::TrailingSeparator, rc); + // Remove + if (action) { + QAction *a = new QAction(tr("Remove action '%1'").arg(action->objectName()), 0); + qVariantSetValue(itemData, action); + a->setData(itemData); + connect(a, SIGNAL(triggered()), this, SLOT(slotRemoveSelectedAction())); + rc.push_back(a); + } + + QAction *remove_toolbar = new QAction(tr("Remove Toolbar '%1'").arg(m_toolBar->objectName()), 0); + connect(remove_toolbar, SIGNAL(triggered()), this, SLOT(slotRemoveToolBar())); + rc.push_back(remove_toolbar); + return rc; +} + +bool ToolBarEventFilter::handleContextMenuEvent(QContextMenuEvent * event ) +{ + event->accept(); + + const QPoint globalPos = event->globalPos(); + const ActionList al = contextMenuActions(event->globalPos()); + + QMenu menu(0); + const ActionList::const_iterator acend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it) + menu.addAction(*it); + menu.exec(globalPos); + return true; +} + +void ToolBarEventFilter::slotRemoveSelectedAction() +{ + QAction *action = qobject_cast(sender()); + if (!action) + return; + + QAction *a = qvariant_cast(action->data()); + Q_ASSERT(a != 0); + + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + + const ActionList actions = m_toolBar->actions(); + const int pos = actions.indexOf(a); + QAction *action_before = 0; + if (pos != -1 && actions.count() > pos + 1) + action_before = actions.at(pos + 1); + + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(m_toolBar, a, action_before); + fw->commandHistory()->push(cmd); +} + +void ToolBarEventFilter::slotRemoveToolBar() +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + DeleteToolBarCommand *cmd = new DeleteToolBarCommand(fw); + cmd->init(m_toolBar); + fw->commandHistory()->push(cmd); +} + +void ToolBarEventFilter::slotInsertSeparator() +{ + QDesignerFormWindowInterface *fw = formWindow(); + QAction *theSender = qobject_cast(sender()); + QAction *previous = qvariant_cast(theSender->data()); + fw->beginCommand(tr("Insert Separator")); + QAction *action = createAction(fw, QLatin1String("separator"), true); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, previous); + fw->commandHistory()->push(cmd); + fw->endCommand(); +} + +QDesignerFormWindowInterface *ToolBarEventFilter::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(m_toolBar); +} + +QAction *ToolBarEventFilter::createAction(QDesignerFormWindowInterface *fw, const QString &objectName, bool separator) +{ + QAction *action = new QAction(fw); + fw->core()->widgetFactory()->initialize(action); + if (separator) + action->setSeparator(true); + + action->setObjectName(objectName); + fw->ensureUniqueObjectName(action); + + qdesigner_internal::AddActionCommand *cmd = new qdesigner_internal::AddActionCommand(fw); + cmd->init(action); + fw->commandHistory()->push(cmd); + + return action; +} + +void ToolBarEventFilter::adjustDragIndicator(const QPoint &pos) +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + if (QDesignerActionProviderExtension *a = qt_extension(core->extensionManager(), m_toolBar)) + a->adjustIndicator(pos); + } +} + +void ToolBarEventFilter::hideDragIndicator() +{ + adjustDragIndicator(QPoint(-1, -1)); +} + +bool ToolBarEventFilter::handleMousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || withinHandleArea(m_toolBar, event->pos())) + return false; + + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + // Keep selection in sync + fw->clearSelection(false); + if (QDesignerObjectInspector *oi = qobject_cast(core->objectInspector())) { + oi->clearSelection(); + oi->selectObject(m_toolBar); + } + core->propertyEditor()->setObject(m_toolBar); + } + m_startPosition = m_toolBar->mapFromGlobal(event->globalPos()); + event->accept(); + return true; +} + +bool ToolBarEventFilter::handleMouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos())) + return false; + + // Accept the event, otherwise, form window selection will trigger + m_startPosition = QPoint(); + event->accept(); + return true; +} + +bool ToolBarEventFilter::handleMouseMoveEvent(QMouseEvent *event) +{ + if (m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos())) + return false; + + const QPoint pos = m_toolBar->mapFromGlobal(event->globalPos()); + if ((pos - m_startPosition).manhattanLength() > qApp->startDragDistance()) { + startDrag(m_startPosition, event->modifiers()); + m_startPosition = QPoint(); + event->accept(); + return true; + } + return false; +} + +bool ToolBarEventFilter::handleDragEnterMoveEvent(QDragMoveEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d) + return false; + + if (d->actionList().isEmpty()) { + event->ignore(); + hideDragIndicator(); + return true; + } + + QAction *action = d->actionList().first(); + if (!action || action->menu() || m_toolBar->actions().contains(action) || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) { + event->ignore(); + hideDragIndicator(); + return true; + } + + d->accept(event); + adjustDragIndicator(event->pos()); + return true; +} + +bool ToolBarEventFilter::handleDragLeaveEvent(QDragLeaveEvent *) +{ + hideDragIndicator(); + return false; +} + +bool ToolBarEventFilter::handleDropEvent(QDropEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast(event->mimeData()); + if (!d) + return false; + + if (d->actionList().isEmpty()) { + event->ignore(); + hideDragIndicator(); + return true; + } + + QAction *action = d->actionList().first(); + + const ActionList actions = m_toolBar->actions(); + if (!action || actions.contains(action)) { + event->ignore(); + hideDragIndicator(); + return true; + } + + // Try to find action to 'insert before'. Click on action or in free area, else ignore. + QAction *beforeAction = 0; + const QPoint pos = event->pos(); + const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation()); + if (index != -1) { + beforeAction = actions.at(index); + } else { + if (!freeArea(m_toolBar).contains(pos)) { + event->ignore(); + hideDragIndicator(); + return true; + } + } + + event->acceptProposedAction(); + QDesignerFormWindowInterface *fw = formWindow(); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, beforeAction); + fw->commandHistory()->push(cmd); + hideDragIndicator(); + return true; +} + +void ToolBarEventFilter::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers) +{ + const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation()); + if (index == - 1) + return; + + const ActionList actions = m_toolBar->actions(); + QAction *action = actions.at(index); + QDesignerFormWindowInterface *fw = formWindow(); + + const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction; + if (dropAction == Qt::MoveAction) { + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + const int nextIndex = index + 1; + QAction *nextAction = nextIndex < actions.size() ? actions.at(nextIndex) : 0; + cmd->init(m_toolBar, action, nextAction); + fw->commandHistory()->push(cmd); + } + + QDrag *drag = new QDrag(m_toolBar); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap( action)); + drag->setMimeData(new ActionRepositoryMimeData(action, dropAction)); + + if (drag->start(dropAction) == Qt::IgnoreAction) { + hideDragIndicator(); + if (dropAction == Qt::MoveAction) { + const ActionList currentActions = m_toolBar->actions(); + QAction *previous = 0; + if (index >= 0 && index < currentActions.size()) + previous = currentActions.at(index); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, previous); + fw->commandHistory()->push(cmd); + } + } +} + +QAction *ToolBarEventFilter::actionAt(const QToolBar *tb, const QPoint &pos) +{ + const int index = actionIndexAt(tb, pos, tb->orientation()); + if (index == -1) + return 0; + return tb->actions().at(index); +} + +//that's a trick to get access to the initStyleOption which is a protected member +class FriendlyToolBar : public QToolBar { +public: + friend class ToolBarEventFilter; +}; + +QRect ToolBarEventFilter::handleArea(const QToolBar *tb) +{ + QStyleOptionToolBar opt; + static_cast(tb)->initStyleOption(&opt); + return tb->style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, tb); +} + +bool ToolBarEventFilter::withinHandleArea(const QToolBar *tb, const QPoint &pos) +{ + return handleArea(tb).contains(pos); +} + +// Determine the free area behind the last action. +QRect ToolBarEventFilter::freeArea(const QToolBar *tb) +{ + QRect rc = QRect(QPoint(0, 0), tb->size()); + const ActionList actionList = tb->actions(); + QRect exclusionRectangle = actionList.empty() ? handleArea(tb) : tb->actionGeometry(actionList.back()); + switch (tb->orientation()) { + case Qt::Horizontal: + switch (tb->layoutDirection()) { + case Qt::LayoutDirectionAuto: // Should never happen + case Qt::LeftToRight: + rc.setX(exclusionRectangle.right() + 1); + break; + case Qt::RightToLeft: + rc.setRight(exclusionRectangle.x()); + break; + } + break; + case Qt::Vertical: + rc.setY(exclusionRectangle.bottom() + 1); + break; + } + return rc; +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_toolbar_p.h b/designer/lib/shared/qdesigner_toolbar_p.h new file mode 100644 index 0000000..8ec3e74 --- /dev/null +++ b/designer/lib/shared/qdesigner_toolbar_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TOOLBAR_H +#define QDESIGNER_TOOLBAR_H + +#include "shared_global_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QToolBar; +class QRect; +class QAction; + +namespace qdesigner_internal { + +class PromotionTaskMenu; + +// Special event filter for tool bars in designer. +// Handles drag and drop to and from. Ensures that each +// child widget is WA_TransparentForMouseEvents to enable drag and drop. + +class QDESIGNER_SHARED_EXPORT ToolBarEventFilter : public QObject { + Q_OBJECT + +public: + static void install(QToolBar *tb); + + // Find action by position. Note that QToolBar::actionAt() will + // not work as designer sets WA_TransparentForMouseEvents on its tool bar buttons + // to be able to drag them. This function will return the dummy + // sentinel action when applied to tool bars created by designer if the position matches. + static QAction *actionAt(const QToolBar *tb, const QPoint &pos); + + static bool withinHandleArea(const QToolBar *tb, const QPoint &pos); + static QRect handleArea(const QToolBar *tb); + static QRect freeArea(const QToolBar *tb); + + // Utility to create an action + static QAction *createAction(QDesignerFormWindowInterface *fw, const QString &objectName, bool separator); + + virtual bool eventFilter (QObject *watched, QEvent *event); + + // Helper for task menu extension + QList contextMenuActions(const QPoint &globalPos = QPoint(-1, -1)); + + static ToolBarEventFilter *eventFilterOf(const QToolBar *tb); + +private slots: + void slotRemoveSelectedAction(); + void slotRemoveToolBar(); + void slotInsertSeparator(); + +private: + explicit ToolBarEventFilter(QToolBar *tb); + + bool handleContextMenuEvent(QContextMenuEvent * event); + bool handleDragEnterMoveEvent(QDragMoveEvent *event); + bool handleDragLeaveEvent(QDragLeaveEvent *); + bool handleDropEvent(QDropEvent *event); + bool handleMousePressEvent(QMouseEvent *event); + bool handleMouseReleaseEvent(QMouseEvent *event); + bool handleMouseMoveEvent(QMouseEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + void adjustDragIndicator(const QPoint &pos); + void hideDragIndicator(); + void startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers); + bool withinHandleArea(const QPoint &pos) const; + + QToolBar *m_toolBar; + PromotionTaskMenu *m_promotionTaskMenu; + QPoint m_startPosition; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_TOOLBAR_H diff --git a/designer/lib/shared/qdesigner_toolbox.cpp b/designer/lib/shared/qdesigner_toolbox.cpp new file mode 100644 index 0000000..dceddae --- /dev/null +++ b/designer/lib/shared/qdesigner_toolbox.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_toolbox_p.h" +#include "qdesigner_command_p.h" +#include "orderdialog_p.h" +#include "promotiontaskmenu_p.h" +#include "formwindowbase_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QToolBoxHelper::QToolBoxHelper(QToolBox *toolbox) : + QObject(toolbox), + m_toolbox(toolbox), + m_actionDeletePage(new QAction(tr("Delete Page"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_actionChangePageOrder(new QAction(tr("Change Page Order..."), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionChangePageOrder, SIGNAL(triggered()), this, SLOT(changeOrder())); + + m_toolbox->installEventFilter(this); +} + +void QToolBoxHelper::install(QToolBox *toolbox) +{ + new QToolBoxHelper(toolbox); +} + +bool QToolBoxHelper::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::ChildPolished: + // Install on the buttons + if (watched == m_toolbox) { + QChildEvent *ce = static_cast(event); + if (!qstrcmp(ce->child()->metaObject()->className(), "QToolBoxButton")) + ce->child()->installEventFilter(this); + } + break; + case QEvent::ContextMenu: + if (watched != m_toolbox) { + // An action invoked from the passive interactor (ToolBox button) might + // cause its deletion within its event handler, triggering a warning. Re-post + // the event to the toolbox. + QContextMenuEvent *current = static_cast(event); + QContextMenuEvent *copy = new QContextMenuEvent(current->reason(), current->pos(), current-> globalPos(), current->modifiers()); + QApplication::postEvent(m_toolbox, copy); + current->accept(); + return true; + } + break; + case QEvent::MouseButtonRelease: + if (watched != m_toolbox) + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + fw->clearSelection(); + fw->selectWidget(m_toolbox, true); + } + break; + default: + break; + } + return QObject::eventFilter(watched, event); +} + +QToolBoxHelper *QToolBoxHelper::helperOf(const QToolBox *toolbox) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = toolbox->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QToolBoxHelper *h = qobject_cast(o)) + return h; + } + return 0; +} + +QMenu *QToolBoxHelper::addToolBoxContextMenuActions(const QToolBox *toolbox, QMenu *popup) +{ + QToolBoxHelper *helper = helperOf(toolbox); + if (!helper) + return 0; + return helper->addContextMenuActions(popup); +} + +void QToolBoxHelper::removeCurrentPage() +{ + if (m_toolbox->currentIndex() == -1 || !m_toolbox->widget(m_toolbox->currentIndex())) + return; + + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::DeleteToolBoxPageCommand *cmd = new qdesigner_internal::DeleteToolBoxPageCommand(fw); + cmd->init(m_toolbox); + fw->commandHistory()->push(cmd); + } +} + +void QToolBoxHelper::addPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::AddToolBoxPageCommand *cmd = new qdesigner_internal::AddToolBoxPageCommand(fw); + cmd->init(m_toolbox, qdesigner_internal::AddToolBoxPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QToolBoxHelper::changeOrder() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox); + + if (!fw) + return; + + const QWidgetList oldPages = qdesigner_internal::OrderDialog::pagesOfContainer(fw->core(), m_toolbox); + const int pageCount = oldPages.size(); + if (pageCount < 2) + return; + + qdesigner_internal::OrderDialog dlg(fw); + dlg.setPageList(oldPages); + if (dlg.exec() == QDialog::Rejected) + return; + + const QWidgetList newPages = dlg.pageList(); + if (newPages == oldPages) + return; + + fw->beginCommand(tr("Change Page Order")); + for(int i=0; i < pageCount; ++i) { + if (newPages.at(i) == m_toolbox->widget(i)) + continue; + qdesigner_internal::MoveToolBoxPageCommand *cmd = new qdesigner_internal::MoveToolBoxPageCommand(fw); + cmd->init(m_toolbox, newPages.at(i), i); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void QToolBoxHelper::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::AddToolBoxPageCommand *cmd = new qdesigner_internal::AddToolBoxPageCommand(fw); + cmd->init(m_toolbox, qdesigner_internal::AddToolBoxPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +QPalette::ColorRole QToolBoxHelper::currentItemBackgroundRole() const +{ + const QWidget *w = m_toolbox->widget(0); + if (!w) + return QPalette::Window; + return w->backgroundRole(); +} + +void QToolBoxHelper::setCurrentItemBackgroundRole(QPalette::ColorRole role) +{ + const int count = m_toolbox->count(); + for (int i = 0; i < count; ++i) { + QWidget *w = m_toolbox->widget(i); + w->setBackgroundRole(role); + w->update(); + } +} + +QMenu *QToolBoxHelper::addContextMenuActions(QMenu *popup) const +{ + QMenu *pageMenu = 0; + const int count = m_toolbox->count(); + m_actionDeletePage->setEnabled(count > 1); + if (count) { + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(m_toolbox->currentIndex() + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = m_toolbox->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(m_toolbox), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + } + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + if (count > 1) { + popup->addAction(m_actionChangePageOrder); + } + popup->addSeparator(); + return pageMenu; +} + +// -------- QToolBoxWidgetPropertySheet + +static const char *currentItemTextKey = "currentItemText"; +static const char *currentItemNameKey = "currentItemName"; +static const char *currentItemIconKey = "currentItemIcon"; +static const char *currentItemToolTipKey = "currentItemToolTip"; +static const char *tabSpacingKey = "tabSpacing"; + +enum { tabSpacingDefault = -1 }; + +QToolBoxWidgetPropertySheet::QToolBoxWidgetPropertySheet(QToolBox *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_toolBox(object) +{ + createFakeProperty(QLatin1String(currentItemTextKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentItemNameKey), QString()); + createFakeProperty(QLatin1String(currentItemIconKey), qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); + if (formWindowBase()) + formWindowBase()->addReloadableProperty(this, indexOf(QLatin1String(currentItemIconKey))); + createFakeProperty(QLatin1String(currentItemToolTipKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(tabSpacingKey), QVariant(tabSpacingDefault)); +} + +QToolBoxWidgetPropertySheet::ToolBoxProperty QToolBoxWidgetPropertySheet::toolBoxPropertyFromName(const QString &name) +{ + typedef QHash ToolBoxPropertyHash; + static ToolBoxPropertyHash toolBoxPropertyHash; + if (toolBoxPropertyHash.empty()) { + toolBoxPropertyHash.insert(QLatin1String(currentItemTextKey), PropertyCurrentItemText); + toolBoxPropertyHash.insert(QLatin1String(currentItemNameKey), PropertyCurrentItemName); + toolBoxPropertyHash.insert(QLatin1String(currentItemIconKey), PropertyCurrentItemIcon); + toolBoxPropertyHash.insert(QLatin1String(currentItemToolTipKey), PropertyCurrentItemToolTip); + toolBoxPropertyHash.insert(QLatin1String(tabSpacingKey), PropertyTabSpacing); + } + return toolBoxPropertyHash.value(name, PropertyToolBoxNone); +} + +void QToolBoxWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + m_toolBox->layout()->setSpacing(value.toInt()); + return; + case PropertyToolBoxNone: + QDesignerPropertySheet::setProperty(index, value); + return; + default: + break; + } + // index-dependent + const int currentIndex = m_toolBox->currentIndex(); + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) + return; + + switch (toolBoxProperty) { + case PropertyCurrentItemText: + m_toolBox->setItemText(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].text = qVariantValue(value); + break; + case PropertyCurrentItemName: + currentWidget->setObjectName(value.toString()); + break; + case PropertyCurrentItemIcon: + m_toolBox->setItemIcon(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].icon = qVariantValue(value); + break; + case PropertyCurrentItemToolTip: + m_toolBox->setItemToolTip(currentIndex, qvariant_cast(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].tooltip = qVariantValue(value); + break; + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } +} + +bool QToolBoxWidgetPropertySheet::isEnabled(int index) const +{ + switch (toolBoxPropertyFromName(propertyName(index))) { + case PropertyToolBoxNone: // independent of index + case PropertyTabSpacing: + return QDesignerPropertySheet::isEnabled(index); + default: + break; + } + return m_toolBox->currentIndex() != -1; +} + +QVariant QToolBoxWidgetPropertySheet::property(int index) const +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + return m_toolBox->layout()->spacing(); + case PropertyToolBoxNone: + return QDesignerPropertySheet::property(index); + default: + break; + } + // index-dependent + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) { + if (toolBoxProperty == PropertyCurrentItemIcon) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + if (toolBoxProperty == PropertyCurrentItemText) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (toolBoxProperty == PropertyCurrentItemToolTip) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + return QVariant(QString()); + } + + // index-dependent + switch (toolBoxProperty) { + case PropertyCurrentItemText: + return qVariantFromValue(m_pageToData.value(currentWidget).text); + case PropertyCurrentItemName: + return currentWidget->objectName(); + case PropertyCurrentItemIcon: + return qVariantFromValue(m_pageToData.value(currentWidget).icon); + case PropertyCurrentItemToolTip: + return qVariantFromValue(m_pageToData.value(currentWidget).tooltip); + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } + return QVariant(); +} + +bool QToolBoxWidgetPropertySheet::reset(int index) +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + setProperty(index, QVariant(tabSpacingDefault)); + return true; + case PropertyToolBoxNone: + return QDesignerPropertySheet::reset(index); + default: + break; + } + // index-dependent + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) + return false; + + // index-dependent + switch (toolBoxProperty) { + case PropertyCurrentItemName: + setProperty(index, QString()); + break; + case PropertyCurrentItemToolTip: + m_pageToData[currentWidget].tooltip = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentItemText: + m_pageToData[currentWidget].text = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentItemIcon: + m_pageToData[currentWidget].icon = qdesigner_internal::PropertySheetIconValue(); + setProperty(index, QIcon()); + break; + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } + return true; +} + +bool QToolBoxWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + switch (toolBoxPropertyFromName(propertyName)) { + case PropertyCurrentItemText: + case PropertyCurrentItemName: + case PropertyCurrentItemToolTip: + case PropertyCurrentItemIcon: + return false; + default: + break; + } + return true; +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_toolbox_p.h b/designer/lib/shared/qdesigner_toolbox_p.h new file mode 100644 index 0000000..33de256 --- /dev/null +++ b/designer/lib/shared/qdesigner_toolbox_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TOOLBOX_H +#define QDESIGNER_TOOLBOX_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +class QToolBox; + +class QAction; +class QMenu; + +class QDESIGNER_SHARED_EXPORT QToolBoxHelper : public QObject +{ + Q_OBJECT + + explicit QToolBoxHelper(QToolBox *toolbox); +public: + // Install helper on QToolBox + static void install(QToolBox *toolbox); + static QToolBoxHelper *helperOf(const QToolBox *toolbox); + // Convenience to add a menu on a toolbox + static QMenu *addToolBoxContextMenuActions(const QToolBox *toolbox, QMenu *popup); + + QPalette::ColorRole currentItemBackgroundRole() const; + void setCurrentItemBackgroundRole(QPalette::ColorRole role); + + bool eventFilter(QObject *watched, QEvent *event); + // Add context menu and return page submenu or 0. + + QMenu *addContextMenuActions(QMenu *popup) const; + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + void changeOrder(); + +private: + QToolBox *m_toolbox; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + QAction *m_actionChangePageOrder; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the page properties +class QDESIGNER_SHARED_EXPORT QToolBoxWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QToolBoxWidgetPropertySheet(QToolBox *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + enum ToolBoxProperty { PropertyCurrentItemText, PropertyCurrentItemName, PropertyCurrentItemIcon, + PropertyCurrentItemToolTip, PropertyTabSpacing, PropertyToolBoxNone }; + + static ToolBoxProperty toolBoxPropertyFromName(const QString &name); + QToolBox *m_toolBox; + struct PageData + { + qdesigner_internal::PropertySheetStringValue text; + qdesigner_internal::PropertySheetStringValue tooltip; + qdesigner_internal::PropertySheetIconValue icon; + }; + QMap m_pageToData; +}; + +typedef QDesignerPropertySheetFactory QToolBoxWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_TOOLBOX_H diff --git a/designer/lib/shared/qdesigner_utils.cpp b/designer/lib/shared/qdesigner_utils.cpp new file mode 100644 index 0000000..cf4ea17 --- /dev/null +++ b/designer/lib/shared/qdesigner_utils.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_utils_p.h" +#include "qdesigner_propertycommand_p.h" +#include "abstractformbuilder.h" +#include "formwindowbase_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal +{ + QDESIGNER_SHARED_EXPORT void designerWarning(const QString &message) + { + qWarning("Designer: %s", qPrintable(message)); + } + + void reloadTreeItem(DesignerIconCache *iconCache, QTreeWidgetItem *item) + { + if (!item) + return; + + for (int c = 0; c < item->columnCount(); c++) { + const QVariant v = item->data(c, Qt::DecorationPropertyRole); + if (qVariantCanConvert(v)) + item->setIcon(c, iconCache->icon(qVariantValue(v))); + } + } + + void reloadListItem(DesignerIconCache *iconCache, QListWidgetItem *item) + { + if (!item) + return; + + const QVariant v = item->data(Qt::DecorationPropertyRole); + if (qVariantCanConvert(v)) + item->setIcon(iconCache->icon(qVariantValue(v))); + } + + void reloadTableItem(DesignerIconCache *iconCache, QTableWidgetItem *item) + { + if (!item) + return; + + const QVariant v = item->data(Qt::DecorationPropertyRole); + if (qVariantCanConvert(v)) + item->setIcon(iconCache->icon(qVariantValue(v))); + } + + void reloadIconResources(DesignerIconCache *iconCache, QObject *object) + { + if (QListWidget *listWidget = qobject_cast(object)) { + for (int i = 0; i < listWidget->count(); i++) + reloadListItem(iconCache, listWidget->item(i)); + } else if (QComboBox *comboBox = qobject_cast(object)) { + for (int i = 0; i < comboBox->count(); i++) { + const QVariant v = comboBox->itemData(i, Qt::DecorationPropertyRole); + if (qVariantCanConvert(v)) { + QIcon icon = iconCache->icon(qVariantValue(v)); + comboBox->setItemIcon(i, icon); + comboBox->setItemData(i, icon); + } + } + } else if (QTreeWidget *treeWidget = qobject_cast(object)) { + reloadTreeItem(iconCache, treeWidget->headerItem()); + QQueue itemsQueue; + for (int i = 0; i < treeWidget->topLevelItemCount(); i++) + itemsQueue.enqueue(treeWidget->topLevelItem(i)); + while (!itemsQueue.isEmpty()) { + QTreeWidgetItem *item = itemsQueue.dequeue(); + for (int i = 0; i < item->childCount(); i++) + itemsQueue.enqueue(item->child(i)); + reloadTreeItem(iconCache, item); + } + } else if (QTableWidget *tableWidget = qobject_cast(object)) { + const int columnCount = tableWidget->columnCount(); + const int rowCount = tableWidget->rowCount(); + for (int c = 0; c < columnCount; c++) + reloadTableItem(iconCache, tableWidget->horizontalHeaderItem(c)); + for (int r = 0; r < rowCount; r++) + reloadTableItem(iconCache, tableWidget->verticalHeaderItem(r)); + for (int c = 0; c < columnCount; c++) + for (int r = 0; r < rowCount; r++) + reloadTableItem(iconCache, tableWidget->item(r, c)); + } + } + + // ------------- DesignerMetaEnum + DesignerMetaEnum::DesignerMetaEnum(const QString &name, const QString &scope, const QString &separator) : + MetaEnum(name, scope, separator) + { + } + + + QString DesignerMetaEnum::toString(int value, SerializationMode sm, bool *ok) const + { + // find value + bool valueOk; + const QString item = valueToKey(value, &valueOk); + if (ok) + *ok = valueOk; + + if (!valueOk || sm == NameOnly) + return item; + + QString qualifiedItem; + appendQualifiedName(item, qualifiedItem); + return qualifiedItem; + } + + QString DesignerMetaEnum::messageToStringFailed(int value) const + { + return QCoreApplication::translate("DesignerMetaEnum", "%1 is not a valid enumeration value of '%2'.").arg(value).arg(name()); + } + + QString DesignerMetaEnum::messageParseFailed(const QString &s) const + { + return QCoreApplication::translate("DesignerMetaEnum", "'%1' could not be converted to an enumeration value of type '%2'.").arg(s).arg(name()); + } + // -------------- DesignerMetaFlags + DesignerMetaFlags::DesignerMetaFlags(const QString &name, const QString &scope, const QString &separator) : + MetaEnum(name, scope, separator) + { + } + + QStringList DesignerMetaFlags::flags(int ivalue) const + { + typedef MetaEnum::KeyToValueMap::const_iterator KeyToValueMapIterator; + QStringList rc; + const uint v = static_cast(ivalue); + const KeyToValueMapIterator cend = keyToValueMap().constEnd(); + for (KeyToValueMapIterator it = keyToValueMap().constBegin();it != cend; ++it ) { + const uint itemValue = it.value(); + // Check for equality first as flag values can be 0 or -1, too. Takes preference over a bitwise flag + if (v == itemValue) { + rc.clear(); + rc.push_back(it.key()); + return rc; + } + // Do not add 0-flags (None-flags) + if (itemValue) + if ((v & itemValue) == itemValue) + rc.push_back(it.key()); + } + return rc; + } + + + QString DesignerMetaFlags::toString(int value, SerializationMode sm) const + { + const QStringList flagIds = flags(value); + if (flagIds.empty()) + return QString(); + + const QChar delimiter = QLatin1Char('|'); + QString rc; + const QStringList::const_iterator cend = flagIds.constEnd(); + for (QStringList::const_iterator it = flagIds.constBegin(); it != cend; ++it) { + if (!rc.isEmpty()) + rc += delimiter ; + if (sm == FullyQualified) + appendQualifiedName(*it, rc); + else + rc += *it; + } + return rc; + } + + + int DesignerMetaFlags::parseFlags(const QString &s, bool *ok) const + { + if (s.isEmpty()) { + if (ok) + *ok = true; + return 0; + } + uint flags = 0; + bool valueOk = true; + QStringList keys = s.split(QString(QLatin1Char('|'))); + const QStringList::iterator cend = keys.end(); + for (QStringList::iterator it = keys.begin(); it != cend; ++it) { + const uint flagValue = keyToValue(*it, &valueOk); + if (!valueOk) { + flags = 0; + break; + } + flags |= flagValue; + } + if (ok) + *ok = valueOk; + return static_cast(flags); + } + + QString DesignerMetaFlags::messageParseFailed(const QString &s) const + { + return QCoreApplication::translate("DesignerMetaFlags", "'%1' could not be converted to a flag value of type '%2'.").arg(s).arg(name()); + } + + // ---------- PropertySheetEnumValue + + PropertySheetEnumValue::PropertySheetEnumValue(int v, const DesignerMetaEnum &me) : + value(v), + metaEnum(me) + { + } + PropertySheetEnumValue::PropertySheetEnumValue() : + value(0) + { + } + + // ---------------- PropertySheetFlagValue + PropertySheetFlagValue::PropertySheetFlagValue(int v, const DesignerMetaFlags &mf) : + value(v), + metaFlags(mf) + { + } + + PropertySheetFlagValue::PropertySheetFlagValue() : + value(0) + { + } + + // ---------------- PropertySheetPixmapValue + PropertySheetPixmapValue::PropertySheetPixmapValue(const QString &path) : m_path(path) + { + } + + PropertySheetPixmapValue::PropertySheetPixmapValue() + { + } + + PropertySheetPixmapValue::PixmapSource PropertySheetPixmapValue::getPixmapSource(QDesignerFormEditorInterface *core, const QString & path) + { + if (const QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core)) + return lang->isLanguageResource(path) ? LanguageResourcePixmap : FilePixmap; + return path.startsWith(QLatin1Char(':')) ? ResourcePixmap : FilePixmap; + } + + int PropertySheetPixmapValue::compare(const PropertySheetPixmapValue &other) const + { + return m_path.compare(other.m_path); + } + + QString PropertySheetPixmapValue::path() const + { + return m_path; + } + + void PropertySheetPixmapValue::setPath(const QString &path) + { + if (m_path == path) + return; + m_path = path; + } + + // ---------- PropertySheetIconValue + PropertySheetIconValue::PropertySheetIconValue(const PropertySheetPixmapValue &pixmap) + { + setPixmap(QIcon::Normal, QIcon::Off, pixmap); + } + + PropertySheetIconValue::PropertySheetIconValue() + { + } + + bool PropertySheetIconValue::equals(const PropertySheetIconValue &rhs) const + { + return m_paths == rhs.m_paths; + } + + bool PropertySheetIconValue::operator<(const PropertySheetIconValue &other) const + { + QMapIterator itThis(m_paths); + QMapIterator itOther(other.m_paths); + while (itThis.hasNext() && itOther.hasNext()) { + const ModeStateKey thisPair = itThis.next().key(); + const ModeStateKey otherPair = itOther.next().key(); + if (thisPair < otherPair) + return true; + else if (otherPair < thisPair) + return false; + const int crc = itThis.value().compare(itOther.value()); + if (crc < 0) + return true; + if (crc > 0) + return false; + } + if (itOther.hasNext()) + return true; + return false; + } + + PropertySheetPixmapValue PropertySheetIconValue::pixmap(QIcon::Mode mode, QIcon::State state) const + { + const ModeStateKey pair = qMakePair(mode, state); + return m_paths.value(pair); + } + + void PropertySheetIconValue::setPixmap(QIcon::Mode mode, QIcon::State state, const PropertySheetPixmapValue &pixmap) + { + const ModeStateKey pair = qMakePair(mode, state); + if (pixmap.path().isEmpty()) + m_paths.remove(pair); + else + m_paths.insert(pair, pixmap); + } + + QPixmap DesignerPixmapCache::pixmap(const PropertySheetPixmapValue &value) const + { + QMap::const_iterator it = m_cache.constFind(value); + if (it != m_cache.constEnd()) + return it.value(); + + QPixmap pix = QPixmap(value.path()); + m_cache.insert(value, pix); + return pix; + } + + void DesignerPixmapCache::clear() + { + m_cache.clear(); + } + + DesignerPixmapCache::DesignerPixmapCache(QObject *parent) + : QObject(parent) + { + } + + QIcon DesignerIconCache::icon(const PropertySheetIconValue &value) const + { + QMap::const_iterator it = m_cache.constFind(value); + if (it != m_cache.constEnd()) + return it.value(); + + QIcon icon; + QMap, PropertySheetPixmapValue> paths = value.paths(); + QMapIterator, PropertySheetPixmapValue> itPath(paths); + while (itPath.hasNext()) { + QPair pair = itPath.next().key(); + icon.addFile(itPath.value().path(), QSize(), pair.first, pair.second); + } + m_cache.insert(value, icon); + return icon; + } + + void DesignerIconCache::clear() + { + m_cache.clear(); + } + + DesignerIconCache::DesignerIconCache(DesignerPixmapCache *pixmapCache, QObject *parent) + : QObject(parent), + m_pixmapCache(pixmapCache) + { + + } + + PropertySheetStringValue::PropertySheetStringValue(const QString &value, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(value), m_translatable(translatable), m_disambiguation(disambiguation), m_comment(comment) + { } + + QString PropertySheetStringValue::value() const + { + return m_value; + } + + void PropertySheetStringValue::setValue(const QString &value) + { + m_value = value; + } + + bool PropertySheetStringValue::translatable() const + { + return m_translatable; + } + + void PropertySheetStringValue::setTranslatable(bool translatable) + { + m_translatable = translatable; + } + + QString PropertySheetStringValue::disambiguation() const + { + return m_disambiguation; + } + + void PropertySheetStringValue::setDisambiguation(const QString &disambiguation) + { + m_disambiguation = disambiguation; + } + + QString PropertySheetStringValue::comment() const + { + return m_comment; + } + + void PropertySheetStringValue::setComment(const QString &comment) + { + m_comment = comment; + } + + bool PropertySheetStringValue::equals(const PropertySheetStringValue &rhs) const + { + return (m_value == rhs.m_value) && (m_translatable == rhs.m_translatable) + && (m_disambiguation == rhs.m_disambiguation) && (m_comment == rhs.m_comment); + } + + PropertySheetKeySequenceValue::PropertySheetKeySequenceValue(const QKeySequence &value, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(value), + m_standardKey(QKeySequence::UnknownKey), + m_translatable(translatable), + m_disambiguation(disambiguation), + m_comment(comment) + { } + + PropertySheetKeySequenceValue::PropertySheetKeySequenceValue(const QKeySequence::StandardKey &standardKey, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(QKeySequence(standardKey)), + m_standardKey(standardKey), + m_translatable(translatable), + m_disambiguation(disambiguation), + m_comment(comment) + { } + + QKeySequence PropertySheetKeySequenceValue::value() const + { + return m_value; + } + + void PropertySheetKeySequenceValue::setValue(const QKeySequence &value) + { + m_value = value; + m_standardKey = QKeySequence::UnknownKey; + } + + QKeySequence::StandardKey PropertySheetKeySequenceValue::standardKey() const + { + return m_standardKey; + } + + void PropertySheetKeySequenceValue::setStandardKey(const QKeySequence::StandardKey &standardKey) + { + m_value = QKeySequence(standardKey); + m_standardKey = standardKey; + } + + bool PropertySheetKeySequenceValue::isStandardKey() const + { + return m_standardKey != QKeySequence::UnknownKey; + } + + QString PropertySheetKeySequenceValue::comment() const + { + return m_comment; + } + + void PropertySheetKeySequenceValue::setComment(const QString &comment) + { + m_comment = comment; + } + + QString PropertySheetKeySequenceValue::disambiguation() const + { + return m_disambiguation; + } + + void PropertySheetKeySequenceValue::setDisambiguation(const QString &disambiguation) + { + m_disambiguation = disambiguation; + } + + bool PropertySheetKeySequenceValue::translatable() const + { + return m_translatable; + } + + void PropertySheetKeySequenceValue::setTranslatable(bool translatable) + { + m_translatable = translatable; + } + + bool PropertySheetKeySequenceValue::equals(const PropertySheetKeySequenceValue &rhs) const + { + return (m_value == rhs.m_value) && (m_standardKey == rhs.m_standardKey) + && (m_translatable == rhs.m_translatable) && (m_disambiguation == rhs.m_disambiguation) && (m_comment == rhs.m_comment); + } + + class StateMap + { + public: + StateMap() + { + m_stateToFlag.insert(qMakePair(QIcon::Normal, QIcon::Off), 0x01); + m_stateToFlag.insert(qMakePair(QIcon::Normal, QIcon::On), 0x02); + m_stateToFlag.insert(qMakePair(QIcon::Disabled, QIcon::Off), 0x04); + m_stateToFlag.insert(qMakePair(QIcon::Disabled, QIcon::On), 0x08); + m_stateToFlag.insert(qMakePair(QIcon::Active, QIcon::Off), 0x10); + m_stateToFlag.insert(qMakePair(QIcon::Active, QIcon::On), 0x20); + m_stateToFlag.insert(qMakePair(QIcon::Selected, QIcon::Off), 0x40); + m_stateToFlag.insert(qMakePair(QIcon::Selected, QIcon::On), 0x80); + + m_flagToState.insert(0x01, qMakePair(QIcon::Normal, QIcon::Off)); + m_flagToState.insert(0x02, qMakePair(QIcon::Normal, QIcon::On)); + m_flagToState.insert(0x04, qMakePair(QIcon::Disabled, QIcon::Off)); + m_flagToState.insert(0x08, qMakePair(QIcon::Disabled, QIcon::On)); + m_flagToState.insert(0x10, qMakePair(QIcon::Active, QIcon::Off)); + m_flagToState.insert(0x20, qMakePair(QIcon::Active, QIcon::On)); + m_flagToState.insert(0x40, qMakePair(QIcon::Selected, QIcon::Off)); + m_flagToState.insert(0x80, qMakePair(QIcon::Selected, QIcon::On)); + } + uint flag(const QPair &pair) const + { + return m_stateToFlag.value(pair); + } + QPair state(uint flag) const + { + return m_flagToState.value(flag); + } + private: + QMap, uint > m_stateToFlag; + QMap > m_flagToState; + }; + + Q_GLOBAL_STATIC(StateMap, stateMap) + + uint PropertySheetIconValue::mask() const + { + uint flags = 0; + QMapIterator itPath(m_paths); + while (itPath.hasNext()) + flags |= stateMap()->flag(itPath.next().key()); + return flags; + } + + uint PropertySheetIconValue::compare(const PropertySheetIconValue &other) const + { + uint diffMask = mask() | other.mask(); + for (int i = 0; i < 8; i++) { + uint flag = 1 << i; + if (diffMask & flag) { // if state is set in both icons, compare the values + const ModeStateKey state = stateMap()->state(flag); + if (pixmap(state.first, state.second) == other.pixmap(state.first, state.second)) + diffMask &= ~flag; + } + } + return diffMask; + } + + void PropertySheetIconValue::assign(const PropertySheetIconValue &other, uint mask) + { + for (int i = 0; i < 8; i++) { + uint flag = 1 << i; + if (mask & flag) { + const ModeStateKey state = stateMap()->state(flag); + setPixmap(state.first, state.second, other.pixmap(state.first, state.second)); + } + } + } + + PropertySheetIconValue::ModeStateToPixmapMap PropertySheetIconValue::paths() const + { + return m_paths; + } + + QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand *createTextPropertyCommand(const QString &propertyName, const QString &text, QObject *object, QDesignerFormWindowInterface *fw) + { + if (text.isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(object, propertyName); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(object, propertyName, text); + return cmd; + } + + QDESIGNER_SHARED_EXPORT QAction *preferredEditAction(QDesignerFormEditorInterface *core, QWidget *managedWidget) + { + QAction *action = 0; + if (const QDesignerTaskMenuExtension *taskMenu = qt_extension(core->extensionManager(), managedWidget)) { + action = taskMenu->preferredEditAction(); + if (!action) { + const QList actions = taskMenu->taskActions(); + if (!actions.isEmpty()) + action = actions.first(); + } + } + if (!action) { + if (const QDesignerTaskMenuExtension *taskMenu = qobject_cast( + core->extensionManager()->extension(managedWidget, QLatin1String("QDesignerInternalTaskMenuExtension")))) { + action = taskMenu->preferredEditAction(); + if (!action) { + const QList actions = taskMenu->taskActions(); + if (!actions.isEmpty()) + action = actions.first(); + } + } + } + return action; + } + + QDESIGNER_SHARED_EXPORT bool runUIC(const QString &fileName, UIC_Mode mode, QByteArray& ba, QString &errorMessage) + { + QStringList argv; + QString binary = QLibraryInfo::location(QLibraryInfo::BinariesPath); + binary += QDir::separator(); + switch (mode) { + case UIC_GenerateCode: + binary += QLatin1String("uic"); + break; + case UIC_ConvertV3: + binary += QLatin1String("uic3"); + argv += QLatin1String("-convert"); + break; + } + argv += fileName; + QProcess uic; + uic.start(binary, argv); + if (!uic.waitForStarted()) { + errorMessage = QApplication::translate("Designer", "Unable to launch %1.").arg(binary); + return false; + } + if (!uic.waitForFinished()) { + errorMessage = QApplication::translate("Designer", "%1 timed out.").arg(binary); + return false; + } + if (uic.exitCode()) { + errorMessage = QString::fromAscii(uic.readAllStandardError()); + return false; + } + ba = uic.readAllStandardOutput(); + return true; + } + + QDESIGNER_SHARED_EXPORT QString qtify(const QString &name) + { + QString qname = name; + + Q_ASSERT(qname.isEmpty() == false); + + + if (qname.count() > 1 && qname.at(1).isUpper()) { + const QChar first = qname.at(0); + if (first == QLatin1Char('Q') || first == QLatin1Char('K')) + qname.remove(0, 1); + } + + const int len = qname.count(); + for (int i = 0; i < len && qname.at(i).isUpper(); i++) + qname[i] = qname.at(i).toLower(); + + return qname; + } + + // --------------- UpdateBlocker + UpdateBlocker::UpdateBlocker(QWidget *w) : + m_widget(w), + m_enabled(w->updatesEnabled() && w->isVisible()) + { + if (m_enabled) + m_widget->setUpdatesEnabled(false); + } + + UpdateBlocker::~UpdateBlocker() + { + if (m_enabled) + m_widget->setUpdatesEnabled(true); + } + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_utils_p.h b/designer/lib/shared/qdesigner_utils_p.h new file mode 100644 index 0000000..f209939 --- /dev/null +++ b/designer/lib/shared/qdesigner_utils_p.h @@ -0,0 +1,481 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_UTILS_H +#define QDESIGNER_UTILS_H + +#include "shared_global_p.h" + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +class QDesignerFormWindowCommand; +class DesignerIconCache; +class FormWindowBase; + + +QDESIGNER_SHARED_EXPORT void designerWarning(const QString &message); + +QDESIGNER_SHARED_EXPORT void reloadIconResources(DesignerIconCache *iconCache, QObject *object); + +/* Flag/Enumeration helpers for the property sheet: Enumeration or flag values are returned by the property sheet + * as a pair of meta type and integer value. + * The meta type carries all the information required for the property editor and serialization + * by the form builders (names, etc). + * Note that the property editor uses unqualified names ("Cancel") while the form builder serialization (uic) + * requires the whole string + * ("QDialogButtonBox::Cancel" or "com.trolltech.qt.gui.QDialogButtonBox.StandardButton.Cancel").*/ + +/* --------- MetaEnum: Base class representing a QMetaEnum with lookup functions + * in both ways. Template of int type since unsigned is more suitable for flags. + * The keyToValue() is ignorant of scopes, it can handle fully qualified or unqualified names. */ + +template +class MetaEnum +{ +public: + typedef QMap KeyToValueMap; + + MetaEnum(const QString &name, const QString &scope, const QString &separator); + MetaEnum() {} + void addKey(IntType value, const QString &name); + + QString valueToKey(IntType value, bool *ok = 0) const; + // Ignorant of scopes. + IntType keyToValue(QString key, bool *ok = 0) const; + + const QString &name() const { return m_name; } + const QString &scope() const { return m_scope; } + const QString &separator() const { return m_separator; } + + const QStringList &keys() const { return m_keys; } + const KeyToValueMap &keyToValueMap() const { return m_keyToValueMap; } + +protected: + void appendQualifiedName(const QString &key, QString &target) const; + +private: + QString m_name; + QString m_scope; + QString m_separator; + KeyToValueMap m_keyToValueMap; + QStringList m_keys; +}; + +template +MetaEnum::MetaEnum(const QString &name, const QString &scope, const QString &separator) : + m_name(name), + m_scope(scope), + m_separator(separator) +{ +} + +template +void MetaEnum::addKey(IntType value, const QString &name) +{ + m_keyToValueMap.insert(name, value); + m_keys.append(name); +} + +template +QString MetaEnum::valueToKey(IntType value, bool *ok) const +{ + const QString rc = m_keyToValueMap.key(value); + if (ok) + *ok = !rc.isEmpty(); + return rc; +} + +template +IntType MetaEnum::keyToValue(QString key, bool *ok) const +{ + if (!m_scope.isEmpty() && key.startsWith(m_scope)) + key.remove(0, m_scope.size() + m_separator.size()); + const Q_TYPENAME KeyToValueMap::const_iterator it = m_keyToValueMap.find(key); + const bool found = it != m_keyToValueMap.constEnd(); + if (ok) + *ok = found; + return found ? it.value() : IntType(0); +} + +template +void MetaEnum::appendQualifiedName(const QString &key, QString &target) const +{ + if (!m_scope.isEmpty()) { + target += m_scope; + target += m_separator; + } + target += key; +} + +// -------------- DesignerMetaEnum: Meta type for enumerations + +class QDESIGNER_SHARED_EXPORT DesignerMetaEnum : public MetaEnum +{ +public: + DesignerMetaEnum(const QString &name, const QString &scope, const QString &separator); + DesignerMetaEnum() {} + + enum SerializationMode { FullyQualified, NameOnly }; + QString toString(int value, SerializationMode sm, bool *ok = 0) const; + + QString messageToStringFailed(int value) const; + QString messageParseFailed(const QString &s) const; + + // parse a string (ignorant of scopes) + int parseEnum(const QString &s, bool *ok = 0) const { return keyToValue(s, ok); } +}; + +// -------------- DesignerMetaFlags: Meta type for flags. +// Note that while the handling of flags is done using unsigned integers, the actual values returned +// by the property system are integers. + +class QDESIGNER_SHARED_EXPORT DesignerMetaFlags : public MetaEnum +{ +public: + DesignerMetaFlags(const QString &name, const QString &scope, const QString &separator); + DesignerMetaFlags() {} + + enum SerializationMode { FullyQualified, NameOnly }; + QString toString(int value, SerializationMode sm) const; + QStringList flags(int value) const; + + QString messageParseFailed(const QString &s) const; + // parse a string (ignorant of scopes) + int parseFlags(const QString &s, bool *ok = 0) const; +}; + +// -------------- EnumValue: Returned by the property sheet for enumerations + +struct QDESIGNER_SHARED_EXPORT PropertySheetEnumValue +{ + PropertySheetEnumValue(int v, const DesignerMetaEnum &me); + PropertySheetEnumValue(); + + int value; + DesignerMetaEnum metaEnum; +}; + +// -------------- FlagValue: Returned by the property sheet for flags + +struct QDESIGNER_SHARED_EXPORT PropertySheetFlagValue +{ + PropertySheetFlagValue(int v, const DesignerMetaFlags &mf); + PropertySheetFlagValue(); + + int value; + DesignerMetaFlags metaFlags; +}; + +// -------------- PixmapValue: Returned by the property sheet for pixmaps +class QDESIGNER_SHARED_EXPORT PropertySheetPixmapValue +{ +public: + PropertySheetPixmapValue(const QString &path); + PropertySheetPixmapValue(); + + bool operator==(const PropertySheetPixmapValue &other) const { return compare(other) == 0; } + bool operator!=(const PropertySheetPixmapValue &other) const { return compare(other) != 0; } + bool operator<(const PropertySheetPixmapValue &other) const { return compare(other) < 0; } + + // Check where a pixmap comes from + enum PixmapSource { LanguageResourcePixmap , ResourcePixmap, FilePixmap }; + static PixmapSource getPixmapSource(QDesignerFormEditorInterface *core, const QString & path); + + PixmapSource pixmapSource(QDesignerFormEditorInterface *core) const { return getPixmapSource(core, m_path); } + + QString path() const; + void setPath(const QString &path); // passing the empty path resets the pixmap + + int compare(const PropertySheetPixmapValue &other) const; + +private: + QString m_path; +}; + +// -------------- IconValue: Returned by the property sheet for icons + +class QDESIGNER_SHARED_EXPORT PropertySheetIconValue +{ + public: + PropertySheetIconValue(const PropertySheetPixmapValue &pixmap); + PropertySheetIconValue(); + + bool operator==(const PropertySheetIconValue &other) const { return equals(other); } + bool operator!=(const PropertySheetIconValue &other) const { return !equals(other); } + bool operator<(const PropertySheetIconValue &other) const; + + PropertySheetPixmapValue pixmap(QIcon::Mode mode, QIcon::State state) const; + void setPixmap(QIcon::Mode mode, QIcon::State state, const PropertySheetPixmapValue &path); // passing the empty path resets the pixmap + + uint mask() const; + uint compare(const PropertySheetIconValue &other) const; + void assign(const PropertySheetIconValue &other, uint mask); + + typedef QPair ModeStateKey; + typedef QMap ModeStateToPixmapMap; + + ModeStateToPixmapMap paths() const; + +private: + bool equals(const PropertySheetIconValue &rhs) const; + + ModeStateToPixmapMap m_paths; +}; + +class QDESIGNER_SHARED_EXPORT DesignerPixmapCache : public QObject +{ + Q_OBJECT +public: + DesignerPixmapCache(QObject *parent = 0); + QPixmap pixmap(const PropertySheetPixmapValue &value) const; + void clear(); +signals: + void reloaded(); +private: + mutable QMap m_cache; + friend class FormWindowBase; +}; + +class QDESIGNER_SHARED_EXPORT DesignerIconCache : public QObject +{ + Q_OBJECT +public: + explicit DesignerIconCache(DesignerPixmapCache *pixmapCache, QObject *parent = 0); + QIcon icon(const PropertySheetIconValue &value) const; + void clear(); +signals: + void reloaded(); +private: + mutable QMap m_cache; + DesignerPixmapCache *m_pixmapCache; + friend class FormWindowBase; +}; + +// -------------- StringValue: Returned by the property sheet for strings +class QDESIGNER_SHARED_EXPORT PropertySheetStringValue +{ +public: + explicit PropertySheetStringValue(const QString &value = QString(), + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + + bool operator==(const PropertySheetStringValue &other) const { return equals(other); } + bool operator!=(const PropertySheetStringValue &other) const { return !equals(other); } + + QString value() const; + void setValue(const QString &value); + bool translatable() const; + void setTranslatable(bool translatable); + QString disambiguation() const; + void setDisambiguation(const QString &disambiguation); + QString comment() const; + void setComment(const QString &comment); + +private: + bool equals(const PropertySheetStringValue &rhs) const; + + QString m_value; + bool m_translatable; + QString m_disambiguation; + QString m_comment; +}; + + + +// -------------- StringValue: Returned by the property sheet for strings +class QDESIGNER_SHARED_EXPORT PropertySheetKeySequenceValue +{ +public: + explicit PropertySheetKeySequenceValue(const QKeySequence &value = QKeySequence(), + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + explicit PropertySheetKeySequenceValue(const QKeySequence::StandardKey &standardKey, + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + + bool operator==(const PropertySheetKeySequenceValue &other) const { return equals(other); } + bool operator!=(const PropertySheetKeySequenceValue &other) const { return !equals(other); } + + QKeySequence value() const; + void setValue(const QKeySequence &value); + QKeySequence::StandardKey standardKey() const; + void setStandardKey(const QKeySequence::StandardKey &standardKey); + bool isStandardKey() const; + + bool translatable() const; + void setTranslatable(bool translatable); + QString disambiguation() const; + void setDisambiguation(const QString &disambiguation); + QString comment() const; + void setComment(const QString &comment); + +private: + bool equals(const PropertySheetKeySequenceValue &rhs) const; + + QKeySequence m_value; + QKeySequence::StandardKey m_standardKey; + bool m_translatable; + QString m_disambiguation; + QString m_comment; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + + +// NOTE: Do not move this code, needed for GCC 3.3 +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetEnumValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetFlagValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetPixmapValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetIconValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetStringValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetKeySequenceValue) + + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + +// Create a command to change a text property (that is, create a reset property command if the text is empty) +QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand *createTextPropertyCommand(const QString &propertyName, const QString &text, QObject *object, QDesignerFormWindowInterface *fw); + +// Returns preferred task menu action for managed widget +QDESIGNER_SHARED_EXPORT QAction *preferredEditAction(QDesignerFormEditorInterface *core, QWidget *managedWidget); + +// Convenience to run UIC +enum UIC_Mode { UIC_GenerateCode, UIC_ConvertV3 }; +QDESIGNER_SHARED_EXPORT bool runUIC(const QString &fileName, UIC_Mode mode, QByteArray& ba, QString &errorMessage); + +// Find a suitable variable name for a class. +QDESIGNER_SHARED_EXPORT QString qtify(const QString &name); + +/* UpdateBlocker: Blocks the updates of the widget passed on while in scope. + * Does nothing if the incoming widget already has updatesEnabled==false + * which is important to avoid side-effects when putting it into QStackedLayout. */ + +class QDESIGNER_SHARED_EXPORT UpdateBlocker { + Q_DISABLE_COPY(UpdateBlocker) + +public: + UpdateBlocker(QWidget *w); + ~UpdateBlocker(); + +private: + QWidget *m_widget; + const bool m_enabled; +}; + +namespace Utils { + +inline int valueOf(const QVariant &value, bool *ok = 0) +{ + if (qVariantCanConvert(value)) { + if (ok) + *ok = true; + return qVariantValue(value).value; + } + else if (qVariantCanConvert(value)) { + if (ok) + *ok = true; + return qVariantValue(value).value; + } + return value.toInt(ok); +} + +inline bool isObjectAncestorOf(QObject *ancestor, QObject *child) +{ + QObject *obj = child; + while (obj != 0) { + if (obj == ancestor) + return true; + obj = obj->parent(); + } + return false; +} + +inline bool isCentralWidget(QDesignerFormWindowInterface *fw, QWidget *widget) +{ + if (! fw || ! widget) + return false; + + if (widget == fw->mainContainer()) + return true; + + // ### generalize for other containers + if (QMainWindow *mw = qobject_cast(fw->mainContainer())) { + return mw->centralWidget() == widget; + } + + return false; +} + +} // namespace Utils + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_UTILS_H diff --git a/designer/lib/shared/qdesigner_widget.cpp b/designer/lib/shared/qdesigner_widget.cpp new file mode 100644 index 0000000..9bbe584 --- /dev/null +++ b/designer/lib/shared/qdesigner_widget.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_widget_p.h" +#include "formwindowbase_p.h" +#include "grid_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* QDesignerDialog / QDesignerWidget are used to paint a grid on QDialog and QWidget main containers + * and container extension pages. + * The paint routines work as follows: + * We need to clean the background here (to make the parent grid disappear in case we are a container page + * and to make palette background settings take effect), + * which would normally break style sheets with background settings. + * So, we manually make the style paint after cleaning. On top comes the grid + * In addition, this code works around + * the QStyleSheetStyle setting Qt::WA_StyledBackground to false for subclasses of QWidget. + */ + +QDesignerDialog::QDesignerDialog(QDesignerFormWindowInterface *fw, QWidget *parent) : + QDialog(parent), + m_formWindow(qobject_cast(fw)) +{ +} + +void QDesignerDialog::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + QStyleOption opt; + opt.initFrom(this); + p.fillRect(e->rect(), palette().brush(backgroundRole())); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + if (m_formWindow && m_formWindow->gridVisible()) + m_formWindow->designerGrid().paint(p, this, e); +} + +QDesignerWidget::QDesignerWidget(QDesignerFormWindowInterface* formWindow, QWidget *parent) : + QWidget(parent), + m_formWindow(qobject_cast(formWindow)) +{ +} + +QDesignerWidget::~QDesignerWidget() +{ +} + +QDesignerFormWindowInterface* QDesignerWidget::formWindow() const +{ + return m_formWindow; +} + +void QDesignerWidget::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + QStyleOption opt; + opt.initFrom(this); + p.fillRect(e->rect(), palette().brush(backgroundRole())); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + if (m_formWindow && m_formWindow->gridVisible()) + m_formWindow->designerGrid().paint(p, this, e); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_widget_p.h b/designer/lib/shared/qdesigner_widget_p.h new file mode 100644 index 0000000..b84fc18 --- /dev/null +++ b/designer/lib/shared/qdesigner_widget_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_WIDGET_H +#define QDESIGNER_WIDGET_H + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + class FormWindowBase; +} + +class QDESIGNER_SHARED_EXPORT QDesignerWidget : public QWidget +{ + Q_OBJECT +public: + explicit QDesignerWidget(QDesignerFormWindowInterface* formWindow, QWidget *parent = 0); + virtual ~QDesignerWidget(); + + QDesignerFormWindowInterface* formWindow() const; + + void updatePixmap(); + + virtual QSize minimumSizeHint() const + { return QWidget::minimumSizeHint().expandedTo(QSize(16, 16)); } + +protected: + virtual void paintEvent(QPaintEvent *e); + +private: + qdesigner_internal::FormWindowBase* m_formWindow; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerDialog : public QDialog +{ + Q_OBJECT +public: + explicit QDesignerDialog(QDesignerFormWindowInterface *fw, QWidget *parent); + + virtual QSize minimumSizeHint() const + { return QWidget::minimumSizeHint().expandedTo(QSize(16, 16)); } + +protected: + void paintEvent(QPaintEvent *e); + +private: + qdesigner_internal::FormWindowBase* m_formWindow; +}; + +class QDESIGNER_SHARED_EXPORT Line : public QFrame +{ + Q_OBJECT + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) +public: + explicit Line(QWidget *parent) : QFrame(parent) + { setAttribute(Qt::WA_MouseNoMask); setFrameStyle(HLine | Sunken); } + + inline void setOrientation(Qt::Orientation orient) + { setFrameShape(orient == Qt::Horizontal ? HLine : VLine); } + + inline Qt::Orientation orientation() const + { return frameShape() == HLine ? Qt::Horizontal : Qt::Vertical; } +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGET_H diff --git a/designer/lib/shared/qdesigner_widgetbox.cpp b/designer/lib/shared/qdesigner_widgetbox.cpp new file mode 100644 index 0000000..e196e08 --- /dev/null +++ b/designer/lib/shared/qdesigner_widgetbox.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_widgetbox_p.h" +#include "qdesigner_utils_p.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +QDesignerWidgetBox::QDesignerWidgetBox(QWidget *parent, Qt::WindowFlags flags) + : QDesignerWidgetBoxInterface(parent, flags), + m_loadMode(LoadMerge) +{ + +} + +QDesignerWidgetBox::LoadMode QDesignerWidgetBox::loadMode() const +{ + return m_loadMode; +} + +void QDesignerWidgetBox::setLoadMode(LoadMode lm) +{ + m_loadMode = lm; +} + +// Convenience to find a widget by class name +bool QDesignerWidgetBox::findWidget(const QDesignerWidgetBoxInterface *wbox, + const QString &className, + const QString &category, + Widget *widgetData) +{ + // Note that entry names do not necessarily match the class name + // (at least, not for the standard widgets), so, + // look in the XML for the class name of the first widget to appear + const QString widgetTag = QLatin1String("categoryCount(); + for (int c = 0; c < catCount; c++) { + const Category cat = wbox->category(c); + if (category.isEmpty() || cat.name() == category) { + const int widgetCount = cat.widgetCount(); + for (int w = 0; w < widgetCount; w++) { + const Widget widget = cat.widget(w); + QString xml = widget.domXml(); // Erase the tag that can be present starting from 4.4 + const int widgetTagIndex = xml.indexOf(widgetTag); + if (widgetTagIndex != -1) { + xml.remove(0, widgetTagIndex); + if (regexp.exactMatch(xml)) { + *widgetData = widget; + return true; + } + } + } + } + } + return false; +} + +// Convenience to create a Dom Widget from widget box xml code. +DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel, + QString *errorMessage) +{ + QXmlStreamReader reader(xml); + DomUI *ui = 0; + + // The xml description must either contain a root element "ui" with a child element "widget" + // or "widget" as the root element (4.3 legacy) + const QString widgetTag = QLatin1String("widget"); + + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + const QStringRef name = reader.name(); + if (ui) { + reader.raiseError(tr("Unexpected element <%1>").arg(name.toString())); + continue; + } + + if (name.compare(QLatin1String("widget"), Qt::CaseInsensitive) == 0) { // 4.3 legacy, wrap into DomUI + ui = new DomUI; + DomWidget *widget = new DomWidget; + widget->read(reader); + ui->setElementWidget(widget); + } else if (name.compare(QLatin1String("ui"), Qt::CaseInsensitive) == 0) { // 4.4 + ui = new DomUI; + ui->read(reader); + } else { + reader.raiseError(tr("Unexpected element <%1>").arg(name.toString())); + } + } + } + + if (reader.hasError()) { + delete ui; + *errorMessage = tr("A parse error occurred at line %1, column %2 of the XML code " + "specified for the widget %3: %4\n%5") + .arg(reader.lineNumber()).arg(reader.columnNumber()).arg(name) + .arg(reader.errorString()).arg(xml); + return 0; + } + + if (!ui || !ui->elementWidget()) { + delete ui; + *errorMessage = tr("The XML code specified for the widget %1 does not contain " + "any widget elements.\n%2").arg(name).arg(xml); + return 0; + } + + if (insertFakeTopLevel) { + DomWidget *fakeTopLevel = new DomWidget; + fakeTopLevel->setAttributeClass(QLatin1String("QWidget")); + QList children; + children.push_back(ui->takeElementWidget()); + fakeTopLevel->setElementWidget(children); + ui->setElementWidget(fakeTopLevel); + } + + return ui; +} + +// Convenience to create a Dom Widget from widget box xml code. +DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel) +{ + QString errorMessage; + DomUI *rc = xmlToUi(name, xml, insertFakeTopLevel, &errorMessage); + if (!rc) + qdesigner_internal::designerWarning(errorMessage); + return rc; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_widgetbox_p.h b/designer/lib/shared/qdesigner_widgetbox_p.h new file mode 100644 index 0000000..5b6da9b --- /dev/null +++ b/designer/lib/shared/qdesigner_widgetbox_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_WIDGETBOX_H +#define QDESIGNER_WIDGETBOX_H + +#include "shared_global_p.h" +#include + +QT_BEGIN_NAMESPACE + +class DomUI; + +namespace qdesigner_internal { + +// A widget box with a load mode that allows for updating custom widgets. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetBox : public QDesignerWidgetBoxInterface +{ + Q_OBJECT +public: + enum LoadMode { LoadMerge, LoadReplace, LoadCustomWidgetsOnly }; + + explicit QDesignerWidgetBox(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + LoadMode loadMode() const; + void setLoadMode(LoadMode lm); + + virtual bool loadContents(const QString &contents) = 0; + + // Convenience to access the widget box icon of a widget. Empty category + // matches all + virtual QIcon iconForWidget(const QString &className, + const QString &category = QString()) const = 0; + + // Convenience to find a widget by class name. Empty category matches all + static bool findWidget(const QDesignerWidgetBoxInterface *wbox, + const QString &className, + const QString &category /* = QString() */, + Widget *widgetData); + // Convenience functions to create a DomWidget from widget box xml. + static DomUI *xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel, QString *errorMessage); + static DomUI *xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel); + +private: + LoadMode m_loadMode; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGETBOX_H diff --git a/designer/lib/shared/qdesigner_widgetitem.cpp b/designer/lib/shared/qdesigner_widgetitem.cpp new file mode 100644 index 0000000..3b9d655 --- /dev/null +++ b/designer/lib/shared/qdesigner_widgetitem.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_widgetitem_p.h" +#include "qdesigner_widget_p.h" +#include "widgetfactory_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { DebugWidgetItem = 0 }; +enum { MinimumLength = 10 }; + +// Widget item creation function to be registered as factory method with +// QLayoutPrivate +static QWidgetItem *createDesignerWidgetItem(const QLayout *layout, QWidget *widget) +{ + Qt::Orientations orientations; + if (qdesigner_internal::QDesignerWidgetItem::check(layout, widget, &orientations)) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItem: Creating on " << layout << widget << orientations; + return new qdesigner_internal::QDesignerWidgetItem(layout, widget, orientations); + } + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItem: Noncontainer: " << layout << widget; + + return 0; +} + +static QString sizePolicyToString(const QSizePolicy &p) +{ + QString rc; { + QTextStream str(&rc); + str << "Control=" << p.controlType() << " expdirs=" << p.expandingDirections() + << " hasHeightForWidth=" << p.hasHeightForWidth() + << " H: Policy=" << p.horizontalPolicy() + << " stretch=" << p.horizontalStretch() + << " V: Policy=" << p.verticalPolicy() + << " stretch=" << p.verticalStretch(); + } + return rc; +} + +// Find the layout the item is contained in, recursing over +// child layouts +static const QLayout *findLayoutOfItem(const QLayout *haystack, const QLayoutItem *needle) +{ + const int count = haystack->count(); + for (int i = 0; i < count; i++) { + QLayoutItem *item = haystack->itemAt(i); + if (item == needle) + return haystack; + if (QLayout *childLayout = item->layout()) + if (const QLayout *containing = findLayoutOfItem(childLayout, needle)) + return containing; + } + return 0; +} + + +namespace qdesigner_internal { + +// ------------------ QDesignerWidgetItem +QDesignerWidgetItem::QDesignerWidgetItem(const QLayout *containingLayout, QWidget *w, Qt::Orientations o) : + QWidgetItemV2(w), + m_orientations(o), + m_nonLaidOutMinSize(w->minimumSizeHint()), + m_nonLaidOutSizeHint(w->sizeHint()), + m_cachedContainingLayout(containingLayout) +{ + // Initialize the minimum size to prevent nonlaid-out frames/widgets + // from being slammed to zero + const QSize minimumSize = w->minimumSize(); + if (!minimumSize.isEmpty()) + m_nonLaidOutMinSize = minimumSize; + expand(&m_nonLaidOutMinSize); + expand(&m_nonLaidOutSizeHint); + w->installEventFilter(this); + connect(containingLayout, SIGNAL(destroyed()), this, SLOT(layoutChanged())); + if (DebugWidgetItem ) + qDebug() << "QDesignerWidgetItem" << w << sizePolicyToString(w->sizePolicy()) << m_nonLaidOutMinSize << m_nonLaidOutSizeHint; +} + +void QDesignerWidgetItem::expand(QSize *s) const +{ + // Expand the size if its too small + if (m_orientations & Qt::Horizontal && s->width() <= 0) + s->setWidth(MinimumLength); + if (m_orientations & Qt::Vertical && s->height() <= 0) + s->setHeight(MinimumLength); +} + +QSize QDesignerWidgetItem::minimumSize() const +{ + // Just track the size in case we are laid-out or stretched. + const QSize baseMinSize = QWidgetItemV2::minimumSize(); + QWidget * w = constWidget(); + if (w->layout() || subjectToStretch(containingLayout(), w)) { + m_nonLaidOutMinSize = baseMinSize; + return baseMinSize; + } + // Nonlaid out: Maintain last laid-out size + const QSize rc = baseMinSize.expandedTo(m_nonLaidOutMinSize); + if (DebugWidgetItem > 1) + qDebug() << "minimumSize" << constWidget() << baseMinSize << rc; + return rc; +} + +QSize QDesignerWidgetItem::sizeHint() const +{ + // Just track the size in case we are laid-out or stretched. + const QSize baseSizeHint = QWidgetItemV2::sizeHint(); + QWidget * w = constWidget(); + if (w->layout() || subjectToStretch(containingLayout(), w)) { + m_nonLaidOutSizeHint = baseSizeHint; + return baseSizeHint; + } + // Nonlaid out: Maintain last laid-out size + const QSize rc = baseSizeHint.expandedTo(m_nonLaidOutSizeHint); + if (DebugWidgetItem > 1) + qDebug() << "sizeHint" << constWidget() << baseSizeHint << rc; + return rc; +} + +bool QDesignerWidgetItem::subjectToStretch(const QLayout *layout, QWidget *w) +{ + if (!layout) + return false; + // Are we under some stretch factor? + if (const QBoxLayout *bl = qobject_cast(layout)) { + const int index = bl->indexOf(w); + Q_ASSERT(index != -1); + return bl->stretch(index) != 0; + } + if (const QGridLayout *cgl = qobject_cast(layout)) { + QGridLayout *gl = const_cast(cgl); + const int index = cgl->indexOf(w); + Q_ASSERT(index != -1); + int row, column, rowSpan, columnSpan; + gl->getItemPosition (index, &row, &column, &rowSpan, &columnSpan); + const int rend = row + rowSpan; + const int cend = column + columnSpan; + for (int r = row; r < rend; r++) + if (cgl->rowStretch(r) != 0) + return true; + for (int c = column; c < cend; c++) + if (cgl->columnStretch(c) != 0) + return true; + } + return false; +} + +/* Return the orientations mask for a layout, specifying + * in which directions squeezing should be prevented. */ +static Qt::Orientations layoutOrientation(const QLayout *layout) +{ + if (const QBoxLayout *bl = qobject_cast(layout)) { + const QBoxLayout::Direction direction = bl->direction(); + return direction == QBoxLayout::LeftToRight || direction == QBoxLayout::RightToLeft ? Qt::Horizontal : Qt::Vertical; + } + if (qobject_cast(layout)) + return Qt::Vertical; + return Qt::Horizontal|Qt::Vertical; +} + +// Check for a non-container extension container +bool QDesignerWidgetItem::isContainer(const QDesignerFormEditorInterface *core, QWidget *w) +{ + if (!WidgetFactory::isFormEditorObject(w)) + return false; + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widx = wdb->indexOfObject(w); + if (widx == -1 || !wdb->item(widx)->isContainer()) + return false; + if (qt_extension(core->extensionManager(), w)) + return false; + return true; +} + +bool QDesignerWidgetItem::check(const QLayout *layout, QWidget *w, Qt::Orientations *ptrToOrientations) +{ + // Check for form-editor non-containerextension-containers (QFrame, etc) + // within laid-out form editor widgets. No check for managed() here as we + // want container pages and widgets in the process of being morphed as + // well. Avoid nested layouts (as the effective stretch cannot be easily + // computed and may mess things up). Won't work for Q3 Group boxes. + if (ptrToOrientations) + *ptrToOrientations = 0; + + const QObject *layoutParent = layout->parent(); + if (!layoutParent || !layoutParent->isWidgetType() || !WidgetFactory::isFormEditorObject(layoutParent)) + return false; + + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w); + if (!fw || !isContainer(fw->core(), w)) + return false; + + // If it is a box, restrict to its orientation + if (ptrToOrientations) + *ptrToOrientations = layoutOrientation(layout); + + return true; +} + +QSize QDesignerWidgetItem::nonLaidOutMinSize() const +{ + return m_nonLaidOutMinSize; +} + +void QDesignerWidgetItem::setNonLaidOutMinSize(const QSize &s) +{ + if (DebugWidgetItem > 1) + qDebug() << "setNonLaidOutMinSize" << constWidget() << s; + m_nonLaidOutMinSize = s; +} + +QSize QDesignerWidgetItem::nonLaidOutSizeHint() const +{ + return m_nonLaidOutSizeHint; +} + +void QDesignerWidgetItem::setNonLaidOutSizeHint(const QSize &s) +{ + if (DebugWidgetItem > 1) + qDebug() << "setNonLaidOutSizeHint" << constWidget() << s; + m_nonLaidOutSizeHint = s; +} + +void QDesignerWidgetItem::install() +{ + QLayoutPrivate::widgetItemFactoryMethod = createDesignerWidgetItem; +} + +void QDesignerWidgetItem::deinstall() +{ + QLayoutPrivate::widgetItemFactoryMethod = 0; +} + +const QLayout *QDesignerWidgetItem::containingLayout() const +{ + if (!m_cachedContainingLayout) { + if (QWidget *parentWidget = constWidget()->parentWidget()) + if (QLayout *parentLayout = parentWidget->layout()) { + m_cachedContainingLayout = findLayoutOfItem(parentLayout, this); + if (m_cachedContainingLayout) + connect(m_cachedContainingLayout, SIGNAL(destroyed()), this, SLOT(layoutChanged())); + } + if (DebugWidgetItem) + qDebug() << Q_FUNC_INFO << " found " << m_cachedContainingLayout << " after reparenting " << constWidget(); + } + return m_cachedContainingLayout; +} + +void QDesignerWidgetItem::layoutChanged() +{ + if (DebugWidgetItem) + qDebug() << Q_FUNC_INFO; + m_cachedContainingLayout = 0; +} + +bool QDesignerWidgetItem::eventFilter(QObject * /* watched */, QEvent *event) +{ + if (event->type() == QEvent::ParentChange) + layoutChanged(); + return false; +} + +// ------------------ QDesignerWidgetItemInstaller + +int QDesignerWidgetItemInstaller::m_instanceCount = 0; + +QDesignerWidgetItemInstaller::QDesignerWidgetItemInstaller() +{ + if (m_instanceCount++ == 0) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItemInstaller: installing"; + QDesignerWidgetItem::install(); + } +} + +QDesignerWidgetItemInstaller::~QDesignerWidgetItemInstaller() +{ + if (--m_instanceCount == 0) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItemInstaller: deinstalling"; + QDesignerWidgetItem::deinstall(); + } +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qdesigner_widgetitem_p.h b/designer/lib/shared/qdesigner_widgetitem_p.h new file mode 100644 index 0000000..0f1d0a2 --- /dev/null +++ b/designer/lib/shared/qdesigner_widgetitem_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNERWIDGETITEM_H +#define DESIGNERWIDGETITEM_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +// QDesignerWidgetItem: A Layout Item that is used for non-containerextension- +// containers (QFrame, etc) on Designer forms. It prevents its widget +// from being slammed to size 0 if the widget has no layout: +// Pre 4.5, this item ensured only that QWidgets and QFrames were not squeezed +// to size 0 since they have an invalid size hint when non-laid out. +// Since 4.5, the item is used for every non-containerextension-container. +// In case the container has itself a layout, it merely tracks the minimum +// size. If the container has no layout and is not subject to some stretch +// factor, it will return the last valid size. The effect is that after +// breaking a layout on a container within a layout, it just maintains its +// last size and is not slammed to 0,0. In addition, it can be resized. +// The class keeps track of the containing layout by tracking widget reparent +// and destroyed slots as Designer will for example re-create grid layouts to +// shrink them. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetItem : public QObject, public QWidgetItemV2 { + Q_DISABLE_COPY(QDesignerWidgetItem) + Q_OBJECT +public: + explicit QDesignerWidgetItem(const QLayout *containingLayout, QWidget *w, Qt::Orientations o = Qt::Horizontal|Qt::Vertical); + + const QLayout *containingLayout() const; + + inline QWidget *constWidget() const { return const_cast(this)->widget(); } + + virtual QSize minimumSize() const; + virtual QSize sizeHint() const; + + // Resize: Takes effect if the contained widget does not have a layout + QSize nonLaidOutMinSize() const; + void setNonLaidOutMinSize(const QSize &s); + + QSize nonLaidOutSizeHint() const; + void setNonLaidOutSizeHint(const QSize &s); + + // Check whether a QDesignerWidgetItem should be installed + static bool check(const QLayout *layout, QWidget *w, Qt::Orientations *ptrToOrientations = 0); + + // Register itself using QLayoutPrivate's widget item factory method hook + static void install(); + static void deinstall(); + + // Check for a non-container extension container + static bool isContainer(const QDesignerFormEditorInterface *core, QWidget *w); + + static bool subjectToStretch(const QLayout *layout, QWidget *w); + + virtual bool eventFilter(QObject * watched, QEvent * event); + +private slots: + void layoutChanged(); + +private: + void expand(QSize *s) const; + bool subjectToStretch() const; + + const Qt::Orientations m_orientations; + mutable QSize m_nonLaidOutMinSize; + mutable QSize m_nonLaidOutSizeHint; + mutable const QLayout *m_cachedContainingLayout; +}; + +// Helper class that ensures QDesignerWidgetItem is installed while an +// instance is in scope. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetItemInstaller { + Q_DISABLE_COPY(QDesignerWidgetItemInstaller) + +public: + QDesignerWidgetItemInstaller(); + ~QDesignerWidgetItemInstaller(); + +private: + static int m_instanceCount; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/qlayout_widget.cpp b/designer/lib/shared/qlayout_widget.cpp new file mode 100644 index 0000000..bf9b285 --- /dev/null +++ b/designer/lib/shared/qlayout_widget.cpp @@ -0,0 +1,2100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlayout_widget_p.h" +#include "qdesigner_utils_p.h" +#include "layout_p.h" +#include "layoutinfo_p.h" +#include "invisible_widget_p.h" +#include "qdesigner_widgetitem_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +enum { ShiftValue = 1 }; +enum { debugLayout = 0 }; +enum { FormLayoutColumns = 2 }; +enum { indicatorSize = 2 }; +// Grid/form Helpers: get info (overloads to make templates work) + +namespace { // Do not use static, will break HP-UX due to templates + +QT_USE_NAMESPACE + +// overloads to make templates over QGridLayout/QFormLayout work +inline int gridRowCount(const QGridLayout *gridLayout) +{ + return gridLayout->rowCount(); +} + +inline int gridColumnCount(const QGridLayout *gridLayout) +{ + return gridLayout->columnCount(); +} + +// QGridLayout/QFormLayout Helpers: get item position (overloads to make templates work) +inline void getGridItemPosition(QGridLayout *gridLayout, int index, + int *row, int *column, int *rowspan, int *colspan) +{ + gridLayout->getItemPosition(index, row, column, rowspan, colspan); +} + +QRect gridItemInfo(QGridLayout *grid, int index) +{ + int row, column, rowSpan, columnSpan; + // getItemPosition is not const, grmbl.. + grid->getItemPosition(index, &row, &column, &rowSpan, &columnSpan); + return QRect(column, row, columnSpan, rowSpan); +} + +inline int gridRowCount(const QFormLayout *formLayout) { return formLayout->rowCount(); } +inline int gridColumnCount(const QFormLayout *) { return FormLayoutColumns; } + +inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + qdesigner_internal::getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan); +} + +QRect gridItemInfo(const QFormLayout *form, int index) +{ + int row; + int column; + int colspan; + qdesigner_internal::getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); + return QRect(column, row, colspan, 1); +} +} // namespace anonymous + +QT_BEGIN_NAMESPACE + +static const char *objectNameC = "objectName"; +static const char *sizeConstraintC = "sizeConstraint"; + +/* A padding spacer element that is used to represent an empty form layout cell. It should grow with its cell. + * Should not be used on a grid as it causes resizing inconsistencies */ +namespace qdesigner_internal { + class PaddingSpacerItem : public QSpacerItem { + public: + PaddingSpacerItem() : QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) {} + virtual Qt::Orientations expandingDirections () const { return Qt::Vertical | Qt::Horizontal; } + }; +} + +static inline QSpacerItem *createGridSpacer() +{ + return new QSpacerItem(0, 0); +} + +static inline QSpacerItem *createFormSpacer() +{ + return new qdesigner_internal::PaddingSpacerItem; +} + +// QGridLayout/QFormLayout Helpers: Debug items of GridLikeLayout +template +static QDebug debugGridLikeLayout(QDebug str, const GridLikeLayout &gl) +{ + const int count = gl.count(); + str << "Grid: " << gl.objectName() << gridRowCount(&gl) << " rows x " << gridColumnCount(&gl) + << " cols " << count << " items\n"; + for (int i = 0; i < count; i++) { + QLayoutItem *item = gl.itemAt(i); + str << "Item " << i << item << item->widget() << gridItemInfo(const_cast(&gl), i) << " empty=" << qdesigner_internal::LayoutInfo::isEmptyItem(item) << "\n"; + } + return str; +} + +static inline QDebug operator<<(QDebug str, const QGridLayout &gl) { return debugGridLikeLayout(str, gl); } +static inline QDebug operator<<(QDebug str, const QFormLayout &fl) { return debugGridLikeLayout(str, fl); } + +static inline bool isEmptyFormLayoutRow(const QFormLayout *fl, int row) +{ + // Spanning can never be empty + if (fl->itemAt(row, QFormLayout::SpanningRole)) + return false; + return qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::LabelRole)) && qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::FieldRole)); +} + +static inline bool canSimplifyFormLayout(const QFormLayout *formLayout, const QRect &restrictionArea) +{ + if (restrictionArea.x() >= FormLayoutColumns) + return false; + // Try to find empty rows + const int bottomCheckRow = qMin(formLayout->rowCount(), restrictionArea.top() + restrictionArea.height()); + for (int r = restrictionArea.y(); r < bottomCheckRow; r++) + if (isEmptyFormLayoutRow(formLayout, r)) + return true; + return false; +} + +// recreate a managed layout (which does not automagically remove +// empty rows/columns like grid or form layout) in case it needs to shrink + +static QLayout *recreateManagedLayout(const QDesignerFormEditorInterface *core, QWidget *w, QLayout *lt) +{ + const qdesigner_internal::LayoutInfo::Type t = qdesigner_internal::LayoutInfo::layoutType(core, lt); + qdesigner_internal::LayoutProperties properties; + const int mask = properties.fromPropertySheet(core, lt, qdesigner_internal::LayoutProperties::AllProperties); + qdesigner_internal::LayoutInfo::deleteLayout(core, w); + QLayout *rc = core->widgetFactory()->createLayout(w, 0, t); + properties.toPropertySheet(core, rc, mask, true); + return rc; +} + +// QGridLayout/QFormLayout Helpers: find an item on a form/grid. Return index +template +int findGridItemAt(GridLikeLayout *gridLayout, int at_row, int at_column) +{ + Q_ASSERT(gridLayout); + const int count = gridLayout->count(); + for (int index = 0; index < count; index++) { + int row, column, rowspan, colspan; + getGridItemPosition(gridLayout, index, &row, &column, &rowspan, &colspan); + if (at_row >= row && at_row < (row + rowspan) + && at_column >= column && at_column < (column + colspan)) { + return index; + } + } + return -1; +} +// QGridLayout/QFormLayout Helpers: remove dummy spacers on form/grid +template +static bool removeEmptyCellsOnGrid(GridLikeLayout *grid, const QRect &area) +{ + // check if there are any items in the way. Should be only spacers + // Unique out items that span rows/columns. + QVector indexesToBeRemoved; + indexesToBeRemoved.reserve(grid->count()); + const int rightColumn = area.x() + area.width(); + const int bottomRow = area.y() + area.height(); + for (int c = area.x(); c < rightColumn; c++) + for (int r = area.y(); r < bottomRow; r++) { + const int index = findGridItemAt(grid, r ,c); + if (index != -1) + if (QLayoutItem *item = grid->itemAt(index)) { + if (qdesigner_internal::LayoutInfo::isEmptyItem(item)) { + if (indexesToBeRemoved.indexOf(index) == -1) + indexesToBeRemoved.push_back(index); + } else { + return false; + } + } + } + // remove, starting from last + if (!indexesToBeRemoved.empty()) { + qStableSort(indexesToBeRemoved.begin(), indexesToBeRemoved.end()); + for (int i = indexesToBeRemoved.size() - 1; i >= 0; i--) + delete grid->takeAt(indexesToBeRemoved[i]); + } + return true; +} + +namespace qdesigner_internal { +// --------- LayoutProperties + +LayoutProperties::LayoutProperties() +{ + clear(); +} + +void LayoutProperties::clear() +{ + qFill(m_margins, m_margins + MarginCount, 0); + qFill(m_marginsChanged, m_marginsChanged + MarginCount, false); + qFill(m_spacings, m_spacings + SpacingsCount, 0); + qFill(m_spacingsChanged, m_spacingsChanged + SpacingsCount, false); + + m_objectName = QVariant(); + m_objectNameChanged = false; + m_sizeConstraint = QVariant(QLayout::SetDefaultConstraint); + m_sizeConstraintChanged = false; + + m_fieldGrowthPolicyChanged = m_rowWrapPolicyChanged = m_labelAlignmentChanged = m_formAlignmentChanged = false; + m_fieldGrowthPolicy = m_rowWrapPolicy = m_formAlignment = QVariant(); + + m_boxStretchChanged = m_gridRowStretchChanged = m_gridColumnStretchChanged = m_gridRowMinimumHeightChanged = false; + m_boxStretch = m_gridRowStretch = m_gridColumnStretch = m_gridRowMinimumHeight = QVariant(); +} + +int LayoutProperties::visibleProperties(const QLayout *layout) +{ + // Grid like layout have 2 spacings. + const bool isFormLayout = qobject_cast(layout); + const bool isGridLike = qobject_cast(layout) || isFormLayout; + int rc = ObjectNameProperty|LeftMarginProperty|TopMarginProperty|RightMarginProperty|BottomMarginProperty| + SizeConstraintProperty; + + rc |= isGridLike ? (HorizSpacingProperty|VertSpacingProperty) : SpacingProperty; + if (isFormLayout) { + rc |= FieldGrowthPolicyProperty|RowWrapPolicyProperty|LabelAlignmentProperty|FormAlignmentProperty; + } else { + if (isGridLike) { + rc |= GridRowStretchProperty|GridColumnStretchProperty|GridRowMinimumHeightProperty|GridColumnMinimumWidthProperty; + } else { + rc |= BoxStretchProperty; + } + } + return rc; +} + +static const char *marginPropertyNamesC[] = {"leftMargin", "topMargin", "rightMargin", "bottomMargin"}; +static const char *spacingPropertyNamesC[] = {"spacing", "horizontalSpacing", "verticalSpacing" }; +static const char *fieldGrowthPolicyPropertyC = "fieldGrowthPolicy"; +static const char *rowWrapPolicyPropertyC = "rowWrapPolicy"; +static const char *labelAlignmentPropertyC = "labelAlignment"; +static const char *formAlignmentPropertyC = "formAlignment"; +static const char *boxStretchPropertyC = "stretch"; +static const char *gridRowStretchPropertyC = "rowStretch"; +static const char *gridColumnStretchPropertyC = "columnStretch"; +static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight"; +static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth"; + +static bool intValueFromSheet(const QDesignerPropertySheetExtension *sheet, const QString &name, int *value, bool *changed) +{ + const int sheetIndex = sheet->indexOf(name); + if (sheetIndex == -1) + return false; + *value = sheet->property(sheetIndex).toInt(); + *changed = sheet->isChanged(sheetIndex); + return true; +} + +static void variantPropertyFromSheet(int mask, int flag, const QDesignerPropertySheetExtension *sheet, const QString &name, + QVariant *value, bool *changed, int *returnMask) +{ + if (mask & flag) { + const int sIndex = sheet->indexOf(name); + if (sIndex != -1) { + *value = sheet->property(sIndex); + *changed = sheet->isChanged(sIndex); + *returnMask |= flag; + } + } +} + +int LayoutProperties::fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask) +{ + int rc = 0; + const QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), l); + Q_ASSERT(sheet); + // name + if (mask & ObjectNameProperty) { + const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); + Q_ASSERT(nameIndex != -1); + m_objectName = sheet->property(nameIndex); + m_objectNameChanged = sheet->isChanged(nameIndex); + rc |= ObjectNameProperty; + } + // -- Margins + const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; + for (int i = 0; i < MarginCount; i++) + if (mask & marginFlags[i]) + if (intValueFromSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins + i, m_marginsChanged + i)) + rc |= marginFlags[i]; + + const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; + for (int i = 0; i < SpacingsCount; i++) + if (mask & spacingFlags[i]) + if (intValueFromSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings + i, m_spacingsChanged + i)) + rc |= spacingFlags[i]; + // sizeConstraint, flags + variantPropertyFromSheet(mask, SizeConstraintProperty, sheet, QLatin1String(sizeConstraintC), &m_sizeConstraint, &m_sizeConstraintChanged, &rc); + variantPropertyFromSheet(mask, FieldGrowthPolicyProperty, sheet, QLatin1String(fieldGrowthPolicyPropertyC), &m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); + variantPropertyFromSheet(mask, RowWrapPolicyProperty, sheet, QLatin1String(rowWrapPolicyPropertyC), &m_rowWrapPolicy, &m_rowWrapPolicyChanged, &rc); + variantPropertyFromSheet(mask, LabelAlignmentProperty, sheet, QLatin1String(labelAlignmentPropertyC), &m_labelAlignment, &m_labelAlignmentChanged, &rc); + variantPropertyFromSheet(mask, FormAlignmentProperty, sheet, QLatin1String(formAlignmentPropertyC), &m_formAlignment, &m_formAlignmentChanged, &rc); + variantPropertyFromSheet(mask, BoxStretchProperty, sheet, QLatin1String(boxStretchPropertyC), &m_boxStretch, & m_boxStretchChanged, &rc); + variantPropertyFromSheet(mask, GridRowStretchProperty, sheet, QLatin1String(gridRowStretchPropertyC), &m_gridRowStretch, &m_gridRowStretchChanged, &rc); + variantPropertyFromSheet(mask, GridColumnStretchProperty, sheet, QLatin1String(gridColumnStretchPropertyC), &m_gridColumnStretch, &m_gridColumnStretchChanged, &rc); + variantPropertyFromSheet(mask, GridRowMinimumHeightProperty, sheet, QLatin1String(gridRowMinimumHeightPropertyC), &m_gridRowMinimumHeight, &m_gridRowMinimumHeightChanged, &rc); + variantPropertyFromSheet(mask, GridColumnMinimumWidthProperty, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), &m_gridColumnMinimumWidth, &m_gridColumnMinimumWidthChanged, &rc); + return rc; +} + +static bool intValueToSheet(QDesignerPropertySheetExtension *sheet, const QString &name, int value, bool changed, bool applyChanged) + +{ + + const int sheetIndex = sheet->indexOf(name); + if (sheetIndex == -1) { + qWarning() << " LayoutProperties: Attempt to set property " << name << " that does not exist for the layout."; + return false; + } + sheet->setProperty(sheetIndex, QVariant(value)); + if (applyChanged) + sheet->setChanged(sheetIndex, changed); + return true; +} + +static void variantPropertyToSheet(int mask, int flag, bool applyChanged, QDesignerPropertySheetExtension *sheet, const QString &name, + const QVariant &value, bool changed, int *returnMask) +{ + if (mask & flag) { + const int sIndex = sheet->indexOf(name); + if (sIndex != -1) { + sheet->setProperty(sIndex, value); + if (applyChanged) + sheet->setChanged(sIndex, changed); + *returnMask |= flag; + } + } +} + +int LayoutProperties::toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask, bool applyChanged) const +{ + int rc = 0; + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), l); + Q_ASSERT(sheet); + // name + if (mask & ObjectNameProperty) { + const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); + Q_ASSERT(nameIndex != -1); + sheet->setProperty(nameIndex, m_objectName); + if (applyChanged) + sheet->setChanged(nameIndex, m_objectNameChanged); + rc |= ObjectNameProperty; + } + // margins + const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; + for (int i = 0; i < MarginCount; i++) + if (mask & marginFlags[i]) + if (intValueToSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins[i], m_marginsChanged[i], applyChanged)) + rc |= marginFlags[i]; + + const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; + for (int i = 0; i < SpacingsCount; i++) + if (mask & spacingFlags[i]) + if (intValueToSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings[i], m_spacingsChanged[i], applyChanged)) + rc |= spacingFlags[i]; + // sizeConstraint + variantPropertyToSheet(mask, SizeConstraintProperty, applyChanged, sheet, QLatin1String(sizeConstraintC), m_sizeConstraint, m_sizeConstraintChanged, &rc); + variantPropertyToSheet(mask, FieldGrowthPolicyProperty, applyChanged, sheet, QLatin1String(fieldGrowthPolicyPropertyC), m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); + variantPropertyToSheet(mask, RowWrapPolicyProperty, applyChanged, sheet, QLatin1String(rowWrapPolicyPropertyC), m_rowWrapPolicy, m_rowWrapPolicyChanged, &rc); + variantPropertyToSheet(mask, LabelAlignmentProperty, applyChanged, sheet, QLatin1String(labelAlignmentPropertyC), m_labelAlignment, m_labelAlignmentChanged, &rc); + variantPropertyToSheet(mask, FormAlignmentProperty, applyChanged, sheet, QLatin1String(formAlignmentPropertyC), m_formAlignment, m_formAlignmentChanged, &rc); + variantPropertyToSheet(mask, BoxStretchProperty, applyChanged, sheet, QLatin1String(boxStretchPropertyC), m_boxStretch, m_boxStretchChanged, &rc); + variantPropertyToSheet(mask, GridRowStretchProperty, applyChanged, sheet, QLatin1String(gridRowStretchPropertyC), m_gridRowStretch, m_gridRowStretchChanged, &rc); + variantPropertyToSheet(mask, GridColumnStretchProperty, applyChanged, sheet, QLatin1String(gridColumnStretchPropertyC), m_gridColumnStretch, m_gridColumnStretchChanged, &rc); + variantPropertyToSheet(mask, GridRowMinimumHeightProperty, applyChanged, sheet, QLatin1String(gridRowMinimumHeightPropertyC), m_gridRowMinimumHeight, m_gridRowMinimumHeightChanged, &rc); + variantPropertyToSheet(mask, GridColumnMinimumWidthProperty, applyChanged, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), m_gridColumnMinimumWidth, m_gridColumnMinimumWidthChanged, &rc); + return rc; +} + +// ---------------- LayoutHelper +LayoutHelper::LayoutHelper() +{ +} + +LayoutHelper::~LayoutHelper() +{ +} + +int LayoutHelper::indexOf(const QLayout *lt, const QWidget *widget) +{ + if (!lt) + return -1; + + const int itemCount = lt->count(); + for (int i = 0; i < itemCount; i++) + if (lt->itemAt(i)->widget() == widget) + return i; + return -1; +} + +QRect LayoutHelper::itemInfo(QLayout *lt, const QWidget *widget) const +{ + const int index = indexOf(lt, widget); + if (index == -1) { + qWarning() << "LayoutHelper::itemInfo: " << widget << " not in layout " << lt; + return QRect(0, 0, 1, 1); + } + return itemInfo(lt, index); +} + + // ---------------- BoxLayoutHelper + class BoxLayoutHelper : public LayoutHelper { + public: + BoxLayoutHelper(const Qt::Orientation orientation) : m_orientation(orientation) {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *, const QWidget *); + virtual void popState(const QDesignerFormEditorInterface *, QWidget *); + + virtual bool canSimplify(const QDesignerFormEditorInterface *, const QWidget *, const QRect &) const { return false; } + virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &) {} + + // Helper for restoring layout states + typedef QVector LayoutItemVector; + static LayoutItemVector disassembleLayout(QLayout *lt); + static QLayoutItem *findItemOfWidget(const LayoutItemVector &lv, QWidget *w); + + private: + typedef QVector BoxLayoutState; + + static BoxLayoutState state(const QBoxLayout*lt); + + QStack m_states; + const Qt::Orientation m_orientation; + }; + + QRect BoxLayoutHelper::itemInfo(QLayout * /*lt*/, int index) const + { + return m_orientation == Qt::Horizontal ? QRect(index, 0, 1, 1) : QRect(0, index, 1, 1); + } + + void BoxLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QBoxLayout *boxLayout = qobject_cast(lt); + Q_ASSERT(boxLayout); + boxLayout->insertWidget(m_orientation == Qt::Horizontal ? info.x() : info.y(), w); + } + + void BoxLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QBoxLayout *boxLayout = qobject_cast(lt); + Q_ASSERT(boxLayout); + boxLayout->removeWidget(widget); + } + + void BoxLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QBoxLayout *boxLayout = qobject_cast(lt)) { + const int index = boxLayout->indexOf(before); + if (index != -1) { + const bool visible = before->isVisible(); + delete boxLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + boxLayout->insertWidget(index, after); + ok = true; + } + } + if (!ok) + qWarning() << "BoxLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + BoxLayoutHelper::BoxLayoutState BoxLayoutHelper::state(const QBoxLayout*lt) + { + BoxLayoutState rc; + if (const int count = lt->count()) { + rc.reserve(count); + for (int i = 0; i < count; i++) + if (QWidget *w = lt->itemAt(i)->widget()) + rc.push_back(w); + } + return rc; + } + + void BoxLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *w) + { + const QBoxLayout *boxLayout = qobject_cast(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(boxLayout); + m_states.push(state(boxLayout)); + } + + QLayoutItem *BoxLayoutHelper::findItemOfWidget(const LayoutItemVector &lv, QWidget *w) + { + const LayoutItemVector::const_iterator cend = lv.constEnd(); + for (LayoutItemVector::const_iterator it = lv.constBegin(); it != cend; ++it) + if ( (*it)->widget() == w) + return *it; + + return 0; + } + + BoxLayoutHelper::LayoutItemVector BoxLayoutHelper::disassembleLayout(QLayout *lt) + { + // Take items + const int count = lt->count(); + if (count == 0) + return LayoutItemVector(); + LayoutItemVector rc; + rc.reserve(count); + for (int i = count - 1; i >= 0; i--) + rc.push_back(lt->takeAt(i)); + return rc; + } + + void BoxLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *w) + { + QBoxLayout *boxLayout = qobject_cast(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(boxLayout); + const BoxLayoutState savedState = m_states.pop(); + const BoxLayoutState currentState = state(boxLayout); + // Check for equality/empty. Note that this will currently + // always trigger as box layouts do not have a state apart from + // the order and there is no layout order editor yet. + if (savedState == state(boxLayout)) + return; + + const int count = savedState.size(); + Q_ASSERT(count == currentState.size()); + // Take items and reassemble in saved order + const LayoutItemVector items = disassembleLayout(boxLayout); + for (int i = 0; i < count; i++) { + QLayoutItem *item = findItemOfWidget(items, savedState[i]); + Q_ASSERT(item); + boxLayout->addItem(item); + } + } + + // Grid Layout state. Datatypically store the state of a GridLayout as a map of + // widgets to QRect(columns, rows) and size. Used to store the state for undo operations + // that do not change the widgets within the layout; also provides some manipulation + // functions and ability to apply the state to a layout provided its widgets haven't changed. + struct GridLayoutState { + GridLayoutState(); + + void fromLayout(QGridLayout *l); + void applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const; + + void insertRow(int row); + void insertColumn(int column); + + bool simplify(const QRect &r, bool testOnly); + void removeFreeRow(int row); + void removeFreeColumn(int column); + + + // State of a cell in one dimension + enum DimensionCellState { + Free, + Spanned, // Item spans it + Occupied // Item bordering on it + }; + // Horiontal, Vertical pair of state + typedef QPair CellState; + typedef QVector CellStates; + + // Figure out states of a cell and return as a flat vector of + // [column1, column2,...] (address as row * columnCount + col) + static CellStates cellStates(const QList &rects, int numRows, int numColumns); + + typedef QMap WidgetItemMap; + WidgetItemMap widgetItemMap; + int rowCount; + int colCount; + }; + + static inline bool needsSpacerItem(const GridLayoutState::CellState &cs) { + return cs.first == GridLayoutState::Free && cs.second == GridLayoutState::Free; + } + + static inline QDebug operator<<(QDebug str, const GridLayoutState &gs) + { + str << "GridLayoutState: " << gs.rowCount << " rows x " << gs.colCount + << " cols " << gs.widgetItemMap.size() << " items\n"; + + const GridLayoutState::WidgetItemMap::const_iterator wcend = gs.widgetItemMap.constEnd(); + for (GridLayoutState::WidgetItemMap::const_iterator it = gs.widgetItemMap.constBegin(); it != wcend; ++it) + str << "Item " << it.key() << it.value() << '\n'; + return str; + } + + GridLayoutState::GridLayoutState() : + rowCount(0), + colCount(0) + { + } + + GridLayoutState::CellStates GridLayoutState::cellStates(const QList &rects, int numRows, int numColumns) + { + CellStates rc = CellStates(numRows * numColumns, CellState(Free, Free)); + const QList::const_iterator rcend = rects.constEnd(); + for (QList::const_iterator it = rects.constBegin(); it != rcend; ++it) { + const int leftColumn = it->x(); + const int topRow = it->y(); + const int rightColumn = leftColumn + it->width() - 1; + const int bottomRow = topRow + it->height() - 1; + for (int r = topRow; r <= bottomRow; r++) + for (int c = leftColumn; c <= rightColumn; c++) { + const int flatIndex = r * numColumns + c; + // Bordering horizontally? + DimensionCellState &horizState = rc[flatIndex].first; + if (c == leftColumn || c == rightColumn) { + horizState = Occupied; + } else { + if (horizState < Spanned) + horizState = Spanned; + } + // Bordering vertically? + DimensionCellState &vertState = rc[flatIndex].second; + if (r == topRow || r == bottomRow) { + vertState = Occupied; + } else { + if (vertState < Spanned) + vertState = Spanned; + } + } + } + if (debugLayout) { + qDebug() << "GridLayoutState::cellStates: " << numRows << " x " << numColumns; + for (int r = 0; r < numRows; r++) + for (int c = 0; c < numColumns; c++) + qDebug() << " Row: " << r << " column: " << c << rc[r * numColumns + c]; + } + return rc; + } + + void GridLayoutState::fromLayout(QGridLayout *l) + { + rowCount = l->rowCount(); + colCount = l->columnCount(); + const int count = l->count(); + for (int i = 0; i < count; i++) { + QLayoutItem *item = l->itemAt(i); + if (!LayoutInfo::isEmptyItem(item)) + widgetItemMap.insert(item->widget(), gridItemInfo(l, i)); + } + } + + void GridLayoutState::applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const + { + typedef QMap LayoutItemRectMap; + QGridLayout *grid = qobject_cast(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(grid); + if (debugLayout) + qDebug() << ">GridLayoutState::applyToLayout" << *this << *grid; + const bool shrink = grid->rowCount() > rowCount || grid->columnCount() > colCount; + // Build a map of existing items to rectangles via widget map, delete spacers + LayoutItemRectMap itemMap; + while (grid->count()) { + QLayoutItem *item = grid->takeAt(0); + if (!LayoutInfo::isEmptyItem(item)) { + QWidget *itemWidget = item->widget(); + const WidgetItemMap::const_iterator it = widgetItemMap.constFind(itemWidget); + if (it == widgetItemMap.constEnd()) + qFatal("GridLayoutState::applyToLayout: Attempt to apply to a layout that has a widget '%s'/'%s' added after saving the state.", + itemWidget->metaObject()->className(), itemWidget->objectName().toUtf8().constData()); + itemMap.insert(item, it.value()); + } else { + delete item; + } + } + Q_ASSERT(itemMap.size() == widgetItemMap.size()); + // recreate if shrink + if (shrink) + grid = static_cast(recreateManagedLayout(core, w, grid)); + + // Add widgets items + const LayoutItemRectMap::const_iterator icend = itemMap.constEnd(); + for (LayoutItemRectMap::const_iterator it = itemMap.constBegin(); it != icend; ++it) { + const QRect info = it.value(); + grid->addItem(it.key(), info.y(), info.x(), info.height(), info.width()); + } + // create spacers + const CellStates cs = cellStates(itemMap.values(), rowCount, colCount); + for (int r = 0; r < rowCount; r++) + for (int c = 0; c < colCount; c++) + if (needsSpacerItem(cs[r * colCount + c])) + grid->addItem(createGridSpacer(), r, c); + grid->activate(); + if (debugLayout) + qDebug() << "= row) { + it.value().translate(0, 1); + } else { //Over it: Does it span it -> widen? + const int rowSpan = it.value().height(); + if (rowSpan > 1 && topRow + rowSpan > row) + it.value().setHeight(rowSpan + 1); + } + } + } + + void GridLayoutState::insertColumn(int column) + { + colCount++; + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int leftColumn = it.value().x(); + if (leftColumn >= column) { + it.value().translate(1, 0); + } else { // Left of it: Does it span it -> widen? + const int colSpan = it.value().width(); + if (colSpan > 1 && leftColumn + colSpan > column) + it.value().setWidth(colSpan + 1); + } + } + } + + // Simplify: Remove empty columns/rows and such ones that are only spanned (shrink + // spanning items). + // 'AB.C.' 'ABC' + // 'DDDD.' ==> 'DDD' + // 'EF.G.' 'EFG' + bool GridLayoutState::simplify(const QRect &r, bool testOnly) + { + // figure out free rows/columns. + QVector occupiedRows(rowCount, false); + QVector occupiedColumns(colCount, false); + // Mark everything outside restriction rectangle as occupied + const int restrictionLeftColumn = r.x(); + const int restrictionRightColumn = restrictionLeftColumn + r.width(); + const int restrictionTopRow = r.y(); + const int restrictionBottomRow = restrictionTopRow + r.height(); + if (restrictionLeftColumn > 0 || restrictionRightColumn < colCount || + restrictionTopRow > 0 || restrictionBottomRow < rowCount) { + for (int r = 0; r < rowCount; r++) + if (r < restrictionTopRow || r >= restrictionBottomRow) + occupiedRows[r] = true; + for (int c = 0; c < colCount; c++) + if (c < restrictionLeftColumn || c >= restrictionRightColumn) + occupiedColumns[c] = true; + } + // figure out free fields and tick off occupied rows and columns + const CellStates cs = cellStates(widgetItemMap.values(), rowCount, colCount); + for (int r = 0; r < rowCount; r++) + for (int c = 0; c < colCount; c++) { + const CellState &state = cs[r * colCount + c]; + if (state.first == Occupied) + occupiedColumns[c] = true; + if (state.second == Occupied) + occupiedRows[r] = true; + } + // Any free rows/columns? + if (occupiedRows.indexOf(false) == -1 && occupiedColumns.indexOf(false) == -1) + return false; + if (testOnly) + return true; + // remove rows + for (int r = rowCount - 1; r >= 0; r--) + if (!occupiedRows[r]) + removeFreeRow(r); + // remove columns + for (int c = colCount - 1; c >= 0; c--) + if (!occupiedColumns[c]) + removeFreeColumn(c); + return true; + } + + void GridLayoutState::removeFreeRow(int removeRow) + { + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int r = it.value().y(); + Q_ASSERT(r != removeRow); // Free rows only + if (r < removeRow) { // Does the item span it? - shrink it + const int rowSpan = it.value().height(); + if (rowSpan > 1) { + const int bottomRow = r + rowSpan; + if (bottomRow > removeRow) + it.value().setHeight(rowSpan - 1); + } + } else + if (r > removeRow) // Item below it? - move. + it.value().translate(0, -1); + } + rowCount--; + } + + void GridLayoutState::removeFreeColumn(int removeColumn) + { + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int c = it.value().x(); + Q_ASSERT(c != removeColumn); // Free columns only + if (c < removeColumn) { // Does the item span it? - shrink it + const int colSpan = it.value().width(); + if (colSpan > 1) { + const int rightColumn = c + colSpan; + if (rightColumn > removeColumn) + it.value().setWidth(colSpan - 1); + } + } else + if (c > removeColumn) // Item to the right of it? - move. + it.value().translate(-1, 0); + } + colCount--; + } + + // ---------------- GridLayoutHelper + class GridLayoutHelper : public LayoutHelper { + public: + GridLayoutHelper() {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); + + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const; + virtual void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea); + + static void insertRow(QGridLayout *grid, int row); + + private: + QStack m_states; + }; + + void GridLayoutHelper::insertRow(QGridLayout *grid, int row) + { + GridLayoutState state; + state.fromLayout(grid); + state.insertRow(row); + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(grid); + state.applyToLayout(fw->core(), grid->parentWidget()); + } + + QRect GridLayoutHelper::itemInfo(QLayout * lt, int index) const + { + QGridLayout *grid = qobject_cast(lt); + Q_ASSERT(grid); + return gridItemInfo(grid, index); + } + + void GridLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QGridLayout *gridLayout = qobject_cast(lt); + Q_ASSERT(gridLayout); + // check if there are any items. Should be only spacers, else something is wrong + const int row = info.y(); + int column = info.x(); + int colSpan = info.width(); + int rowSpan = info.height(); + // If not empty: A multiselection was dropped on an empty item, insert row + // and spread items along new row + if (!removeEmptyCellsOnGrid(gridLayout, info)) { + int freeColumn = -1; + colSpan = rowSpan = 1; + // First look to the right for a free column + const int columnCount = gridLayout->columnCount(); + for (int c = column; c < columnCount; c++) { + const int idx = findGridItemAt(gridLayout, row, c); + if (idx != -1 && LayoutInfo::isEmptyItem(gridLayout->itemAt(idx))) { + freeColumn = c; + break; + } + } + if (freeColumn != -1) { + removeEmptyCellsOnGrid(gridLayout, QRect(freeColumn, row, 1, 1)); + column = freeColumn; + } else { + GridLayoutHelper::insertRow(gridLayout, row); + column = 0; + } + } + gridLayout->addWidget(w, row , column, rowSpan, colSpan); + } + + void GridLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QGridLayout *gridLayout = qobject_cast(lt); + Q_ASSERT(gridLayout); + const int index = gridLayout->indexOf(widget); + if (index == -1) { + qWarning() << "GridLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; + return; + } + // delete old item and pad with by spacer items + int row, column, rowspan, colspan; + gridLayout->getItemPosition(index, &row, &column, &rowspan, &colspan); + delete gridLayout->takeAt(index); + const int rightColumn = column + colspan; + const int bottomRow = row + rowspan; + for (int c = column; c < rightColumn; c++) + for (int r = row; r < bottomRow; r++) + gridLayout->addItem(createGridSpacer(), r, c); + } + + void GridLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QGridLayout *gridLayout = qobject_cast(lt)) { + const int index = gridLayout->indexOf(before); + if (index != -1) { + int row, column, rowSpan, columnSpan; + gridLayout->getItemPosition (index, &row, &column, &rowSpan, &columnSpan); + const bool visible = before->isVisible(); + delete gridLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + gridLayout->addWidget(after, row, column, rowSpan, columnSpan); + ok = true; + } + } + if (!ok) + qWarning() << "GridLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + void GridLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) + { + QGridLayout *gridLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + m_states.push(gs); + } + + void GridLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) + { + Q_ASSERT(!m_states.empty()); + const GridLayoutState state = m_states.pop(); + state.applyToLayout(core, widgetWithManagedLayout); + } + + bool GridLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const + { + QGridLayout *gridLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + return gs.simplify(restrictionArea, true); + } + + void GridLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) + { + QGridLayout *gridLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + if (debugLayout) + qDebug() << ">GridLayoutHelper::simplify" << *gridLayout; + GridLayoutState gs; + gs.fromLayout(gridLayout); + if (gs.simplify(restrictionArea, false)) + gs.applyToLayout(core, widgetWithManagedLayout); + if (debugLayout) + qDebug() << " WidgetPair; + typedef QVector FormLayoutState; + + FormLayoutHelper() {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); + + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *, const QRect &) const; + virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &); + + private: + static FormLayoutState state(const QFormLayout *lt); + + QStack m_states; + }; + + QRect FormLayoutHelper::itemInfo(QLayout * lt, int index) const + { + QFormLayout *form = qobject_cast(lt); + Q_ASSERT(form); + int row, column, colspan; + getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); + return QRect(column, row, colspan, 1); + } + + void FormLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + if (debugLayout) + qDebug() << "FormLayoutHelper::insertWidget:" << w << info; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QFormLayout *formLayout = qobject_cast(lt); + Q_ASSERT(formLayout); + // check if there are any nonspacer items? (Drop on 3rd column or drop of a multiselection + // on an empty item. As the Form layout does not have insert semantics; we need to manually insert a row + const bool insert = !removeEmptyCellsOnGrid(formLayout, info); + formLayoutAddWidget(formLayout, w, info, insert); + QLayoutSupport::createEmptyCells(formLayout); + } + + void FormLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QFormLayout *formLayout = qobject_cast(lt); + Q_ASSERT(formLayout); + const int index = formLayout->indexOf(widget); + if (index == -1) { + qWarning() << "FormLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; + return; + } + // delete old item and pad with by spacer items + int row, column, colspan; + getFormLayoutItemPosition(formLayout, index, &row, &column, 0, &colspan); + if (debugLayout) + qDebug() << "FormLayoutHelper::removeWidget: #" << index << widget << " at " << row << column << colspan; + delete formLayout->takeAt(index); + if (colspan > 1 || column == 0) + formLayout->setItem(row, QFormLayout::LabelRole, createFormSpacer()); + if (colspan > 1 || column == 1) + formLayout->setItem(row, QFormLayout::FieldRole, createFormSpacer()); + } + + void FormLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QFormLayout *formLayout = qobject_cast(lt)) { + const int index = formLayout->indexOf(before); + if (index != -1) { + int row; + QFormLayout::ItemRole role; + formLayout->getItemPosition (index, &row, &role); + const bool visible = before->isVisible(); + delete formLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + formLayout->setWidget(row, role, after); + ok = true; + } + } + if (!ok) + qWarning() << "FormLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + FormLayoutHelper::FormLayoutState FormLayoutHelper::state(const QFormLayout *lt) + { + const int rowCount = lt->rowCount(); + if (rowCount == 0) + return FormLayoutState(); + FormLayoutState rc(rowCount, WidgetPair(0, 0)); + const int count = lt->count(); + int row, column, colspan; + for (int i = 0; i < count; i++) { + QLayoutItem *item = lt->itemAt(i); + if (!LayoutInfo::isEmptyItem(item)) { + QWidget *w = item->widget(); + Q_ASSERT(w); + getFormLayoutItemPosition(lt, i, &row, &column, 0, &colspan); + if (colspan > 1 || column == 0) + rc[row].first = w; + if (colspan > 1 || column == 1) + rc[row].second = w; + } + } + if (debugLayout) { + qDebug() << "FormLayoutHelper::state: " << rowCount; + for (int r = 0; r < rowCount; r++) + qDebug() << " Row: " << r << rc[r].first << rc[r].second; + } + return rc; + } + + void FormLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) + { + QFormLayout *formLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + m_states.push(state(formLayout)); + } + + void FormLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) + { + QFormLayout *formLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(!m_states.empty() && formLayout); + + const FormLayoutState storedState = m_states.pop(); + const FormLayoutState currentState = state(formLayout); + if (currentState == storedState) + return; + const int rowCount = storedState.size(); + // clear out, shrink if required, but maintain items via map, pad spacers + const BoxLayoutHelper::LayoutItemVector items = BoxLayoutHelper::disassembleLayout(formLayout); + if (rowCount < formLayout->rowCount()) + formLayout = static_cast(recreateManagedLayout(core, widgetWithManagedLayout, formLayout )); + for (int r = 0; r < rowCount; r++) { + QWidget *widgets[FormLayoutColumns] = { storedState[r].first, storedState[r].second }; + const bool spanning = widgets[0] != 0 && widgets[0] == widgets[1]; + if (spanning) { + formLayout->setWidget(r, QFormLayout::SpanningRole, widgets[0]); + } else { + for (int c = 0; c < FormLayoutColumns; c++) { + const QFormLayout::ItemRole role = c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; + if (widgets[c]) { + Q_ASSERT(BoxLayoutHelper::findItemOfWidget(items, widgets[c])); + formLayout->setWidget(r, role, widgets[c]); + } else { + formLayout->setItem(r, role, createFormSpacer()); + } + } + } + } + } + + bool FormLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const + { + const QFormLayout *formLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + return canSimplifyFormLayout(formLayout, restrictionArea); + } + + void FormLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) + { + typedef QPair LayoutItemPair; + typedef QVector LayoutItemPairs; + + QFormLayout *formLayout = qobject_cast(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + if (debugLayout) + qDebug() << "FormLayoutHelper::simplify"; + // Transform into vector of item pairs + const int rowCount = formLayout->rowCount(); + LayoutItemPairs pairs(rowCount, LayoutItemPair(0, 0)); + for (int i = formLayout->count() - 1; i >= 0; i--) { + int row, col,colspan; + getFormLayoutItemPosition(formLayout, i, &row, &col, 0, &colspan); + if (colspan > 1) { + pairs[row].first = pairs[row].second = formLayout->takeAt(i); + } else { + if (col == 0) + pairs[row].first = formLayout->takeAt(i); + else + pairs[row].second = formLayout->takeAt(i); + } + } + // Weed out empty ones + const int bottomCheckRow = qMin(rowCount, restrictionArea.y() + restrictionArea.height()); + for (int r = bottomCheckRow - 1; r >= restrictionArea.y(); r--) + if (LayoutInfo::isEmptyItem(pairs[r].first) && LayoutInfo::isEmptyItem(pairs[r].second)) { + delete pairs[r].first; + delete pairs[r].second; + pairs.remove(r); + } + const int simpleRowCount = pairs.size(); + if (simpleRowCount < rowCount) + formLayout = static_cast(recreateManagedLayout(core, widgetWithManagedLayout, formLayout)); + // repopulate + for (int r = 0; r < simpleRowCount; r++) { + const bool spanning = pairs[r].first == pairs[r].second; + if (spanning) { + formLayout->setItem(r, QFormLayout::SpanningRole, pairs[r].first); + } else { + formLayout->setItem(r, QFormLayout::LabelRole, pairs[r].first); + formLayout->setItem(r, QFormLayout::FieldRole, pairs[r].second); + } + } + } + +LayoutHelper *LayoutHelper::createLayoutHelper(int type) +{ + LayoutHelper *rc = 0; + switch (type) { + case LayoutInfo::HBox: + rc = new BoxLayoutHelper(Qt::Horizontal); + break; + case LayoutInfo::VBox: + rc = new BoxLayoutHelper(Qt::Vertical); + break; + case LayoutInfo::Grid: + rc = new GridLayoutHelper; + break; + case LayoutInfo::Form: + return new FormLayoutHelper; + default: + break; + } + Q_ASSERT(rc); + return rc; +} + +// ---- QLayoutSupport (LayoutDecorationExtension) +QLayoutSupport::QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent) : + QObject(parent), + m_formWindow(formWindow), + m_helper(helper), + m_widget(widget), + m_currentIndex(-1), + m_currentInsertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode) +{ +} + +QLayout * QLayoutSupport::layout() const +{ + return LayoutInfo::managedLayout(m_formWindow->core(), m_widget); +} + +void QLayoutSupport::hideIndicator(Indicator i) +{ + if (m_indicators[i]) + m_indicators[i]->hide(); +} + +void QLayoutSupport::showIndicator(Indicator i, const QRect &geometry, const QPalette &p) +{ + if (!m_indicators[i]) + m_indicators[i] = new qdesigner_internal::InvisibleWidget(m_widget); + QWidget *indicator = m_indicators[i]; + indicator->setAutoFillBackground(true); + indicator->setPalette(p); + indicator->setGeometry(geometry); + indicator->show(); + indicator->raise(); +} + +QLayoutSupport::~QLayoutSupport() +{ + delete m_helper; + for (int i = 0; i < NumIndicators; i++) + if (m_indicators[i]) + m_indicators[i]->deleteLater(); +} + +QGridLayout * QLayoutSupport::gridLayout() const +{ + return qobject_cast(LayoutInfo::managedLayout(m_formWindow->core(), m_widget)); +} + +QRect QLayoutSupport::itemInfo(int index) const +{ + return m_helper->itemInfo(LayoutInfo::managedLayout(m_formWindow->core(), m_widget), index); +} + +void QLayoutSupport::setInsertMode(InsertMode im) +{ + m_currentInsertMode = im; +} + +void QLayoutSupport::setCurrentCell(const QPair &cell) +{ + m_currentCell = cell; +} + +void QLayoutSupport::adjustIndicator(const QPoint &pos, int index) +{ + if (index == -1) { // first item goes anywhere + hideIndicator(LeftIndicator); + hideIndicator(TopIndicator); + hideIndicator(RightIndicator); + hideIndicator(BottomIndicator); + return; + } + m_currentIndex = index; + m_currentInsertMode = QDesignerLayoutDecorationExtension::InsertWidgetMode; + + QLayoutItem *item = layout()->itemAt(index); + const QRect g = extendedGeometry(index); + // ### cleanup + if (LayoutInfo::isEmptyItem(item)) { + // Empty grid/form cell. Draw a rectangle + QPalette redPalette; + redPalette.setColor(QPalette::Window, Qt::red); + + showIndicator(LeftIndicator, QRect(g.x(), g.y(), indicatorSize, g.height()), redPalette); + showIndicator(TopIndicator, QRect(g.x(), g.y(), g.width(), indicatorSize), redPalette); + showIndicator(RightIndicator, QRect(g.right(), g.y(), indicatorSize, g.height()), redPalette); + showIndicator(BottomIndicator, QRect(g.x(), g.bottom(), g.width(), indicatorSize), redPalette); + setCurrentCellFromIndicatorOnEmptyCell(m_currentIndex); + } else { + // Append/Insert. Draw a bar left/right or above/below + QPalette bluePalette; + bluePalette.setColor(QPalette::Window, Qt::blue); + hideIndicator(LeftIndicator); + hideIndicator(TopIndicator); + + const int fromRight = g.right() - pos.x(); + const int fromBottom = g.bottom() - pos.y(); + + const int fromLeft = pos.x() - g.x(); + const int fromTop = pos.y() - g.y(); + + const int fromLeftRight = qMin(fromRight, fromLeft ); + const int fromBottomTop = qMin(fromBottom, fromTop); + + const Qt::Orientation indicatorOrientation = fromLeftRight < fromBottomTop ? Qt::Vertical : Qt::Horizontal; + + if (supportsIndicatorOrientation(indicatorOrientation)) { + const QRect r(layout()->geometry().topLeft(), layout()->parentWidget()->size()); + switch (indicatorOrientation) { + case Qt::Vertical: { + hideIndicator(BottomIndicator); + const bool closeToLeft = fromLeftRight == fromLeft; + showIndicator(RightIndicator, QRect(closeToLeft ? g.x() : g.right() + 1 - indicatorSize, 0, indicatorSize, r.height()), bluePalette); + + const int incr = closeToLeft ? 0 : +1; + setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); + } + break; + case Qt::Horizontal: { + hideIndicator(RightIndicator); + const bool closeToTop = fromBottomTop == fromTop; + showIndicator(BottomIndicator, QRect(r.x(), closeToTop ? g.y() : g.bottom() + 1 - indicatorSize, r.width(), indicatorSize), bluePalette); + + const int incr = closeToTop ? 0 : +1; + setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); + } + break; + } + } else { + hideIndicator(RightIndicator); + hideIndicator(BottomIndicator); + } // can handle indicatorOrientation + } +} + +int QLayoutSupport::indexOf(QLayoutItem *i) const +{ + const QLayout *lt = layout(); + if (!lt) + return -1; + + int index = 0; + + while (QLayoutItem *item = lt->itemAt(index)) { + if (item == i) + return index; + + ++index; + } + + return -1; +} + +int QLayoutSupport::indexOf(QWidget *widget) const +{ + const QLayout *lt = layout(); + if (!lt) + return -1; + + int index = 0; + while (QLayoutItem *item = lt->itemAt(index)) { + if (item->widget() == widget) + return index; + + ++index; + } + + return -1; +} + +QList QLayoutSupport::widgets(QLayout *layout) const +{ + if (!layout) + return QList(); + + QList lst; + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + ++index; + + QWidget *widget = item->widget(); + if (widget && formWindow()->isManaged(widget)) + lst.append(widget); + } + + return lst; +} + +int QLayoutSupport::findItemAt(QGridLayout *gridLayout, int at_row, int at_column) +{ + return findGridItemAt(gridLayout, at_row, at_column); +} + +// Quick check whether simplify should be enabled for grids. May return false positives. +// Note: Calculating the occupied area does not work as spanning items may also be simplified. + +bool QLayoutSupport::canSimplifyQuickCheck(const QGridLayout *gl) +{ + if (!gl) + return false; + const int colCount = gl->columnCount(); + const int rowCount = gl->rowCount(); + if (colCount < 2 || rowCount < 2) + return false; + // try to find a spacer. + const int count = gl->count(); + for (int index = 0; index < count; index++) + if (LayoutInfo::isEmptyItem(gl->itemAt(index))) + return true; + return false; +} + +bool QLayoutSupport::canSimplifyQuickCheck(const QFormLayout *fl) +{ + return canSimplifyFormLayout(fl, QRect(QPoint(0, 0), QSize(32767, 32767))); +} + +// remove dummy spacers +bool QLayoutSupport::removeEmptyCells(QGridLayout *grid, const QRect &area) +{ + return removeEmptyCellsOnGrid(grid, area); +} + +void QLayoutSupport::createEmptyCells(QGridLayout *gridLayout) +{ + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + + const GridLayoutState::CellStates cs = GridLayoutState::cellStates(gs.widgetItemMap.values(), gs.rowCount, gs.colCount); + for (int c = 0; c < gs.colCount; c++) + for (int r = 0; r < gs.rowCount; r++) + if (needsSpacerItem(cs[r * gs.colCount + c])) { + const int existingItemIndex = findItemAt(gridLayout, r, c); + if (existingItemIndex == -1) + gridLayout->addItem(createGridSpacer(), r, c); + } +} + +bool QLayoutSupport::removeEmptyCells(QFormLayout *formLayout, const QRect &area) +{ + return removeEmptyCellsOnGrid(formLayout, area); +} + +void QLayoutSupport::createEmptyCells(QFormLayout *formLayout) +{ + // No spanning items here.. + if (const int rowCount = formLayout->rowCount()) + for (int c = 0; c < FormLayoutColumns; c++) + for (int r = 0; r < rowCount; r++) + if (findGridItemAt(formLayout, r, c) == -1) + formLayout->setItem(r, c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole, createFormSpacer()); +} + +int QLayoutSupport::findItemAt(const QPoint &pos) const +{ + if (!layout()) + return -1; + + const QLayout *lt = layout(); + const int count = lt->count(); + + if (count == 0) + return -1; + + int best = -1; + int bestIndex = -1; + + for (int index = 0; index < count; index++) { + QLayoutItem *item = lt->itemAt(index); + bool visible = true; + // When dragging widgets within layout, the source widget is invisible and must not be hit + if (const QWidget *w = item->widget()) + visible = w->isVisible(); + if (visible) { + const QRect g = item->geometry(); + + const int dist = (g.center() - pos).manhattanLength(); + if (best == -1 || dist < best) { + best = dist; + bestIndex = index; + } + } + } + return bestIndex; +} + +// ------------ QBoxLayoutSupport (LayoutDecorationExtension) +namespace { +class QBoxLayoutSupport: public QLayoutSupport +{ +public: + QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent = 0); + + virtual void insertWidget(QWidget *widget, const QPair &cell); + virtual void removeWidget(QWidget *widget); + virtual void simplify() {} + virtual void insertRow(int /*row*/) {} + virtual void insertColumn(int /*column*/) {} + + virtual int findItemAt(int /*at_row*/, int /*at_column*/) const { return -1; } + +private: + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); + virtual bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const; + virtual QRect extendedGeometry(int index) const; + + const Qt::Orientation m_orientation; +}; + +void QBoxLayoutSupport::removeWidget(QWidget *widget) +{ + QLayout *lt = layout(); + const int index = lt->indexOf(widget); + // Adjust the current cell in case a widget was dragged within the same layout to a position + // of higher index, which happens as follows: + // Drag start: The widget is hidden + // Drop: Current cell is stored, widget is removed and re-added, causing an index offset that needs to be compensated + QPair currCell = currentCell(); + switch (m_orientation) { + case Qt::Horizontal: + if (currCell.second > 0 && index < currCell.second ) { + currCell.second--; + setCurrentCell(currCell); + } + break; + case Qt::Vertical: + if (currCell.first > 0 && index < currCell.first) { + currCell.first--; + setCurrentCell(currCell); + } + break; + } + helper()->removeWidget(lt, widget); +} + +QBoxLayoutSupport::QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent) : + QLayoutSupport(formWindow, widget, new BoxLayoutHelper(orientation), parent), + m_orientation(orientation) +{ +} + +void QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(int index) +{ + qDebug() << "QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(): Warning: found a fake spacer inside a vbox layout at " << index; + setCurrentCell(qMakePair(0, 0)); +} + +void QBoxLayoutSupport::insertWidget(QWidget *widget, const QPair &cell) +{ + switch (m_orientation) { + case Qt::Horizontal: + helper()->insertWidget(layout(), QRect(cell.second, 0, 1, 1), widget); + break; + case Qt::Vertical: + helper()->insertWidget(layout(), QRect(0, cell.first, 1, 1), widget); + break; + } +} + +void QBoxLayoutSupport::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) +{ + if (m_orientation == Qt::Horizontal && indicatorOrientation == Qt::Vertical) { + setCurrentCell(qMakePair(0, index + increment)); + } else if (m_orientation == Qt::Vertical && indicatorOrientation == Qt::Horizontal) { + setCurrentCell(qMakePair(index + increment, 0)); + } +} + +bool QBoxLayoutSupport::supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const +{ + return m_orientation != indicatorOrientation; +} + +QRect QBoxLayoutSupport::extendedGeometry(int index) const +{ + QLayoutItem *item = layout()->itemAt(index); + // start off with item geometry + QRect g = item->geometry(); + + const QRect info = itemInfo(index); + + // On left border: extend to widget border + if (info.x() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.rx() = layout()->geometry().left(); + g.setTopLeft(topLeft); + } + + // On top border: extend to widget border + if (info.y() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.ry() = layout()->geometry().top(); + g.setTopLeft(topLeft); + } + + // is this the last item? + const QBoxLayout *box = static_cast(layout()); + if (index < box->count() -1) + return g; // Nope. + + // extend to widget border + QPoint bottomRight = g.bottomRight(); + switch (m_orientation) { + case Qt::Vertical: + bottomRight.ry() = layout()->geometry().bottom(); + break; + case Qt::Horizontal: + bottomRight.rx() = layout()->geometry().right(); + break; + } + g.setBottomRight(bottomRight); + return g; +} + +// -------------- Base class for QGridLayout-like support classes (LayoutDecorationExtension) +template +class GridLikeLayoutSupportBase: public QLayoutSupport +{ +public: + + GridLikeLayoutSupportBase(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = 0) : + QLayoutSupport(formWindow, widget, helper, parent) {} + + void insertWidget(QWidget *widget, const QPair &cell); + virtual void removeWidget(QWidget *widget) { helper()->removeWidget(layout(), widget); } + virtual int findItemAt(int row, int column) const; + +protected: + GridLikeLayout *gridLikeLayout() const { + return qobject_cast(LayoutInfo::managedLayout(formWindow()->core(), widget())); + } + +private: + + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); + virtual bool supportsIndicatorOrientation(Qt::Orientation) const { return true; } + + virtual QRect extendedGeometry(int index) const; + + // Overwrite to check the insertion position (if there are limits) + virtual void checkCellForInsertion(int * /*row*/, int * /*col*/) const {} +}; + +template +void GridLikeLayoutSupportBase::setCurrentCellFromIndicatorOnEmptyCell(int index) +{ + GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + + setInsertMode(InsertWidgetMode); + int row, column, rowspan, colspan; + + getGridItemPosition(grid, index, &row, &column, &rowspan, &colspan); + setCurrentCell(qMakePair(row, column)); +} + +template +void GridLikeLayoutSupportBase::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) { + const QRect info = itemInfo(index); + switch (indicatorOrientation) { + case Qt::Vertical: { + setInsertMode(InsertColumnMode); + int row = info.top(); + int column = increment ? info.right() + 1 : info.left(); + checkCellForInsertion(&row, &column); + setCurrentCell(qMakePair(row , column)); + } + break; + case Qt::Horizontal: { + setInsertMode(InsertRowMode); + int row = increment ? info.bottom() + 1 : info.top(); + int column = info.left(); + checkCellForInsertion(&row, &column); + setCurrentCell(qMakePair(row, column)); + } + break; + } +} + +template +void GridLikeLayoutSupportBase::insertWidget(QWidget *widget, const QPair &cell) +{ + helper()->insertWidget(layout(), QRect(cell.second, cell.first, 1, 1), widget); +} + +template +int GridLikeLayoutSupportBase::findItemAt(int at_row, int at_column) const +{ + GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + return findGridItemAt(grid, at_row, at_column); +} + +template +QRect GridLikeLayoutSupportBase::extendedGeometry(int index) const +{ + QLayoutItem *item = layout()->itemAt(index); + // start off with item geometry + QRect g = item->geometry(); + + const QRect info = itemInfo(index); + + // On left border: extend to widget border + if (info.x() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.rx() = layout()->geometry().left(); + g.setTopLeft(topLeft); + } + + // On top border: extend to widget border + if (info.y() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.ry() = layout()->geometry().top(); + g.setTopLeft(topLeft); + } + const GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + + // extend to widget border + QPoint bottomRight = g.bottomRight(); + if (gridRowCount(grid) == info.y()) + bottomRight.ry() = layout()->geometry().bottom(); + if (gridColumnCount(grid) == info.x()) + bottomRight.rx() = layout()->geometry().right(); + g.setBottomRight(bottomRight); + return g; +} + +// -------------- QGridLayoutSupport (LayoutDecorationExtension) +class QGridLayoutSupport: public GridLikeLayoutSupportBase +{ +public: + + QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + + virtual void simplify(); + virtual void insertRow(int row); + virtual void insertColumn(int column); + +private: +}; + +QGridLayoutSupport::QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : + GridLikeLayoutSupportBase(formWindow, widget, new GridLayoutHelper, parent) +{ +} + +void QGridLayoutSupport::insertRow(int row) +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutHelper::insertRow(grid, row); +} + +void QGridLayoutSupport::insertColumn(int column) +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutState state; + state.fromLayout(grid); + state.insertColumn(column); + state.applyToLayout(formWindow()->core(), widget()); +} + +void QGridLayoutSupport::simplify() +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutState state; + state.fromLayout(grid); + + const QRect fullArea = QRect(0, 0, state.colCount, state.rowCount); + if (state.simplify(fullArea, false)) + state.applyToLayout(formWindow()->core(), widget()); +} + +// -------------- QFormLayoutSupport (LayoutDecorationExtension) +class QFormLayoutSupport: public GridLikeLayoutSupportBase +{ +public: + QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + + virtual void simplify() {} + virtual void insertRow(int /*row*/) {} + virtual void insertColumn(int /*column*/) {} + +private: + virtual void checkCellForInsertion(int * row, int *col) const; +}; + +QFormLayoutSupport::QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : + GridLikeLayoutSupportBase(formWindow, widget, new FormLayoutHelper, parent) +{ +} + +void QFormLayoutSupport::checkCellForInsertion(int *row, int *col) const +{ + if (*col >= FormLayoutColumns) { // Clamp to 2 columns + *col = 1; + (*row)++; + } +} +} // anonymous namespace + +QLayoutSupport *QLayoutSupport::createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) +{ + const QLayout *layout = LayoutInfo::managedLayout(formWindow->core(), widget); + Q_ASSERT(layout); + QLayoutSupport *rc = 0; + switch (LayoutInfo::layoutType(formWindow->core(), layout)) { + case LayoutInfo::HBox: + rc = new QBoxLayoutSupport(formWindow, widget, Qt::Horizontal, parent); + break; + case LayoutInfo::VBox: + rc = new QBoxLayoutSupport(formWindow, widget, Qt::Vertical, parent); + break; + case LayoutInfo::Grid: + rc = new QGridLayoutSupport(formWindow, widget, parent); + break; + case LayoutInfo::Form: + rc = new QFormLayoutSupport(formWindow, widget, parent); + break; + default: + break; + } + Q_ASSERT(rc); + return rc; +} +} // namespace qdesigner_internal + +// -------------- QLayoutWidget +QLayoutWidget::QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent) + : QWidget(parent), m_formWindow(formWindow), + m_leftMargin(0), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0) +{ +} + +void QLayoutWidget::paintEvent(QPaintEvent*) +{ + if (m_formWindow->currentTool() != 0) + return; + + // only draw red borders if we're editting widgets + + QPainter p(this); + + QMap > excludedRowsForColumn; + QMap > excludedColumnsForRow; + + QLayout *lt = layout(); + QGridLayout *grid = qobject_cast(lt); + if (lt) { + if (const int count = lt->count()) { + p.setPen(QPen(QColor(255, 0, 0, 35), 1)); + for (int i = 0; i < count; i++) { + QLayoutItem *item = lt->itemAt(i); + if (grid) { + int row, column, rowSpan, columnSpan; + grid->getItemPosition(i, &row, &column, &rowSpan, &columnSpan); + QMap rows; + QMap columns; + for (int i = rowSpan; i > 1; i--) + rows[row + i - 2] = true; + for (int i = columnSpan; i > 1; i--) + columns[column + i - 2] = true; + + while (rowSpan > 0) { + excludedColumnsForRow[row + rowSpan - 1].unite(columns); + rowSpan--; + } + while (columnSpan > 0) { + excludedRowsForColumn[column + columnSpan - 1].unite(rows); + columnSpan--; + } + } + if (item->spacerItem()) { + const QRect geometry = item->geometry(); + if (!geometry.isNull()) + p.drawRect(geometry.adjusted(1, 1, -2, -2)); + } + } + } + } + if (grid) { + p.setPen(QPen(QColor(0, 0x80, 0, 0x80), 1)); + const int rowCount = grid->rowCount(); + const int columnCount = grid->columnCount(); + for (int i = 0; i < rowCount; i++) { + for (int j = 0; j < columnCount; j++) { + const QRect cellRect = grid->cellRect(i, j); + if (j < columnCount - 1 && excludedColumnsForRow.value(i).value(j, false) == false) { + const double y0 = (i == 0) + ? 0 : (grid->cellRect(i - 1, j).bottom() + cellRect.top()) / 2.0; + const double y1 = (i == rowCount - 1) + ? height() - 1 : (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; + const double x = (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; + p.drawLine(QPointF(x, y0), QPointF(x, y1)); + } + if (i < rowCount - 1 && excludedRowsForColumn.value(j).value(i, false) == false) { + const double x0 = (j == 0) + ? 0 : (grid->cellRect(i, j - 1).right() + cellRect.left()) / 2.0; + const double x1 = (j == columnCount - 1) + ? width() - 1 : (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; + const double y = (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; + p.drawLine(QPointF(x0, y), QPointF(x1, y)); + } + } + } + } + p.setPen(QPen(QColor(255, 0, 0, 128), 1)); + p.drawRect(0, 0, width() - 1, height() - 1); +} + +bool QLayoutWidget::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::LayoutRequest: { + (void) QWidget::event(e); + // Magic: We are layouted, but the parent is not.. + if (layout() && qdesigner_internal::LayoutInfo::layoutType(formWindow()->core(), parentWidget()) == qdesigner_internal::LayoutInfo::NoLayout) { + resize(layout()->totalMinimumSize().expandedTo(size())); + } + + update(); + + return true; + } + + default: + break; + } + + return QWidget::event(e); +} + +int QLayoutWidget::layoutLeftMargin() const +{ + if (m_leftMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(&margin, 0, 0, 0); + return margin; + } + return m_leftMargin; +} + +void QLayoutWidget::setLayoutLeftMargin(int layoutMargin) +{ + m_leftMargin = layoutMargin; + if (layout()) { + int newMargin = m_leftMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(newMargin, top, right, bottom); + } +} + +int QLayoutWidget::layoutTopMargin() const +{ + if (m_topMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, &margin, 0, 0); + return margin; + } + return m_topMargin; +} + +void QLayoutWidget::setLayoutTopMargin(int layoutMargin) +{ + m_topMargin = layoutMargin; + if (layout()) { + int newMargin = m_topMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, newMargin, right, bottom); + } +} + +int QLayoutWidget::layoutRightMargin() const +{ + if (m_rightMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, 0, &margin, 0); + return margin; + } + return m_rightMargin; +} + +void QLayoutWidget::setLayoutRightMargin(int layoutMargin) +{ + m_rightMargin = layoutMargin; + if (layout()) { + int newMargin = m_rightMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, top, newMargin, bottom); + } +} + +int QLayoutWidget::layoutBottomMargin() const +{ + if (m_bottomMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, 0, 0, &margin); + return margin; + } + return m_bottomMargin; +} + +void QLayoutWidget::setLayoutBottomMargin(int layoutMargin) +{ + m_bottomMargin = layoutMargin; + if (layout()) { + int newMargin = m_bottomMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, top, right, newMargin); + } +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qlayout_widget_p.h b/designer/lib/shared/qlayout_widget_p.h new file mode 100644 index 0000000..33bab5a --- /dev/null +++ b/designer/lib/shared/qlayout_widget_p.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QLAYOUT_WIDGET_H +#define QLAYOUT_WIDGET_H + +#include "shared_global_p.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class QGridLayout; +class QFormLayout; + +namespace qdesigner_internal { +// ---- LayoutProperties: Helper struct that stores all layout-relevant properties +// with functions to retrieve and apply to property sheets. Can be used to store the state +// for undo commands and while rebuilding layouts. +struct QDESIGNER_SHARED_EXPORT LayoutProperties +{ + LayoutProperties(); + void clear(); + + enum Margins { LeftMargin, TopMargin, RightMargin, BottomMargin, MarginCount }; + enum Spacings { Spacing, HorizSpacing, VertSpacing, SpacingsCount }; + + enum PropertyMask { + ObjectNameProperty = 0x1, + LeftMarginProperty = 0x2, TopMarginProperty = 0x4, RightMarginProperty = 0x8, BottomMarginProperty = 0x10, + SpacingProperty = 0x20, HorizSpacingProperty = 0x40, VertSpacingProperty = 0x80, + SizeConstraintProperty = 0x100, + FieldGrowthPolicyProperty = 0x200, RowWrapPolicyProperty = 0x400, LabelAlignmentProperty = 0x0800, FormAlignmentProperty = 0x1000, + BoxStretchProperty = 0x2000, GridRowStretchProperty = 0x4000, GridColumnStretchProperty = 0x8000, + GridRowMinimumHeightProperty = 0x10000, GridColumnMinimumWidthProperty = 0x20000, + AllProperties = 0xFFFF}; + + // return a PropertyMask of visible properties + static int visibleProperties(const QLayout *layout); + + // Retrieve from /apply to sheet: A property mask is returned indicating the properties found in the sheet + int fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask = AllProperties); + int toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask = AllProperties, bool applyChanged = true) const; + + int m_margins[MarginCount]; + bool m_marginsChanged[MarginCount]; + + int m_spacings[SpacingsCount]; + bool m_spacingsChanged[SpacingsCount]; + + QVariant m_objectName; // receives a PropertySheetStringValue + bool m_objectNameChanged; + QVariant m_sizeConstraint; + bool m_sizeConstraintChanged; + + bool m_fieldGrowthPolicyChanged; + QVariant m_fieldGrowthPolicy; + bool m_rowWrapPolicyChanged; + QVariant m_rowWrapPolicy; + bool m_labelAlignmentChanged; + QVariant m_labelAlignment; + bool m_formAlignmentChanged; + QVariant m_formAlignment; + + bool m_boxStretchChanged; + QVariant m_boxStretch; + + bool m_gridRowStretchChanged; + QVariant m_gridRowStretch; + + bool m_gridColumnStretchChanged; + QVariant m_gridColumnStretch; + + bool m_gridRowMinimumHeightChanged; + QVariant m_gridRowMinimumHeight; + + bool m_gridColumnMinimumWidthChanged; + QVariant m_gridColumnMinimumWidth; +}; + +// -- LayoutHelper: For use with the 'insert widget'/'delete widget' command, +// able to store and restore states. +// This could become part of 'QDesignerLayoutDecorationExtensionV2', +// but to keep any existing old extensions working, it is provided as +// separate class with a factory function. +class LayoutHelper { +protected: + LayoutHelper(); + +public: + virtual ~LayoutHelper(); + + static LayoutHelper *createLayoutHelper(int type); + + static int indexOf(const QLayout *lt, const QWidget *widget); + + // Return area of an item (x == columns) + QRect itemInfo(QLayout *lt, const QWidget *widget) const; + + virtual QRect itemInfo(QLayout *lt, int index) const = 0; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w) = 0; + virtual void removeWidget(QLayout *lt, QWidget *widget) = 0; + // Since 4.5: The 'morphing' feature requires an API for replacing widgets on layouts. + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after) = 0; + + // Simplify a grid, remove empty columns, rows within the rectangle + // The DeleteWidget command restricts the area to be simplified. + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const = 0; + virtual void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) = 0; + + // Push and pop a state. Can be used for implementing undo for + // simplify/row, column insertion commands, provided that + // the widgets remain the same. + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) = 0; + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) = 0; +}; + +// Base class for layout decoration extensions. +class QDESIGNER_SHARED_EXPORT QLayoutSupport: public QObject, public QDesignerLayoutDecorationExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerLayoutDecorationExtension) + +protected: + QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = 0); + +public: + virtual ~QLayoutSupport(); + + inline QDesignerFormWindowInterface *formWindow() const { return m_formWindow; } + + // DecorationExtension V2 + LayoutHelper* helper() const { return m_helper; } + + // DecorationExtension + virtual int currentIndex() const { return m_currentIndex; } + + virtual InsertMode currentInsertMode() const { return m_currentInsertMode; } + + virtual QPair currentCell() const { return m_currentCell; } + + virtual int findItemAt(const QPoint &pos) const; + virtual int indexOf(QWidget *widget) const; + virtual int indexOf(QLayoutItem *item) const; + + virtual void adjustIndicator(const QPoint &pos, int index); + + virtual QList widgets(QLayout *layout) const; + + // Pad empty cells with dummy spacers. Called by layouting commands. + static void createEmptyCells(QGridLayout *gridLayout); + // remove dummy spacers in the area. Returns false if there are non-empty items in the way + static bool removeEmptyCells(QGridLayout *gridLayout, const QRect &area); + static void createEmptyCells(QFormLayout *formLayout); // ditto. + static bool removeEmptyCells(QFormLayout *formLayout, const QRect &area); + + // grid helpers: find item index + static int findItemAt(QGridLayout *, int row, int column); + // grid helpers: Quick check whether simplify should be enabled for grids. May return false positives. + static bool canSimplifyQuickCheck(const QGridLayout *); + static bool canSimplifyQuickCheck(const QFormLayout *fl); + // Factory function, create layout support according to layout type of widget + static QLayoutSupport *createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + +protected: + // figure out insertion position and mode from indicator on empty cell if supported + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index) = 0; + // figure out insertion position and mode from indicator + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) = 0; + + // Overwrite to return the extended geometry of an item, that is, + // if it is a border item, include the widget border for the indicator to work correctly + virtual QRect extendedGeometry(int index) const = 0; + virtual bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const = 0; + + QRect itemInfo(int index) const; + QLayout *layout() const; + QGridLayout *gridLayout() const; + QWidget *widget() const { return m_widget; } + + void setInsertMode(InsertMode im); + void setCurrentCell(const QPair &cell); + +private: + enum Indicator { LeftIndicator, TopIndicator, RightIndicator, BottomIndicator, NumIndicators }; + + void hideIndicator(Indicator i); + void showIndicator(Indicator i, const QRect &geometry, const QPalette &); + + QDesignerFormWindowInterface *m_formWindow; + LayoutHelper* m_helper; + + QPointer m_widget; + QPointer m_indicators[NumIndicators]; + int m_currentIndex; + InsertMode m_currentInsertMode; + QPair m_currentCell; +}; +} // namespace qdesigner_internal + +// Red layout widget. +class QDESIGNER_SHARED_EXPORT QLayoutWidget: public QWidget +{ + Q_OBJECT +public: + explicit QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent = 0); + + int layoutLeftMargin() const; + void setLayoutLeftMargin(int layoutMargin); + + int layoutTopMargin() const; + void setLayoutTopMargin(int layoutMargin); + + int layoutRightMargin() const; + void setLayoutRightMargin(int layoutMargin); + + int layoutBottomMargin() const; + void setLayoutBottomMargin(int layoutMargin); + + inline QDesignerFormWindowInterface *formWindow() const { return m_formWindow; } + +protected: + virtual bool event(QEvent *e); + virtual void paintEvent(QPaintEvent *e); + +private: + QDesignerFormWindowInterface *m_formWindow; + int m_leftMargin; + int m_topMargin; + int m_rightMargin; + int m_bottomMargin; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGET_H diff --git a/designer/lib/shared/qscripthighlighter.cpp b/designer/lib/shared/qscripthighlighter.cpp new file mode 100644 index 0000000..34a0c19 --- /dev/null +++ b/designer/lib/shared/qscripthighlighter.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscripthighlighter_p.h" + +#include + +QT_BEGIN_NAMESPACE + +static const QSet &qscriptKeywords() { + static QSet keywords; + if (keywords.empty()) { + keywords.insert(QLatin1String("Infinity")); + keywords.insert(QLatin1String("NaN")); + keywords.insert(QLatin1String("abstract")); + keywords.insert(QLatin1String("boolean")); + keywords.insert(QLatin1String("break")); + keywords.insert(QLatin1String("byte")); + keywords.insert(QLatin1String("case")); + keywords.insert(QLatin1String("catch")); + keywords.insert(QLatin1String("char")); + keywords.insert(QLatin1String("class")); + keywords.insert(QLatin1String("const")); + keywords.insert(QLatin1String("constructor")); + keywords.insert(QLatin1String("continue")); + keywords.insert(QLatin1String("debugger")); + keywords.insert(QLatin1String("default")); + keywords.insert(QLatin1String("delete")); + keywords.insert(QLatin1String("do")); + keywords.insert(QLatin1String("double")); + keywords.insert(QLatin1String("else")); + keywords.insert(QLatin1String("enum")); + keywords.insert(QLatin1String("export")); + keywords.insert(QLatin1String("extends")); + keywords.insert(QLatin1String("false")); + keywords.insert(QLatin1String("final")); + keywords.insert(QLatin1String("finally")); + keywords.insert(QLatin1String("float")); + keywords.insert(QLatin1String("for")); + keywords.insert(QLatin1String("function")); + keywords.insert(QLatin1String("goto")); + keywords.insert(QLatin1String("if")); + keywords.insert(QLatin1String("implements")); + keywords.insert(QLatin1String("import")); + keywords.insert(QLatin1String("in")); + keywords.insert(QLatin1String("instanceof")); + keywords.insert(QLatin1String("int")); + keywords.insert(QLatin1String("interface")); + keywords.insert(QLatin1String("long")); + keywords.insert(QLatin1String("native")); + keywords.insert(QLatin1String("new")); + keywords.insert(QLatin1String("package")); + keywords.insert(QLatin1String("private")); + keywords.insert(QLatin1String("protected")); + keywords.insert(QLatin1String("public")); + keywords.insert(QLatin1String("return")); + keywords.insert(QLatin1String("short")); + keywords.insert(QLatin1String("static")); + keywords.insert(QLatin1String("super")); + keywords.insert(QLatin1String("switch")); + keywords.insert(QLatin1String("synchronized")); + keywords.insert(QLatin1String("this")); + keywords.insert(QLatin1String("throw")); + keywords.insert(QLatin1String("throws")); + keywords.insert(QLatin1String("transient")); + keywords.insert(QLatin1String("true")); + keywords.insert(QLatin1String("try")); + keywords.insert(QLatin1String("typeof")); + keywords.insert(QLatin1String("undefined")); + keywords.insert(QLatin1String("var")); + keywords.insert(QLatin1String("void")); + keywords.insert(QLatin1String("volatile")); + keywords.insert(QLatin1String("while")); + keywords.insert(QLatin1String("with")); // end + } + return keywords; +} + +static QSet alphaChars() { + QSet rc; + const QString alpha = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + foreach (QChar chr, alpha) + rc.insert(chr); + return rc; +} + +namespace qdesigner_internal { + +QScriptHighlighter::QScriptHighlighter(QTextDocument *parent) + : QSyntaxHighlighter(parent) +{ + m_numberFormat.setForeground(Qt::blue); + m_stringFormat.setForeground(Qt::darkGreen); + m_typeFormat.setForeground(Qt::darkMagenta); + m_keywordFormat.setForeground(Qt::darkYellow); + m_labelFormat.setForeground(Qt::darkRed); + m_commentFormat.setForeground(Qt::red); + //m_commentFormat.setFontFamily("times"); + m_commentFormat.setFontItalic(true); + m_preProcessorFormat.setForeground(Qt::darkBlue); +} + +void QScriptHighlighter::highlightBlock(const QString &text) +{ + // states + enum { + StateStandard, + StateCommentStart1, + StateCCommentStart2, + StateCppCommentStart2, + StateCComment, + StateCppComment, + StateCCommentEnd1, + StateCCommentEnd2, + StateStringStart, + StateString, + StateStringEnd, + StateString2Start, + StateString2, + StateString2End, + StateNumber, + StatePreProcessor, + NumStates + }; + // tokens + enum { + InputAlpha, + InputNumber, + InputAsterix, + InputSlash, + InputParen, + InputSpace, + InputHash, + InputQuotation, + InputApostrophe, + InputSep, + NumInputs + }; + + static const uchar table[NumStates][NumInputs] = { + { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStandard + { StateStandard, StateNumber, StateCCommentStart2, StateCppCommentStart2, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCommentStart1 + { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentStart2 + { StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // CppCommentStart2 + { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCComment + { StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // StateCppComment + { StateCComment, StateCComment, StateCCommentEnd1, StateCCommentEnd2, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentEnd1 + { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCCommentEnd2 + { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateStringStart + { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateString + { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStringEnd + { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2Start + { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2 + { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateString2End + { StateNumber, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateNumber + { StatePreProcessor, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard } // StatePreProcessor + }; + + QString buffer; + buffer.reserve(text.length()); + QTextCharFormat emptyFormat; + + int state = StateStandard; + const int previousState = previousBlockState(); + if (previousState != -1) + state = previousState; + + if (text.isEmpty()) { + setCurrentBlockState(previousState); + return; + } + + int input = -1; + int i = 0; + bool lastWasBackSlash = false; + bool makeLastStandard = false; + + static const QSet alphabeth = alphaChars(); + static const QString mathChars = QString::fromLatin1("xXeE"); + static const QString numbers = QString::fromLatin1("0123456789"); + bool questionMark = false; + QChar lastChar; + QString firstWord; + forever { + const QChar c = text.at(i); + + if (lastWasBackSlash) { + input = InputSep; + } else { + switch (c.toLatin1()) { + case '*': + input = InputAsterix; + break; + case '/': + input = InputSlash; + break; + case '(': case '[': case '{': + input = InputParen; + if (state == StateStandard + || state == StateNumber + || state == StatePreProcessor + || state == StateCCommentEnd2 + || state == StateCCommentEnd1 + || state == StateString2End + || state == StateStringEnd + ) + //blockData->parentheses << Parenthesis(Parenthesis::Open, c, i); + break; + case ')': case ']': case '}': + input = InputParen; + if (state == StateStandard + || state == StateNumber + || state == StatePreProcessor + || state == StateCCommentEnd2 + || state == StateCCommentEnd1 + || state == StateString2End + || state == StateStringEnd + ) { + //blockData->parentheses << Parenthesis(Parenthesis::Closed, c, i); + } + break; + case '#': + input = InputHash; + break; + case '"': + input = InputQuotation; + break; + case '\'': + input = InputApostrophe; + break; + case ' ': + input = InputSpace; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '0': + if (alphabeth.contains(lastChar) + && (!mathChars.contains(lastChar) || !numbers.contains(text.at(i - 1)))) { + input = InputAlpha; + } else { + if (input == InputAlpha && numbers.contains(lastChar)) + input = InputAlpha; + else + input = InputNumber; + } + break; + case ':': { + input = InputAlpha; + QChar nextChar = QLatin1Char(' '); + if (i < text.length() - 1) + nextChar = text.at(i + 1); + if (state == StateStandard && !questionMark && + lastChar != QLatin1Char(':') && nextChar != QLatin1Char(':')) { + for (int j = 0; j < i; ++j) { + if (format(j) == emptyFormat) + setFormat(j, 1, m_labelFormat); + } + } + break; + } + default: { + if (!questionMark && c == QLatin1Char('?')) + questionMark = true; + if (c.isLetter() || c == QLatin1Char('_')) + input = InputAlpha; + else + input = InputSep; + } break; + } + } + + lastWasBackSlash = !lastWasBackSlash && c == QLatin1Char('\\'); + + if (input == InputAlpha) + buffer += c; + + state = table[state][input]; + + switch (state) { + case StateStandard: { + setFormat(i, 1, emptyFormat); + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + if (!buffer.isEmpty() && input != InputAlpha ) { + highlightKeyword(i, buffer); + buffer.clear(); + } + } break; + case StateCommentStart1: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = true; + buffer.resize(0); + break; + case StateCCommentStart2: + setFormat(i - 1, 2, m_commentFormat); + makeLastStandard = false; + buffer.resize(0); + break; + case StateCppCommentStart2: + setFormat(i - 1, 2, m_commentFormat); + makeLastStandard = false; + buffer.resize(0); + break; + case StateCComment: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCppComment: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCCommentEnd1: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCCommentEnd2: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateStringStart: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_stringFormat); + buffer.resize(0); + break; + case StateStringEnd: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString2Start: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString2: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_stringFormat); + buffer.resize(0); + break; + case StateString2End: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateNumber: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat( i, 1, m_numberFormat); + buffer.resize(0); + break; + case StatePreProcessor: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_preProcessorFormat); + buffer.resize(0); + break; + } + + lastChar = c; + i++; + if (i >= text.length()) + break; + } + + highlightKeyword(text.length(), buffer); + + if (state == StateCComment + || state == StateCCommentEnd1 + || state == StateCCommentStart2 + ) { + state = StateCComment; + } else if (state == StateString) { + state = StateString; + } else if (state == StateString2) { + state = StateString2; + } else { + state = StateStandard; + } + + setCurrentBlockState(state); +} + +void QScriptHighlighter::highlightKeyword(int currentPos, const QString &buffer) +{ + if (buffer.isEmpty()) + return; + + if (buffer.at(0) == QLatin1Char('Q')) { + setFormat(currentPos - buffer.length(), buffer.length(), m_typeFormat); + } else { + if (qscriptKeywords().contains(buffer)) { + setFormat(currentPos - buffer.length(), buffer.length(), m_keywordFormat); + } + } +} +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qscripthighlighter_p.h b/designer/lib/shared/qscripthighlighter_p.h new file mode 100644 index 0000000..cd42bf0 --- /dev/null +++ b/designer/lib/shared/qscripthighlighter_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSCRIPTSYNTAXHIGHLIGHTER_H +#define QSCRIPTSYNTAXHIGHLIGHTER_H + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QScriptHighlighter : public QSyntaxHighlighter +{ +public: + explicit QScriptHighlighter(QTextDocument *parent); + virtual void highlightBlock(const QString &text); + +private: + void highlightKeyword(int currentPos, const QString &buffer); + + QTextCharFormat m_numberFormat; + QTextCharFormat m_stringFormat; + QTextCharFormat m_typeFormat; + QTextCharFormat m_keywordFormat; + QTextCharFormat m_labelFormat; + QTextCharFormat m_commentFormat; + QTextCharFormat m_preProcessorFormat; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/qsimpleresource.cpp b/designer/lib/shared/qsimpleresource.cpp new file mode 100644 index 0000000..deacaeb --- /dev/null +++ b/designer/lib/shared/qsimpleresource.cpp @@ -0,0 +1,418 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsimpleresource_p.h" +#include "widgetfactory_p.h" +#include "widgetdatabase_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList DomWidgetDataList; + typedef QList DomPropertyList; + typedef QList CustomWidgetInterfaces; +} + +namespace qdesigner_internal { + +bool QSimpleResource::m_warningsEnabled = true; + +QSimpleResource::QSimpleResource(QDesignerFormEditorInterface *core) : + QAbstractFormBuilder(), + m_core(core) +{ + QString workingDirectory = QDir::homePath(); + workingDirectory += QDir::separator(); + workingDirectory += QLatin1String(".designer"); + setWorkingDirectory(QDir(workingDirectory)); +#ifndef QT_FORMBUILDER_NO_SCRIPT + // Disable scripting in the editors. + formScriptRunner()-> setOptions(QFormScriptRunner::DisableScripts); +#endif +} + +QSimpleResource::~QSimpleResource() +{ + +} + +QBrush QSimpleResource::setupBrush(DomBrush *brush) +{ + return QAbstractFormBuilder::setupBrush(brush); +} + +DomBrush *QSimpleResource::saveBrush(const QBrush &brush) +{ + return QAbstractFormBuilder::saveBrush(brush); +} + +QIcon QSimpleResource::nameToIcon(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QSimpleResource::nameToIcon() is obsoleted"; + return QIcon(); +} + +QString QSimpleResource::iconToFilePath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::iconToFilePath() is obsoleted"; + return QString(); +} + +QString QSimpleResource::iconToQrcPath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::iconToQrcPath() is obsoleted"; + return QString(); +} + +QPixmap QSimpleResource::nameToPixmap(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QSimpleResource::nameToPixmap() is obsoleted"; + return QPixmap(); +} + +QString QSimpleResource::pixmapToFilePath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::pixmapToFilePath() is obsoleted"; + return QString(); +} + +QString QSimpleResource::pixmapToQrcPath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::pixmapToQrcPath() is obsoleted"; + return QString(); +} + +DomScript *QSimpleResource::createScript(const QString &script, ScriptSource source) +{ + if (script.isEmpty()) + return 0; + DomScript *domScript = new DomScript(); + switch (source) { + case ScriptExtension: + domScript->setAttributeSource(QLatin1String("extension")); + break; + case ScriptDesigner: + domScript->setAttributeSource(QLatin1String("designer")); + break; + case ScriptCustomWidgetPlugin: + domScript->setAttributeSource(QLatin1String("customwidgetplugin")); + break; + } + domScript->setAttributeLanguage(QLatin1String("Qt Script")); + domScript->setText(script); + return domScript; +} + +// Add a script to a list of DomScripts unless empty +void QSimpleResource::addScript(const QString &script, ScriptSource source, DomScripts &domScripts) +{ + if (DomScript *domScript = createScript(script, source)) { + domScripts += domScript; + } +} + +void QSimpleResource::addExtensionDataToDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget) +{ + QExtensionManager *emgr = core->extensionManager(); + if (QDesignerExtraInfoExtension *extra = qt_extension(emgr, widget)) { + extra->saveWidgetExtraInfo(ui_widget); + } + if (QDesignerScriptExtension *scriptExt = qt_extension(emgr, widget)) { + // Add internal state + const QVariantMap data = scriptExt->data(); + if (!data.empty()) { + // Convert the map to a DomState. + // We pass on the widget for property introspection. Thus, non-designable properties + // that have to be converted using QMetaObject (enums and the like) will work. + DomPropertyList properties; + const QVariantMap::const_iterator vcend = data.constEnd(); + for (QVariantMap::const_iterator it = data.constBegin(); it != vcend; ++it) { + if (DomProperty *prop = variantToDomProperty(afb, widget->metaObject(), it.key(), it.value())) + properties += prop; + } + if (!properties.empty()) { + DomWidgetData *domData = new DomWidgetData; + domData->setElementProperty(properties); + DomWidgetDataList domDataList; + domDataList += domData; + ui_widget->setElementWidgetData(domDataList); + } + + } + // Add script + const QString script = scriptExt->script(); + if (!script.isEmpty()) { + DomScripts domScripts = ui_widget->elementScript(); + addScript(script, ScriptExtension, domScripts); + ui_widget->setElementScript(domScripts); + } + } +} + +void QSimpleResource::applyExtensionDataFromDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget, bool applyState) +{ + QExtensionManager *emgr = core->extensionManager(); + if (QDesignerExtraInfoExtension *extra = qt_extension(emgr, widget)) { + extra->loadWidgetExtraInfo(ui_widget); + } + if (applyState) { + if (QDesignerScriptExtension *scriptExt = qt_extension(emgr, widget)) { + // Apply the state. + // We pass on the widget for property introspection. Thus, non-designable properties + // that have to be converted using QMetaObject (enums and the like) will work. + QVariantMap data; + DomWidgetDataList domDataList = ui_widget->elementWidgetData(); + if (!domDataList.empty()) { + foreach (const DomWidgetData *domData, domDataList) { + const DomPropertyList properties = domData->elementProperty(); + foreach(const DomProperty *prop, properties) { + const QVariant vprop = domPropertyToVariant(afb, widget->metaObject(), prop); + if (vprop.type() != QVariant::Invalid) + data.insert(prop->attributeName(), vprop); + } + } + } + scriptExt->setData(data); + } + } +} + +QString QSimpleResource::customWidgetScript(QDesignerFormEditorInterface *core, QObject *object) +{ + return customWidgetScript(core, qdesigner_internal::WidgetFactory::classNameOf(core, object)); +} + +bool QSimpleResource::hasCustomWidgetScript(QDesignerFormEditorInterface *, QObject *) +{ + return false; +} + +QString QSimpleResource::customWidgetScript(QDesignerFormEditorInterface *, const QString &) +{ + return QString(); +} + +bool QSimpleResource::setWarningsEnabled(bool warningsEnabled) +{ + const bool rc = m_warningsEnabled; + m_warningsEnabled = warningsEnabled; + return rc; +} + +bool QSimpleResource::warningsEnabled() +{ + return m_warningsEnabled; +} + +// Custom widgets handling helpers + +// Add unique fake slots and signals to lists +bool QSimpleResource::addFakeMethods(const DomSlots *domSlots, QStringList &fakeSlots, QStringList &fakeSignals) +{ + if (!domSlots) + return false; + + bool rc = false; + foreach (const QString &fakeSlot, domSlots->elementSlot()) + if (fakeSlots.indexOf(fakeSlot) == -1) { + fakeSlots += fakeSlot; + rc = true; + } + + foreach (const QString &fakeSignal, domSlots->elementSignal()) + if (fakeSignals.indexOf(fakeSignal) == -1) { + fakeSignals += fakeSignal; + rc = true; + } + return rc; +} + +void QSimpleResource::addFakeMethodsToWidgetDataBase(const DomCustomWidget *domCustomWidget, WidgetDataBaseItem *item) +{ + const DomSlots *domSlots = domCustomWidget->elementSlots(); + if (!domSlots) + return; + + // Merge in new slots, signals + QStringList fakeSlots = item->fakeSlots(); + QStringList fakeSignals = item->fakeSignals(); + if (addFakeMethods(domSlots, fakeSlots, fakeSignals)) { + item->setFakeSlots(fakeSlots); + item->setFakeSignals(fakeSignals); + } +} + +// Perform one iteration of adding the custom widgets to the database, +// looking up the base class and inheriting its data. +// Remove the succeeded custom widgets from the list. +// Classes whose base class could not be found are left in the list. + +void QSimpleResource::addCustomWidgetsToWidgetDatabase(const QDesignerFormEditorInterface *core, + QList& custom_widget_list) +{ + QDesignerWidgetDataBaseInterface *db = core->widgetDataBase(); + for (int i=0; i < custom_widget_list.size(); ) { + bool classInserted = false; + DomCustomWidget *custom_widget = custom_widget_list[i]; + const QString customClassName = custom_widget->elementClass(); + const QString base_class = custom_widget->elementExtends(); + QString includeFile; + IncludeType includeType = IncludeLocal; + if (const DomHeader *header = custom_widget->elementHeader()) { + includeFile = header->text(); + if (header->hasAttributeLocation() && header->attributeLocation() == QLatin1String("global")) + includeType = IncludeGlobal; + } + const bool domIsContainer = custom_widget->elementContainer(); + // Append a new item + if (base_class.isEmpty()) { + WidgetDataBaseItem *item = new WidgetDataBaseItem(customClassName); + item->setPromoted(false); + item->setGroup(QCoreApplication::translate("Designer", "Custom Widgets")); + item->setIncludeFile(buildIncludeFile(includeFile, includeType)); + item->setContainer(domIsContainer); + item->setCustom(true); + addFakeMethodsToWidgetDataBase(custom_widget, item); + db->append(item); + custom_widget_list.removeAt(i); + classInserted = true; + } else { + // Create a new entry cloned from base class. Note that this will ignore existing + // classes, eg, plugin custom widgets. + QDesignerWidgetDataBaseItemInterface *item = + appendDerived(db, customClassName, QCoreApplication::translate("Designer", "Promoted Widgets"), + base_class, + buildIncludeFile(includeFile, includeType), + true,true); + // Ok, base class found. + if (item) { + // Hack to accommodate for old UI-files in which "container" is not set properly: + // Apply "container" from DOM only if true (else, eg classes from QFrame might not accept + // dropping child widgets on them as container=false). This also allows for + // QWidget-derived stacked pages. + if (domIsContainer) + item->setContainer(domIsContainer); + + addFakeMethodsToWidgetDataBase(custom_widget, static_cast(item)); + custom_widget_list.removeAt(i); + classInserted = true; + } + } + // Skip failed item. + if (!classInserted) + i++; + } + +} + +void QSimpleResource::handleDomCustomWidgets(const QDesignerFormEditorInterface *core, + const DomCustomWidgets *dom_custom_widgets) +{ + if (dom_custom_widgets == 0) + return; + QList custom_widget_list = dom_custom_widgets->elementCustomWidget(); + // Attempt to insert each item derived from its base class. + // This should at most require two iterations in the event that the classes are out of order + // (derived first, max depth: promoted custom plugin = 2) + for (int iteration = 0; iteration < 2; iteration++) { + addCustomWidgetsToWidgetDatabase(core, custom_widget_list); + if (custom_widget_list.empty()) + return; + } + // Oops, there are classes left whose base class could not be found. + // Default them to QWidget with warnings. + const QString fallBackBaseClass = QLatin1String("QWidget"); + for (int i=0; i < custom_widget_list.size(); i++ ) { + DomCustomWidget *custom_widget = custom_widget_list[i]; + const QString customClassName = custom_widget->elementClass(); + const QString base_class = custom_widget->elementExtends(); + qDebug() << "** WARNING The base class " << base_class << " of the custom widget class " << customClassName + << " could not be found. Defaulting to " << fallBackBaseClass << '.'; + custom_widget->setElementExtends(fallBackBaseClass); + } + // One more pass. + addCustomWidgetsToWidgetDatabase(core, custom_widget_list); +} + +// ------------ FormBuilderClipboard + +FormBuilderClipboard::FormBuilderClipboard(QWidget *w) +{ + m_widgets += w; +} + +bool FormBuilderClipboard::empty() const +{ + return m_widgets.empty() && m_actions.empty(); +} +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/qsimpleresource_p.h b/designer/lib/shared/qsimpleresource_p.h new file mode 100644 index 0000000..5977422 --- /dev/null +++ b/designer/lib/shared/qsimpleresource_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSIMPLERESOURCE_H +#define QSIMPLERESOURCE_H + +#include "shared_global_p.h" +#include "abstractformbuilder.h" +#include + +QT_BEGIN_NAMESPACE + +class DomScript; +class DomCustomWidgets; +class DomCustomWidget; +class DomSlots; + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class WidgetDataBaseItem; + +class QDESIGNER_SHARED_EXPORT QSimpleResource : public QAbstractFormBuilder +{ +public: + explicit QSimpleResource(QDesignerFormEditorInterface *core); + virtual ~QSimpleResource(); + + QBrush setupBrush(DomBrush *brush); + DomBrush *saveBrush(const QBrush &brush); + + inline QDesignerFormEditorInterface *core() const + { return m_core; } + + // Query extensions for additional data + static void addExtensionDataToDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget); + static void applyExtensionDataFromDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget, + bool applyState); + // Enable warnings while saving. Turn off for backups. + static bool setWarningsEnabled(bool warningsEnabled); + static bool warningsEnabled(); + // Return the script returned by the CustomWidget codeTemplate API + static QString customWidgetScript(QDesignerFormEditorInterface *core, QObject *object); + static QString customWidgetScript(QDesignerFormEditorInterface *core, const QString &className); + static bool hasCustomWidgetScript(QDesignerFormEditorInterface *core, QObject *object); + + // Implementation for FormBuilder::createDomCustomWidgets() that adds + // the custom widgets to the widget database + static void handleDomCustomWidgets(const QDesignerFormEditorInterface *core, + const DomCustomWidgets *dom_custom_widgets); + +protected: + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath); + virtual QString iconToFilePath(const QIcon &pm) const; + virtual QString iconToQrcPath(const QIcon &pm) const; + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath); + virtual QString pixmapToFilePath(const QPixmap &pm) const; + virtual QString pixmapToQrcPath(const QPixmap &pm) const; + + enum ScriptSource { ScriptDesigner, ScriptExtension, ScriptCustomWidgetPlugin }; + static DomScript*createScript(const QString &script, ScriptSource source); + typedef QList DomScripts; + static void addScript(const QString &script, ScriptSource source, DomScripts &domScripts); + + static bool addFakeMethods(const DomSlots *domSlots, QStringList &fakeSlots, QStringList &fakeSignals); + +private: + static void addCustomWidgetsToWidgetDatabase(const QDesignerFormEditorInterface *core, + QList& custom_widget_list); + static void addFakeMethodsToWidgetDataBase(const DomCustomWidget *domCustomWidget, WidgetDataBaseItem *item); + + static bool m_warningsEnabled; + QDesignerFormEditorInterface *m_core; +}; + +// Contents of clipboard for formbuilder copy and paste operations +// (Actions and widgets) +struct QDESIGNER_SHARED_EXPORT FormBuilderClipboard { + typedef QList ActionList; + + FormBuilderClipboard() {} + FormBuilderClipboard(QWidget *w); + + bool empty() const; + + QWidgetList m_widgets; + ActionList m_actions; +}; + +// Base class for a form builder used in the editor that +// provides copy and paste.(move into base interface) +class QDESIGNER_SHARED_EXPORT QEditorFormBuilder : public QSimpleResource +{ +public: + explicit QEditorFormBuilder(QDesignerFormEditorInterface *core) : QSimpleResource(core) {} + + virtual bool copy(QIODevice *dev, const FormBuilderClipboard &selection) = 0; + virtual DomUI *copy(const FormBuilderClipboard &selection) = 0; + + // A widget parent needs to be specified, otherwise, the widget factory cannot locate the form window via parent + // and thus is not able to construct special widgets (QLayoutWidget). + virtual FormBuilderClipboard paste(DomUI *ui, QWidget *widgetParent, QObject *actionParent = 0) = 0; + virtual FormBuilderClipboard paste(QIODevice *dev, QWidget *widgetParent, QObject *actionParent = 0) = 0; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/qtresourceeditordialog.cpp b/designer/lib/shared/qtresourceeditordialog.cpp new file mode 100644 index 0000000..c974928 --- /dev/null +++ b/designer/lib/shared/qtresourceeditordialog.cpp @@ -0,0 +1,2223 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "abstractformeditor.h" +#include "qtresourceeditordialog_p.h" +#include "ui_qtresourceeditordialog.h" +#include "qtresourcemodel_p.h" +#include "iconloader_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *rccRootTag = "RCC"; +static const char *rccTag = "qresource"; +static const char *rccFileTag = "file"; +static const char *rccAliasAttribute = "alias"; +static const char *rccPrefixAttribute = "prefix"; +static const char *rccLangAttribute = "lang"; +static const char *SplitterPosition = "SplitterPosition"; +static const char *Geometry = "Geometry"; +static const char *QrcDialogC = "QrcDialog"; + +static QString msgOverwrite(const QString &fname) +{ + return QApplication::translate("QtResourceEditorDialog", "%1 already exists.\nDo you want to replace it?", 0, QApplication::UnicodeUTF8).arg(fname); +} + +static QString msgTagMismatch(const QString &got, const QString &expected) +{ + return QApplication::translate("QtResourceEditorDialog", "The file does not appear to be a resource file; element '%1' was found where '%2' was expected.").arg(got).arg(expected); +} + +namespace { + +// below 3 data classes should be derived from QSharedData and made implicit shared class +struct QtResourceFileData { + QString path; + QString alias; + bool operator==(const QtResourceFileData &other) const { + if (path == other.path && alias == other.alias) + return true; + return false; + } +}; + +struct QtResourcePrefixData { + QString prefix; + QString language; + QList resourceFileList; + bool operator==(const QtResourcePrefixData &other) const { + if (prefix == other.prefix && language == other.language && resourceFileList == other.resourceFileList) + return true; + return false; + } +}; + +struct QtQrcFileData { + QString qrcPath; + QList resourceList; + bool operator==(const QtQrcFileData &other) const { + if (qrcPath == other.qrcPath && resourceList == other.resourceList) + return true; + return false; + } +}; + +bool loadResourceFileData(const QDomElement &fileElem, QtResourceFileData *fileData, QString *errorMessage) +{ + if (!fileData) + return false; + + if (fileElem.tagName() != QLatin1String(rccFileTag)) { + *errorMessage = msgTagMismatch(fileElem.tagName(), QLatin1String(rccFileTag)); + return false; + } + + QtResourceFileData &data = *fileData; + + data.path = fileElem.text(); + data.alias = fileElem.attribute(QLatin1String(rccAliasAttribute)); + + return true; +} + +static bool loadResourcePrefixData(const QDomElement &prefixElem, QtResourcePrefixData *prefixData, QString *errorMessage) +{ + if (!prefixData) + return false; + + if (prefixElem.tagName() != QLatin1String(rccTag)) { + *errorMessage = msgTagMismatch(prefixElem.tagName(), QLatin1String(rccTag)); + return false; + } + + QtResourcePrefixData &data = *prefixData; + + data.prefix = prefixElem.attribute(QLatin1String(rccPrefixAttribute)); + data.language = prefixElem.attribute(QLatin1String(rccLangAttribute)); + QDomElement fileElem = prefixElem.firstChildElement(); + while (!fileElem.isNull()) { + QtResourceFileData fileData; + if (!loadResourceFileData(fileElem, &fileData, errorMessage)) + return false; + data.resourceFileList.append(fileData); + fileElem = fileElem.nextSiblingElement(); + } + return true; +} + +static bool loadQrcFileData(const QDomDocument &doc, const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage) +{ + if (!qrcFileData) + return false; + + QtQrcFileData &data = *qrcFileData; + + QDomElement docElem = doc.documentElement(); + if (docElem.tagName() != QLatin1String(rccRootTag)) { + *errorMessage = msgTagMismatch(docElem.tagName(), QLatin1String(rccRootTag)); + return false; + } + + QDomElement prefixElem = docElem.firstChildElement(); + while (!prefixElem.isNull()) { + QtResourcePrefixData prefixData; + if (!loadResourcePrefixData(prefixElem, &prefixData, errorMessage)) + return false; + data.resourceList.append(prefixData); + prefixElem = prefixElem.nextSiblingElement(); + } + + data.qrcPath = path; + + return true; +} + +QDomElement saveResourceFileData(QDomDocument &doc, const QtResourceFileData &fileData) +{ + QDomElement fileElem = doc.createElement(QLatin1String(rccFileTag)); + if (!fileData.alias.isEmpty()) + fileElem.setAttribute(QLatin1String(rccAliasAttribute), fileData.alias); + + QDomText textElem = doc.createTextNode(fileData.path); + fileElem.appendChild(textElem); + + return fileElem; +} + +QDomElement saveResourcePrefixData(QDomDocument &doc, const QtResourcePrefixData &prefixData) +{ + QDomElement prefixElem = doc.createElement(QLatin1String(rccTag)); + if (!prefixData.prefix.isEmpty()) + prefixElem.setAttribute(QLatin1String(rccPrefixAttribute), prefixData.prefix); + if (!prefixData.language.isEmpty()) + prefixElem.setAttribute(QLatin1String(rccLangAttribute), prefixData.language); + + QListIterator itFile(prefixData.resourceFileList); + while (itFile.hasNext()) { + QDomElement fileElem = saveResourceFileData(doc, itFile.next()); + prefixElem.appendChild(fileElem); + } + + return prefixElem; +} + +QDomDocument saveQrcFileData(const QtQrcFileData &qrcFileData) +{ + QDomDocument doc; + QDomElement docElem = doc.createElement(QLatin1String(rccRootTag)); + QListIterator itPrefix(qrcFileData.resourceList); + while (itPrefix.hasNext()) { + QDomElement prefixElem = saveResourcePrefixData(doc, itPrefix.next()); + + docElem.appendChild(prefixElem); + } + doc.appendChild(docElem); + + return doc; +} +// --------------- QtResourceFile +class QtResourceFile { +public: + friend class QtQrcManager; + + QString path() const { return m_path; } + QString alias() const { return m_alias; } + QString fullPath() const { return m_fullPath; } +private: + QtResourceFile() {} + + QString m_path; + QString m_alias; + QString m_fullPath; +}; + +class QtResourcePrefix { +public: + friend class QtQrcManager; + + QString prefix() const { return m_prefix; } + QString language() const { return m_language; } + QList resourceFiles() const { return m_resourceFiles; } +private: + QtResourcePrefix() {} + + QString m_prefix; + QString m_language; + QList m_resourceFiles; + +}; +// ------------------- QtQrcFile +class QtQrcFile { +public: + friend class QtQrcManager; + + QString path() const { return m_path; } + QString fileName() const { return m_fileName; } + QList resourcePrefixList() const { return m_resourcePrefixes; } + QtQrcFileData initialState() const { return m_initialState; } + +private: + QtQrcFile() { } + + void setPath(const QString &path) { + m_path = path; + QFileInfo fi(path); + m_fileName = fi.fileName(); + } + + QString m_path; + QString m_fileName; + QList m_resourcePrefixes; + QtQrcFileData m_initialState; +}; + +// ------------------ QtQrcManager +class QtQrcManager : public QObject +{ + Q_OBJECT +public: + QtQrcManager(QObject *parent = 0); + ~QtQrcManager(); + + QList qrcFiles() const; + + // helpers + QtQrcFile *qrcFileOf(const QString &path) const; + QtQrcFile *qrcFileOf(QtResourcePrefix *resourcePrefix) const; + QtQrcFile *qrcFileOf(QtResourceFile *resourceFile) const; + QtResourcePrefix *resourcePrefixOf(QtResourceFile *resourceFile) const; + + QtQrcFile *importQrcFile(const QtQrcFileData &qrcFileData, QtQrcFile *beforeQrcFile = 0); + void exportQrcFile(QtQrcFile *qrcFile, QtQrcFileData *qrcFileData) const; + + QList resourceFilesOf(const QString &resourceFullPath) const; + QIcon icon(const QString &resourceFullPath) const; + bool exists(const QString &resourceFullPath) const; + bool exists(QtQrcFile *qrcFile) const; + + QtQrcFile *prevQrcFile(QtQrcFile *qrcFile) const; + QtQrcFile *nextQrcFile(QtQrcFile *qrcFile) const; + QtResourcePrefix *prevResourcePrefix(QtResourcePrefix *resourcePrefix) const; + QtResourcePrefix *nextResourcePrefix(QtResourcePrefix *resourcePrefix) const; + QtResourceFile *prevResourceFile(QtResourceFile *resourceFile) const; + QtResourceFile *nextResourceFile(QtResourceFile *resourceFile) const; + + void clear(); + +public slots: + + QtQrcFile *insertQrcFile(const QString &path, QtQrcFile *beforeQrcFile = 0, bool newFile = false); + void moveQrcFile(QtQrcFile *qrcFile, QtQrcFile *beforeQrcFile); + void setInitialState(QtQrcFile *qrcFile, const QtQrcFileData &initialState); + void removeQrcFile(QtQrcFile *qrcFile); + + QtResourcePrefix *insertResourcePrefix(QtQrcFile *qrcFile, const QString &prefix, + const QString &language, QtResourcePrefix *beforeResourcePrefix = 0); + void moveResourcePrefix(QtResourcePrefix *resourcePrefix, QtResourcePrefix *beforeResourcePrefix); // the same qrc file??? + void changeResourcePrefix(QtResourcePrefix *resourcePrefix, const QString &newPrefix); + void changeResourceLanguage(QtResourcePrefix *resourcePrefix, const QString &newLanguage); + void removeResourcePrefix(QtResourcePrefix *resourcePrefix); + + QtResourceFile *insertResourceFile(QtResourcePrefix *resourcePrefix, const QString &path, + const QString &alias, QtResourceFile *beforeResourceFile = 0); + void moveResourceFile(QtResourceFile *resourceFile, QtResourceFile *beforeResourceFile); // the same prefix??? + void changeResourceAlias(QtResourceFile *resourceFile, const QString &newAlias); + void removeResourceFile(QtResourceFile *resourceFile); + +signals: + void qrcFileInserted(QtQrcFile *qrcFile); + void qrcFileMoved(QtQrcFile *qrcFile, QtQrcFile *oldBeforeQrcFile); + void qrcFileRemoved(QtQrcFile *qrcFile); + + void resourcePrefixInserted(QtResourcePrefix *resourcePrefix); + void resourcePrefixMoved(QtResourcePrefix *resourcePrefix, QtResourcePrefix *oldBeforeResourcePrefix); + void resourcePrefixChanged(QtResourcePrefix *resourcePrefix, const QString &oldPrefix); + void resourceLanguageChanged(QtResourcePrefix *resourcePrefix, const QString &oldLanguage); + void resourcePrefixRemoved(QtResourcePrefix *resourcePrefix); + + void resourceFileInserted(QtResourceFile *resourceFile); + void resourceFileMoved(QtResourceFile *resourceFile, QtResourceFile *oldBeforeResourceFile); + void resourceAliasChanged(QtResourceFile *resourceFile, const QString &oldAlias); + void resourceFileRemoved(QtResourceFile *resourceFile); +private: + + QList m_qrcFiles; + QMap m_pathToQrc; + QMap m_qrcFileToExists; + QMap m_prefixToQrc; + QMap m_fileToPrefix; + QMap > m_fullPathToResourceFiles; + QMap m_fullPathToIcon; + QMap m_fullPathToExists; +}; + +QtQrcManager::QtQrcManager(QObject *parent) + : QObject(parent) +{ + +} + +QtQrcManager::~QtQrcManager() +{ + clear(); +} + +QList QtQrcManager::qrcFiles() const +{ + return m_qrcFiles; +} + +QtQrcFile *QtQrcManager::qrcFileOf(const QString &path) const +{ + return m_pathToQrc.value(path); +} + +QtQrcFile *QtQrcManager::qrcFileOf(QtResourcePrefix *resourcePrefix) const +{ + return m_prefixToQrc.value(resourcePrefix); +} + +QtQrcFile *QtQrcManager::qrcFileOf(QtResourceFile *resourceFile) const +{ + return qrcFileOf(resourcePrefixOf(resourceFile)); +} + +QtResourcePrefix *QtQrcManager::resourcePrefixOf(QtResourceFile *resourceFile) const +{ + return m_fileToPrefix.value(resourceFile); +} + +QtQrcFile *QtQrcManager::importQrcFile(const QtQrcFileData &qrcFileData, QtQrcFile *beforeQrcFile) +{ + QtQrcFile *qrcFile = insertQrcFile(qrcFileData.qrcPath, beforeQrcFile); + if (!qrcFile) + return 0; + QListIterator itPrefix(qrcFileData.resourceList); + while (itPrefix.hasNext()) { + const QtResourcePrefixData &prefixData = itPrefix.next(); + QtResourcePrefix *resourcePrefix = insertResourcePrefix(qrcFile, prefixData.prefix, prefixData.language, 0); + QListIterator itFile(prefixData.resourceFileList); + while (itFile.hasNext()) { + const QtResourceFileData &fileData = itFile.next(); + insertResourceFile(resourcePrefix, fileData.path, fileData.alias, 0); + } + } + setInitialState(qrcFile, qrcFileData); + return qrcFile; +} + +void QtQrcManager::exportQrcFile(QtQrcFile *qrcFile, QtQrcFileData *qrcFileData) const +{ + if (!qrcFileData) + return; + + if (!qrcFile) + return; + + QtQrcFileData &data = *qrcFileData; + + QList resourceList; + + QList resourcePrefixes = qrcFile->resourcePrefixList(); + QListIterator itPrefix(resourcePrefixes); + while (itPrefix.hasNext()) { + QList resourceFileList; + + QtResourcePrefix *prefix = itPrefix.next(); + + QList resourceFiles = prefix->resourceFiles(); + QListIterator itFile(resourceFiles); + while (itFile.hasNext()) { + QtResourceFile *file = itFile.next(); + QtResourceFileData fileData; + fileData.path = file->path(); + fileData.alias = file->alias(); + resourceFileList << fileData; + } + QtResourcePrefixData prefixData; + prefixData.prefix = prefix->prefix(); + prefixData.language = prefix->language(); + prefixData.resourceFileList = resourceFileList; + + resourceList << prefixData; + } + data = QtQrcFileData(); + data.qrcPath = qrcFile->path(); + data.resourceList = resourceList; +} + +QList QtQrcManager::resourceFilesOf(const QString &resourcePath) const +{ + return m_fullPathToResourceFiles.value(resourcePath); +} + +QIcon QtQrcManager::icon(const QString &resourceFullPath) const +{ + return m_fullPathToIcon.value(resourceFullPath); +} + +bool QtQrcManager::exists(const QString &resourceFullPath) const +{ + return m_fullPathToExists.value(resourceFullPath, false); +} + +bool QtQrcManager::exists(QtQrcFile *qrcFile) const +{ + return m_qrcFileToExists.value(qrcFile, false); +} + +QtQrcFile *QtQrcManager::prevQrcFile(QtQrcFile *qrcFile) const +{ + if (!qrcFile) + return 0; + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx <= 0) + return 0; + return m_qrcFiles.at(idx - 1); +} + +QtQrcFile *QtQrcManager::nextQrcFile(QtQrcFile *qrcFile) const +{ + if (!qrcFile) + return 0; + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0 || idx == m_qrcFiles.size() - 1) + return 0; + return m_qrcFiles.at(idx + 1); +} + +QtResourcePrefix *QtQrcManager::prevResourcePrefix(QtResourcePrefix *resourcePrefix) const +{ + if (!resourcePrefix) + return 0; + QList prefixes = qrcFileOf(resourcePrefix)->resourcePrefixList(); + const int idx = prefixes.indexOf(resourcePrefix); + if (idx <= 0) + return 0; + return prefixes.at(idx - 1); +} + +QtResourcePrefix *QtQrcManager::nextResourcePrefix(QtResourcePrefix *resourcePrefix) const +{ + if (!resourcePrefix) + return 0; + QList prefixes = qrcFileOf(resourcePrefix)->resourcePrefixList(); + const int idx = prefixes.indexOf(resourcePrefix); + if (idx < 0 || idx == prefixes.size() - 1) + return 0; + return prefixes.at(idx + 1); +} + +QtResourceFile *QtQrcManager::prevResourceFile(QtResourceFile *resourceFile) const +{ + if (!resourceFile) + return 0; + QList files = resourcePrefixOf(resourceFile)->resourceFiles(); + const int idx = files.indexOf(resourceFile); + if (idx <= 0) + return 0; + return files.at(idx - 1); +} + +QtResourceFile *QtQrcManager::nextResourceFile(QtResourceFile *resourceFile) const +{ + if (!resourceFile) + return 0; + QList files = resourcePrefixOf(resourceFile)->resourceFiles(); + const int idx = files.indexOf(resourceFile); + if (idx < 0 || idx == files.size() - 1) + return 0; + return files.at(idx + 1); +} + +void QtQrcManager::clear() +{ + QList oldQrcFiles = qrcFiles(); + QListIterator it(oldQrcFiles); + while (it.hasNext()) + removeQrcFile(it.next()); +} + +QtQrcFile *QtQrcManager::insertQrcFile(const QString &path, QtQrcFile *beforeQrcFile, bool newFile) +{ + if (m_pathToQrc.contains(path)) + return 0; + + int idx = m_qrcFiles.indexOf(beforeQrcFile); + if (idx < 0) + idx = m_qrcFiles.size(); + + QtQrcFile *qrcFile = new QtQrcFile(); + qrcFile->setPath(path); + + m_qrcFiles.insert(idx, qrcFile); + m_pathToQrc[path] = qrcFile; + + const QFileInfo fi(path); + m_qrcFileToExists[qrcFile] = fi.exists() || newFile; + + emit qrcFileInserted(qrcFile); + return qrcFile; +} + +void QtQrcManager::moveQrcFile(QtQrcFile *qrcFile, QtQrcFile *beforeQrcFile) +{ + if (qrcFile == beforeQrcFile) + return; + + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0) + return; + + int beforeIdx = m_qrcFiles.indexOf(beforeQrcFile); + if (beforeIdx < 0) + beforeIdx = m_qrcFiles.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtQrcFile *oldBefore = 0; + if (idx < m_qrcFiles.size() - 1) + oldBefore = m_qrcFiles.at(idx + 1); + + m_qrcFiles.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + m_qrcFiles.insert(beforeIdx, qrcFile); + + emit qrcFileMoved(qrcFile, oldBefore); +} + +void QtQrcManager::setInitialState(QtQrcFile *qrcFile, const QtQrcFileData &initialState) +{ + qrcFile->m_initialState = initialState; +} + +void QtQrcManager::removeQrcFile(QtQrcFile *qrcFile) +{ + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0) + return; + + QList resourcePrefixes = qrcFile->resourcePrefixList(); + QListIterator it(resourcePrefixes); + while (it.hasNext()) + removeResourcePrefix(it.next()); + + emit qrcFileRemoved(qrcFile); + + m_qrcFiles.removeAt(idx); + m_pathToQrc.remove(qrcFile->path()); + m_qrcFileToExists.remove(qrcFile); + delete qrcFile; +} + +QtResourcePrefix *QtQrcManager::insertResourcePrefix(QtQrcFile *qrcFile, const QString &prefix, + const QString &language, QtResourcePrefix *beforeResourcePrefix) +{ + if (!qrcFile) + return 0; + + int idx = qrcFile->m_resourcePrefixes.indexOf(beforeResourcePrefix); + if (idx < 0) + idx = qrcFile->m_resourcePrefixes.size(); + + QtResourcePrefix *resourcePrefix = new QtResourcePrefix(); + resourcePrefix->m_prefix = prefix; + resourcePrefix->m_language = language; + + qrcFile->m_resourcePrefixes.insert(idx, resourcePrefix); + m_prefixToQrc[resourcePrefix] = qrcFile; + + emit resourcePrefixInserted(resourcePrefix); + return resourcePrefix; +} + +void QtQrcManager::moveResourcePrefix(QtResourcePrefix *resourcePrefix, QtResourcePrefix *beforeResourcePrefix) +{ + if (resourcePrefix == beforeResourcePrefix) + return; + + QtQrcFile *qrcFile = qrcFileOf(resourcePrefix); + if (!qrcFile) + return; + + if (beforeResourcePrefix && qrcFileOf(beforeResourcePrefix) != qrcFile) + return; + + const int idx = qrcFile->m_resourcePrefixes.indexOf(resourcePrefix); + + int beforeIdx = qrcFile->m_resourcePrefixes.indexOf(beforeResourcePrefix); + if (beforeIdx < 0) + beforeIdx = qrcFile->m_resourcePrefixes.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtResourcePrefix *oldBefore = 0; + if (idx < qrcFile->m_resourcePrefixes.size() - 1) + oldBefore = qrcFile->m_resourcePrefixes.at(idx + 1); + + qrcFile->m_resourcePrefixes.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + qrcFile->m_resourcePrefixes.insert(beforeIdx, resourcePrefix); + + emit resourcePrefixMoved(resourcePrefix, oldBefore); +} + +void QtQrcManager::changeResourcePrefix(QtResourcePrefix *resourcePrefix, const QString &newPrefix) +{ + if (!resourcePrefix) + return; + + const QString oldPrefix = resourcePrefix->m_prefix; + if (oldPrefix == newPrefix) + return; + + resourcePrefix->m_prefix = newPrefix; + + emit resourcePrefixChanged(resourcePrefix, oldPrefix); +} + +void QtQrcManager::changeResourceLanguage(QtResourcePrefix *resourcePrefix, const QString &newLanguage) +{ + if (!resourcePrefix) + return; + + const QString oldLanguage = resourcePrefix->m_language; + if (oldLanguage == newLanguage) + return; + + resourcePrefix->m_language = newLanguage; + + emit resourceLanguageChanged(resourcePrefix, oldLanguage); +} + +void QtQrcManager::removeResourcePrefix(QtResourcePrefix *resourcePrefix) +{ + QtQrcFile *qrcFile = qrcFileOf(resourcePrefix); + if (!qrcFile) + return; + + const int idx = qrcFile->m_resourcePrefixes.indexOf(resourcePrefix); + + QList resourceFiles = resourcePrefix->resourceFiles(); + QListIterator it(resourceFiles); + while (it.hasNext()) + removeResourceFile(it.next()); + + emit resourcePrefixRemoved(resourcePrefix); + + qrcFile->m_resourcePrefixes.removeAt(idx); + m_prefixToQrc.remove(resourcePrefix); + delete resourcePrefix; +} + +QtResourceFile *QtQrcManager::insertResourceFile(QtResourcePrefix *resourcePrefix, const QString &path, + const QString &alias, QtResourceFile *beforeResourceFile) +{ + if (!resourcePrefix) + return 0; + + int idx = resourcePrefix->m_resourceFiles.indexOf(beforeResourceFile); + if (idx < 0) + idx = resourcePrefix->m_resourceFiles.size(); + + QtResourceFile *resourceFile = new QtResourceFile(); + resourceFile->m_path = path; + resourceFile->m_alias = alias; + const QFileInfo fi(qrcFileOf(resourcePrefix)->path()); + const QDir dir(fi.absolutePath()); + const QString fullPath = dir.absoluteFilePath(path); + resourceFile->m_fullPath = fullPath; + + resourcePrefix->m_resourceFiles.insert(idx, resourceFile); + m_fileToPrefix[resourceFile] = resourcePrefix; + m_fullPathToResourceFiles[fullPath].append(resourceFile); + if (!m_fullPathToIcon.contains(fullPath)) { + m_fullPathToIcon[fullPath] = QIcon(fullPath); + const QFileInfo fullInfo(fullPath); + m_fullPathToExists[fullPath] = fullInfo.exists(); + } + + emit resourceFileInserted(resourceFile); + return resourceFile; +} + +void QtQrcManager::moveResourceFile(QtResourceFile *resourceFile, QtResourceFile *beforeResourceFile) +{ + if (resourceFile == beforeResourceFile) + return; + + QtResourcePrefix *resourcePrefix = resourcePrefixOf(resourceFile); + if (!resourcePrefix) + return; + + if (beforeResourceFile && resourcePrefixOf(beforeResourceFile) != resourcePrefix) + return; + + const int idx = resourcePrefix->m_resourceFiles.indexOf(resourceFile); + + int beforeIdx = resourcePrefix->m_resourceFiles.indexOf(beforeResourceFile); + if (beforeIdx < 0) + beforeIdx = resourcePrefix->m_resourceFiles.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtResourceFile *oldBefore = 0; + if (idx < resourcePrefix->m_resourceFiles.size() - 1) + oldBefore = resourcePrefix->m_resourceFiles.at(idx + 1); + + resourcePrefix->m_resourceFiles.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + resourcePrefix->m_resourceFiles.insert(beforeIdx, resourceFile); + + emit resourceFileMoved(resourceFile, oldBefore); +} + +void QtQrcManager::changeResourceAlias(QtResourceFile *resourceFile, const QString &newAlias) +{ + if (!resourceFile) + return; + + const QString oldAlias = resourceFile->m_alias; + if (oldAlias == newAlias) + return; + + resourceFile->m_alias = newAlias; + + emit resourceAliasChanged(resourceFile, oldAlias); +} + +void QtQrcManager::removeResourceFile(QtResourceFile *resourceFile) +{ + QtResourcePrefix *resourcePrefix = resourcePrefixOf(resourceFile); + if (!resourcePrefix) + return; + + const int idx = resourcePrefix->m_resourceFiles.indexOf(resourceFile); + + emit resourceFileRemoved(resourceFile); + + resourcePrefix->m_resourceFiles.removeAt(idx); + m_fileToPrefix.remove(resourceFile); + const QString fullPath = resourceFile->fullPath(); + m_fullPathToResourceFiles[fullPath].removeAll(resourceFile); // optimize me + if (m_fullPathToResourceFiles[fullPath].isEmpty()) { + m_fullPathToResourceFiles.remove(fullPath); + m_fullPathToIcon.remove(fullPath); + m_fullPathToExists.remove(fullPath); + } + delete resourceFile; +} + + + +} + +// ----------------- QtResourceEditorDialogPrivate +class QtResourceEditorDialogPrivate +{ + QtResourceEditorDialog *q_ptr; + Q_DECLARE_PUBLIC(QtResourceEditorDialog) +public: + QtResourceEditorDialogPrivate(); + + void slotQrcFileInserted(QtQrcFile *qrcFile); + void slotQrcFileMoved(QtQrcFile *qrcFile); + void slotQrcFileRemoved(QtQrcFile *qrcFile); + + QStandardItem *insertResourcePrefix(QtResourcePrefix *resourcePrefix); + + void slotResourcePrefixInserted(QtResourcePrefix *resourcePrefix) { insertResourcePrefix(resourcePrefix); } + void slotResourcePrefixMoved(QtResourcePrefix *resourcePrefix); + void slotResourcePrefixChanged(QtResourcePrefix *resourcePrefix); + void slotResourceLanguageChanged(QtResourcePrefix *resourcePrefix); + void slotResourcePrefixRemoved(QtResourcePrefix *resourcePrefix); + void slotResourceFileInserted(QtResourceFile *resourceFile); + void slotResourceFileMoved(QtResourceFile *resourceFile); + void slotResourceAliasChanged(QtResourceFile *resourceFile); + void slotResourceFileRemoved(QtResourceFile *resourceFile); + + void slotCurrentQrcFileChanged(QListWidgetItem *item); + void slotCurrentTreeViewItemChanged(const QModelIndex &index); + void slotListWidgetContextMenuRequested(const QPoint &pos); + void slotTreeViewContextMenuRequested(const QPoint &pos); + void slotTreeViewItemChanged(QStandardItem *item); + + void slotNewQrcFile(); + void slotImportQrcFile(); + void slotRemoveQrcFile(); + void slotMoveUpQrcFile(); + void slotMoveDownQrcFile(); + + void slotNewPrefix(); + void slotAddFiles(); + void slotChangePrefix(); + void slotChangeLanguage(); + void slotChangeAlias(); + void slotClonePrefix(); + void slotRemove(); + void slotMoveUp(); + void slotMoveDown(); + + bool loadQrcFile(const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage); + bool loadQrcFile(const QString &path, QtQrcFileData *qrcFileData); + bool saveQrcFile(const QtQrcFileData &qrcFileData); + + QString qrcFileText(QtQrcFile *qrcFile) const; + + QMessageBox::StandardButton warning(const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) const; + + QString browseForNewLocation(const QString &resourceFile, const QDir &rootDir) const; + QString copyResourceFile(const QString &resourceFile, const QString &destPath) const; + QtResourceFile *getCurrentResourceFile() const; + QtResourcePrefix *getCurrentResourcePrefix() const; + void selectTreeRow(QStandardItem *item); + QString getSaveFileNameWithExtension(QWidget *parent, + const QString &title, QString dir, const QString &filter, const QString &extension) const; + QString qrcStartDirectory() const; + + Ui::QtResourceEditorDialog m_ui; + QDesignerFormEditorInterface *m_core; + QtResourceModel *m_resourceModel; + QDesignerDialogGuiInterface *m_dlgGui; + QtQrcManager *m_qrcManager; + QList m_initialState; + + QMap m_qrcFileToItem; + QMap m_itemToQrcFile; + QMap m_resourcePrefixToPrefixItem; + QMap m_resourcePrefixToLanguageItem; + QMap m_prefixItemToResourcePrefix; + QMap m_languageItemToResourcePrefix; + QMap m_resourceFileToPathItem; + QMap m_resourceFileToAliasItem; + QMap m_pathItemToResourceFile; + QMap m_aliasItemToResourceFile; + + bool m_ignoreCurrentChanged; + bool m_firstQrcFileDialog; + QtQrcFile *m_currentQrcFile; + + QAction *m_newQrcFileAction; + QAction *m_importQrcFileAction; + QAction *m_removeQrcFileAction; + QAction *m_moveUpQrcFileAction; + QAction *m_moveDownQrcFileAction; + + QAction *m_newPrefixAction; + QAction *m_addResourceFileAction; + QAction *m_changePrefixAction; + QAction *m_changeLanguageAction; + QAction *m_changeAliasAction; + QAction *m_clonePrefixAction; + QAction *m_moveUpAction; + QAction *m_moveDownAction; + QAction *m_removeAction; + + QStandardItemModel *m_treeModel; + QItemSelectionModel *m_treeSelection; +}; + +QtResourceEditorDialogPrivate::QtResourceEditorDialogPrivate() : + q_ptr(0), + m_core(0), + m_resourceModel(0), + m_dlgGui(0), + m_qrcManager(0), + m_ignoreCurrentChanged(false), + m_firstQrcFileDialog(true), + m_currentQrcFile(0), + m_newQrcFileAction(0), + m_importQrcFileAction(0), + m_removeQrcFileAction(0), + m_moveUpQrcFileAction(0), + m_moveDownQrcFileAction(0), + m_newPrefixAction(0), + m_addResourceFileAction(0), + m_changePrefixAction(0), + m_changeLanguageAction(0), + m_changeAliasAction(0), + m_clonePrefixAction(0), + m_moveUpAction(0), + m_moveDownAction(0), + m_removeAction(0), + m_treeModel(0), + m_treeSelection(0) +{ +} + +QMessageBox::StandardButton QtResourceEditorDialogPrivate::warning(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) const +{ + return m_dlgGui->message(q_ptr, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, title, text, buttons, defaultButton); +} + +QString QtResourceEditorDialogPrivate::qrcFileText(QtQrcFile *qrcFile) const +{ + const QString path = qrcFile->path(); + const QString fileName = qrcFile->fileName(); + const QFileInfo fi(path); + if (fi.exists() && !fi.isWritable()) + return QApplication::translate("QtResourceEditorDialog", "%1 [read-only]").arg(fileName); + if (!m_qrcManager->exists(qrcFile)) + return QApplication::translate("QtResourceEditorDialog", "%1 [missing]").arg(fileName); + return fileName; +} + +void QtResourceEditorDialogPrivate::slotQrcFileInserted(QtQrcFile *qrcFile) +{ + QListWidgetItem *currentItem = m_ui.qrcFileList->currentItem(); + int idx = m_ui.qrcFileList->count(); + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(qrcFile); + QListWidgetItem *nextItem = m_qrcFileToItem.value(nextQrcFile); + if (nextItem) { + const int row = m_ui.qrcFileList->row(nextItem); + if (row >= 0) + idx = row; + } + const QString path = qrcFile->path(); + QListWidgetItem *item = new QListWidgetItem(qrcFileText(qrcFile)); + item->setToolTip(path); + m_ignoreCurrentChanged = true; + m_ui.qrcFileList->insertItem(idx, item); + m_ui.qrcFileList->setCurrentItem(currentItem); + m_ignoreCurrentChanged = false; + m_qrcFileToItem[qrcFile] = item; + m_itemToQrcFile[item] = qrcFile; + if (!m_qrcManager->exists(qrcFile)) + item->setForeground(QBrush(Qt::red)); +} + +void QtResourceEditorDialogPrivate::slotQrcFileMoved(QtQrcFile *qrcFile) +{ + QListWidgetItem *currentItem = m_ui.qrcFileList->currentItem(); + QListWidgetItem *item = m_qrcFileToItem.value(qrcFile); + m_ignoreCurrentChanged = true; + m_ui.qrcFileList->takeItem(m_ui.qrcFileList->row(item)); + + int idx = m_ui.qrcFileList->count(); + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(qrcFile); + QListWidgetItem *nextItem = m_qrcFileToItem.value(nextQrcFile); + if (nextItem) { + int row = m_ui.qrcFileList->row(nextItem); + if (row >= 0) + idx = row; + } + m_ui.qrcFileList->insertItem(idx, item); + if (currentItem == item) + m_ui.qrcFileList->setCurrentItem(item); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotQrcFileRemoved(QtQrcFile *qrcFile) +{ + QListWidgetItem *item = m_qrcFileToItem.value(qrcFile); + if (item == m_ui.qrcFileList->currentItem()) + m_ui.qrcFileList->setCurrentItem(0); // this should trigger list view signal currentItemChanged(0), and slot should set m_currentQrcFile to 0 + m_ignoreCurrentChanged = true; + delete item; + m_ignoreCurrentChanged = false; + m_itemToQrcFile.remove(item); + m_qrcFileToItem.remove(qrcFile); +} + +QStandardItem *QtResourceEditorDialogPrivate::insertResourcePrefix(QtResourcePrefix *resourcePrefix) +{ + if (m_qrcManager->qrcFileOf(resourcePrefix) != m_currentQrcFile) + return 0; + + QtResourcePrefix *prevResourcePrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + QStandardItem *prevItem = m_resourcePrefixToPrefixItem.value(prevResourcePrefix); + + int row = 0; + if (prevItem) + row = m_treeModel->indexFromItem(prevItem).row() + 1; + + QStandardItem *prefixItem = new QStandardItem(); + QStandardItem *languageItem = new QStandardItem(); + QList items; + items << prefixItem; + items << languageItem; + m_treeModel->insertRow(row, items); + const QModelIndex newIndex = m_treeModel->indexFromItem(prefixItem); + m_ui.resourceTreeView->setExpanded(newIndex, true); + prefixItem->setFlags(prefixItem->flags() | Qt::ItemIsEditable); + languageItem->setFlags(languageItem->flags() | Qt::ItemIsEditable); + m_resourcePrefixToPrefixItem[resourcePrefix] = prefixItem; + m_resourcePrefixToLanguageItem[resourcePrefix] = languageItem; + m_prefixItemToResourcePrefix[prefixItem] = resourcePrefix; + m_languageItemToResourcePrefix[languageItem] = resourcePrefix; + slotResourcePrefixChanged(resourcePrefix); + slotResourceLanguageChanged(resourcePrefix); + return prefixItem; +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixMoved(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *prefixItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!prefixItem) + return; + + QStandardItem *languageItem = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!languageItem) + return; + + const QModelIndex index = m_treeModel->indexFromItem(prefixItem); + const bool expanded = m_ui.resourceTreeView->isExpanded(index); + m_ignoreCurrentChanged = true; + const QList items = m_treeModel->takeRow(index.row()); + + int row = m_treeModel->rowCount(); + QtResourcePrefix *nextResourcePrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + QStandardItem *nextItem = m_resourcePrefixToPrefixItem.value(nextResourcePrefix); + if (nextItem) + row = m_treeModel->indexFromItem(nextItem).row(); + m_treeModel->insertRow(row, items); + m_ignoreCurrentChanged = false; + m_ui.resourceTreeView->setExpanded(m_treeModel->indexFromItem(items.at(0)), expanded); +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixChanged(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *item = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!item) + return; + + m_ignoreCurrentChanged = true; + QString prefix = resourcePrefix->prefix(); + if (prefix.isEmpty()) + prefix = QApplication::translate("QtResourceEditorDialog", "", 0, QApplication::UnicodeUTF8); + item->setText(prefix); + item->setToolTip(prefix); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceLanguageChanged(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *item = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!item) + return; + + m_ignoreCurrentChanged = true; + const QString language = resourcePrefix->language(); + item->setText(language); + item->setToolTip(language); + + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixRemoved(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *prefixItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!prefixItem) + return; + + QStandardItem *languageItem = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!languageItem) + return; + + m_ignoreCurrentChanged = true; + m_treeModel->takeRow(m_treeModel->indexFromItem(prefixItem).row()); + delete prefixItem; + delete languageItem; + m_ignoreCurrentChanged = false; + m_prefixItemToResourcePrefix.remove(prefixItem); + m_languageItemToResourcePrefix.remove(languageItem); + m_resourcePrefixToPrefixItem.remove(resourcePrefix); + m_resourcePrefixToLanguageItem.remove(resourcePrefix); +} + +void QtResourceEditorDialogPrivate::slotResourceFileInserted(QtResourceFile *resourceFile) +{ + QtResourcePrefix *resourcePrefix = m_qrcManager->resourcePrefixOf(resourceFile); + if (m_qrcManager->qrcFileOf(resourcePrefix) != m_currentQrcFile) + return; + + QtResourceFile *prevResourceFile = m_qrcManager->prevResourceFile(resourceFile); + QStandardItem *prevItem = m_resourceFileToPathItem.value(prevResourceFile); + + QStandardItem *pathItem = new QStandardItem(resourceFile->path()); + QStandardItem *aliasItem = new QStandardItem(); + QStandardItem *parentItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + QList items; + items << pathItem; + items << aliasItem; + + int row = 0; + if (prevItem) + row = m_treeModel->indexFromItem(prevItem).row() + 1; + + parentItem->insertRow(row, items); + + pathItem->setFlags(pathItem->flags() & ~Qt::ItemIsEditable); + aliasItem->setFlags(aliasItem->flags() | Qt::ItemIsEditable); + m_resourceFileToPathItem[resourceFile] = pathItem; + m_resourceFileToAliasItem[resourceFile] = aliasItem; + m_pathItemToResourceFile[pathItem] = resourceFile; + m_aliasItemToResourceFile[aliasItem] = resourceFile; + pathItem->setToolTip(resourceFile->path()); + pathItem->setIcon(m_qrcManager->icon(resourceFile->fullPath())); + if (!m_qrcManager->exists(resourceFile->fullPath())) { + pathItem->setText(QApplication::translate("QtResourceEditorDialog", "%1 [missing]").arg(resourceFile->path())); + QBrush redBrush(Qt::red); + pathItem->setForeground(redBrush); + aliasItem->setForeground(redBrush); + } + slotResourceAliasChanged(resourceFile); +} + +void QtResourceEditorDialogPrivate::slotResourceFileMoved(QtResourceFile *resourceFile) +{ + QStandardItem *pathItem = m_resourceFileToPathItem.value(resourceFile); + if (!pathItem) + return; + + QStandardItem *aliasItem = m_resourceFileToAliasItem.value(resourceFile); + if (!aliasItem) + return; + + QStandardItem *parentItem = pathItem->parent(); + m_ignoreCurrentChanged = true; + const QList items = parentItem->takeRow(m_treeModel->indexFromItem(pathItem).row()); + + int row = parentItem->rowCount(); + QtResourceFile *nextResourceFile = m_qrcManager->nextResourceFile(resourceFile); + QStandardItem *nextItem = m_resourceFileToPathItem.value(nextResourceFile); + if (nextItem) + row = m_treeModel->indexFromItem(nextItem).row(); + parentItem->insertRow(row, items); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceAliasChanged(QtResourceFile *resourceFile) +{ + QStandardItem *item = m_resourceFileToAliasItem.value(resourceFile); + if (!item) + return; + + m_ignoreCurrentChanged = true; + const QString alias = resourceFile->alias(); + item->setText(alias); + item->setToolTip(alias); + + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceFileRemoved(QtResourceFile *resourceFile) +{ + QStandardItem *pathItem = m_resourceFileToPathItem.value(resourceFile); + if (!pathItem) + return; + + QStandardItem *aliasItem = m_resourceFileToAliasItem.value(resourceFile); + if (!aliasItem) + return; + + QStandardItem *parentItem = pathItem->parent(); + + m_ignoreCurrentChanged = true; + parentItem->takeRow(m_treeModel->indexFromItem(pathItem).row()); + delete pathItem; + delete aliasItem; + m_ignoreCurrentChanged = false; + m_pathItemToResourceFile.remove(pathItem); + m_aliasItemToResourceFile.remove(aliasItem); + m_resourceFileToPathItem.remove(resourceFile); + m_resourceFileToAliasItem.remove(resourceFile); +} + + +void QtResourceEditorDialogPrivate::slotCurrentQrcFileChanged(QListWidgetItem *item) +{ + if (m_ignoreCurrentChanged) + return; + + QtQrcFile *newCurrentQrcFile = m_itemToQrcFile.value(item); + + if (newCurrentQrcFile == m_currentQrcFile) + return; + + if (m_currentQrcFile) { + QMap currentPrefixList = m_resourcePrefixToPrefixItem; + QMapIterator itPrefix(currentPrefixList); + while (itPrefix.hasNext()) { + QtResourcePrefix *resourcePrefix = itPrefix.next().key(); + QList currentResourceFiles = resourcePrefix->resourceFiles(); + QListIterator itFile(currentResourceFiles); + while (itFile.hasNext()) + slotResourceFileRemoved(itFile.next()); + slotResourcePrefixRemoved(resourcePrefix); + } + } + + m_currentQrcFile = newCurrentQrcFile; + slotCurrentTreeViewItemChanged(QModelIndex()); + QStandardItem *firstPrefix = 0; // select first prefix + if (m_currentQrcFile) { + QList newPrefixList = m_currentQrcFile->resourcePrefixList(); + QListIterator itPrefix(newPrefixList); + while (itPrefix.hasNext()) { + QtResourcePrefix *resourcePrefix = itPrefix.next(); + if (QStandardItem *newPrefixItem = insertResourcePrefix(resourcePrefix)) + if (!firstPrefix) + firstPrefix = newPrefixItem; + QList newResourceFiles = resourcePrefix->resourceFiles(); + QListIterator itFile(newResourceFiles); + while (itFile.hasNext()) + slotResourceFileInserted(itFile.next()); + } + } + m_ui.resourceTreeView->setCurrentIndex(firstPrefix ? m_treeModel->indexFromItem(firstPrefix) : QModelIndex()); + + m_removeQrcFileAction->setEnabled(m_currentQrcFile); + m_moveUpQrcFileAction->setEnabled(m_currentQrcFile && m_qrcManager->prevQrcFile(m_currentQrcFile)); + m_moveDownQrcFileAction->setEnabled(m_currentQrcFile && m_qrcManager->nextQrcFile(m_currentQrcFile)); +} + +void QtResourceEditorDialogPrivate::slotCurrentTreeViewItemChanged(const QModelIndex &index) +{ + QStandardItem *item = m_treeModel->itemFromIndex(index); + QtResourceFile *resourceFile = m_pathItemToResourceFile.value(item); + if (!resourceFile) + resourceFile = m_aliasItemToResourceFile.value(item); + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (!resourcePrefix) + resourcePrefix = m_languageItemToResourcePrefix.value(item); + + bool moveUpEnabled = false; + bool moveDownEnabled = false; + bool currentItem = resourceFile || resourcePrefix; + + if (resourceFile) { + if (m_qrcManager->prevResourceFile(resourceFile)) + moveUpEnabled = true; + if (m_qrcManager->nextResourceFile(resourceFile)) + moveDownEnabled = true; + } else if (resourcePrefix) { + if (m_qrcManager->prevResourcePrefix(resourcePrefix)) + moveUpEnabled = true; + if (m_qrcManager->nextResourcePrefix(resourcePrefix)) + moveDownEnabled = true; + } + + m_newPrefixAction->setEnabled(m_currentQrcFile); + m_addResourceFileAction->setEnabled(currentItem); + m_changePrefixAction->setEnabled(currentItem); + m_changeLanguageAction->setEnabled(currentItem); + m_changeAliasAction->setEnabled(resourceFile); + m_removeAction->setEnabled(currentItem); + m_moveUpAction->setEnabled(moveUpEnabled); + m_moveDownAction->setEnabled(moveDownEnabled); + m_clonePrefixAction->setEnabled(currentItem); +} + +void QtResourceEditorDialogPrivate::slotListWidgetContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_newQrcFileAction); + menu.addAction(m_importQrcFileAction); + menu.addAction(m_removeQrcFileAction); + menu.addSeparator(); + menu.addAction(m_moveUpQrcFileAction); + menu.addAction(m_moveDownQrcFileAction); + menu.exec(m_ui.qrcFileList->mapToGlobal(pos)); +} + +void QtResourceEditorDialogPrivate::slotTreeViewContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_newPrefixAction); + menu.addAction(m_addResourceFileAction); + menu.addAction(m_removeAction); + menu.addSeparator(); + menu.addAction(m_changePrefixAction); + menu.addAction(m_changeLanguageAction); + menu.addAction(m_changeAliasAction); + menu.addSeparator(); + menu.addAction(m_clonePrefixAction); + menu.addSeparator(); + menu.addAction(m_moveUpAction); + menu.addAction(m_moveDownAction); + menu.exec(m_ui.resourceTreeView->mapToGlobal(pos)); +} + +void QtResourceEditorDialogPrivate::slotTreeViewItemChanged(QStandardItem *item) +{ + if (m_ignoreCurrentChanged) + return; + + const QString newValue = item->text(); + QtResourceFile *resourceFile = m_aliasItemToResourceFile.value(item); + if (resourceFile) { + m_qrcManager->changeResourceAlias(resourceFile, newValue); + return; + } + + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (resourcePrefix) { + m_qrcManager->changeResourcePrefix(resourcePrefix, newValue); + return; + } + + resourcePrefix = m_languageItemToResourcePrefix.value(item); + if (resourcePrefix) { + m_qrcManager->changeResourceLanguage(resourcePrefix, newValue); + return; + } +} + +QString QtResourceEditorDialogPrivate::getSaveFileNameWithExtension(QWidget *parent, + const QString &title, QString dir, const QString &filter, const QString &extension) const +{ + const QChar dot = QLatin1Char('.'); + + QString saveFile; + while (true) { + saveFile = m_dlgGui->getSaveFileName(parent, title, dir, filter, 0, QFileDialog::DontConfirmOverwrite); + if (saveFile.isEmpty()) + return saveFile; + + const QFileInfo fInfo(saveFile); + if (fInfo.suffix().isEmpty() && !fInfo.fileName().endsWith(dot)) { + saveFile += dot; + saveFile += extension; + } + + const QFileInfo fi(saveFile); + if (!fi.exists()) + break; + + if (warning(title, msgOverwrite(fi.fileName()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + break; + + dir = saveFile; + } + return saveFile; +} + +QString QtResourceEditorDialogPrivate::qrcStartDirectory() const +{ + if (!m_currentQrcFile) + return QString(); + const QDir dir = QFileInfo(m_currentQrcFile->path()).dir(); + return dir.exists() ? dir.absolutePath() : QString(); +} + +void QtResourceEditorDialogPrivate::slotNewQrcFile() +{ + const QString qrcPath = getSaveFileNameWithExtension(q_ptr, + QApplication::translate("QtResourceEditorDialog", "New Resource File", 0, QApplication::UnicodeUTF8), + m_firstQrcFileDialog ? qrcStartDirectory() : QString(), + QApplication::translate("QtResourceEditorDialog", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8), + QLatin1String("qrc")); + if (qrcPath.isEmpty()) + return; + + m_firstQrcFileDialog = false; + if (QtQrcFile *sameQrcFile = m_qrcManager->qrcFileOf(qrcPath)) { + // QMessageBox ??? + QListWidgetItem *item = m_qrcFileToItem.value(sameQrcFile); + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + return; + } + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + + QtQrcFile *qrcFile = m_qrcManager->insertQrcFile(qrcPath, nextQrcFile, true); + m_ui.qrcFileList->setCurrentItem(m_qrcFileToItem.value(qrcFile)); +} + +void QtResourceEditorDialogPrivate::slotImportQrcFile() +{ + const QString qrcPath = m_dlgGui->getOpenFileName(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Import Resource File", 0, QApplication::UnicodeUTF8), + m_firstQrcFileDialog ? qrcStartDirectory() : QString(), + QApplication::translate("QtResourceEditorDialog", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8)); + if (qrcPath.isEmpty()) + return; + m_firstQrcFileDialog = false; + if (QtQrcFile *sameQrcFile = m_qrcManager->qrcFileOf(qrcPath)) { + // QMessageBox ??? + QListWidgetItem *item = m_qrcFileToItem.value(sameQrcFile); + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + return; + } + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + + QtQrcFileData qrcFileData; + loadQrcFile(qrcPath, &qrcFileData); + QtQrcFile *qrcFile = m_qrcManager->importQrcFile(qrcFileData, nextQrcFile); + m_ui.qrcFileList->setCurrentItem(m_qrcFileToItem.value(qrcFile)); +} + +void QtResourceEditorDialogPrivate::slotRemoveQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *currentQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + if (!currentQrcFile) + currentQrcFile = m_qrcManager->prevQrcFile(m_currentQrcFile); + + m_qrcManager->removeQrcFile(m_currentQrcFile); + QListWidgetItem *item = m_qrcFileToItem.value(currentQrcFile); + if (item) { + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + } +} + +void QtResourceEditorDialogPrivate::slotMoveUpQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *prevQrcFile = m_qrcManager->prevQrcFile(m_currentQrcFile); + if (!prevQrcFile) + return; + + m_qrcManager->moveQrcFile(m_currentQrcFile, prevQrcFile); +} + +void QtResourceEditorDialogPrivate::slotMoveDownQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + if (!nextQrcFile) + return; + nextQrcFile = m_qrcManager->nextQrcFile(nextQrcFile); + + m_qrcManager->moveQrcFile(m_currentQrcFile, nextQrcFile); +} + +QtResourceFile *QtResourceEditorDialogPrivate::getCurrentResourceFile() const +{ + QStandardItem *currentItem = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + + + QtResourceFile *currentResourceFile = 0; + if (currentItem) { + currentResourceFile = m_pathItemToResourceFile.value(currentItem); + if (!currentResourceFile) + currentResourceFile = m_aliasItemToResourceFile.value(currentItem); + } + return currentResourceFile; +} + +QtResourcePrefix *QtResourceEditorDialogPrivate::getCurrentResourcePrefix() const +{ + QStandardItem *currentItem = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + + QtResourcePrefix *currentResourcePrefix = 0; + if (currentItem) { + currentResourcePrefix = m_prefixItemToResourcePrefix.value(currentItem); + if (!currentResourcePrefix) { + currentResourcePrefix = m_languageItemToResourcePrefix.value(currentItem); + if (!currentResourcePrefix) { + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (currentResourceFile) + currentResourcePrefix = m_qrcManager->resourcePrefixOf(currentResourceFile); + } + } + } + return currentResourcePrefix; +} + +void QtResourceEditorDialogPrivate::selectTreeRow(QStandardItem *item) +{ + const QModelIndex index = m_treeModel->indexFromItem(item); + m_treeSelection->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_treeSelection->setCurrentIndex(index, QItemSelectionModel::Select); +} + +void QtResourceEditorDialogPrivate::slotNewPrefix() +{ + if (!m_currentQrcFile) + return; + + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + QtResourcePrefix *nextResourcePrefix = m_qrcManager->nextResourcePrefix(currentResourcePrefix); + QtResourcePrefix *newResourcePrefix = m_qrcManager->insertResourcePrefix(m_currentQrcFile, + QApplication::translate("QtResourceEditorDialog", "newPrefix", 0, QApplication::UnicodeUTF8), + QString(), nextResourcePrefix); + if (!newResourcePrefix) + return; + + QStandardItem *newItem = m_resourcePrefixToPrefixItem.value(newResourcePrefix); + if (!newItem) + return; + + const QModelIndex index = m_treeModel->indexFromItem(newItem); + selectTreeRow(newItem); + m_ui.resourceTreeView->edit(index); +} + +static inline QString outOfPathWarning(const QString &fname) +{ + return QApplication::translate("QtResourceEditorDialog", + "

Warning: The file

" + "

%1

" + "

is outside of the current resource file's parent directory.

").arg(fname); +} + +static inline QString outOfPathWarningInfo() +{ + return QApplication::translate("QtResourceEditorDialog", + "

To resolve the issue, press:

" + "" + "" + "" + "
Copyto copy the file to the resource file's parent directory.
Copy As...to copy the file into a subdirectory of the resource file's parent directory.
Keepto use its current location.
"); +} + +void QtResourceEditorDialogPrivate::slotAddFiles() +{ + if (!m_currentQrcFile) + return; + + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (!currentResourcePrefix) + return; + + QString initialPath = m_currentQrcFile->path(); + if (currentResourceFile) { + QFileInfo fi(currentResourceFile->fullPath()); + initialPath = fi.absolutePath(); + } + + const QStringList resourcePaths = m_dlgGui->getOpenImageFileNames(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Add Files", 0, QApplication::UnicodeUTF8), + initialPath); + if (resourcePaths.isEmpty()) + return; + + QtResourceFile *nextResourceFile = m_qrcManager->nextResourceFile(currentResourceFile); + if (!currentResourceFile) { + QList resourceFiles = currentResourcePrefix->resourceFiles(); + if (resourceFiles.count() > 0) + nextResourceFile = resourceFiles.first(); + } + + const QFileInfo fi(m_currentQrcFile->path()); + const QString destDir = fi.absolutePath(); + const QDir dir(fi.absolutePath()); + QStringListIterator itResourcePath(resourcePaths); + while (itResourcePath.hasNext()) { + QString resourcePath = itResourcePath.next(); + QString relativePath = dir.relativeFilePath(resourcePath); + if (relativePath.startsWith(QLatin1String(".."))) { + QMessageBox msgBox(QMessageBox::Warning, + QApplication::translate("QtResourceEditorDialog", "Incorrect Path", 0, QApplication::UnicodeUTF8), + outOfPathWarning(relativePath), QMessageBox::Cancel); + msgBox.setInformativeText(outOfPathWarningInfo()); + QPushButton *copyButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Copy", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *copyAsButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Copy As...", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *keepButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Keep", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *skipButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Skip", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + msgBox.setEscapeButton(QMessageBox::Cancel); + msgBox.setDefaultButton(copyButton); + msgBox.exec(); + QString destPath; + if (msgBox.clickedButton() == keepButton) { + // nothing + } else if (msgBox.clickedButton() == copyButton) { + QFileInfo resInfo(resourcePath); + QDir dd(destDir); + destPath = dd.absoluteFilePath(resInfo.fileName()); + if (dd.exists(resInfo.fileName())) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + msgOverwrite(resInfo.fileName()), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Yes) + continue; + } + resourcePath = copyResourceFile(resourcePath, destPath); // returns empty string in case copy failed or was canceled + } else if (msgBox.clickedButton() == copyAsButton) { + destPath = browseForNewLocation(resourcePath, dir); // returns empty string in case browsing was canceled + if (destPath.isEmpty()) + continue; + resourcePath = copyResourceFile(resourcePath, destPath); // returns empty string in case copy failed or was canceled + } else if (msgBox.clickedButton() == skipButton) { // skipped + continue; + } else { // canceled + return; + } + if (resourcePath.isEmpty()) + continue; + } + relativePath = dir.relativeFilePath(resourcePath); + QtResourceFile *newResourceFile = m_qrcManager->insertResourceFile(currentResourcePrefix, relativePath, QString(), nextResourceFile); + + QStandardItem *newItem = m_resourceFileToPathItem.value(newResourceFile); + if (newItem) + selectTreeRow(newItem); + } +} + +void QtResourceEditorDialogPrivate::slotChangePrefix() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + QStandardItem *item = m_resourcePrefixToPrefixItem.value(currentResourcePrefix); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotChangeLanguage() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + QStandardItem *item = m_resourcePrefixToLanguageItem.value(currentResourcePrefix); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotChangeAlias() +{ + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (!currentResourceFile) + return; + + QStandardItem *item = m_resourceFileToAliasItem.value(currentResourceFile); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotClonePrefix() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + bool ok; + QString suffix = QInputDialog::getText(q_ptr, QApplication::translate("QtResourceEditorDialog", "Clone Prefix", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Enter the suffix which you want to add to the names of the cloned files.\n" + "This could for example be a language extension like \"_de\".", 0, QApplication::UnicodeUTF8), + QLineEdit::Normal, QString(), &ok); + if (!ok) + return; + + QtResourcePrefix *newResourcePrefix = m_qrcManager->insertResourcePrefix(m_currentQrcFile, currentResourcePrefix->prefix(), + currentResourcePrefix->language(), m_qrcManager->nextResourcePrefix(currentResourcePrefix)); + if (newResourcePrefix) { + QList files = currentResourcePrefix->resourceFiles(); + QListIterator itFile(files); + while (itFile.hasNext()) { + QtResourceFile *resourceFile = itFile.next(); + QString path = resourceFile->path(); + QFileInfo fi(path); + QDir dir(fi.dir()); + QString oldSuffix = fi.completeSuffix(); + if (!oldSuffix.isEmpty()) + oldSuffix = QLatin1Char('.') + oldSuffix; + const QString newBaseName = fi.baseName() + suffix + oldSuffix; + const QString newPath = QDir::cleanPath(dir.filePath(newBaseName)); + m_qrcManager->insertResourceFile(newResourcePrefix, newPath, + resourceFile->alias()); + } + } +} + +void QtResourceEditorDialogPrivate::slotRemove() +{ + QStandardItem *item = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + if (!item) + return; + + QtResourceFile *resourceFile = m_pathItemToResourceFile.value(item); + if (!resourceFile) + resourceFile = m_aliasItemToResourceFile.value(item); + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (!resourcePrefix) + resourcePrefix = m_languageItemToResourcePrefix.value(item); + + QStandardItem *newCurrentItem = 0; + + if (resourceFile) { + QtResourceFile *nextFile = m_qrcManager->nextResourceFile(resourceFile); + if (!nextFile) + nextFile = m_qrcManager->prevResourceFile(resourceFile); + newCurrentItem = m_resourceFileToPathItem.value(nextFile); + if (!newCurrentItem) + newCurrentItem = m_resourcePrefixToPrefixItem.value(m_qrcManager->resourcePrefixOf(resourceFile)); + } + if (!newCurrentItem) { + QtResourcePrefix *nextPrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + if (!nextPrefix) + nextPrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + newCurrentItem = m_resourcePrefixToPrefixItem.value(nextPrefix); + } + + selectTreeRow(newCurrentItem); + + if (resourcePrefix) + m_qrcManager->removeResourcePrefix(resourcePrefix); + else if (resourceFile) + m_qrcManager->removeResourceFile(resourceFile); +} + +void QtResourceEditorDialogPrivate::slotMoveUp() +{ + if (QtResourceFile *resourceFile = getCurrentResourceFile()) { + QtResourceFile *prevFile = m_qrcManager->prevResourceFile(resourceFile); + + if (!prevFile) + return; + + m_qrcManager->moveResourceFile(resourceFile, prevFile); + selectTreeRow(m_resourceFileToPathItem.value(resourceFile)); + } else if (QtResourcePrefix *resourcePrefix = getCurrentResourcePrefix()) { + QtResourcePrefix *prevPrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + + if (!prevPrefix) + return; + + m_qrcManager->moveResourcePrefix(resourcePrefix, prevPrefix); + selectTreeRow(m_resourcePrefixToPrefixItem.value(resourcePrefix)); + } +} + +void QtResourceEditorDialogPrivate::slotMoveDown() +{ + if (QtResourceFile *resourceFile = getCurrentResourceFile()) { + QtResourceFile *nextFile = m_qrcManager->nextResourceFile(resourceFile); + + if (!nextFile) + return; + + m_qrcManager->moveResourceFile(resourceFile, m_qrcManager->nextResourceFile(nextFile)); + selectTreeRow(m_resourceFileToPathItem.value(resourceFile)); + } else if (QtResourcePrefix *resourcePrefix = getCurrentResourcePrefix()) { + QtResourcePrefix *nextPrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + + if (!nextPrefix) + return; + + m_qrcManager->moveResourcePrefix(resourcePrefix, m_qrcManager->nextResourcePrefix(nextPrefix)); + selectTreeRow(m_resourcePrefixToPrefixItem.value(resourcePrefix)); + } +} + +QString QtResourceEditorDialogPrivate::browseForNewLocation(const QString &resourceFile, const QDir &rootDir) const +{ + QFileInfo fi(resourceFile); + const QString initialPath = rootDir.absoluteFilePath(fi.fileName()); + while (1) { + QString newPath = m_dlgGui->getSaveFileName(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Copy As", 0, QApplication::UnicodeUTF8), + initialPath); + QString relativePath = rootDir.relativeFilePath(newPath); + if (relativePath.startsWith(QLatin1String(".."))) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy As", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "

The selected file:

" + "

%1

is outside of the current resource file's directory:

%2

" + "

Please select another path within this directory.

", 0, + QApplication::UnicodeUTF8).arg(relativePath).arg(rootDir.absolutePath()), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) + return QString(); + } else { + return newPath; + } + } + + return QString(); +} + +QString QtResourceEditorDialogPrivate::copyResourceFile(const QString &resourceFile, const QString &destPath) const +{ + QFileInfo fi(destPath); + if (fi.exists()) { + while (fi.exists() && !QFile::remove(destPath)) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not overwrite %1.", 0, QApplication::UnicodeUTF8).arg(fi.fileName()), + QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Retry) + return QString(); + } + } + while (!QFile::copy(resourceFile, destPath)) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not copy\n%1\nto\n%2", + 0, QApplication::UnicodeUTF8).arg(resourceFile).arg(destPath), + QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Retry) + return QString(); + } + return destPath; +} +bool QtResourceEditorDialogPrivate::loadQrcFile(const QString &path, QtQrcFileData *qrcFileData) +{ + QString errorMessage; + const bool rc = loadQrcFile(path, qrcFileData, &errorMessage); +// if (!rc) +// warning(QApplication::translate("QtResourceEditorDialog", "Resource File Load Error"), errorMessage); + return rc; +} +bool QtResourceEditorDialogPrivate::loadQrcFile(const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage) +{ + if (!qrcFileData) + return false; + + qrcFileData->qrcPath = path; + + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + // there is sufficient hint while loading a form and after opening the editor (qrc marked marked with red and with [missing] text) + //*errorMessage = QApplication::translate("QtResourceEditorDialog", "Unable to open %1 for reading: %2").arg(path).arg(file.errorString()); + return false; + } + + QByteArray dataArray = file.readAll(); + file.close(); + + QDomDocument doc; + int errLine, errCol; + if (!doc.setContent(dataArray, errorMessage, &errLine, &errCol)) { + *errorMessage = QCoreApplication::translate("QtResourceEditorDialog", "A parse error occurred at line %1, column %2 of %3:\n%4").arg(errLine).arg(errCol).arg(path).arg(*errorMessage); + return false; + } + + return loadQrcFileData(doc, path, qrcFileData, errorMessage); +} + +bool QtResourceEditorDialogPrivate::saveQrcFile(const QtQrcFileData &qrcFileData) +{ + QFile file(qrcFileData.qrcPath); + while (!file.open(QIODevice::WriteOnly)) { + QMessageBox msgBox(QMessageBox::Warning, + QApplication::translate("QtResourceEditorDialog", "Save Resource File", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not write %1: %2", 0, QApplication::UnicodeUTF8).arg(qrcFileData.qrcPath).arg(file.errorString()), + QMessageBox::Cancel|QMessageBox::Ignore|QMessageBox::Retry); + msgBox.setEscapeButton(QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ignore); + switch (msgBox.exec()) { + case QMessageBox::Retry: + break; // nothing + case QMessageBox::Ignore: + return true; + default: + return false; + } + } + + QDomDocument doc = saveQrcFileData(qrcFileData); + + QByteArray dataArray = doc.toByteArray(2); + file.write(dataArray); + + file.close(); + return true; +} + +QtResourceEditorDialog::QtResourceEditorDialog(QDesignerFormEditorInterface *core, QDesignerDialogGuiInterface *dlgGui, QWidget *parent) + : QDialog(parent), d_ptr(new QtResourceEditorDialogPrivate()) +{ + d_ptr->q_ptr = this; + d_ptr->m_ui.setupUi(this); + d_ptr->m_qrcManager = new QtQrcManager(this); + d_ptr->m_dlgGui = dlgGui; + d_ptr->m_core = core; + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Edit Resources")); + + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileInserted(QtQrcFile*)), + this, SLOT(slotQrcFileInserted(QtQrcFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileMoved(QtQrcFile*,QtQrcFile*)), + this, SLOT(slotQrcFileMoved(QtQrcFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileRemoved(QtQrcFile*)), + this, SLOT(slotQrcFileRemoved(QtQrcFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixInserted(QtResourcePrefix*)), + this, SLOT(slotResourcePrefixInserted(QtResourcePrefix*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixMoved(QtResourcePrefix*,QtResourcePrefix*)), + this, SLOT(slotResourcePrefixMoved(QtResourcePrefix*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixChanged(QtResourcePrefix*,QString)), + this, SLOT(slotResourcePrefixChanged(QtResourcePrefix*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceLanguageChanged(QtResourcePrefix*,QString)), + this, SLOT(slotResourceLanguageChanged(QtResourcePrefix*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixRemoved(QtResourcePrefix*)), + this, SLOT(slotResourcePrefixRemoved(QtResourcePrefix*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileInserted(QtResourceFile*)), + this, SLOT(slotResourceFileInserted(QtResourceFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileMoved(QtResourceFile*,QtResourceFile*)), + this, SLOT(slotResourceFileMoved(QtResourceFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceAliasChanged(QtResourceFile*,QString)), + this, SLOT(slotResourceAliasChanged(QtResourceFile*))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileRemoved(QtResourceFile*)), + this, SLOT(slotResourceFileRemoved(QtResourceFile*))); + + QIcon upIcon = qdesigner_internal::createIconSet(QString::fromUtf8("up.png")); + QIcon downIcon = qdesigner_internal::createIconSet(QString::fromUtf8("down.png")); + QIcon minusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("minus-16.png")); + QIcon newIcon = qdesigner_internal::createIconSet(QString::fromUtf8("filenew-16.png")); + QIcon openIcon = qdesigner_internal::createIconSet(QString::fromUtf8("fileopen-16.png")); + QIcon removeIcon = qdesigner_internal::createIconSet(QString::fromUtf8("editdelete-16.png")); + QIcon addPrefixIcon = qdesigner_internal::createIconSet(QString::fromUtf8("prefix-add.png")); + + d_ptr->m_newQrcFileAction = new QAction(newIcon, tr("New..."), this); + d_ptr->m_newQrcFileAction->setToolTip(tr("New Resource File")); + d_ptr->m_importQrcFileAction = new QAction(openIcon, tr("Open..."), this); + d_ptr->m_importQrcFileAction->setToolTip(tr("Open Resource File")); + d_ptr->m_removeQrcFileAction = new QAction(removeIcon, tr("Remove"), this); + d_ptr->m_moveUpQrcFileAction = new QAction(upIcon, tr("Move Up"), this); + d_ptr->m_moveDownQrcFileAction = new QAction(downIcon, tr("Move Down"), this); + + d_ptr->m_newPrefixAction = new QAction(addPrefixIcon, tr("Add Prefix"), this); + d_ptr->m_newPrefixAction->setToolTip(tr("Add Prefix")); + d_ptr->m_addResourceFileAction = new QAction(openIcon, tr("Add Files..."), this); + d_ptr->m_changePrefixAction = new QAction(tr("Change Prefix"), this); + d_ptr->m_changeLanguageAction = new QAction(tr("Change Language"), this); + d_ptr->m_changeAliasAction = new QAction(tr("Change Alias"), this); + d_ptr->m_clonePrefixAction = new QAction(tr("Clone Prefix..."), this); + d_ptr->m_removeAction = new QAction(minusIcon, tr("Remove"), this); + d_ptr->m_moveUpAction = new QAction(upIcon, tr("Move Up"), this); + d_ptr->m_moveDownAction = new QAction(downIcon, tr("Move Down"), this); + + d_ptr->m_ui.newQrcButton->setDefaultAction(d_ptr->m_newQrcFileAction); + d_ptr->m_ui.importQrcButton->setDefaultAction(d_ptr->m_importQrcFileAction); + d_ptr->m_ui.removeQrcButton->setDefaultAction(d_ptr->m_removeQrcFileAction); + + d_ptr->m_ui.newResourceButton->setDefaultAction(d_ptr->m_newPrefixAction); + d_ptr->m_ui.addResourceButton->setDefaultAction(d_ptr->m_addResourceFileAction); + d_ptr->m_ui.removeResourceButton->setDefaultAction(d_ptr->m_removeAction); + + connect(d_ptr->m_newQrcFileAction, SIGNAL(triggered()), this, SLOT(slotNewQrcFile())); + connect(d_ptr->m_importQrcFileAction, SIGNAL(triggered()), this, SLOT(slotImportQrcFile())); + connect(d_ptr->m_removeQrcFileAction, SIGNAL(triggered()), this, SLOT(slotRemoveQrcFile())); + connect(d_ptr->m_moveUpQrcFileAction, SIGNAL(triggered()), this, SLOT(slotMoveUpQrcFile())); + connect(d_ptr->m_moveDownQrcFileAction, SIGNAL(triggered()), this, SLOT(slotMoveDownQrcFile())); + + connect(d_ptr->m_newPrefixAction, SIGNAL(triggered()), this, SLOT(slotNewPrefix())); + connect(d_ptr->m_addResourceFileAction, SIGNAL(triggered()), this, SLOT(slotAddFiles())); + connect(d_ptr->m_changePrefixAction, SIGNAL(triggered()), this, SLOT(slotChangePrefix())); + connect(d_ptr->m_changeLanguageAction, SIGNAL(triggered()), this, SLOT(slotChangeLanguage())); + connect(d_ptr->m_changeAliasAction, SIGNAL(triggered()), this, SLOT(slotChangeAlias())); + connect(d_ptr->m_clonePrefixAction, SIGNAL(triggered()), this, SLOT(slotClonePrefix())); + connect(d_ptr->m_removeAction, SIGNAL(triggered()), this, SLOT(slotRemove())); + connect(d_ptr->m_moveUpAction, SIGNAL(triggered()), this, SLOT(slotMoveUp())); + connect(d_ptr->m_moveDownAction, SIGNAL(triggered()), this, SLOT(slotMoveDown())); + + d_ptr->m_ui.qrcFileList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(d_ptr->m_ui.qrcFileList, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotListWidgetContextMenuRequested(QPoint))); + connect(d_ptr->m_ui.qrcFileList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(slotCurrentQrcFileChanged(QListWidgetItem*))); + + d_ptr->m_treeModel = new QStandardItemModel(this); + d_ptr->m_treeModel->setColumnCount(2); + d_ptr->m_treeModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Prefix / Path"))); + d_ptr->m_treeModel->setHorizontalHeaderItem(1, new QStandardItem(tr("Language / Alias"))); + d_ptr->m_ui.resourceTreeView->setModel(d_ptr->m_treeModel); + d_ptr->m_ui.resourceTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + d_ptr->m_treeSelection = d_ptr->m_ui.resourceTreeView->selectionModel(); + connect(d_ptr->m_ui.resourceTreeView->header(), SIGNAL(sectionDoubleClicked(int)), d_ptr->m_ui.resourceTreeView, SLOT(resizeColumnToContents(int))); + d_ptr->m_ui.resourceTreeView->setTextElideMode(Qt::ElideLeft); + + connect(d_ptr->m_ui.resourceTreeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotTreeViewContextMenuRequested(QPoint))); + connect(d_ptr->m_treeModel, SIGNAL(itemChanged(QStandardItem*)), + this, SLOT(slotTreeViewItemChanged(QStandardItem*))); + connect(d_ptr->m_treeSelection, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(slotCurrentTreeViewItemChanged(QModelIndex))); + + d_ptr->m_ui.resourceTreeView->setColumnWidth(0, 200); + + d_ptr->slotCurrentTreeViewItemChanged(QModelIndex()); + d_ptr->m_removeQrcFileAction->setEnabled(false); + d_ptr->m_moveUpQrcFileAction->setEnabled(false); + d_ptr->m_moveDownQrcFileAction->setEnabled(false); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(QrcDialogC)); + + d_ptr->m_ui.splitter->restoreState(settings->value(QLatin1String(SplitterPosition)).toByteArray()); + if (settings->contains(QLatin1String(Geometry))) + setGeometry(settings->value(QLatin1String(Geometry)).toRect()); + + settings->endGroup(); +} + +QtResourceEditorDialog::~QtResourceEditorDialog() +{ + QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager(); + settings->beginGroup(QLatin1String(QrcDialogC)); + + settings->setValue(QLatin1String(SplitterPosition), d_ptr->m_ui.splitter->saveState()); + settings->setValue(QLatin1String(Geometry), geometry()); + settings->endGroup(); +} + +QtResourceModel *QtResourceEditorDialog::model() const +{ + return d_ptr->m_resourceModel; +} + +void QtResourceEditorDialog::setResourceModel(QtResourceModel *model) +{ + d_ptr->m_resourceModel = model; + + QtResourceSet *resourceSet = d_ptr->m_resourceModel->currentResourceSet(); + if (!resourceSet) { + // disable everything but cancel button + return; + } + + d_ptr->m_initialState.clear(); + + // enable qrcBox + + QStringList paths = resourceSet->activeQrcPaths(); + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + QtQrcFileData qrcFileData; + d_ptr->loadQrcFile(path, &qrcFileData); + d_ptr->m_initialState << qrcFileData; + d_ptr->m_qrcManager->importQrcFile(qrcFileData); + } + if (d_ptr->m_ui.qrcFileList->count() > 0) { + d_ptr->m_ui.qrcFileList->item(0)->setSelected(true); + } +} + +QString QtResourceEditorDialog::selectedResource() const +{ + //QtResourcePrefix *currentResourcePrefix = d_ptr->m_qrcManager->resourcePrefixOf(currentResourceFile); + QtResourcePrefix *currentResourcePrefix = d_ptr->getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return QString(); + + const QChar slash(QLatin1Char('/')); + QString resource = currentResourcePrefix->prefix(); + if (!resource.startsWith(slash)) + resource.prepend(slash); + if (!resource.endsWith(slash)) + resource.append(slash); + resource.prepend(QLatin1Char(':')); + + QtResourceFile *currentResourceFile = d_ptr->getCurrentResourceFile(); + if (!currentResourceFile) + return resource; + + QString resourceEnding = currentResourceFile->path(); + if (!currentResourceFile->alias().isEmpty()) + resourceEnding = currentResourceFile->alias(); + + const QString dotSlash(QLatin1String("./")); + const QString dotDotSlash(QLatin1String("../")); + while (1) { + if (resourceEnding.startsWith(slash)) + resourceEnding = resourceEnding.mid(1); + else if (resourceEnding.startsWith(dotSlash)) + resourceEnding = resourceEnding.mid(dotSlash.count()); + else if (resourceEnding.startsWith(dotDotSlash)) + resourceEnding = resourceEnding.mid(dotDotSlash.count()); + else + break; + } + + resource.append(resourceEnding); + + return resource; +} + +void QtResourceEditorDialog::displayResourceFailures(const QString &logOutput, QDesignerDialogGuiInterface *dlgGui, QWidget *parent) +{ + const QString msg = tr("

Warning: There have been problems while reloading the resources:

%1
").arg(logOutput); + dlgGui->message(parent, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, + tr("Resource Warning"), msg); +} + +void QtResourceEditorDialog::accept() +{ + QStringList newQrcPaths; + QList currentState; + + QList qrcFiles = d_ptr->m_qrcManager->qrcFiles(); + QListIterator itQrc(qrcFiles); + while (itQrc.hasNext()) { + QtQrcFile *qrcFile = itQrc.next(); + QtQrcFileData qrcFileData; + d_ptr->m_qrcManager->exportQrcFile(qrcFile, &qrcFileData); + currentState << qrcFileData; + if (qrcFileData == qrcFile->initialState()) { + // nothing + } else { + d_ptr->m_resourceModel->setWatcherEnabled(qrcFileData.qrcPath, false); + bool ok = d_ptr->saveQrcFile(qrcFileData); + d_ptr->m_resourceModel->setWatcherEnabled(qrcFileData.qrcPath, true); + if (!ok) + return; + + d_ptr->m_resourceModel->setModified(qrcFileData.qrcPath); + } + newQrcPaths << qrcFileData.qrcPath; + } + + if (currentState == d_ptr->m_initialState) { + // nothing + } else { + int errorCount; + QString errorMessages; + d_ptr->m_resourceModel->currentResourceSet()->activateQrcPaths(newQrcPaths, &errorCount, &errorMessages); + if (errorCount) + displayResourceFailures(errorMessages, d_ptr->m_dlgGui, this); + } + QDialog::accept(); +} + +QString QtResourceEditorDialog::editResources(QDesignerFormEditorInterface *core, + QtResourceModel *model, + QDesignerDialogGuiInterface *dlgGui, + QWidget *parent) +{ + QtResourceEditorDialog dialog(core, dlgGui, parent); + dialog.setResourceModel(model); + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedResource(); + return QString(); +} + +QT_END_NAMESPACE + +#include "moc_qtresourceeditordialog_p.cpp" +#include "qtresourceeditordialog.moc" diff --git a/designer/lib/shared/qtresourceeditordialog.ui b/designer/lib/shared/qtresourceeditordialog.ui new file mode 100644 index 0000000..fa760d9 --- /dev/null +++ b/designer/lib/shared/qtresourceeditordialog.ui @@ -0,0 +1,177 @@ + + QtResourceEditorDialog + + + + 0 + 0 + 469 + 317 + + + + Dialog + + + + + + Qt::Horizontal + + + false + + + + + + + + 0 + 0 + + + + + + + + New File + + + N + + + + + + + Remove File + + + R + + + + + + + Qt::Horizontal + + + QSizePolicy::Ignored + + + + 21 + 20 + + + + + + + + I + + + + + + + + + + + + + + New Resource + + + N + + + + + + + A + + + + + + + Remove Resource or File + + + R + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QtResourceEditorDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + QtResourceEditorDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/qtresourceeditordialog_p.h b/designer/lib/shared/qtresourceeditordialog_p.h new file mode 100644 index 0000000..da51431 --- /dev/null +++ b/designer/lib/shared/qtresourceeditordialog_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEEDITOR_H +#define QTRESOURCEEDITOR_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QDesignerDialogGuiInterface; +class QDesignerFormEditorInterface; + +class QtResourceEditorDialog : public QDialog +{ + Q_OBJECT +public: + QtResourceModel *model() const; + void setResourceModel(QtResourceModel *model); + + QString selectedResource() const; + + static QString editResources(QDesignerFormEditorInterface *core, QtResourceModel *model, + QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + + // Helper to display a message box with rcc logs in case of errors. + static void displayResourceFailures(const QString &logOutput, QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + +public slots: + virtual void accept(); + +private: + QtResourceEditorDialog(QDesignerFormEditorInterface *core, QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + ~QtResourceEditorDialog(); + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtResourceEditorDialog) + Q_DISABLE_COPY(QtResourceEditorDialog) + + Q_PRIVATE_SLOT(d_func(), void slotQrcFileInserted(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotQrcFileMoved(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotQrcFileRemoved(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixInserted(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixMoved(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixChanged(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceLanguageChanged(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixRemoved(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileInserted(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileMoved(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceAliasChanged(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileRemoved(QtResourceFile *)) + + Q_PRIVATE_SLOT(d_func(), void slotCurrentQrcFileChanged(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeViewItemChanged(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotTreeViewContextMenuRequested(const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotTreeViewItemChanged(QStandardItem *)) + + Q_PRIVATE_SLOT(d_func(), void slotNewQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotImportQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotRemoveQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotMoveUpQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotMoveDownQrcFile()) + + Q_PRIVATE_SLOT(d_func(), void slotNewPrefix()) + Q_PRIVATE_SLOT(d_func(), void slotAddFiles()) + Q_PRIVATE_SLOT(d_func(), void slotChangePrefix()) + Q_PRIVATE_SLOT(d_func(), void slotChangeLanguage()) + Q_PRIVATE_SLOT(d_func(), void slotChangeAlias()) + Q_PRIVATE_SLOT(d_func(), void slotClonePrefix()) + Q_PRIVATE_SLOT(d_func(), void slotRemove()) + Q_PRIVATE_SLOT(d_func(), void slotMoveUp()) + Q_PRIVATE_SLOT(d_func(), void slotMoveDown()) +}; + +QT_END_NAMESPACE + +#endif + diff --git a/designer/lib/shared/qtresourcemodel.cpp b/designer/lib/shared/qtresourcemodel.cpp new file mode 100644 index 0000000..54063dc --- /dev/null +++ b/designer/lib/shared/qtresourcemodel.cpp @@ -0,0 +1,646 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtresourcemodel_p.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { debugResourceModel = 0 }; + +// ------------------- QtResourceSetPrivate +class QtResourceSetPrivate +{ + QtResourceSet *q_ptr; + Q_DECLARE_PUBLIC(QtResourceSet) +public: + QtResourceSetPrivate(QtResourceModel *model = 0); + + QtResourceModel *m_resourceModel; +}; + +QtResourceSetPrivate::QtResourceSetPrivate(QtResourceModel *model) : + q_ptr(0), + m_resourceModel(model) +{ +} + +// -------------------- QtResourceModelPrivate +class QtResourceModelPrivate +{ + QtResourceModel *q_ptr; + Q_DECLARE_PUBLIC(QtResourceModel) + Q_DISABLE_COPY(QtResourceModelPrivate) +public: + QtResourceModelPrivate(); + void activate(QtResourceSet *resourceSet, const QStringList &newPaths, int *errorCount = 0, QString *errorMessages = 0); + void removeOldPaths(QtResourceSet *resourceSet, const QStringList &newPaths); + + QMap m_pathToModified; + QMap m_resourceSetToPaths; + QMap m_resourceSetToReload; // while path is recreated it needs to be reregistered + // (it is - in the new current resource set, but when the path was used in + // other resource set + // then later when that resource set is activated it needs to be reregistered) + QMap m_newlyCreated; // all created but not activated yet + // (if was active at some point and it's not now it will not be on that map) + QMap > m_pathToResourceSet; + QtResourceSet *m_currentResourceSet; + + typedef QMap PathDataMap; + PathDataMap m_pathToData; + + QMap m_pathToContents; // qrc path to its contents. + QMap m_fileToQrc; // this map contains the content of active resource set only. + // Activating different resource set changes the contents. + + QFileSystemWatcher *m_fileWatcher; + bool m_fileWatcherEnabled; + QMap m_fileWatchedMap; +private: + void registerResourceSet(QtResourceSet *resourceSet); + void unregisterResourceSet(QtResourceSet *resourceSet); + void setWatcherEnabled(const QString &path, bool enable); + void addWatcher(const QString &path); + void removeWatcher(const QString &path); + + void slotFileChanged(const QString &); + + const QByteArray *createResource(const QString &path, QStringList *contents, int *errorCount, QIODevice &errorDevice) const; + void deleteResource(const QByteArray *data) const; +}; + +QtResourceModelPrivate::QtResourceModelPrivate() : + q_ptr(0), + m_currentResourceSet(0), + m_fileWatcher(0), + m_fileWatcherEnabled(true) +{ +} + +// --------------------- QtResourceSet +QtResourceSet::QtResourceSet() : + d_ptr(new QtResourceSetPrivate) +{ + d_ptr->q_ptr = this; +} + +QtResourceSet::QtResourceSet(QtResourceModel *model) : + d_ptr(new QtResourceSetPrivate(model)) +{ + d_ptr->q_ptr = this; +} + +QtResourceSet::~QtResourceSet() +{ +} + +QStringList QtResourceSet::activeQrcPaths() const +{ + QtResourceSet *that = const_cast(this); + return d_ptr->m_resourceModel->d_ptr->m_resourceSetToPaths.value(that); +} + +void QtResourceSet::activateQrcPaths(const QStringList &paths, int *errorCount, QString *errorMessages) +{ + d_ptr->m_resourceModel->d_ptr->activate(this, paths, errorCount, errorMessages); +} + +bool QtResourceSet::isModified(const QString &path) const +{ + return d_ptr->m_resourceModel->isModified(path); +} + +void QtResourceSet::setModified(const QString &path) +{ + d_ptr->m_resourceModel->setModified(path); +} + +// ------------------- QtResourceModelPrivate +const QByteArray *QtResourceModelPrivate::createResource(const QString &path, QStringList *contents, int *errorCount, QIODevice &errorDevice) const +{ + typedef RCCResourceLibrary::ResourceDataFileMap ResourceDataFileMap; + const QByteArray *rc = 0; + *errorCount = -1; + contents->clear(); + do { + // run RCC + RCCResourceLibrary library; + library.setVerbose(true); + library.setInputFiles(QStringList(path)); + library.setFormat(RCCResourceLibrary::Binary); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + if (!library.readFiles(/* ignore errors*/ true, errorDevice)) + break; + // return code cannot be fully trusted, might still be empty + const ResourceDataFileMap resMap = library.resourceDataFileMap(); + if (resMap.empty()) + break; + + if (!library.output(buffer, errorDevice)) + break; + + *errorCount = library.failedResources().size(); + *contents = resMap.keys(); + + buffer.close(); + rc = new QByteArray(buffer.data()); + } while (false); + + if (debugResourceModel) + qDebug() << "createResource" << path << "returns data=" << rc << " hasWarnings=" << *errorCount; + return rc; +} + +void QtResourceModelPrivate::deleteResource(const QByteArray *data) const +{ + if (data) { + if (debugResourceModel) + qDebug() << "deleteResource"; + delete data; + } +} + +void QtResourceModelPrivate::registerResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + + // unregister old paths (all because the order of registration is important), later it can be optimized a bit + QStringList toRegister = resourceSet->activeQrcPaths(); + QStringListIterator itRegister(toRegister); + while (itRegister.hasNext()) { + const QString path = itRegister.next(); + if (debugResourceModel) + qDebug() << "registerResourceSet " << path; + const PathDataMap::const_iterator itRcc = m_pathToData.constFind(path); + if (itRcc != m_pathToData.constEnd()) { // otherwise data was not created yet + if (!QResource::registerResource(reinterpret_cast(itRcc.value()->constData()))) { + qDebug() << "** WARNING: Failed to register " << path << " (QResource failure)."; + } else { + QStringList contents = m_pathToContents.value(path); + QStringListIterator itContents(contents); + while (itContents.hasNext()) { + const QString filePath = itContents.next(); + if (!m_fileToQrc.contains(filePath)) // the first loaded resource has higher priority in qt resource system + m_fileToQrc.insert(filePath, path); + } + } + } + } +} + +void QtResourceModelPrivate::unregisterResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + + // unregister old paths (all because the order of registration is importans), later it can be optimized a bit + QStringList toUnregister = resourceSet->activeQrcPaths(); + QStringListIterator itUnregister(toUnregister); + while (itUnregister.hasNext()) { + const QString path = itUnregister.next(); + if (debugResourceModel) + qDebug() << "unregisterResourceSet " << path; + const PathDataMap::const_iterator itRcc = m_pathToData.constFind(path); + if (itRcc != m_pathToData.constEnd()) { // otherwise data was not created yet + if (!QResource::unregisterResource(reinterpret_cast(itRcc.value()->constData()))) + qDebug() << "** WARNING: Failed to unregister " << path << " (QResource failure)."; + } + } + m_fileToQrc.clear(); +} + +void QtResourceModelPrivate::activate(QtResourceSet *resourceSet, const QStringList &newPaths, int *errorCountPtr, QString *errorMessages) +{ + if (debugResourceModel) + qDebug() << "activate " << resourceSet; + if (errorCountPtr) + *errorCountPtr = 0; + if (errorMessages) + errorMessages->clear(); + + QBuffer errorStream; + errorStream.open(QIODevice::WriteOnly); + + int errorCount = 0; + int generatedCount = 0; + bool newResourceSetChanged = false; + + if (resourceSet && resourceSet->activeQrcPaths() != newPaths && !m_newlyCreated.contains(resourceSet)) + newResourceSetChanged = true; + + PathDataMap newPathToData = m_pathToData; + + QStringListIterator itPath(newPaths); + while (itPath.hasNext()) { + const QString path = itPath.next(); + if (resourceSet && !m_pathToResourceSet[path].contains(resourceSet)) + m_pathToResourceSet[path].append(resourceSet); + const QMap::iterator itMod = m_pathToModified.find(path); + if (itMod == m_pathToModified.end() || itMod.value()) { // new path or path is already created, but needs to be recreated + QStringList contents; + int qrcErrorCount; + generatedCount++; + if (const QByteArray *data = createResource(path, &contents, &qrcErrorCount, errorStream)) { + newPathToData.insert(path, data); + if (qrcErrorCount) // Count single failed files as sort of 1/2 error + errorCount++; + addWatcher(path); + } else { + newPathToData.remove(path); + errorCount++; + } + m_pathToModified.insert(path, false); + m_pathToContents.insert(path, contents); + newResourceSetChanged = true; + const QMap >::iterator itReload = m_pathToResourceSet.find(path); + if (itReload != m_pathToResourceSet.end()) { + QList resources = itReload.value(); + QListIterator itRes(resources); + while (itRes.hasNext()) { + QtResourceSet *res = itRes.next(); + if (res != resourceSet) { + m_resourceSetToReload[res] = true; + } + } + } + } else { // path is already created, don't need to recreate + } + } + + QList oldData = m_pathToData.values(); + QList newData = newPathToData.values(); + + QList toDelete; + QListIterator itOld(oldData); + if (itOld.hasNext()) { + const QByteArray *array = itOld.next(); + if (!newData.contains(array)) + toDelete.append(array); + } + + // Nothing can fail below here? + if (generatedCount) { + if (errorCountPtr) + *errorCountPtr = errorCount; + errorStream.close(); + const QString stderrOutput = QString::fromUtf8(errorStream.data()); + if (debugResourceModel) + qDebug() << "Output: (" << errorCount << ")\n" << stderrOutput; + if (errorMessages) + *errorMessages = stderrOutput; + } + // register + const QMap::iterator itReload = m_resourceSetToReload.find(resourceSet); + if (itReload != m_resourceSetToReload.end()) { + if (itReload.value()) { + newResourceSetChanged = true; + m_resourceSetToReload.insert(resourceSet, false); + } + } + + QStringList oldActivePaths; + if (m_currentResourceSet) + oldActivePaths = m_currentResourceSet->activeQrcPaths(); + + const bool needReregister = (oldActivePaths != newPaths) || newResourceSetChanged; + + QMap::iterator itNew = m_newlyCreated.find(resourceSet); + if (itNew != m_newlyCreated.end()) { + m_newlyCreated.remove(resourceSet); + if (needReregister) + newResourceSetChanged = true; + } + + if (!newResourceSetChanged && !needReregister && (m_currentResourceSet == resourceSet)) { + foreach (const QByteArray *data, toDelete) + deleteResource(data); + + return; // nothing changed + } + + if (needReregister) + unregisterResourceSet(m_currentResourceSet); + + foreach (const QByteArray *data, toDelete) + deleteResource(data); + + m_pathToData = newPathToData; + m_currentResourceSet = resourceSet; + + if (resourceSet) + removeOldPaths(resourceSet, newPaths); + + if (needReregister) + registerResourceSet(m_currentResourceSet); + + emit q_ptr->resourceSetActivated(m_currentResourceSet, newResourceSetChanged); + + // deactivates the paths from old current resource set + // add new paths to the new current resource set + // reloads all paths which are marked as modified from the current resource set; + // activates the paths from current resource set + // emits resourceSetActivated() (don't emit only in case when old resource set is the same as new one + // AND no path was reloaded AND the list of paths is exactly the same) +} + +void QtResourceModelPrivate::removeOldPaths(QtResourceSet *resourceSet, const QStringList &newPaths) +{ + QStringList oldPaths = m_resourceSetToPaths.value(resourceSet); + if (oldPaths != newPaths) { + // remove old + QStringListIterator itOldPaths(oldPaths); + while (itOldPaths.hasNext()) { + QString oldPath = itOldPaths.next(); + if (!newPaths.contains(oldPath)) { + const QMap >::iterator itRemove = m_pathToResourceSet.find(oldPath); + if (itRemove != m_pathToResourceSet.end()) { + const int idx = itRemove.value().indexOf(resourceSet); + if (idx >= 0) + itRemove.value().removeAt(idx); + if (itRemove.value().count() == 0) { + PathDataMap::iterator it = m_pathToData.find(oldPath); + if (it != m_pathToData.end()) + deleteResource(it.value()); + m_pathToResourceSet.erase(itRemove); + m_pathToModified.remove(oldPath); + m_pathToContents.remove(oldPath); + m_pathToData.remove(oldPath); + removeWatcher(oldPath); + } + } + } + } + m_resourceSetToPaths[resourceSet] = newPaths; + } +} + +void QtResourceModelPrivate::setWatcherEnabled(const QString &path, bool enable) +{ + m_fileWatcher->removePath(path); + + if (!enable) + return; + + QFileInfo fi(path); + if (fi.exists()) + m_fileWatcher->addPath(path); +} + +void QtResourceModelPrivate::addWatcher(const QString &path) +{ + QMap::ConstIterator it = m_fileWatchedMap.constFind(path); + if (it != m_fileWatchedMap.constEnd() && it.value() == false) + return; + + m_fileWatchedMap.insert(path, true); + if (!m_fileWatcherEnabled) + return; + setWatcherEnabled(path, true); +} + +void QtResourceModelPrivate::removeWatcher(const QString &path) +{ + if (!m_fileWatchedMap.contains(path)) + return; + + m_fileWatchedMap.remove(path); + if (!m_fileWatcherEnabled) + return; + setWatcherEnabled(path, false); +} + +void QtResourceModelPrivate::slotFileChanged(const QString &path) +{ + setWatcherEnabled(path, false); + emit q_ptr->qrcFileModifiedExternally(path); + setWatcherEnabled(path, true); //readd +} + +// ----------------------- QtResourceModel +QtResourceModel::QtResourceModel(QObject *parent) : + QObject(parent), + d_ptr(new QtResourceModelPrivate) +{ + d_ptr->q_ptr = this; + + d_ptr->m_fileWatcher = new QFileSystemWatcher(this); + connect(d_ptr->m_fileWatcher, SIGNAL(fileChanged(QString)), + this, SLOT(slotFileChanged(QString))); +} + +QtResourceModel::~QtResourceModel() +{ + blockSignals(true); + QList resourceList = resourceSets(); + QListIterator it(resourceList); + while (it.hasNext()) + removeResourceSet(it.next()); + blockSignals(false); +} + +QStringList QtResourceModel::loadedQrcFiles() const +{ + return d_ptr->m_pathToModified.keys(); +} + +bool QtResourceModel::isModified(const QString &path) const +{ + QMap::const_iterator it = d_ptr->m_pathToModified.find(path); + if (it != d_ptr->m_pathToModified.constEnd()) + return it.value(); + return true; +} + +void QtResourceModel::setModified(const QString &path) +{ + QMap::const_iterator itMod = d_ptr->m_pathToModified.find(path); + if (itMod == d_ptr->m_pathToModified.constEnd()) + return; + + d_ptr->m_pathToModified[path] = true; + QMap >::const_iterator it = d_ptr->m_pathToResourceSet.constFind(path); + if (it == d_ptr->m_pathToResourceSet.constEnd()) + return; + + QList resourceList = it.value(); + QListIterator itReload(resourceList); + while (itReload.hasNext()) + d_ptr->m_resourceSetToReload.insert(itReload.next(), true); +} + +QList QtResourceModel::resourceSets() const +{ + return d_ptr->m_resourceSetToPaths.keys(); +} + +QtResourceSet *QtResourceModel::currentResourceSet() const +{ + return d_ptr->m_currentResourceSet; +} + +void QtResourceModel::setCurrentResourceSet(QtResourceSet *resourceSet, int *errorCount, QString *errorMessages) +{ + d_ptr->activate(resourceSet, d_ptr->m_resourceSetToPaths.value(resourceSet), errorCount, errorMessages); +} + +QtResourceSet *QtResourceModel::addResourceSet(const QStringList &paths) +{ + QtResourceSet *newResource = new QtResourceSet(this); + d_ptr->m_resourceSetToPaths.insert(newResource, paths); + d_ptr->m_resourceSetToReload.insert(newResource, false); + d_ptr->m_newlyCreated.insert(newResource, true); + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + d_ptr->m_pathToResourceSet[path].append(newResource); + } + return newResource; +} + +// TODO +void QtResourceModel::removeResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + if (currentResourceSet() == resourceSet) + setCurrentResourceSet(0); + + // remove rcc files for those paths which are not used in any other resource set + d_ptr->removeOldPaths(resourceSet, QStringList()); + + d_ptr->m_resourceSetToPaths.remove(resourceSet); + d_ptr->m_resourceSetToReload.remove(resourceSet); + d_ptr->m_newlyCreated.remove(resourceSet); + delete resourceSet; +} + +void QtResourceModel::reload(const QString &path, int *errorCount, QString *errorMessages) +{ + setModified(path); + + d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages); +} + +void QtResourceModel::reload(int *errorCount, QString *errorMessages) +{ + QMap::iterator it = d_ptr->m_pathToModified.begin(); + QMap::iterator itEnd = d_ptr->m_pathToModified.end(); // will it be valid when I iterate the map and change it??? + while (it != itEnd) { + it = d_ptr->m_pathToModified.insert(it.key(), true); + ++it; + } + + QMap::iterator itReload = d_ptr->m_resourceSetToReload.begin(); + QMap::iterator itReloadEnd = d_ptr->m_resourceSetToReload.end(); + while (itReload != itReloadEnd) { + itReload = d_ptr->m_resourceSetToReload.insert(itReload.key(), true); // empty resourceSets could be omitted here + ++itReload; + } + + d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages); +} + +QMap QtResourceModel::contents() const +{ + return d_ptr->m_fileToQrc; +} + +QString QtResourceModel::qrcPath(const QString &file) const +{ + return d_ptr->m_fileToQrc.value(file); +} + +void QtResourceModel::setWatcherEnabled(bool enable) +{ + if (d_ptr->m_fileWatcherEnabled == enable) + return; + + d_ptr->m_fileWatcherEnabled = enable; + + QMapIterator it(d_ptr->m_fileWatchedMap); + if (it.hasNext()) + d_ptr->setWatcherEnabled(it.next().key(), d_ptr->m_fileWatcherEnabled); +} + +bool QtResourceModel::isWatcherEnabled() const +{ + return d_ptr->m_fileWatcherEnabled; +} + +void QtResourceModel::setWatcherEnabled(const QString &path, bool enable) +{ + QMap::Iterator it = d_ptr->m_fileWatchedMap.find(path); + if (it == d_ptr->m_fileWatchedMap.end()) + return; + + if (it.value() == enable) + return; + + it.value() = enable; + + if (!d_ptr->m_fileWatcherEnabled) + return; + + d_ptr->setWatcherEnabled(it.key(), enable); +} + +bool QtResourceModel::isWatcherEnabled(const QString &path) +{ + return d_ptr->m_fileWatchedMap.value(path, false); +} + +QT_END_NAMESPACE + +#include "moc_qtresourcemodel_p.cpp" diff --git a/designer/lib/shared/qtresourcemodel_p.h b/designer/lib/shared/qtresourcemodel_p.h new file mode 100644 index 0000000..8184eb8 --- /dev/null +++ b/designer/lib/shared/qtresourcemodel_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEMODEL_H +#define QTRESOURCEMODEL_H + +#include "shared_global_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QtResourceModel; + +class QDESIGNER_SHARED_EXPORT QtResourceSet // one instance per one form +{ +public: + QStringList activeQrcPaths() const; + + // activateQrcPaths(): if this QtResourceSet is active it emits resourceSetActivated(); + // otherwise only in case if active QtResource set contains one of + // paths which was marked as modified by this resource set, the signal + // is emitted (with reload = true); + // If new path appears on the list it is automatically added to + // loaded list of QtResourceModel. In addition it is marked as modified in case + // QtResourceModel didn't contain the path. + // If some path is removed from that list (and is not used in any other + // resource set) it is automatically unloaded. The removed file can also be + // marked as modified (later when another resource set which contains + // removed path is activated will be reloaded) + void activateQrcPaths(const QStringList &paths, int *errorCount = 0, QString *errorMessages = 0); + + bool isModified(const QString &path) const; // for all paths in resource model (redundant here, maybe it should be removed from here) + void setModified(const QString &path); // for all paths in resource model (redundant here, maybe it should be removed from here) +private: + QtResourceSet(); + QtResourceSet(QtResourceModel *model); + ~QtResourceSet(); + friend class QtResourceModel; + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtResourceSet) + Q_DISABLE_COPY(QtResourceSet) +}; + +class QDESIGNER_SHARED_EXPORT QtResourceModel : public QObject // one instance per whole designer +{ + Q_OBJECT +public: + QtResourceModel(QObject *parent = 0); + ~QtResourceModel(); + + QStringList loadedQrcFiles() const; + bool isModified(const QString &path) const; // only for paths which are on loadedQrcFiles() list + void setModified(const QString &path); // only for paths which are on loadedQrcPaths() list + + QList resourceSets() const; + + QtResourceSet *currentResourceSet() const; + void setCurrentResourceSet(QtResourceSet *resourceSet, int *errorCount = 0, QString *errorMessages = 0); + + QtResourceSet *addResourceSet(const QStringList &paths); + void removeResourceSet(QtResourceSet *resourceSet); + + void reload(const QString &path, int *errorCount = 0, QString *errorMessages = 0); + void reload(int *errorCount = 0, QString *errorMessages = 0); + + // Contents of the current resource set (content file to qrc path) + QMap contents() const; + // Find the qrc file belonging to the contained file (from current resource set) + QString qrcPath(const QString &file) const; + + void setWatcherEnabled(bool enable); + bool isWatcherEnabled() const; + + void setWatcherEnabled(const QString &path, bool enable); + bool isWatcherEnabled(const QString &path); + +signals: + void resourceSetActivated(QtResourceSet *resourceSet, bool resourceSetChanged); // resourceSetChanged since last time it was activated! + void qrcFileModifiedExternally(const QString &path); + +private: + friend class QtResourceSet; + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtResourceModel) + Q_DISABLE_COPY(QtResourceModel) + + Q_PRIVATE_SLOT(d_func(), void slotFileChanged(const QString &)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/qtresourceview.cpp b/designer/lib/shared/qtresourceview.cpp new file mode 100644 index 0000000..6d4c522 --- /dev/null +++ b/designer/lib/shared/qtresourceview.cpp @@ -0,0 +1,906 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "qtresourceview_p.h" +#include "qtresourcemodel_p.h" +#include "qtresourceeditordialog_p.h" +#include "iconloader_p.h" +#include "filterwidget_p.h" // For FilterWidget + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *elementResourceData = "resource"; +static const char *typeAttribute = "type"; +static const char *typeImage = "image"; +static const char *typeStyleSheet = "stylesheet"; +static const char *typeOther = "other"; +static const char *fileAttribute = "file"; +static const char *SplitterPosition = "SplitterPosition"; +static const char *Geometry = "Geometry"; +static const char *ResourceViewDialogC = "ResourceDialog"; + +// ---------------- ResourceListWidget: A list widget that has drag enabled +class ResourceListWidget : public QListWidget { +public: + ResourceListWidget(QWidget *parent = 0); + +protected: + virtual void startDrag(Qt::DropActions supportedActions); +}; + +ResourceListWidget::ResourceListWidget(QWidget *parent) : + QListWidget(parent) +{ + setDragEnabled(true); +} + +void ResourceListWidget::startDrag(Qt::DropActions supportedActions) +{ + if (supportedActions == Qt::MoveAction) + return; + + QListWidgetItem *item = currentItem(); + if (!item) + return; + + const QString filePath = item->data(Qt::UserRole).toString(); + const QIcon icon = item->icon(); + + QMimeData *mimeData = new QMimeData; + const QtResourceView::ResourceType type = icon.isNull() ? QtResourceView::ResourceOther : QtResourceView::ResourceImage; + mimeData->setText(QtResourceView::encodeMimeData(type , filePath)); + + QDrag *drag = new QDrag(this); + if (!icon.isNull()) { + const QSize size = icon.actualSize(iconSize()); + drag->setPixmap(icon.pixmap(size)); + drag->setHotSpot(QPoint(size.width() / 2, size.height() / 2)); + } + + drag->setMimeData(mimeData); + drag->exec(Qt::CopyAction); +} + +/* TODO + + 1) load the icons in separate thread...Hmm..if Qt is configured with threads.... +*/ + +// ---------------------------- QtResourceViewPrivate +class QtResourceViewPrivate +{ + QtResourceView *q_ptr; + Q_DECLARE_PUBLIC(QtResourceView) +public: + QtResourceViewPrivate(QDesignerFormEditorInterface *core); + + void slotResourceSetActivated(QtResourceSet *resourceSet); + void slotCurrentPathChanged(QTreeWidgetItem *); + void slotCurrentResourceChanged(QListWidgetItem *); + void slotResourceActivated(QListWidgetItem *); + void slotEditResources(); + void slotReloadResources(); + void slotCopyResourcePath(); + void slotListWidgetContextMenuRequested(const QPoint &pos); + void slotFilterChanged(const QString &pattern); + void createPaths(); + QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent); + void createResources(const QString &path); + void storeExpansionState(); + void applyExpansionState(); + void restoreSettings(); + void saveSettings(); + void updateActions(); + void filterOutResources(); + + QPixmap makeThumbnail(const QPixmap &pix) const; + + QDesignerFormEditorInterface *m_core; + QtResourceModel *m_resourceModel; + QToolBar *m_toolBar; + qdesigner_internal::FilterWidget *m_filterWidget; + QTreeWidget *m_treeWidget; + QListWidget *m_listWidget; + QSplitter *m_splitter; + QMap m_pathToContents; // full path to contents file names (full path to its resource filenames) + QMap m_pathToParentPath; // full path to full parent path + QMap m_pathToSubPaths; // full path to full sub paths + QMap m_pathToItem; + QMap m_itemToPath; + QMap m_resourceToItem; + QMap m_itemToResource; + QAction *m_editResourcesAction; + QAction *m_reloadResourcesAction; + QAction *m_copyResourcePathAction; + + QMap m_expansionState; + + bool m_ignoreGuiSignals; + QString m_settingsKey; + bool m_resourceEditingEnabled; + QString m_filterPattern; +}; + +QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) : + q_ptr(0), + m_core(core), + m_resourceModel(0), + m_toolBar(new QToolBar), + m_treeWidget(new QTreeWidget), + m_listWidget(new ResourceListWidget), + m_splitter(0), + m_editResourcesAction(0), + m_reloadResourcesAction(0), + m_copyResourcePathAction(0), + m_ignoreGuiSignals(false), + m_resourceEditingEnabled(true) +{ +} + +void QtResourceViewPrivate::restoreSettings() +{ + if (m_settingsKey.isEmpty()) + return; + + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(m_settingsKey); + + m_splitter->restoreState(settings->value(QLatin1String(SplitterPosition)).toByteArray()); + settings->endGroup(); +} + +void QtResourceViewPrivate::saveSettings() +{ + if (m_settingsKey.isEmpty()) + return; + + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(m_settingsKey); + + settings->setValue(QLatin1String(SplitterPosition), m_splitter->saveState()); + settings->endGroup(); +} + +void QtResourceViewPrivate::slotEditResources() +{ + const QString selectedResource + = QtResourceEditorDialog::editResources(m_core, m_resourceModel, + m_core->dialogGui(), q_ptr); + if (!selectedResource.isEmpty()) + q_ptr->selectResource(selectedResource); +} + +void QtResourceViewPrivate::slotReloadResources() +{ + if (m_resourceModel) { + int errorCount; + QString errorMessages; + m_resourceModel->reload(&errorCount, &errorMessages); + if (errorCount) + QtResourceEditorDialog::displayResourceFailures(errorMessages, m_core->dialogGui(), q_ptr); + } +} + +void QtResourceViewPrivate::slotCopyResourcePath() +{ + const QString path = q_ptr->selectedResource(); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(path); +} + +void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_copyResourcePathAction); + menu.exec(m_listWidget->mapToGlobal(pos)); +} + +void QtResourceViewPrivate::slotFilterChanged(const QString &pattern) +{ + m_filterPattern = pattern; + filterOutResources(); +} + +void QtResourceViewPrivate::storeExpansionState() +{ + QMapIterator it(m_pathToItem); + while (it.hasNext()) { + it.next(); + m_expansionState[it.key()] = it.value()->isExpanded(); + } +} + +void QtResourceViewPrivate::applyExpansionState() +{ + QMapIterator it(m_pathToItem); + while (it.hasNext()) { + it.next(); + it.value()->setExpanded(m_expansionState.value(it.key(), true)); + } +} + +QPixmap QtResourceViewPrivate::makeThumbnail(const QPixmap &pix) const +{ + int w = qMax(48, pix.width()); + int h = qMax(48, pix.height()); + QRect imgRect(0, 0, w, h); + QImage img(w, h, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + if (!pix.isNull()) { + QRect r(0, 0, pix.width(), pix.height()); + r.moveCenter(imgRect.center()); + QPainter p(&img); + p.drawPixmap(r.topLeft(), pix); + } + return QPixmap::fromImage(img); +} + +void QtResourceViewPrivate::updateActions() +{ + bool resourceActive = false; + if (m_resourceModel) + resourceActive = m_resourceModel->currentResourceSet(); + + m_editResourcesAction->setVisible(m_resourceEditingEnabled); + m_editResourcesAction->setEnabled(resourceActive); + m_reloadResourcesAction->setEnabled(resourceActive); + m_filterWidget->setEnabled(resourceActive); +} + +void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) +{ + Q_UNUSED(resourceSet) + + updateActions(); + + storeExpansionState(); + const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem()); + const QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); + m_treeWidget->clear(); + m_pathToContents.clear(); + m_pathToParentPath.clear(); + m_pathToSubPaths.clear(); + m_pathToItem.clear(); + m_itemToPath.clear(); + m_listWidget->clear(); + m_resourceToItem.clear(); + m_itemToResource.clear(); + + createPaths(); + applyExpansionState(); + + if (!currentResource.isEmpty()) + q_ptr->selectResource(currentResource); + else if (!currentPath.isEmpty()) + q_ptr->selectResource(currentPath); + filterOutResources(); +} + +void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item) +{ + if (m_ignoreGuiSignals) + return; + + m_listWidget->clear(); + m_resourceToItem.clear(); + m_itemToResource.clear(); + + if (!item) + return; + + const QString currentPath = m_itemToPath.value(item); + createResources(currentPath); +} + +void QtResourceViewPrivate::slotCurrentResourceChanged(QListWidgetItem *item) +{ + m_copyResourcePathAction->setEnabled(item); + if (m_ignoreGuiSignals) + return; + + emit q_ptr->resourceSelected(m_itemToResource.value(item)); +} + +void QtResourceViewPrivate::slotResourceActivated(QListWidgetItem *item) +{ + if (m_ignoreGuiSignals) + return; + + emit q_ptr->resourceActivated(m_itemToResource.value(item)); +} + +void QtResourceViewPrivate::createPaths() +{ + if (!m_resourceModel) + return; + + // Resource root up until 4.6 was ':', changed to ":/" as of 4.7 + const QString root(QLatin1String(":/")); + + QMap contents = m_resourceModel->contents(); + QMapIterator itContents(contents); + while (itContents.hasNext()) { + const QString filePath = itContents.next().key(); + const QFileInfo fi(filePath); + QString dirPath = fi.absolutePath(); + m_pathToContents[dirPath].append(fi.fileName()); + while (!m_pathToParentPath.contains(dirPath) && dirPath != root) { // create all parent paths + const QFileInfo fd(dirPath); + const QString parentDirPath = fd.absolutePath(); + m_pathToParentPath[dirPath] = parentDirPath; + m_pathToSubPaths[parentDirPath].append(dirPath); + dirPath = parentDirPath; + } + } + + QQueue > pathToParentItemQueue; + pathToParentItemQueue.enqueue(qMakePair(root, static_cast(0))); + while (!pathToParentItemQueue.isEmpty()) { + QPair pathToParentItem = pathToParentItemQueue.dequeue(); + const QString path = pathToParentItem.first; + QTreeWidgetItem *item = createPath(path, pathToParentItem.second); + QStringList subPaths = m_pathToSubPaths.value(path); + QStringListIterator itSubPaths(subPaths); + while (itSubPaths.hasNext()) + pathToParentItemQueue.enqueue(qMakePair(itSubPaths.next(), item)); + } +} + +void QtResourceViewPrivate::filterOutResources() +{ + QMap pathToMatchingContents; // true means the path has any matching contents + QMap pathToVisible; // true means the path has to be shown + + // 1) we go from root path recursively. + // 2) we check every path if it contains at least one matching resource - if empty we add it + // to pathToMatchingContents and pathToVisible with false, if non empty + // we add it with true and change every parent path in pathToVisible to true. + // 3) we hide these items which has pathToVisible value false. + + const bool matchAll = m_filterPattern.isEmpty(); + const QString root(QLatin1String(":/")); + + QQueue pathQueue; + pathQueue.enqueue(root); + while (!pathQueue.isEmpty()) { + const QString path = pathQueue.dequeue(); + + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); + bool hasContents = matchAll; + if (!matchAll) { // the case filter is not empty - we check if the path contains anything + while (it.hasNext()) { + QString fileName = it.next(); + hasContents = fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (hasContents) // the path contains at least one resource which matches the filter + break; + } + } + + pathToMatchingContents[path] = hasContents; + pathToVisible[path] = hasContents; + + if (hasContents) { // if the path is going to be shown we need to show all its parent paths + QString parentPath = m_pathToParentPath.value(path); + while (!parentPath.isEmpty()) { + QString p = parentPath; + if (pathToVisible.value(p)) // parent path is already shown, we break the loop + break; + pathToVisible[p] = true; + parentPath = m_pathToParentPath.value(p); + } + } + + QStringList subPaths = m_pathToSubPaths.value(path); // we do the same for children paths + QStringListIterator itSubPaths(subPaths); + while (itSubPaths.hasNext()) + pathQueue.enqueue(itSubPaths.next()); + } + + // we setup here new path and resource to be activated + const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem()); + QString newCurrentPath = currentPath; + QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); + if (!matchAll) { + bool searchForNewPathWithContents = true; + + if (!currentPath.isEmpty()) { // if the currentPath is empty we will search for a new path too + QMap::ConstIterator it = pathToMatchingContents.constFind(currentPath); + if (it != pathToMatchingContents.constEnd() && it.value()) // the current item has contents, we don't need to search for another path + searchForNewPathWithContents = false; + } + + if (searchForNewPathWithContents) { + // we find the first path with the matching contents + QMap::ConstIterator itContents = pathToMatchingContents.constBegin(); + while (itContents != pathToMatchingContents.constEnd()) { + if (itContents.value()) { + newCurrentPath = itContents.key(); // the new path will be activated + break; + } + + itContents++; + } + } + + QFileInfo fi(currentResource); + if (!fi.fileName().contains(m_filterPattern, Qt::CaseInsensitive)) { // the case when the current resource is filtered out + const QStringList fileNames = m_pathToContents.value(newCurrentPath); + QStringListIterator it(fileNames); + while (it.hasNext()) { // we try to select the first matching resource from the newCurrentPath + QString fileName = it.next(); + if (fileName.contains(m_filterPattern, Qt::CaseInsensitive)) { + QDir dirPath(newCurrentPath); + currentResource = dirPath.absoluteFilePath(fileName); // the new resource inside newCurrentPath will be activated + break; + } + } + } + } + + QTreeWidgetItem *newCurrentItem = m_pathToItem.value(newCurrentPath); + if (currentPath != newCurrentPath) + m_treeWidget->setCurrentItem(newCurrentItem); + else + slotCurrentPathChanged(newCurrentItem); // trigger filtering on the current path + + QListWidgetItem *currentResourceItem = m_resourceToItem.value(currentResource); + if (currentResourceItem) { + m_listWidget->setCurrentItem(currentResourceItem); + m_listWidget->scrollToItem(currentResourceItem); + } + + QMapIterator it(pathToVisible); // hide all paths filtered out + while (it.hasNext()) { + const QString path = it.next().key(); + QTreeWidgetItem *item = m_pathToItem.value(path); + if (item) + item->setHidden(!it.value()); + } +} + +QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent) +{ + QTreeWidgetItem *item = 0; + if (parent) + item = new QTreeWidgetItem(parent); + else + item = new QTreeWidgetItem(m_treeWidget); + m_pathToItem[path] = item; + m_itemToPath[item] = path; + QString substPath; + if (parent) { + QFileInfo di(path); + substPath = di.fileName(); + } else { + substPath = QLatin1String(""); + } + item->setText(0, substPath); + item->setToolTip(0, path); + return item; +} + +void QtResourceViewPrivate::createResources(const QString &path) +{ + const bool matchAll = m_filterPattern.isEmpty(); + + QDir dir(path); + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); + while (it.hasNext()) { + QString fileName = it.next(); + const bool showProperty = matchAll || fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (showProperty) { + QString filePath = dir.absoluteFilePath(fileName); + QFileInfo fi(filePath); + if (fi.isFile()) { + QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); + const QPixmap pix = QPixmap(filePath); + if (pix.isNull()) { + item->setToolTip(filePath); + } else { + item->setIcon(QIcon(makeThumbnail(pix))); + const QSize size = pix.size(); + item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + } + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + item->setData(Qt::UserRole, filePath); + m_itemToResource[item] = filePath; + m_resourceToItem[filePath] = item; + } + } + } +} + +// -------------- QtResourceView + +QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + d_ptr(new QtResourceViewPrivate(core)) +{ + d_ptr->q_ptr = this; + + QIcon editIcon = QIcon::fromTheme("document-properties", qdesigner_internal::createIconSet(QLatin1String("edit.png"))); + d_ptr->m_editResourcesAction = new QAction(editIcon, tr("Edit Resources..."), this); + d_ptr->m_toolBar->addAction(d_ptr->m_editResourcesAction); + connect(d_ptr->m_editResourcesAction, SIGNAL(triggered()), this, SLOT(slotEditResources())); + d_ptr->m_editResourcesAction->setEnabled(false); + + QIcon refreshIcon = QIcon::fromTheme("view-refresh", qdesigner_internal::createIconSet(QLatin1String("reload.png"))); + d_ptr->m_reloadResourcesAction = new QAction(refreshIcon, tr("Reload"), this); + + d_ptr->m_toolBar->addAction(d_ptr->m_reloadResourcesAction); + connect(d_ptr->m_reloadResourcesAction, SIGNAL(triggered()), this, SLOT(slotReloadResources())); + d_ptr->m_reloadResourcesAction->setEnabled(false); + + QIcon copyIcon = QIcon::fromTheme("edit-copy", qdesigner_internal::createIconSet(QLatin1String("editcopy.png"))); + d_ptr->m_copyResourcePathAction = new QAction(copyIcon, tr("Copy Path"), this); + connect(d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), this, SLOT(slotCopyResourcePath())); + d_ptr->m_copyResourcePathAction->setEnabled(false); + + //d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(0, qdesigner_internal::FilterWidget::LayoutAlignNone); + d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(d_ptr->m_toolBar); + d_ptr->m_toolBar->addWidget(d_ptr->m_filterWidget); + connect(d_ptr->m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(slotFilterChanged(QString))); + + d_ptr->m_splitter = new QSplitter; + d_ptr->m_splitter->setChildrenCollapsible(false); + d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget); + d_ptr->m_splitter->addWidget(d_ptr->m_listWidget); + + QLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(d_ptr->m_toolBar); + layout->addWidget(d_ptr->m_splitter); + + d_ptr->m_treeWidget->setColumnCount(1); + d_ptr->m_treeWidget->header()->hide(); + d_ptr->m_treeWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + + d_ptr->m_listWidget->setViewMode(QListView::IconMode); + d_ptr->m_listWidget->setResizeMode(QListView::Adjust); + d_ptr->m_listWidget->setIconSize(QSize(48, 48)); + d_ptr->m_listWidget->setGridSize(QSize(64, 64)); + + connect(d_ptr->m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(slotCurrentPathChanged(QTreeWidgetItem*))); + connect(d_ptr->m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(slotCurrentResourceChanged(QListWidgetItem*))); + connect(d_ptr->m_listWidget, SIGNAL(itemActivated(QListWidgetItem*)), + this, SLOT(slotResourceActivated(QListWidgetItem*))); + d_ptr->m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(d_ptr->m_listWidget, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotListWidgetContextMenuRequested(QPoint))); +} + +QtResourceView::~QtResourceView() +{ + if (!d_ptr->m_settingsKey.isEmpty()) + d_ptr->saveSettings(); +} + +bool QtResourceView::event(QEvent *event) +{ + if (event->type() == QEvent::Show) { + d_ptr->m_listWidget->scrollToItem(d_ptr->m_listWidget->currentItem()); + d_ptr->m_treeWidget->scrollToItem(d_ptr->m_treeWidget->currentItem()); + } + return QWidget::event(event); +} + +QtResourceModel *QtResourceView::model() const +{ + return d_ptr->m_resourceModel; +} + +QString QtResourceView::selectedResource() const +{ + QListWidgetItem *item = d_ptr->m_listWidget->currentItem(); + return d_ptr->m_itemToResource.value(item); +} + +void QtResourceView::selectResource(const QString &resource) +{ + if (resource.isEmpty()) + return; + QFileInfo fi(resource); + QDir dir = fi.absoluteDir(); + if (fi.isDir()) + dir = QDir(resource); + QString dirPath = dir.absolutePath(); + QMap::const_iterator it; + while ((it = d_ptr->m_pathToItem.find(dirPath)) == d_ptr->m_pathToItem.constEnd()) { + if (!dir.cdUp()) + break; + dirPath = dir.absolutePath(); + } + if (it != d_ptr->m_pathToItem.constEnd()) { + QTreeWidgetItem *treeItem = it.value(); + d_ptr->m_treeWidget->setCurrentItem(treeItem); + d_ptr->m_treeWidget->scrollToItem(treeItem); + // expand all up to current one is done by qt + // list widget is already propagated (currrent changed was sent by qt) + QListWidgetItem *item = d_ptr->m_resourceToItem.value(resource); + if (item) { + d_ptr->m_listWidget->setCurrentItem(item); + d_ptr->m_listWidget->scrollToItem(item); + } + } +} + +QString QtResourceView::settingsKey() const +{ + return d_ptr->m_settingsKey; +} + +void QtResourceView::setSettingsKey(const QString &key) +{ + if (d_ptr->m_settingsKey == key) + return; + + d_ptr->m_settingsKey = key; + + if (key.isEmpty()) + return; + + d_ptr->restoreSettings(); +} + +void QtResourceView::setResourceModel(QtResourceModel *model) +{ + if (d_ptr->m_resourceModel) { + disconnect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)), + this, SLOT(slotResourceSetActivated(QtResourceSet*))); + } + + // clear here + d_ptr->m_treeWidget->clear(); + d_ptr->m_listWidget->clear(); + + d_ptr->m_resourceModel = model; + + if (!d_ptr->m_resourceModel) + return; + + connect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet*,bool)), + this, SLOT(slotResourceSetActivated(QtResourceSet*))); + + // fill new here + d_ptr->slotResourceSetActivated(d_ptr->m_resourceModel->currentResourceSet()); +} + +bool QtResourceView::isResourceEditingEnabled() const +{ + return d_ptr->m_resourceEditingEnabled; +} + +void QtResourceView::setResourceEditingEnabled(bool enable) +{ + d_ptr->m_resourceEditingEnabled = enable; + d_ptr->updateActions(); +} + +void QtResourceView::setDragEnabled(bool dragEnabled) +{ + d_ptr->m_listWidget->setDragEnabled(dragEnabled); +} + +bool QtResourceView::dragEnabled() const +{ + return d_ptr->m_listWidget->dragEnabled(); +} + +QString QtResourceView::encodeMimeData(ResourceType resourceType, const QString &path) +{ + QDomDocument doc; + QDomElement elem = doc.createElement(QLatin1String(elementResourceData)); + switch (resourceType) { + case ResourceImage: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeImage)); + break; + case ResourceStyleSheet: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeStyleSheet)); + break; + case ResourceOther: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeOther)); + break; + } + elem.setAttribute(QLatin1String(fileAttribute), path); + doc.appendChild(elem); + return doc.toString(); +} + +bool QtResourceView::decodeMimeData(const QMimeData *md, ResourceType *t, QString *file) +{ + return md->hasText() ? decodeMimeData(md->text(), t, file) : false; +} + +bool QtResourceView::decodeMimeData(const QString &text, ResourceType *t, QString *file) +{ + + const QString docElementName = QLatin1String(elementResourceData); + static const QString docElementString = QLatin1Char('<') + docElementName; + + if (text.isEmpty() || text.indexOf(docElementString) == -1) + return false; + + QDomDocument doc; + if (!doc.setContent(text)) + return false; + + const QDomElement domElement = doc.documentElement(); + if (domElement.tagName() != docElementName) + return false; + + if (t) { + const QString typeAttr = QLatin1String(typeAttribute); + if (domElement.hasAttribute (typeAttr)) { + const QString typeValue = domElement.attribute(typeAttr, QLatin1String(typeOther)); + if (typeValue == QLatin1String(typeImage)) { + *t = ResourceImage; + } else { + *t = typeValue == QLatin1String(typeStyleSheet) ? ResourceStyleSheet : ResourceOther; + } + } + } + if (file) { + const QString fileAttr = QLatin1String(fileAttribute); + if (domElement.hasAttribute(fileAttr)) { + *file = domElement.attribute(fileAttr, QString()); + } else { + file->clear(); + } + } + return true; +} + +// ---------------------------- QtResourceViewDialogPrivate + +class QtResourceViewDialogPrivate +{ + QtResourceViewDialog *q_ptr; + Q_DECLARE_PUBLIC(QtResourceViewDialog) +public: + QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core); + + void slotResourceSelected(const QString &resource) { setOkButtonEnabled(!resource.isEmpty()); } + void setOkButtonEnabled(bool v) { m_box->button(QDialogButtonBox::Ok)->setEnabled(v); } + + QDesignerFormEditorInterface *m_core; + QtResourceView *m_view; + QDialogButtonBox *m_box; +}; + +QtResourceViewDialogPrivate::QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core) : + q_ptr(0), + m_core(core), + m_view(new QtResourceView(core)), + m_box(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) +{ + m_view->setSettingsKey(QLatin1String(ResourceViewDialogC)); +} + +// ------------ QtResourceViewDialog +QtResourceViewDialog::QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + d_ptr(new QtResourceViewDialogPrivate(core)) +{ + setWindowTitle(tr("Select Resource")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + d_ptr->q_ptr = this; + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(d_ptr->m_view); + layout->addWidget(d_ptr->m_box); + connect(d_ptr->m_box, SIGNAL(accepted()), this, SLOT(accept())); + connect(d_ptr->m_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(d_ptr->m_view, SIGNAL(resourceActivated(QString)), this, SLOT(accept())); + connect(d_ptr->m_view, SIGNAL(resourceSelected(QString)), this, SLOT(slotResourceSelected(QString))); + d_ptr->setOkButtonEnabled(false); + d_ptr->m_view->setResourceModel(core->resourceModel()); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(ResourceViewDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + setGeometry(settings->value(QLatin1String(Geometry)).toRect()); + + settings->endGroup(); +} + +QtResourceViewDialog::~QtResourceViewDialog() +{ + QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager(); + settings->beginGroup(QLatin1String(ResourceViewDialogC)); + + settings->setValue(QLatin1String(Geometry), geometry()); + + settings->endGroup(); +} + +QString QtResourceViewDialog::selectedResource() const +{ + return d_ptr->m_view->selectedResource(); +} + +void QtResourceViewDialog::selectResource(const QString &path) +{ + d_ptr->m_view->selectResource(path); +} + +bool QtResourceViewDialog::isResourceEditingEnabled() const +{ + return d_ptr->m_view->isResourceEditingEnabled(); +} + +void QtResourceViewDialog::setResourceEditingEnabled(bool enable) +{ + d_ptr->m_view->setResourceEditingEnabled(enable); +} + +QT_END_NAMESPACE + +#include "moc_qtresourceview_p.cpp" diff --git a/designer/lib/shared/qtresourceview_p.h b/designer/lib/shared/qtresourceview_p.h new file mode 100644 index 0000000..a56c6ed --- /dev/null +++ b/designer/lib/shared/qtresourceview_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEVIEW_H +#define QTRESOURCEVIEW_H + +#include "shared_global_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QtResourceSet; +class QDesignerFormEditorInterface; +class QMimeData; + +class QDESIGNER_SHARED_EXPORT QtResourceView : public QWidget +{ + Q_OBJECT +public: + explicit QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~QtResourceView(); + + void setDragEnabled(bool dragEnabled); + bool dragEnabled() const; + + QtResourceModel *model() const; + void setResourceModel(QtResourceModel *model); + + QString selectedResource() const; + void selectResource(const QString &resource); + + QString settingsKey() const; + void setSettingsKey(const QString &key); + + bool isResourceEditingEnabled() const; + void setResourceEditingEnabled(bool enable); + + // Helpers for handling the drag originating in QtResourceView (Xml/text) + enum ResourceType { ResourceImage, ResourceStyleSheet, ResourceOther }; + static QString encodeMimeData(ResourceType resourceType, const QString &path); + + static bool decodeMimeData(const QMimeData *md, ResourceType *t = 0, QString *file = 0); + static bool decodeMimeData(const QString &text, ResourceType *t = 0, QString *file = 0); + +signals: + void resourceSelected(const QString &resource); + void resourceActivated(const QString &resource); + +protected: + bool event(QEvent *event); + +private: + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtResourceView) + Q_DISABLE_COPY(QtResourceView) + Q_PRIVATE_SLOT(d_func(), void slotResourceSetActivated(QtResourceSet *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentPathChanged(QTreeWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentResourceChanged(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceActivated(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotEditResources()) + Q_PRIVATE_SLOT(d_func(), void slotReloadResources()) + Q_PRIVATE_SLOT(d_func(), void slotCopyResourcePath()) + Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &pos)) + Q_PRIVATE_SLOT(d_func(), void slotFilterChanged(const QString &pattern)) +}; + +class QDESIGNER_SHARED_EXPORT QtResourceViewDialog : public QDialog +{ + Q_OBJECT +public: + explicit QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + virtual ~QtResourceViewDialog(); + + QString selectedResource() const; + void selectResource(const QString &path); + + bool isResourceEditingEnabled() const; + void setResourceEditingEnabled(bool enable); + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtResourceViewDialog) + Q_DISABLE_COPY(QtResourceViewDialog) + Q_PRIVATE_SLOT(d_func(), void slotResourceSelected(const QString &)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/richtexteditor.cpp b/designer/lib/shared/richtexteditor.cpp new file mode 100644 index 0000000..18e4f26 --- /dev/null +++ b/designer/lib/shared/richtexteditor.cpp @@ -0,0 +1,758 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "richtexteditor_p.h" +#include "htmlhighlighter_p.h" +#include "iconselector_p.h" +#include "ui_addlinkdialog.h" +#include "abstractsettings_p.h" + +#include "iconloader_p.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *RichTextDialogC = "RichTextDialog"; +static const char *Geometry = "Geometry"; + +namespace qdesigner_internal { + +class RichTextEditor : public QTextEdit +{ + Q_OBJECT +public: + RichTextEditor(QWidget *parent = 0); + void setDefaultFont(const QFont &font); + + QToolBar *createToolBar(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +public slots: + void setFontBold(bool b); + void setFontPointSize(double); + void setText(const QString &text); + QString text(Qt::TextFormat format) const; + +signals: + void stateChanged(); +}; + +class AddLinkDialog : public QDialog +{ + Q_OBJECT + +public: + AddLinkDialog(RichTextEditor *editor, QWidget *parent = 0); + ~AddLinkDialog(); + + int showDialog(); + +public slots: + void accept(); + +private: + RichTextEditor *m_editor; + Ui::AddLinkDialog *m_ui; +}; + +AddLinkDialog::AddLinkDialog(RichTextEditor *editor, QWidget *parent) : + QDialog(parent), + m_ui(new Ui::AddLinkDialog) +{ + m_ui->setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_editor = editor; +} + +AddLinkDialog::~AddLinkDialog() +{ + delete m_ui; +} + +int AddLinkDialog::showDialog() +{ + // Set initial focus + const QTextCursor cursor = m_editor->textCursor(); + if (cursor.hasSelection()) { + m_ui->titleInput->setText(cursor.selectedText()); + m_ui->urlInput->setFocus(); + } else { + m_ui->titleInput->setFocus(); + } + + return exec(); +} + +void AddLinkDialog::accept() +{ + const QString title = m_ui->titleInput->text(); + const QString url = m_ui->urlInput->text(); + + if (!title.isEmpty()) { + QString html = QLatin1String(""); + html += title; + html += QLatin1String(""); + + m_editor->insertHtml(html); + } + + m_ui->titleInput->clear(); + m_ui->urlInput->clear(); + + QDialog::accept(); +} + +class HtmlTextEdit : public QTextEdit +{ + Q_OBJECT + +public: + HtmlTextEdit(QWidget *parent = 0) + : QTextEdit(parent) + {} + + void contextMenuEvent(QContextMenuEvent *event); + +private slots: + void actionTriggered(QAction *action); +}; + +void HtmlTextEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + QMenu *htmlMenu = new QMenu(tr("Insert HTML entity"), menu); + + typedef struct { + const char *text; + const char *entity; + } Entry; + + const Entry entries[] = { + { "&& (&&)", "&" }, + { "& ", " " }, + { "&< (<)", "<" }, + { "&> (>)", ">" }, + { "&© (Copyright)", "©" }, + { "&® (Trade Mark)", "®" }, + }; + + for (int i = 0; i < 6; ++i) { + QAction *entityAction = new QAction(QLatin1String(entries[i].text), + htmlMenu); + entityAction->setData(QLatin1String(entries[i].entity)); + htmlMenu->addAction(entityAction); + } + + menu->addMenu(htmlMenu); + connect(htmlMenu, SIGNAL(triggered(QAction*)), + SLOT(actionTriggered(QAction*))); + menu->exec(event->globalPos()); + delete menu; +} + +void HtmlTextEdit::actionTriggered(QAction *action) +{ + insertPlainText(action->data().toString()); +} + +class ColorAction : public QAction +{ + Q_OBJECT + +public: + ColorAction(QObject *parent); + + const QColor& color() const { return m_color; } + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); + +private slots: + void chooseColor(); + +private: + QColor m_color; +}; + +ColorAction::ColorAction(QObject *parent): + QAction(parent) +{ + setText(tr("Text Color")); + setColor(Qt::black); + connect(this, SIGNAL(triggered()), this, SLOT(chooseColor())); +} + +void ColorAction::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + QPixmap pix(24, 24); + QPainter painter(&pix); + painter.setRenderHint(QPainter::Antialiasing, false); + painter.fillRect(pix.rect(), m_color); + painter.setPen(m_color.darker()); + painter.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + setIcon(pix); +} + +void ColorAction::chooseColor() +{ + const QColor col = QColorDialog::getColor(m_color, 0); + if (col.isValid() && col != m_color) { + setColor(col); + emit colorChanged(m_color); + } +} + +class RichTextEditorToolBar : public QToolBar +{ + Q_OBJECT +public: + RichTextEditorToolBar(QDesignerFormEditorInterface *core, + RichTextEditor *editor, + QWidget *parent = 0); + +public slots: + void updateActions(); + +private slots: + void alignmentActionTriggered(QAction *action); + void sizeInputActivated(const QString &size); + void colorChanged(const QColor &color); + void setVAlignSuper(bool super); + void setVAlignSub(bool sub); + void insertLink(); + void insertImage(); + +private: + QAction *m_bold_action; + QAction *m_italic_action; + QAction *m_underline_action; + QAction *m_valign_sup_action; + QAction *m_valign_sub_action; + QAction *m_align_left_action; + QAction *m_align_center_action; + QAction *m_align_right_action; + QAction *m_align_justify_action; + QAction *m_link_action; + QAction *m_image_action; + ColorAction *m_color_action; + QComboBox *m_font_size_input; + + QDesignerFormEditorInterface *m_core; + QPointer m_editor; +}; + +static QAction *createCheckableAction(const QIcon &icon, const QString &text, + QObject *receiver, const char *slot, + QObject *parent = 0) +{ + QAction *result = new QAction(parent); + result->setIcon(icon); + result->setText(text); + result->setCheckable(true); + result->setChecked(false); + if (slot) + QObject::connect(result, SIGNAL(triggered(bool)), receiver, slot); + return result; +} + +RichTextEditorToolBar::RichTextEditorToolBar(QDesignerFormEditorInterface *core, + RichTextEditor *editor, + QWidget *parent) : + QToolBar(parent), + m_link_action(new QAction(this)), + m_image_action(new QAction(this)), + m_color_action(new ColorAction(this)), + m_font_size_input(new QComboBox), + m_core(core), + m_editor(editor) +{ + // Font size combo box + m_font_size_input->setEditable(false); + const QList font_sizes = QFontDatabase::standardSizes(); + foreach (int font_size, font_sizes) + m_font_size_input->addItem(QString::number(font_size)); + + connect(m_font_size_input, SIGNAL(activated(QString)), + this, SLOT(sizeInputActivated(QString))); + addWidget(m_font_size_input); + + addSeparator(); + + // Bold, italic and underline buttons + + m_bold_action = createCheckableAction( + createIconSet(QLatin1String("textbold.png")), + tr("Bold"), editor, SLOT(setFontBold(bool)), this); + m_bold_action->setShortcut(tr("CTRL+B")); + addAction(m_bold_action); + + m_italic_action = createCheckableAction( + createIconSet(QLatin1String("textitalic.png")), + tr("Italic"), editor, SLOT(setFontItalic(bool)), this); + m_italic_action->setShortcut(tr("CTRL+I")); + addAction(m_italic_action); + + m_underline_action = createCheckableAction( + createIconSet(QLatin1String("textunder.png")), + tr("Underline"), editor, SLOT(setFontUnderline(bool)), this); + m_underline_action->setShortcut(tr("CTRL+U")); + addAction(m_underline_action); + + addSeparator(); + + // Left, center, right and justified alignment buttons + + QActionGroup *alignment_group = new QActionGroup(this); + connect(alignment_group, SIGNAL(triggered(QAction*)), + SLOT(alignmentActionTriggered(QAction*))); + + m_align_left_action = createCheckableAction( + createIconSet(QLatin1String("textleft.png")), + tr("Left Align"), editor, 0, alignment_group); + addAction(m_align_left_action); + + m_align_center_action = createCheckableAction( + createIconSet(QLatin1String("textcenter.png")), + tr("Center"), editor, 0, alignment_group); + addAction(m_align_center_action); + + m_align_right_action = createCheckableAction( + createIconSet(QLatin1String("textright.png")), + tr("Right Align"), editor, 0, alignment_group); + addAction(m_align_right_action); + + m_align_justify_action = createCheckableAction( + createIconSet(QLatin1String("textjustify.png")), + tr("Justify"), editor, 0, alignment_group); + addAction(m_align_justify_action); + + addSeparator(); + + // Superscript and subscript buttons + + m_valign_sup_action = createCheckableAction( + createIconSet(QLatin1String("textsuperscript.png")), + tr("Superscript"), + this, SLOT(setVAlignSuper(bool)), this); + addAction(m_valign_sup_action); + + m_valign_sub_action = createCheckableAction( + createIconSet(QLatin1String("textsubscript.png")), + tr("Subscript"), + this, SLOT(setVAlignSub(bool)), this); + addAction(m_valign_sub_action); + + addSeparator(); + + // Insert hyperlink and image buttons + + m_link_action->setIcon(createIconSet(QLatin1String("textanchor.png"))); + m_link_action->setText(tr("Insert &Link")); + connect(m_link_action, SIGNAL(triggered()), SLOT(insertLink())); + addAction(m_link_action); + + m_image_action->setIcon(createIconSet(QLatin1String("insertimage.png"))); + m_image_action->setText(tr("Insert &Image")); + connect(m_image_action, SIGNAL(triggered()), SLOT(insertImage())); + addAction(m_image_action); + + addSeparator(); + + // Text color button + connect(m_color_action, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChanged(QColor))); + addAction(m_color_action); + + connect(editor, SIGNAL(textChanged()), this, SLOT(updateActions())); + connect(editor, SIGNAL(stateChanged()), this, SLOT(updateActions())); + + updateActions(); +} + +void RichTextEditorToolBar::alignmentActionTriggered(QAction *action) +{ + Qt::Alignment new_alignment; + + if (action == m_align_left_action) { + new_alignment = Qt::AlignLeft; + } else if (action == m_align_center_action) { + new_alignment = Qt::AlignCenter; + } else if (action == m_align_right_action) { + new_alignment = Qt::AlignRight; + } else { + new_alignment = Qt::AlignJustify; + } + + m_editor->setAlignment(new_alignment); +} + +void RichTextEditorToolBar::colorChanged(const QColor &color) +{ + m_editor->setTextColor(color); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::sizeInputActivated(const QString &size) +{ + bool ok; + int i = size.toInt(&ok); + if (!ok) + return; + + m_editor->setFontPointSize(i); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::setVAlignSuper(bool super) +{ + const QTextCharFormat::VerticalAlignment align = super ? + QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal; + + QTextCharFormat charFormat = m_editor->currentCharFormat(); + charFormat.setVerticalAlignment(align); + m_editor->setCurrentCharFormat(charFormat); + + m_valign_sub_action->setChecked(false); +} + +void RichTextEditorToolBar::setVAlignSub(bool sub) +{ + const QTextCharFormat::VerticalAlignment align = sub ? + QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal; + + QTextCharFormat charFormat = m_editor->currentCharFormat(); + charFormat.setVerticalAlignment(align); + m_editor->setCurrentCharFormat(charFormat); + + m_valign_sup_action->setChecked(false); +} + +void RichTextEditorToolBar::insertLink() +{ + AddLinkDialog linkDialog(m_editor, this); + linkDialog.showDialog(); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::insertImage() +{ + const QString path = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), QString(), this); + if (!path.isEmpty()) + m_editor->insertHtml(QLatin1String("")); +} + +void RichTextEditorToolBar::updateActions() +{ + if (m_editor == 0) { + setEnabled(false); + return; + } + + const Qt::Alignment alignment = m_editor->alignment(); + const QTextCursor cursor = m_editor->textCursor(); + const QTextCharFormat charFormat = cursor.charFormat(); + const QFont font = charFormat.font(); + const QTextCharFormat::VerticalAlignment valign = + charFormat.verticalAlignment(); + const bool superScript = valign == QTextCharFormat::AlignSuperScript; + const bool subScript = valign == QTextCharFormat::AlignSubScript; + + if (alignment & Qt::AlignLeft) { + m_align_left_action->setChecked(true); + } else if (alignment & Qt::AlignRight) { + m_align_right_action->setChecked(true); + } else if (alignment & Qt::AlignHCenter) { + m_align_center_action->setChecked(true); + } else { + m_align_justify_action->setChecked(true); + } + + m_bold_action->setChecked(font.bold()); + m_italic_action->setChecked(font.italic()); + m_underline_action->setChecked(font.underline()); + m_valign_sup_action->setChecked(superScript); + m_valign_sub_action->setChecked(subScript); + + const int size = font.pointSize(); + const int idx = m_font_size_input->findText(QString::number(size)); + if (idx != -1) + m_font_size_input->setCurrentIndex(idx); + + m_color_action->setColor(m_editor->textColor()); +} + +RichTextEditor::RichTextEditor(QWidget *parent) + : QTextEdit(parent) +{ + connect(this, SIGNAL(currentCharFormatChanged(QTextCharFormat)), + this, SIGNAL(stateChanged())); + connect(this, SIGNAL(cursorPositionChanged()), + this, SIGNAL(stateChanged())); +} + +QToolBar *RichTextEditor::createToolBar(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new RichTextEditorToolBar(core, this, parent); +} + +void RichTextEditor::setFontBold(bool b) +{ + if (b) + setFontWeight(QFont::Bold); + else + setFontWeight(QFont::Normal); +} + +void RichTextEditor::setFontPointSize(double d) +{ + QTextEdit::setFontPointSize(qreal(d)); +} + +void RichTextEditor::setText(const QString &text) +{ + if (Qt::mightBeRichText(text)) + setHtml(text); + else + setPlainText(text); +} + +void RichTextEditor::setDefaultFont(const QFont &font) +{ + document()->setDefaultFont(font); + if (font.pointSize() > 0) + setFontPointSize(font.pointSize()); + else + setFontPointSize(QFontInfo(font).pointSize()); + emit textChanged(); +} + +QString RichTextEditor::text(Qt::TextFormat format) const +{ + switch (format) { + case Qt::LogText: + case Qt::PlainText: + return toPlainText(); + case Qt::RichText: + return toHtml(); + case Qt::AutoText: + break; + } + const QString html = toHtml(); + const QString plain = toPlainText(); + QTextEdit tester; + tester.setPlainText(plain); + return tester.toHtml() == html ? plain : html; +} + +RichTextEditorDialog::RichTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + m_editor(new RichTextEditor()), + m_text_edit(new HtmlTextEdit), + m_tab_widget(new QTabWidget), + m_state(Clean), + m_core(core) +{ + setWindowTitle(tr("Edit text")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_text_edit->setAcceptRichText(false); + new HtmlHighlighter(m_text_edit); + + connect(m_editor, SIGNAL(textChanged()), this, SLOT(richTextChanged())); + connect(m_text_edit, SIGNAL(textChanged()), this, SLOT(sourceChanged())); + + // The toolbar needs to be created after the RichTextEditor + QToolBar *tool_bar = m_editor->createToolBar(core); + tool_bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + + QWidget *rich_edit = new QWidget; + QVBoxLayout *rich_edit_layout = new QVBoxLayout(rich_edit); + rich_edit_layout->addWidget(tool_bar); + rich_edit_layout->addWidget(m_editor); + + QWidget *plain_edit = new QWidget; + QVBoxLayout *plain_edit_layout = new QVBoxLayout(plain_edit); + plain_edit_layout->addWidget(m_text_edit); + + m_tab_widget->setTabPosition(QTabWidget::South); + m_tab_widget->addTab(rich_edit, tr("Rich Text")); + m_tab_widget->addTab(plain_edit, tr("Source")); + connect(m_tab_widget, SIGNAL(currentChanged(int)), + SLOT(tabIndexChanged(int))); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + QPushButton *ok_button = buttonBox->button(QDialogButtonBox::Ok); + ok_button->setText(tr("&OK")); + ok_button->setDefault(true); + buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel")); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(m_tab_widget); + layout->addWidget(buttonBox); + + m_editor->setFocus(); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(RichTextDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +RichTextEditorDialog::~RichTextEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(RichTextDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +int RichTextEditorDialog::showDialog() +{ + m_tab_widget->setCurrentIndex(0); + m_editor->selectAll(); + m_editor->setFocus(); + + return exec(); +} + +void RichTextEditorDialog::setDefaultFont(const QFont &font) +{ + m_editor->setDefaultFont(font); +} + +void RichTextEditorDialog::setText(const QString &text) +{ + m_editor->setText(text); + m_text_edit->setPlainText(text); + m_state = Clean; +} + +QString RichTextEditorDialog::text(Qt::TextFormat format) const +{ + // In autotext mode, if the user has changed the source, use that + if (format == Qt::AutoText && (m_state == Clean || m_state == SourceChanged)) + return m_text_edit->toPlainText(); + // If the plain text HTML editor is selected, first copy its contents over + // to the rich text editor so that it is converted to Qt-HTML or actual + // plain text. + if (m_tab_widget->currentIndex() == SourceIndex && m_state == SourceChanged) + m_editor->setHtml(m_text_edit->toPlainText()); + return m_editor->text(format); +} + +void RichTextEditorDialog::tabIndexChanged(int newIndex) +{ + // Anything changed, is there a need for a conversion? + if (newIndex == SourceIndex && m_state != RichTextChanged) + return; + if (newIndex == RichTextIndex && m_state != SourceChanged) + return; + const State oldState = m_state; + // Remember the cursor position, since it is invalidated by setPlainText + QTextEdit *new_edit = (newIndex == SourceIndex) ? m_text_edit : m_editor; + const int position = new_edit->textCursor().position(); + + if (newIndex == SourceIndex) + m_text_edit->setPlainText(m_editor->text(Qt::RichText)); + else + m_editor->setHtml(m_text_edit->toPlainText()); + + QTextCursor cursor = new_edit->textCursor(); + cursor.movePosition(QTextCursor::End); + if (cursor.position() > position) { + cursor.setPosition(position); + } + new_edit->setTextCursor(cursor); + m_state = oldState; // Changed is triggered by setting the text +} + +void RichTextEditorDialog::richTextChanged() +{ + m_state = RichTextChanged; +} + +void RichTextEditorDialog::sourceChanged() +{ + m_state = SourceChanged; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include "richtexteditor.moc" diff --git a/designer/lib/shared/richtexteditor_p.h b/designer/lib/shared/richtexteditor_p.h new file mode 100644 index 0000000..59cd890 --- /dev/null +++ b/designer/lib/shared/richtexteditor_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef RICHTEXTEDITOR_H +#define RICHTEXTEDITOR_H + +#include +#include +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QTabWidget; +class QToolBar; + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class RichTextEditor; + +class QDESIGNER_SHARED_EXPORT RichTextEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit RichTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~RichTextEditorDialog(); + + int showDialog(); + void setDefaultFont(const QFont &font); + void setText(const QString &text); + QString text(Qt::TextFormat format = Qt::AutoText) const; + +private slots: + void tabIndexChanged(int newIndex); + void richTextChanged(); + void sourceChanged(); + +private: + enum TabIndex { RichTextIndex, SourceIndex }; + enum State { Clean, RichTextChanged, SourceChanged }; + RichTextEditor *m_editor; + QTextEdit *m_text_edit; + QTabWidget *m_tab_widget; + State m_state; + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // RITCHTEXTEDITOR_H diff --git a/designer/lib/shared/scriptcommand.cpp b/designer/lib/shared/scriptcommand.cpp new file mode 100644 index 0000000..e293af4 --- /dev/null +++ b/designer/lib/shared/scriptcommand.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptcommand_p.h" +#include "metadatabase_p.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +ScriptCommand::ScriptCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QCoreApplication::translate("Command", "Change script"), formWindow) +{ +} + +bool ScriptCommand::init(const ObjectList &list, const QString &script) +{ + MetaDataBase *metaDataBase = qobject_cast(formWindow()->core()->metaDataBase()); + if (!metaDataBase) + return false; + + // Save old values + m_oldValues.clear(); + foreach (QObject *obj, list) { + const MetaDataBaseItem* item = metaDataBase->metaDataBaseItem(obj); + if (!item) + return false; + m_oldValues.push_back(ObjectScriptPair(obj, item->script())); + } + m_script = script; + return true; +} + +void ScriptCommand::redo() +{ + MetaDataBase *metaDataBase = qobject_cast(formWindow()->core()->metaDataBase()); + Q_ASSERT(metaDataBase); + + ObjectScriptList::const_iterator cend = m_oldValues.constEnd(); + for (ObjectScriptList::const_iterator it = m_oldValues.constBegin();it != cend; ++it ) { + if (it->first) + metaDataBase->metaDataBaseItem(it->first)->setScript(m_script); + } +} + +void ScriptCommand::undo() +{ + MetaDataBase *metaDataBase = qobject_cast(formWindow()->core()->metaDataBase()); + Q_ASSERT(metaDataBase); + + ObjectScriptList::const_iterator cend = m_oldValues.constEnd(); + for (ObjectScriptList::const_iterator it = m_oldValues.constBegin();it != cend; ++it ) { + if (it->first) + metaDataBase->metaDataBaseItem(it->first)->setScript(it->second); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/scriptcommand_p.h b/designer/lib/shared/scriptcommand_p.h new file mode 100644 index 0000000..2696349 --- /dev/null +++ b/designer/lib/shared/scriptcommand_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_SCRIPTCOMMAND_H +#define QDESIGNER_SCRIPTCOMMAND_H + +#include "qdesigner_formwindowcommand_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT ScriptCommand: public QDesignerFormWindowCommand +{ + ScriptCommand(const ScriptCommand &); + ScriptCommand& operator=(const ScriptCommand &); + +public: + explicit ScriptCommand(QDesignerFormWindowInterface *formWindow); + + typedef QList ObjectList; + bool init(const ObjectList &list, const QString &script); + + virtual void redo(); + virtual void undo(); + +private: + typedef QPair, QString> ObjectScriptPair; + typedef QList ObjectScriptList; + ObjectScriptList m_oldValues; + QString m_script; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_SCRIPTCOMMAND_H diff --git a/designer/lib/shared/scriptdialog.cpp b/designer/lib/shared/scriptdialog.cpp new file mode 100644 index 0000000..f057f0e --- /dev/null +++ b/designer/lib/shared/scriptdialog.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptdialog_p.h" +#include "qscripthighlighter_p.h" + +#include + +#include +#include +#include +#include +#include +#ifdef QT_SCRIPT_LIB +#include +#endif + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // ScriptDialog + ScriptDialog::ScriptDialog(QDesignerDialogGuiInterface *m_dialogGui, QWidget *parent) : + QDialog(parent), + m_dialogGui(m_dialogGui), + m_textEdit(new QTextEdit) + { + setWindowTitle(tr("Edit script")); + setModal(true); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + const QString textHelp = tr("\ +Enter a Qt Script snippet to be executed while loading the form.
\ +The widget and its children are accessible via the \ +variables widget and childWidgets, respectively."); + m_textEdit->setToolTip(textHelp); + m_textEdit->setWhatsThis(textHelp); + m_textEdit->setMinimumSize(QSize(600, 400)); + vboxLayout->addWidget(m_textEdit); + new QScriptHighlighter(m_textEdit->document()); + // button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + connect(buttonBox , SIGNAL(accepted()), this, SLOT(slotAccept())); + vboxLayout->addWidget(buttonBox); + } + + bool ScriptDialog::editScript(QString &script) + { + m_textEdit->setText(script); + if (exec() != Accepted) + return false; + + script = trimmedScript(); + return true; + } + + void ScriptDialog::slotAccept() + { + if (checkScript()) + accept(); + } + + QString ScriptDialog::trimmedScript() const + { + // Ensure a single newline + QString rc = m_textEdit->toPlainText().trimmed(); + if (!rc.isEmpty()) + rc += QLatin1Char('\n'); + return rc; + } + + bool ScriptDialog::checkScript() + { + const QString script = trimmedScript(); + if (script.isEmpty()) + return true; +#ifdef QT_SCRIPT_LIB + QScriptEngine scriptEngine; + if (scriptEngine.canEvaluate(script)) + return true; + m_dialogGui->message(this, QDesignerDialogGuiInterface::ScriptDialogMessage, QMessageBox::Warning, + windowTitle(), tr("Syntax error"), QMessageBox::Ok); + return false; +#else + return true; +#endif + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/scriptdialog_p.h b/designer/lib/shared/scriptdialog_p.h new file mode 100644 index 0000000..98177d8 --- /dev/null +++ b/designer/lib/shared/scriptdialog_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SCRIPTDIALOG_H +#define SCRIPTDIALOG_H + +#include "shared_global_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerDialogGuiInterface; + +class QTextEdit; + +namespace qdesigner_internal { + + // Dialog for showing script errors + class QDESIGNER_SHARED_EXPORT ScriptDialog : public QDialog { + Q_OBJECT + + public: + explicit ScriptDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent); + bool editScript(QString &script); + + private slots: + void slotAccept(); + + private: + QString trimmedScript() const; + bool checkScript(); + + QDesignerDialogGuiInterface *m_dialogGui; + QTextEdit *m_textEdit; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SCRIPTDIALOG_H diff --git a/designer/lib/shared/scripterrordialog.cpp b/designer/lib/shared/scripterrordialog.cpp new file mode 100644 index 0000000..330ed74 --- /dev/null +++ b/designer/lib/shared/scripterrordialog.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scripterrordialog_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static void formatError(const QFormScriptRunner::Error &error, + QTextCursor &cursor) +{ + const QTextCharFormat oldFormat = cursor.charFormat(); + // Message + cursor.insertText(QCoreApplication::translate("ScriptErrorDialog", "An error occurred while running the scripts for \"%1\":\n").arg(error.objectName)); + + QTextCharFormat format(oldFormat); + + // verbatim listing + format.setFontFamily(QLatin1String("Courier")); + cursor.insertText(error.script, format); + + const QString newLine(QLatin1Char('\n')); + + cursor.insertText(newLine); + + // red error + format = oldFormat; + format.setTextOutline(QPen(Qt::red)); + cursor.insertText(error.errorMessage, format); + cursor.insertText(newLine); + cursor.setCharFormat (oldFormat); +} + +namespace qdesigner_internal { + + // ScriptErrorDialog + ScriptErrorDialog::ScriptErrorDialog(const Errors& errors, QWidget *parent) : + QDialog(parent), + m_textEdit(new QTextEdit) + { + setWindowTitle(tr("Script errors")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + m_textEdit->setReadOnly(true); + m_textEdit->setMinimumSize(QSize(600, 400)); + vboxLayout->addWidget(m_textEdit); + // button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + vboxLayout->addWidget(buttonBox); + + // Generate text + QTextCursor cursor = m_textEdit->textCursor(); + cursor.movePosition (QTextCursor::End); + foreach (const QFormScriptRunner::Error error, errors) + formatError(error, cursor); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/scripterrordialog_p.h b/designer/lib/shared/scripterrordialog_p.h new file mode 100644 index 0000000..8d97900 --- /dev/null +++ b/designer/lib/shared/scripterrordialog_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SCRIPTERRORDIALOG_H +#define SCRIPTERRORDIALOG_H + +#include "shared_global_p.h" +#include "formscriptrunner_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QTextEdit; + +namespace qdesigner_internal { + + // Dialog for showing script errors + class QDESIGNER_SHARED_EXPORT ScriptErrorDialog : public QDialog { + Q_OBJECT + + public: + typedef QFormScriptRunner::Errors Errors; + ScriptErrorDialog(const Errors& errors, QWidget *parent); + + private: + QTextEdit *m_textEdit; + + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SCRIPTERRORDIALOG_H diff --git a/designer/lib/shared/selectsignaldialog.ui b/designer/lib/shared/selectsignaldialog.ui new file mode 100644 index 0000000..99387dd --- /dev/null +++ b/designer/lib/shared/selectsignaldialog.ui @@ -0,0 +1,93 @@ + + + SelectSignalDialog + + + + 0 + 0 + 514 + 183 + + + + Go to slot + + + + + + Select signal + + + + + + false + + + false + + + + signal + + + + + class + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SelectSignalDialog + accept() + + + 257 + 335 + + + 157 + 274 + + + + + buttonBox + rejected() + SelectSignalDialog + reject() + + + 325 + 335 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/shared.pri b/designer/lib/shared/shared.pri new file mode 100644 index 0000000..570b208 --- /dev/null +++ b/designer/lib/shared/shared.pri @@ -0,0 +1,189 @@ + +INCLUDEPATH += $$PWD +contains(QT_CONFIG, script): QT += script + +include($$QT_SOURCE_TREE/tools/shared/qtpropertybrowser/qtpropertybrowser.pri) +include($$QT_SOURCE_TREE/tools/shared/deviceskin/deviceskin.pri) +include($$QT_SOURCE_TREE/src/tools/rcc/rcc.pri) +include($$QT_SOURCE_TREE/tools/shared/findwidget/findwidget.pri) +include($$QT_SOURCE_TREE/tools/shared/qtgradienteditor/qtgradienteditor.pri) + +# Input +FORMS += $$PWD/addlinkdialog.ui \ + $$PWD/orderdialog.ui \ + $$PWD/newactiondialog.ui \ + $$PWD/gridpanel.ui \ + $$PWD/signalslotdialog.ui \ + $$PWD/previewconfigurationwidget.ui \ + $$PWD/qtresourceeditordialog.ui \ + $$PWD/newformwidget.ui \ + $$PWD/selectsignaldialog.ui \ + $$PWD/formlayoutrowdialog.ui \ + $$PWD/plugindialog.ui + +HEADERS += \ + $$PWD/shared_global_p.h \ + $$PWD/spacer_widget_p.h \ + $$PWD/layoutinfo_p.h \ + $$PWD/layout_p.h \ + $$PWD/connectionedit_p.h \ + $$PWD/pluginmanager_p.h \ + $$PWD/metadatabase_p.h \ + $$PWD/qdesigner_formeditorcommand_p.h \ + $$PWD/qdesigner_formwindowcommand_p.h \ + $$PWD/qdesigner_command_p.h \ + $$PWD/morphmenu_p.h \ + $$PWD/qdesigner_command2_p.h \ + $$PWD/qdesigner_formbuilder_p.h \ + $$PWD/qdesigner_taskmenu_p.h \ + $$PWD/formlayoutmenu_p.h \ + $$PWD/qdesigner_widget_p.h \ + $$PWD/qdesigner_propertysheet_p.h \ + $$PWD/qdesigner_membersheet_p.h \ + $$PWD/qdesigner_propertyeditor_p.h \ + $$PWD/qdesigner_objectinspector_p.h \ + $$PWD/qdesigner_integration_p.h \ + $$PWD/invisible_widget_p.h \ + $$PWD/qlayout_widget_p.h \ + $$PWD/sheet_delegate_p.h \ + $$PWD/qdesigner_stackedbox_p.h \ + $$PWD/qdesigner_tabwidget_p.h \ + $$PWD/qdesigner_dockwidget_p.h \ + $$PWD/qdesigner_toolbox_p.h \ + $$PWD/qdesigner_dnditem_p.h \ + $$PWD/qsimpleresource_p.h \ + $$PWD/widgetfactory_p.h \ + $$PWD/widgetdatabase_p.h \ + $$PWD/qdesigner_promotion_p.h \ + $$PWD/qdesigner_introspection_p.h \ + $$PWD/promotionmodel_p.h \ + $$PWD/qdesigner_promotiondialog_p.h \ + $$PWD/iconloader_p.h \ + $$PWD/richtexteditor_p.h \ + $$PWD/plaintexteditor_p.h \ + $$PWD/actioneditor_p.h \ + $$PWD/actionrepository_p.h \ + $$PWD/qdesigner_toolbar_p.h \ + $$PWD/qdesigner_menubar_p.h \ + $$PWD/qdesigner_menu_p.h \ + $$PWD/actionprovider_p.h \ + $$PWD/orderdialog_p.h \ + $$PWD/newactiondialog_p.h \ + $$PWD/stylesheeteditor_p.h \ + $$PWD/csshighlighter_p.h \ + $$PWD/shared_enums_p.h \ + $$PWD/textpropertyeditor_p.h \ + $$PWD/propertylineedit_p.h \ + $$PWD/promotiontaskmenu_p.h \ + $$PWD/scripterrordialog_p.h \ + $$PWD/scriptcommand_p.h \ + $$PWD/scriptdialog_p.h \ + $$PWD/qscripthighlighter_p.h \ + $$PWD/gridpanel_p.h \ + $$PWD/grid_p.h \ + $$PWD/formwindowbase_p.h \ + $$PWD/qdesigner_utils_p.h \ + $$PWD/qdesigner_widgetbox_p.h \ + $$PWD/signalslotdialog_p.h \ + $$PWD/extensionfactory_p.h \ + $$PWD/dialoggui_p.h \ + $$PWD/deviceprofile_p.h \ + $$PWD/zoomwidget_p.h \ + $$PWD/previewmanager_p.h \ + $$PWD/previewconfigurationwidget_p.h \ + $$PWD/codedialog_p.h \ + $$PWD/qtresourceeditordialog_p.h \ + $$PWD/qtresourcemodel_p.h \ + $$PWD/qtresourceview_p.h \ + $$PWD/iconselector_p.h \ + $$PWD/htmlhighlighter_p.h \ + $$PWD/qdesigner_widgetitem_p.h \ + $$PWD/qdesigner_qsettings_p.h \ + $$PWD/qdesigner_formwindowmanager_p.h \ + $$PWD/shared_settings_p.h \ + $$PWD/newformwidget_p.h \ + $$PWD/filterwidget_p.h \ + $$PWD/plugindialog_p.h + +SOURCES += \ + $$PWD/spacer_widget.cpp \ + $$PWD/layoutinfo.cpp \ + $$PWD/layout.cpp \ + $$PWD/connectionedit.cpp \ + $$PWD/pluginmanager.cpp \ + $$PWD/qdesigner_formwindowcommand.cpp \ + $$PWD/qdesigner_formeditorcommand.cpp \ + $$PWD/qdesigner_command.cpp \ + $$PWD/morphmenu.cpp \ + $$PWD/qdesigner_command2.cpp \ + $$PWD/qdesigner_propertycommand.cpp \ + $$PWD/qdesigner_formbuilder.cpp \ + $$PWD/qdesigner_taskmenu.cpp \ + $$PWD/formlayoutmenu.cpp \ + $$PWD/qdesigner_widget.cpp \ + $$PWD/qdesigner_dockwidget.cpp \ + $$PWD/qdesigner_propertysheet.cpp \ + $$PWD/qdesigner_membersheet.cpp \ + $$PWD/qdesigner_propertyeditor.cpp \ + $$PWD/qdesigner_objectinspector.cpp \ + $$PWD/qdesigner_integration.cpp \ + $$PWD/qdesigner_dnditem.cpp \ + $$PWD/qsimpleresource.cpp \ + $$PWD/invisible_widget.cpp \ + $$PWD/qlayout_widget.cpp \ + $$PWD/sheet_delegate.cpp \ + $$PWD/metadatabase.cpp \ + $$PWD/qdesigner_stackedbox.cpp \ + $$PWD/qdesigner_tabwidget.cpp \ + $$PWD/qdesigner_toolbox.cpp \ + $$PWD/widgetfactory.cpp \ + $$PWD/widgetdatabase.cpp \ + $$PWD/qdesigner_promotion.cpp \ + $$PWD/qdesigner_introspection.cpp \ + $$PWD/promotionmodel.cpp \ + $$PWD/qdesigner_promotiondialog.cpp \ + $$PWD/richtexteditor.cpp \ + $$PWD/plaintexteditor.cpp \ + $$PWD/actioneditor.cpp \ + $$PWD/actionrepository.cpp \ + $$PWD/qdesigner_toolbar.cpp \ + $$PWD/qdesigner_menubar.cpp \ + $$PWD/qdesigner_menu.cpp \ + $$PWD/orderdialog.cpp \ + $$PWD/newactiondialog.cpp \ + $$PWD/stylesheeteditor.cpp \ + $$PWD/csshighlighter.cpp \ + $$PWD/textpropertyeditor.cpp \ + $$PWD/propertylineedit.cpp \ + $$PWD/promotiontaskmenu.cpp \ + $$PWD/scripterrordialog.cpp \ + $$PWD/scriptcommand.cpp \ + $$PWD/scriptdialog.cpp \ + $$PWD/qscripthighlighter.cpp\ + $$PWD/gridpanel.cpp \ + $$PWD/grid.cpp \ + $$PWD/formwindowbase.cpp \ + $$PWD/qdesigner_utils.cpp \ + $$PWD/qdesigner_widgetbox.cpp \ + $$PWD/iconloader.cpp \ + $$PWD/signalslotdialog.cpp \ + $$PWD/dialoggui.cpp \ + $$PWD/deviceprofile.cpp \ + $$PWD/zoomwidget.cpp \ + $$PWD/previewmanager.cpp \ + $$PWD/previewconfigurationwidget.cpp \ + $$PWD/codedialog.cpp \ + $$PWD/qtresourceeditordialog.cpp \ + $$PWD/qtresourcemodel.cpp \ + $$PWD/qtresourceview.cpp \ + $$PWD/iconselector.cpp \ + $$PWD/htmlhighlighter.cpp \ + $$PWD/qdesigner_widgetitem.cpp \ + $$PWD/qdesigner_qsettings.cpp \ + $$PWD/qdesigner_formwindowmanager.cpp \ + $$PWD/shared_settings.cpp \ + $$PWD/newformwidget.cpp \ + $$PWD/filterwidget.cpp \ + $$PWD/plugindialog.cpp + +RESOURCES += $$PWD/shared.qrc diff --git a/designer/lib/shared/shared.qrc b/designer/lib/shared/shared.qrc new file mode 100644 index 0000000..81be807 --- /dev/null +++ b/designer/lib/shared/shared.qrc @@ -0,0 +1,20 @@ + + + defaultgradients.xml + + templates/forms/Dialog_with_Buttons_Bottom.ui + templates/forms/240x320/Dialog_with_Buttons_Bottom.ui + templates/forms/320x240/Dialog_with_Buttons_Bottom.ui + templates/forms/480x640/Dialog_with_Buttons_Bottom.ui + templates/forms/640x480/Dialog_with_Buttons_Bottom.ui + templates/forms/Dialog_with_Buttons_Right.ui + templates/forms/240x320/Dialog_with_Buttons_Right.ui + templates/forms/320x240/Dialog_with_Buttons_Right.ui + templates/forms/480x640/Dialog_with_Buttons_Right.ui + templates/forms/640x480/Dialog_with_Buttons_Right.ui + templates/forms/Dialog_without_Buttons.ui + templates/forms/Widget.ui + templates/forms/Main_Window.ui + + + diff --git a/designer/lib/shared/shared_enums_p.h b/designer/lib/shared/shared_enums_p.h new file mode 100644 index 0000000..ec101a1 --- /dev/null +++ b/designer/lib/shared/shared_enums_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHAREDENUMS_H +#define SHAREDENUMS_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // Validation mode of text property line edits + enum TextPropertyValidationMode { + // Allow for multiline editing using literal "\n". + ValidationMultiLine, + // Allow for HTML rich text including multiline editing using literal "\n". + ValidationRichText, + // Validate a stylesheet + ValidationStyleSheet, + // Single line mode, suppresses newlines + ValidationSingleLine, + // Allow only for identifier characters + ValidationObjectName, + // Allow only for identifier characters and colons + ValidationObjectNameScope, + // URL + ValidationURL + }; + + // Container types + enum ContainerType { + // A container with pages, at least one of which one must always be present (for example, QTabWidget) + PageContainer, + // Mdi type container. All pages may be deleted, no concept of page order + MdiContainer, + // Wizard container + WizardContainer + }; + + enum AuxiliaryItemDataRoles { + // item->flags while being edited + ItemFlagsShadowRole = 0x13370551 + }; + +} + +QT_END_NAMESPACE + +#endif // SHAREDENUMS_H diff --git a/designer/lib/shared/shared_global_p.h b/designer/lib/shared/shared_global_p.h new file mode 100644 index 0000000..b0d6416 --- /dev/null +++ b/designer/lib/shared/shared_global_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_GLOBAL_H +#define SHARED_GLOBAL_H + +#include + +#ifdef QT_DESIGNER_STATIC +#define QDESIGNER_SHARED_EXTERN +#define QDESIGNER_SHARED_IMPORT +#else +#define QDESIGNER_SHARED_EXTERN Q_DECL_EXPORT +#define QDESIGNER_SHARED_IMPORT Q_DECL_IMPORT +#endif + +#ifndef QT_NO_SHARED_EXPORT +# ifdef QDESIGNER_SHARED_LIBRARY +# define QDESIGNER_SHARED_EXPORT QDESIGNER_SHARED_EXTERN +# else +# define QDESIGNER_SHARED_EXPORT QDESIGNER_SHARED_IMPORT +# endif +#else +# define QDESIGNER_SHARED_EXPORT +#endif + +#endif // SHARED_GLOBAL_H diff --git a/designer/lib/shared/shared_settings.cpp b/designer/lib/shared/shared_settings.cpp new file mode 100644 index 0000000..bffd267 --- /dev/null +++ b/designer/lib/shared/shared_settings.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shared_settings_p.h" +#include "grid_p.h" +#include "previewmanager_p.h" +#include "qdesigner_utils_p.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *designerPath = "/.designer"; +static const char *defaultGridKey = "defaultGrid"; +static const char *previewKey = "Preview"; +static const char *enabledKey = "Enabled"; +static const char *userDeviceSkinsKey= "UserDeviceSkins"; +static const char *zoomKey = "zoom"; +static const char *zoomEnabledKey = "zoomEnabled"; +static const char *deviceProfileIndexKey = "DeviceProfileIndex"; +static const char *deviceProfilesKey = "DeviceProfiles"; +static const char *formTemplatePathsKey = "FormTemplatePaths"; +static const char *formTemplateKey = "FormTemplate"; +static const char *newFormSizeKey = "NewFormSize"; + +using namespace qdesigner_internal; + +static bool checkTemplatePath(const QString &path, bool create) +{ + QDir current(QDir::current()); + if (current.exists(path)) + return true; + + if (!create) + return false; + + if (current.mkpath(path)) + return true; + + qdesigner_internal::designerWarning(QCoreApplication::translate("QDesignerSharedSettings", "The template path %1 could not be created.").arg(path)); + return false; +} + +namespace qdesigner_internal { + +QDesignerSharedSettings::QDesignerSharedSettings(QDesignerFormEditorInterface *core) + : m_settings(core->settingsManager()) +{ +} + +Grid QDesignerSharedSettings::defaultGrid() const +{ + Grid grid; + const QVariantMap defaultGridMap + = m_settings->value(QLatin1String(defaultGridKey), QVariantMap()).toMap(); + if (!defaultGridMap.empty()) + grid.fromVariantMap(defaultGridMap); + return grid; +} + +void QDesignerSharedSettings::setDefaultGrid(const Grid &grid) +{ + m_settings->setValue(QLatin1String(defaultGridKey), grid.toVariantMap()); +} + +const QStringList &QDesignerSharedSettings::defaultFormTemplatePaths() +{ + static QStringList rc; + if (rc.empty()) { + // Ensure default form template paths + const QString templatePath = QLatin1String("/templates"); + // home + QString path = QDir::homePath(); + path += QLatin1String(designerPath); + path += templatePath; + if (checkTemplatePath(path, true)) + rc += path; + + // designer/bin: Might be owned by root in some installations, do not force it. + path = qApp->applicationDirPath(); + path += templatePath; + if (checkTemplatePath(path, false)) + rc += path; + } + return rc; +} + +QStringList QDesignerSharedSettings::formTemplatePaths() const +{ + return m_settings->value(QLatin1String(formTemplatePathsKey), + defaultFormTemplatePaths()).toStringList(); +} + +void QDesignerSharedSettings::setFormTemplatePaths(const QStringList &paths) +{ + m_settings->setValue(QLatin1String(formTemplatePathsKey), paths); +} + +QString QDesignerSharedSettings::formTemplate() const +{ + return m_settings->value(QLatin1String(formTemplateKey)).toString(); +} + +void QDesignerSharedSettings::setFormTemplate(const QString &t) +{ + m_settings->setValue(QLatin1String(formTemplateKey), t); +} + +void QDesignerSharedSettings::setAdditionalFormTemplatePaths(const QStringList &additionalPaths) +{ + // merge template paths + QStringList templatePaths = defaultFormTemplatePaths(); + templatePaths += additionalPaths; + setFormTemplatePaths(templatePaths); +} + +QStringList QDesignerSharedSettings::additionalFormTemplatePaths() const +{ + // get template paths excluding internal ones + QStringList rc = formTemplatePaths(); + foreach (const QString &internalTemplatePath, defaultFormTemplatePaths()) { + const int index = rc.indexOf(internalTemplatePath); + if (index != -1) + rc.removeAt(index); + } + return rc; +} + +QSize QDesignerSharedSettings::newFormSize() const +{ + return m_settings->value(QLatin1String(newFormSizeKey), QSize(0, 0)).toSize(); +} + +void QDesignerSharedSettings::setNewFormSize(const QSize &s) +{ + if (s.isNull()) { + m_settings->remove(QLatin1String(newFormSizeKey)); + } else { + m_settings->setValue(QLatin1String(newFormSizeKey), s); + } +} + + +PreviewConfiguration QDesignerSharedSettings::customPreviewConfiguration() const +{ + PreviewConfiguration configuration; + configuration.fromSettings(QLatin1String(previewKey), m_settings); + return configuration; +} + +void QDesignerSharedSettings::setCustomPreviewConfiguration(const PreviewConfiguration &configuration) +{ + configuration.toSettings(QLatin1String(previewKey), m_settings); +} + +bool QDesignerSharedSettings::isCustomPreviewConfigurationEnabled() const +{ + m_settings->beginGroup(QLatin1String(previewKey)); + bool isEnabled = m_settings->value(QLatin1String(enabledKey), false).toBool(); + m_settings->endGroup(); + return isEnabled; +} + +void QDesignerSharedSettings::setCustomPreviewConfigurationEnabled(bool enabled) +{ + m_settings->beginGroup(QLatin1String(previewKey)); + m_settings->setValue(QLatin1String(enabledKey), enabled); + m_settings->endGroup(); +} + +QStringList QDesignerSharedSettings::userDeviceSkins() const +{ + m_settings->beginGroup(QLatin1String(previewKey)); + QStringList userDeviceSkins + = m_settings->value(QLatin1String(userDeviceSkinsKey), QStringList()).toStringList(); + m_settings->endGroup(); + return userDeviceSkins; +} + +void QDesignerSharedSettings::setUserDeviceSkins(const QStringList &userDeviceSkins) +{ + m_settings->beginGroup(QLatin1String(previewKey)); + m_settings->setValue(QLatin1String(userDeviceSkinsKey), userDeviceSkins); + m_settings->endGroup(); +} + +int QDesignerSharedSettings::zoom() const +{ + return m_settings->value(QLatin1String(zoomKey), 100).toInt(); +} + +void QDesignerSharedSettings::setZoom(int z) +{ + m_settings->setValue(QLatin1String(zoomKey), QVariant(z)); +} + +bool QDesignerSharedSettings::zoomEnabled() const +{ + return m_settings->value(QLatin1String(zoomEnabledKey), false).toBool(); +} + +void QDesignerSharedSettings::setZoomEnabled(bool v) +{ + m_settings->setValue(QLatin1String(zoomEnabledKey), v); +} + +DeviceProfile QDesignerSharedSettings::currentDeviceProfile() const +{ + return deviceProfileAt(currentDeviceProfileIndex()); +} + +void QDesignerSharedSettings::setCurrentDeviceProfileIndex(int i) +{ + m_settings->setValue(QLatin1String(deviceProfileIndexKey), i); +} + +int QDesignerSharedSettings::currentDeviceProfileIndex() const +{ + return m_settings->value(QLatin1String(deviceProfileIndexKey), -1).toInt(); +} + +static inline QString msgWarnDeviceProfileXml(const QString &msg) +{ + return QCoreApplication::translate("QDesignerSharedSettings", "An error has been encountered while parsing device profile XML: %1").arg(msg); +} + +DeviceProfile QDesignerSharedSettings::deviceProfileAt(int idx) const +{ + DeviceProfile rc; + if (idx < 0) + return rc; + const QStringList xmls = deviceProfileXml(); + if (idx >= xmls.size()) + return rc; + QString errorMessage; + if (!rc.fromXml(xmls.at(idx), &errorMessage)) { + rc.clear(); + designerWarning(msgWarnDeviceProfileXml(errorMessage)); + } + return rc; +} + +QStringList QDesignerSharedSettings::deviceProfileXml() const +{ + return m_settings->value(QLatin1String(deviceProfilesKey), QStringList()).toStringList(); +} + +QDesignerSharedSettings::DeviceProfileList QDesignerSharedSettings::deviceProfiles() const +{ + DeviceProfileList rc; + const QStringList xmls = deviceProfileXml(); + if (xmls.empty()) + return rc; + // De-serialize + QString errorMessage; + DeviceProfile dp; + const QStringList::const_iterator scend = xmls.constEnd(); + for (QStringList::const_iterator it = xmls.constBegin(); it != scend; ++it) { + if (dp.fromXml(*it, &errorMessage)) { + rc.push_back(dp); + } else { + designerWarning(msgWarnDeviceProfileXml(errorMessage)); + } + } + return rc; +} + +void QDesignerSharedSettings::setDeviceProfiles(const DeviceProfileList &dp) +{ + QStringList l; + const DeviceProfileList::const_iterator dcend = dp.constEnd(); + for (DeviceProfileList::const_iterator it = dp.constBegin(); it != dcend; ++it) + l.push_back(it->toXml()); + m_settings->setValue(QLatin1String(deviceProfilesKey), l); +} +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/shared_settings_p.h b/designer/lib/shared/shared_settings_p.h new file mode 100644 index 0000000..bea54ce --- /dev/null +++ b/designer/lib/shared/shared_settings_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_SETTINGS_H +#define SHARED_SETTINGS_H + +#include "shared_global_p.h" +#include "deviceprofile_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettingsInterface; + +class QStringList; +class QSize; + +namespace qdesigner_internal { +class Grid; +class PreviewConfiguration; +} + +/*! + Auxiliary methods to store/retrieve settings + */ +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerSharedSettings { +public: + typedef QList DeviceProfileList; + + explicit QDesignerSharedSettings(QDesignerFormEditorInterface *core); + + Grid defaultGrid() const; + void setDefaultGrid(const Grid &grid); + + QStringList formTemplatePaths() const; + void setFormTemplatePaths(const QStringList &paths); + + void setAdditionalFormTemplatePaths(const QStringList &additionalPaths); + QStringList additionalFormTemplatePaths() const; + + QString formTemplate() const; + void setFormTemplate(const QString &t); + + QSize newFormSize() const; + void setNewFormSize(const QSize &s); + + // Check with isCustomPreviewConfigurationEnabled if custom or default + // configuration should be used. + PreviewConfiguration customPreviewConfiguration() const; + void setCustomPreviewConfiguration(const PreviewConfiguration &configuration); + + bool isCustomPreviewConfigurationEnabled() const; + void setCustomPreviewConfigurationEnabled(bool enabled); + + QStringList userDeviceSkins() const; + void setUserDeviceSkins(const QStringList &userDeviceSkins); + + bool zoomEnabled() const; + void setZoomEnabled(bool v); + + // Zoom in percent + int zoom() const; + void setZoom(int z); + + // Embedded Design + DeviceProfile currentDeviceProfile() const; + void setCurrentDeviceProfileIndex(int i); + int currentDeviceProfileIndex() const; + + DeviceProfile deviceProfileAt(int idx) const; + DeviceProfileList deviceProfiles() const; + void setDeviceProfiles(const DeviceProfileList &dp); + + static const QStringList &defaultFormTemplatePaths(); + +protected: + QDesignerSettingsInterface *settings() const { return m_settings; } + +private: + QStringList deviceProfileXml() const; + QDesignerSettingsInterface *m_settings; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHARED_SETTINGS_H diff --git a/designer/lib/shared/sheet_delegate.cpp b/designer/lib/shared/sheet_delegate.cpp new file mode 100644 index 0000000..8872493 --- /dev/null +++ b/designer/lib/shared/sheet_delegate.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sheet_delegate_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +SheetDelegate::SheetDelegate(QTreeView *view, QWidget *parent) + : QItemDelegate(parent), + m_view(view) +{ +} + +void SheetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + const QAbstractItemModel *model = index.model(); + Q_ASSERT(model); + + if (!model->parent(index).isValid()) { + // this is a top-level item. + QStyleOptionButton buttonOption; + + buttonOption.state = option.state; +#ifdef Q_WS_MAC + buttonOption.state |= QStyle::State_Raised; +#endif + buttonOption.state &= ~QStyle::State_HasFocus; + + buttonOption.rect = option.rect; + buttonOption.palette = option.palette; + buttonOption.features = QStyleOptionButton::None; + m_view->style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, m_view); + + QStyleOption branchOption; + static const int i = 9; // ### hardcoded in qcommonstyle.cpp + QRect r = option.rect; + branchOption.rect = QRect(r.left() + i/2, r.top() + (r.height() - i)/2, i, i); + branchOption.palette = option.palette; + branchOption.state = QStyle::State_Children; + + if (m_view->isExpanded(index)) + branchOption.state |= QStyle::State_Open; + + m_view->style()->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, painter, m_view); + + // draw text + QRect textrect = QRect(r.left() + i*2, r.top(), r.width() - ((5*i)/2), r.height()); + QString text = elidedText(option.fontMetrics, textrect.width(), Qt::ElideMiddle, + model->data(index, Qt::DisplayRole).toString()); + m_view->style()->drawItemText(painter, textrect, Qt::AlignCenter, + option.palette, m_view->isEnabled(), text); + + } else { + QItemDelegate::paint(painter, option, index); + } +} + +QSize SheetDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + QStyleOptionViewItem option = opt; + QSize sz = QItemDelegate::sizeHint(opt, index) + QSize(2, 2); + return sz; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/sheet_delegate_p.h b/designer/lib/shared/sheet_delegate_p.h new file mode 100644 index 0000000..6fdf401 --- /dev/null +++ b/designer/lib/shared/sheet_delegate_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef SHEET_DELEGATE_H +#define SHEET_DELEGATE_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QTreeView; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT SheetDelegate: public QItemDelegate +{ + Q_OBJECT +public: + SheetDelegate(QTreeView *view, QWidget *parent); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const; + +private: + QTreeView *m_view; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHEET_DELEGATE_H diff --git a/designer/lib/shared/signalslotdialog.cpp b/designer/lib/shared/signalslotdialog.cpp new file mode 100644 index 0000000..d570970 --- /dev/null +++ b/designer/lib/shared/signalslotdialog.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalslotdialog_p.h" +#include "ui_signalslotdialog.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +#include "qdesigner_formwindowcommand_p.h" +#include "iconloader_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +// Regexp to match a function signature, arguments potentially +// with namespace colons. +static const char *signatureRegExp = "^[\\w+_]+\\(([\\w+:]\\*?,?)*\\)$"; +static const char *methodNameRegExp = "^[\\w+_]+$"; + +static QStandardItem *createEditableItem(const QString &text) +{ + QStandardItem *rc = new QStandardItem(text); + rc->setFlags(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable); + return rc; +} + +static QStandardItem *createDisabledItem(const QString &text) +{ + QStandardItem *rc = new QStandardItem(text); + Qt::ItemFlags flags = rc->flags(); + rc->setFlags(flags & ~(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable)); + return rc; +} + +static void fakeMethodsFromMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, QStringList &slotList, QStringList &signalList) +{ + slotList.clear(); + signalList.clear(); + if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast(core->metaDataBase())) + if (const qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o)) { + slotList = item->fakeSlots(); + signalList = item->fakeSignals(); + } +} + +static void fakeMethodsToMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, const QStringList &slotList, const QStringList &signalList) +{ + if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast(core->metaDataBase())) { + qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o); + Q_ASSERT(item); + item->setFakeSlots(slotList); + item->setFakeSignals(signalList); + } +} + +static void existingMethodsFromMemberSheet(QDesignerFormEditorInterface *core, + QObject *o, + QStringList &slotList, QStringList &signalList) +{ + slotList.clear(); + signalList.clear(); + + QDesignerMemberSheetExtension *msheet = qt_extension(core->extensionManager(), o); + if (!msheet) + return; + + for (int i = 0, count = msheet->count(); i < count; ++i) + if (msheet->isVisible(i)) { + if (msheet->isSlot(i)) + slotList += msheet->signature(i); + else + if (msheet->isSignal(i)) + signalList += msheet->signature(i); + } +} + +namespace { + // Internal helper class: A Delegate that validates using RegExps and additionally checks + // on closing (adds missing parentheses). + class SignatureDelegate : public QItemDelegate { + public: + SignatureDelegate(QObject * parent = 0); + virtual QWidget * createEditor (QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; + virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + + private: + const QRegExp m_signatureRegexp; + const QRegExp m_methodNameRegexp; + }; + + SignatureDelegate::SignatureDelegate(QObject * parent) : + QItemDelegate(parent), + m_signatureRegexp(QLatin1String(signatureRegExp)), + m_methodNameRegexp(QLatin1String(methodNameRegExp)) + { + Q_ASSERT(m_signatureRegexp.isValid()); + Q_ASSERT(m_methodNameRegexp.isValid()); + } + + QWidget * SignatureDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const + { + QWidget *rc = QItemDelegate::createEditor(parent, option, index); + QLineEdit *le = qobject_cast(rc); + Q_ASSERT(le); + le->setValidator(new QRegExpValidator(m_signatureRegexp, le)); + return rc; + } + + void SignatureDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const + { + QLineEdit *le = qobject_cast(editor); + Q_ASSERT(le); + // Did the user just type a name? .. Add parentheses + QString signature = le->text(); + if (!m_signatureRegexp.exactMatch(signature )) { + if (m_methodNameRegexp.exactMatch(signature )) { + signature += QLatin1String("()"); + le->setText(signature); + } else { + return; + } + } + QItemDelegate::setModelData(editor, model, index); + } + + // ------ FakeMethodMetaDBCommand: Undo Command to change fake methods in the meta DB. + class FakeMethodMetaDBCommand : public qdesigner_internal::QDesignerFormWindowCommand { + + public: + explicit FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow); + + void init(QObject *o, + const QStringList &oldFakeSlots, const QStringList &oldFakeSignals, + const QStringList &newFakeSlots, const QStringList &newFakeSignals); + + virtual void undo() { fakeMethodsToMetaDataBase(core(), m_object, m_oldFakeSlots, m_oldFakeSignals); } + virtual void redo() { fakeMethodsToMetaDataBase(core(), m_object, m_newFakeSlots, m_newFakeSignals); } + + private: + QObject *m_object; + QStringList m_oldFakeSlots; + QStringList m_oldFakeSignals; + QStringList m_newFakeSlots; + QStringList m_newFakeSignals; + }; + + FakeMethodMetaDBCommand::FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow) : + qdesigner_internal::QDesignerFormWindowCommand(QApplication::translate("Command", "Change signals/slots"), formWindow), + m_object(0) + { + } + + void FakeMethodMetaDBCommand::init(QObject *o, + const QStringList &oldFakeSlots, const QStringList &oldFakeSignals, + const QStringList &newFakeSlots, const QStringList &newFakeSignals) + { + m_object = o; + m_oldFakeSlots = oldFakeSlots; + m_oldFakeSignals = oldFakeSignals; + m_newFakeSlots = newFakeSlots; + m_newFakeSignals = newFakeSignals; + } +} + +namespace qdesigner_internal { + +// ------ SignalSlotDialogData +void SignalSlotDialogData::clear() +{ + m_existingMethods.clear(); + m_fakeMethods.clear(); +} + +// ------ SignatureModel +SignatureModel::SignatureModel(QObject *parent) : + QStandardItemModel(parent) +{ +} + +bool SignatureModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::EditRole) + return QStandardItemModel::setData(index, value, role); + // check via signal (unless it is the same), in which case we can't be bothered. + const QStandardItem *item = itemFromIndex(index); + Q_ASSERT(item); + const QString signature = value.toString(); + if (item->text() == signature) + return true; + + bool ok = true; + emit checkSignature(signature, &ok); + if (!ok) + return false; + + return QStandardItemModel::setData(index, value, role); +} + +// ------ SignaturePanel +SignaturePanel::SignaturePanel(QObject *parent, QListView *listView, QToolButton *addButton, QToolButton *removeButton, const QString &newPrefix) : + QObject(parent), + m_newPrefix(newPrefix), + m_model(new SignatureModel(this)), + m_listView(listView), + m_removeButton(removeButton) +{ + m_removeButton->setEnabled(false); + + connect(addButton, SIGNAL(clicked()), this, SLOT(slotAdd())); + connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemove())); + + m_listView->setModel(m_model); + SignatureDelegate *delegate = new SignatureDelegate(this); + m_listView->setItemDelegate(delegate); + connect(m_model, SIGNAL(checkSignature(QString,bool*)), this, SIGNAL(checkSignature(QString,bool*))); + + connect(m_listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); +} + +void SignaturePanel::slotAdd() +{ + m_listView->selectionModel()->clearSelection(); + // find unique name + for (int i = 1; ; i++) { + QString newSlot = m_newPrefix; + newSlot += QString::number(i); // Always add number, Avoid setting 'slot' for first entry + newSlot += QLatin1Char('('); + // check for function name independent of parameters + if (m_model->findItems(newSlot, Qt::MatchStartsWith, 0).empty()) { + newSlot += QLatin1Char(')'); + QStandardItem * item = createEditableItem(newSlot); + m_model->appendRow(item); + const QModelIndex index = m_model->indexFromItem (item); + m_listView->setCurrentIndex (index); + m_listView->edit(index); + return; + } + } +} + +int SignaturePanel::count(const QString &signature) const +{ + return m_model->findItems(signature).size(); +} + +void SignaturePanel::slotRemove() +{ + const QModelIndexList selectedIndexes = m_listView->selectionModel()->selectedIndexes (); + if (selectedIndexes.empty()) + return; + + closeEditor(); + // scroll to previous + if (const int row = selectedIndexes.front().row()) + m_listView->setCurrentIndex (selectedIndexes.front().sibling(row - 1, 0)); + + for (int i = selectedIndexes.size() - 1; i >= 0; i--) + qDeleteAll(m_model->takeRow(selectedIndexes[i].row())); +} + +void SignaturePanel::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) +{ + m_removeButton->setEnabled(!selected.indexes().empty()); +} + +void SignaturePanel::setData(const SignalSlotDialogData &d) +{ + m_model->clear(); + + QStandardItem *lastExisting = 0; + foreach(const QString &s, d.m_existingMethods) { + lastExisting = createDisabledItem(s); + m_model->appendRow(lastExisting); + } + foreach(const QString &s, d.m_fakeMethods) + m_model->appendRow(createEditableItem(s)); + if (lastExisting) + m_listView->scrollTo(m_model->indexFromItem(lastExisting)); +} + +QStringList SignaturePanel::fakeMethods() const +{ + QStringList rc; + if (const int rowCount = m_model->rowCount()) + for (int i = 0; i < rowCount; i++) { + const QStandardItem *item = m_model->item(i); + if (item->flags() & Qt::ItemIsEditable) + rc += item->text(); + } + return rc; +} + +void SignaturePanel::closeEditor() +{ + const QModelIndex idx = m_listView->currentIndex(); + if (idx.isValid()) + m_listView->closePersistentEditor(idx); +} + +// ------ SignalSlotDialog + +SignalSlotDialog::SignalSlotDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent, FocusMode mode) : + QDialog(parent), + m_focusMode(mode), + m_ui(new Ui::SignalSlotDialogClass), + m_dialogGui(dialogGui) +{ + setModal(true); + m_ui->setupUi(this); + + const QIcon plusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("plus.png")); + const QIcon minusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("minus.png")); + m_ui->addSlotButton->setIcon(plusIcon); + m_ui->removeSlotButton->setIcon(minusIcon); + m_ui->addSignalButton->setIcon(plusIcon); + m_ui->removeSignalButton->setIcon(minusIcon); + + m_slotPanel = new SignaturePanel(this, m_ui->slotListView, m_ui->addSlotButton, m_ui->removeSlotButton, QLatin1String("slot")); + m_signalPanel = new SignaturePanel(this, m_ui->signalListView, m_ui->addSignalButton, m_ui->removeSignalButton, QLatin1String("signal")); + connect(m_slotPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*))); + connect(m_signalPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*))); + + connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + switch(m_focusMode) { + case FocusSlots: + m_ui->slotListView->setFocus(Qt::OtherFocusReason); + break; + case FocusSignals: + m_ui->signalListView->setFocus(Qt::OtherFocusReason); + break; + } +} + +SignalSlotDialog::~SignalSlotDialog() +{ + delete m_ui; +} + +void SignalSlotDialog::slotCheckSignature(const QString &signature, bool *ok) +{ + QString errorMessage; + do { + if (m_slotPanel->count(signature)) { + errorMessage = tr("There is already a slot with the signature '%1'.").arg(signature); + *ok = false; + break; + } + if (m_signalPanel->count(signature)) { + errorMessage = tr("There is already a signal with the signature '%1'.").arg(signature); + *ok = false; + break; + } + } while (false); + if (!*ok) + m_dialogGui->message(this, QDesignerDialogGuiInterface::SignalSlotDialogMessage, + QMessageBox::Warning, tr("%1 - Duplicate Signature").arg(windowTitle()), errorMessage, QMessageBox::Close); +} + +QDialog::DialogCode SignalSlotDialog::showDialog(SignalSlotDialogData &slotData, SignalSlotDialogData &signalData) +{ + m_slotPanel->setData(slotData); + m_signalPanel->setData(signalData); + + const DialogCode rc = static_cast(exec()); + if (rc == Rejected) + return rc; + + slotData.m_fakeMethods = m_slotPanel->fakeMethods(); + signalData.m_fakeMethods = m_signalPanel->fakeMethods(); + return rc; +} + +bool SignalSlotDialog::editMetaDataBase(QDesignerFormWindowInterface *fw, QObject *object, QWidget *parent, FocusMode mode) +{ + QDesignerFormEditorInterface *core = fw->core(); + SignalSlotDialog dlg(core->dialogGui(), parent, mode); + dlg.setWindowTitle(tr("Signals/Slots of %1").arg(object->objectName())); + + SignalSlotDialogData slotData; + SignalSlotDialogData signalData; + + existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods); + fakeMethodsFromMetaDataBase(core, object, slotData.m_fakeMethods, signalData.m_fakeMethods); + + const QStringList oldSlots = slotData.m_fakeMethods; + const QStringList oldSignals = signalData.m_fakeMethods; + + if (dlg.showDialog(slotData, signalData) == QDialog::Rejected) + return false; + + if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods) + return false; + + FakeMethodMetaDBCommand *cmd = new FakeMethodMetaDBCommand(fw); + cmd->init(object, oldSlots, oldSignals, slotData.m_fakeMethods, signalData.m_fakeMethods); + fw->commandHistory()->push(cmd); + return true; +} + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QWidget *parent, FocusMode mode) +{ + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index == -1) + return false; + + const QString baseClassName = core->widgetDataBase()->item(index)->extends(); + if (baseClassName.isEmpty()) + return false; + + QWidget *widget = core->widgetFactory()->createWidget(baseClassName, 0); + if (!widget) + return false; + const bool rc = editPromotedClass(core, promotedClassName, widget, parent, mode); + widget->deleteLater(); + return rc; +} + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, QObject *baseObject, QWidget *parent, FocusMode mode) +{ + if (!baseObject->isWidgetType()) + return false; + + const QString promotedClassName = promotedCustomClassName(core, qobject_cast(baseObject)); + if (promotedClassName.isEmpty()) + return false; + return editPromotedClass(core, promotedClassName, baseObject, parent, mode); +} + + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QObject *object, QWidget *parent, FocusMode mode) +{ + WidgetDataBase *db = qobject_cast(core->widgetDataBase()); + if (!db) + return false; + + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index == -1) + return false; + + WidgetDataBaseItem* item = static_cast(db->item(index)); + + SignalSlotDialogData slotData; + SignalSlotDialogData signalData; + + existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods); + slotData.m_fakeMethods = item->fakeSlots(); + signalData.m_fakeMethods = item->fakeSignals(); + + const QStringList oldSlots = slotData.m_fakeMethods; + const QStringList oldSignals = signalData.m_fakeMethods; + + SignalSlotDialog dlg(core->dialogGui(), parent, mode); + dlg.setWindowTitle(tr("Signals/Slots of %1").arg(promotedClassName)); + + if (dlg.showDialog(slotData, signalData) == QDialog::Rejected) + return false; + + if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods) + return false; + + item->setFakeSlots(slotData.m_fakeMethods); + item->setFakeSignals(signalData.m_fakeMethods); + + return true; +} + +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/signalslotdialog.ui b/designer/lib/shared/signalslotdialog.ui new file mode 100644 index 0000000..1a8a8d9 --- /dev/null +++ b/designer/lib/shared/signalslotdialog.ui @@ -0,0 +1,129 @@ + + SignalSlotDialogClass + + + + 0 + 0 + 617 + 535 + + + + Signals and slots + + + + + + Slots + + + + + + + + + + + Add + + + ... + + + + + + + Delete + + + ... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Signals + + + + + + + + + + + Add + + + ... + + + + + + + Delete + + + ... + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + diff --git a/designer/lib/shared/signalslotdialog_p.h b/designer/lib/shared/signalslotdialog_p.h new file mode 100644 index 0000000..d9e5348 --- /dev/null +++ b/designer/lib/shared/signalslotdialog_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef _SIGNALSLOTDIALOG_H +#define _SIGNALSLOTDIALOG_H + +#include "shared_global_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerDialogGuiInterface; +class QDesignerMemberSheet; +class QListView; +class QToolButton; +class QItemSelection; + +namespace Ui { + class SignalSlotDialogClass; +} + +namespace qdesigner_internal { + +// Dialog data +struct SignalSlotDialogData { + void clear(); + QStringList m_existingMethods; + QStringList m_fakeMethods; +}; + +// Internal helper class: A model for signatures that allows for verifying duplicates +// (checking signals versus slots and vice versa). +class SignatureModel : public QStandardItemModel { + Q_OBJECT + +public: + SignatureModel(QObject *parent = 0); + virtual bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + +signals: + void checkSignature(const QString &signature, bool *ok); +}; + +// Internal helper class: Panel for editing method signatures. List view with validator, +// add and remove button +class SignaturePanel : public QObject { + Q_OBJECT + +public: + SignaturePanel(QObject *parent, QListView *listView, QToolButton *addButton, QToolButton *removeButton, const QString &newPrefix); + + QStringList fakeMethods() const; + void setData(const SignalSlotDialogData &d); + int count(const QString &signature) const; + +signals: + void checkSignature(const QString &signature, bool *ok); + +private slots: + void slotAdd(); + void slotRemove(); + void slotSelectionChanged(const QItemSelection &, const QItemSelection &); + +private: + void closeEditor(); + + const QString m_newPrefix; + SignatureModel *m_model; + QListView *m_listView; + QToolButton *m_removeButton; +}; + +// Dialog for editing signals and slots. +// Provides static convenience function +// to modify fake signals and slots. They are +// handled in 2 ways: +// 1) For the MainContainer: Fake signals and slots are stored +// in the meta database (per-instance) +// 2) For promoted widgets: Fake signals and slots are stored +// in the widget database (per-class) +// Arguably, we could require the MainContainer to be promoted for that, too, +// but that would require entering a header. + +class QDESIGNER_SHARED_EXPORT SignalSlotDialog : public QDialog { + Q_OBJECT + +public: + enum FocusMode { FocusSlots, FocusSignals }; + + explicit SignalSlotDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent = 0, FocusMode m = FocusSlots); + virtual ~SignalSlotDialog(); + + DialogCode showDialog(SignalSlotDialogData &slotData, SignalSlotDialogData &signalData); + + // Edit fake methods stored in MetaDataBase (per instance, used for main containers) + static bool editMetaDataBase(QDesignerFormWindowInterface *fw, QObject *object, QWidget *parent = 0, FocusMode m = FocusSlots); + + // Edit fake methods of a promoted class stored in WidgetDataBase (synthesizes a widget to obtain existing members). + static bool editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QWidget *parent = 0, FocusMode m = FocusSlots); + // Edit fake methods of a promoted class stored in WidgetDataBase on a base class instance. + static bool editPromotedClass(QDesignerFormEditorInterface *core, QObject *baseObject, QWidget *parent = 0, FocusMode m = FocusSlots); + +private slots: + void slotCheckSignature(const QString &signature, bool *ok); + +private: + // Edit fake methods of a promoted class stored in WidgetDataBase using an instance of the base class. + static bool editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QObject *baseObject, QWidget *parent, FocusMode m); + + const FocusMode m_focusMode; + Ui::SignalSlotDialogClass *m_ui; + QDesignerDialogGuiInterface *m_dialogGui; + SignaturePanel *m_slotPanel; + SignaturePanel *m_signalPanel; +}; +} + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/shared/spacer_widget.cpp b/designer/lib/shared/spacer_widget.cpp new file mode 100644 index 0000000..cef19cf --- /dev/null +++ b/designer/lib/shared/spacer_widget.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "spacer_widget_p.h" +#include "layoutinfo_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// The Spacer widget is Designer representation of QLayoutItem. +// It uses QLayoutItem's sizeHint property as QWidget +// sizeHint and the QLayoutItem's sizeType property as QWidget size policy. +// If it is not within a layout, it adds a margin (m_SizeOffset) around it +// to avoid being shrunk to an invisible state when the sizeHint is reset to 0,0 +// and enables sizeHandle-resizing. In a layout, however, this m_SizeOffset +// should not be applied for pixel-exact design. + +Spacer::Spacer(QWidget *parent) : + QWidget(parent), + m_SizeOffset(3, 3), // A small offset to ensure the spacer is still visible when reset to size 0,0 + m_orientation(Qt::Vertical), + m_interactive(true), + m_layoutState(UnknownLayoutState), + m_sizeHint(0, 0) +{ + setAttribute(Qt::WA_MouseNoMask); + m_formWindow = QDesignerFormWindowInterface::findFormWindow(this); + setSizeType(QSizePolicy::Expanding); +} + +bool Spacer::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::ToolTip: + updateToolTip(); // Tooltip includes size, so, refresh on demand + break; + case QEvent::ParentChange: // Cache information about 'being in layout' which is expensive to calculate. + m_layoutState = UnknownLayoutState; + break; + default: + break; + } + return QWidget::event(e); +} + +bool Spacer::isInLayout() const +{ + if (m_layoutState == UnknownLayoutState) { + m_layoutState = OutsideLayout; + if (m_formWindow) + if (const QWidget *parent = parentWidget()) + if (qdesigner_internal::LayoutInfo::managedLayoutType(m_formWindow->core(), parent) != qdesigner_internal::LayoutInfo::NoLayout) + m_layoutState = InLayout; + } + return m_layoutState == InLayout; +} + +void Spacer::paintEvent(QPaintEvent *) +{ + // Only draw spacers when we're editting widgets + if (m_formWindow != 0 && m_formWindow->currentTool() != 0) + return; + + QPainter p(this); + p.setPen(Qt::blue); + const int w = width(); + const int h = height(); + if (w * h == 0) + return; + + if (w <= m_SizeOffset.width() || h <= m_SizeOffset.height()) { + const int lw = w - 1; + const int lh = h - 1; + switch (m_orientation) { + case Qt::Horizontal: + p.drawLine(0, 0, 0, lh); + p.drawLine(lw, 0, lw, lh); + break; + case Qt::Vertical: + p.drawLine(0, 0, lw, 0); + p.drawLine(0, lh, lw, lh); + break; + } + return; + } + if (m_orientation == Qt::Horizontal) { + const int dist = 3; + const int amplitude = qMin(3, h / 3); + const int base = h / 2; + int i = 0; + p.setPen(Qt::white); + for (i = 0; i < w / 3 +2; ++i) + p.drawLine(i * dist, base - amplitude, i * dist + dist / 2, base + amplitude); + p.setPen(Qt::blue); + for (i = 0; i < w / 3 +2; ++i) + p.drawLine(i * dist + dist / 2, base + amplitude, i * dist + dist, base - amplitude); + const int y = h/2; + p.drawLine(0, y-10, 0, y+10); + p.drawLine(w - 1, y-10, w - 1, y+10); + } else { + const int dist = 3; + const int amplitude = qMin(3, w / 3); + const int base = w / 2; + int i = 0; + p.setPen(Qt::white); + for (i = 0; i < h / 3 +2; ++i) + p.drawLine(base - amplitude, i * dist, base + amplitude,i * dist + dist / 2); + p.setPen(Qt::blue); + for (i = 0; i < h / 3 +2; ++i) + p.drawLine(base + amplitude, i * dist + dist / 2, base - amplitude, i * dist + dist); + const int x = w/2; + p.drawLine(x-10, 0, x+10, 0); + p.drawLine(x-10, h - 1, x+10, h - 1); + } +} + +void Spacer::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); + // When resized by widget handle dragging after a reset (QSize(0, 0)): + // Mark the property as changed (geometry and sizeHint are in sync except for 'changed'). + if (m_formWindow) { + const QSize oldSize = e->oldSize(); + if (oldSize.isNull() || oldSize.width() <= m_SizeOffset.width() || oldSize.height() <= m_SizeOffset.height()) + if (QDesignerPropertySheetExtension *sheet = qt_extension(m_formWindow->core()->extensionManager(), this)) + sheet->setChanged(sheet->indexOf(QLatin1String("sizeHint")), true); + } + + updateMask(); + + if (!m_interactive) + return; + + if (!isInLayout()) { // Allow size-handle resize only if not in layout + const QSize currentSize = size(); + if (currentSize.width() >= m_SizeOffset.width() && currentSize.height() >= m_SizeOffset.height()) + m_sizeHint = currentSize - m_SizeOffset; + } +} + +void Spacer::updateMask() +{ + QRegion r(rect()); + const int w = width(); + const int h = height(); + if (w > 1 && h > 1) { + if (m_orientation == Qt::Horizontal) { + const int amplitude = qMin(3, h / 3); + const int base = h / 2; + r = r.subtract(QRect(1, 0, w - 2, base - amplitude)); + r = r.subtract(QRect(1, base + amplitude, w - 2, h - base - amplitude)); + } else { + const int amplitude = qMin(3, w / 3); + const int base = w / 2; + r = r.subtract(QRect(0, 1, base - amplitude, h - 2)); + r = r.subtract(QRect(base + amplitude, 1, w - base - amplitude, h - 2)); + } + } + setMask(r); +} + +void Spacer::setSizeType(QSizePolicy::Policy t) +{ + const QSizePolicy sizeP = m_orientation == Qt::Vertical ? QSizePolicy(QSizePolicy::Minimum, t) : QSizePolicy(t, QSizePolicy::Minimum); + setSizePolicy(sizeP); +} + + +QSizePolicy::Policy Spacer::sizeType() const +{ + return m_orientation == Qt::Vertical ? sizePolicy().verticalPolicy() : sizePolicy().horizontalPolicy(); +} + +Qt::Alignment Spacer::alignment() const +{ + // For grid layouts + return m_orientation == Qt::Vertical ? Qt::AlignHCenter : Qt::AlignVCenter; +} + +QSize Spacer::sizeHint() const +{ + return isInLayout() ? m_sizeHint : m_sizeHint + m_SizeOffset; +} + +QSize Spacer::sizeHintProperty() const +{ + return m_sizeHint; +} + +void Spacer::setSizeHintProperty(const QSize &s) +{ + m_sizeHint = s; + + if (!isInLayout()) // Visible resize only if not in layout + resize(s + m_SizeOffset); + + updateGeometry(); +} + +Qt::Orientation Spacer::orientation() const +{ + return m_orientation; +} + +void Spacer::setOrientation(Qt::Orientation o) +{ + if (m_orientation == o) + return; + + const QSizePolicy::Policy st = sizeType(); // flip size type + m_orientation = o; + setSizeType(st); + + if (m_interactive) { + m_sizeHint = QSize(m_sizeHint.height(), m_sizeHint.width()); + if (!isInLayout()) + resize(m_sizeHint + m_SizeOffset); + } + + updateMask(); + update(); + updateGeometry(); +} + +void Spacer::updateToolTip() +{ + const QString format = m_orientation == Qt::Horizontal ? tr("Horizontal Spacer '%1', %2 x %3") : tr("Vertical Spacer '%1', %2 x %3"); + QString msg = format.arg(objectName()).arg(m_sizeHint.width()).arg(m_sizeHint.height()); + setToolTip(msg); +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/spacer_widget_p.h b/designer/lib/shared/spacer_widget_p.h new file mode 100644 index 0000000..6e2ab03 --- /dev/null +++ b/designer/lib/shared/spacer_widget_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef SPACER_WIDGET_H +#define SPACER_WIDGET_H + +#include "shared_global_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QDESIGNER_SHARED_EXPORT Spacer: public QWidget +{ + Q_OBJECT + + Q_ENUMS(SizeType) + // Special hack: Make name appear as "spacer name" + Q_PROPERTY(QString spacerName READ objectName WRITE setObjectName) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(QSizePolicy::Policy sizeType READ sizeType WRITE setSizeType) + Q_PROPERTY(QSize sizeHint READ sizeHintProperty WRITE setSizeHintProperty DESIGNABLE true STORED true) + +public: + Spacer(QWidget *parent = 0); + + QSize sizeHint() const; + + QSize sizeHintProperty() const; + void setSizeHintProperty(const QSize &s); + + QSizePolicy::Policy sizeType() const; + void setSizeType(QSizePolicy::Policy t); + + Qt::Alignment alignment() const; + Qt::Orientation orientation() const; + + void setOrientation(Qt::Orientation o); + void setInteractiveMode(bool b) { m_interactive = b; }; + + virtual bool event(QEvent *e); + +protected: + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent* e); + void updateMask(); + +private: + bool isInLayout() const; + void updateToolTip(); + + const QSize m_SizeOffset; + QDesignerFormWindowInterface *m_formWindow; + Qt::Orientation m_orientation; + bool m_interactive; + // Cache information about 'being in layout' which is expensive to calculate. + enum LayoutState { InLayout, OutsideLayout, UnknownLayoutState }; + mutable LayoutState m_layoutState; + QSize m_sizeHint; +}; + +QT_END_NAMESPACE + +#endif // SPACER_WIDGET_H diff --git a/designer/lib/shared/stylesheeteditor.cpp b/designer/lib/shared/stylesheeteditor.cpp new file mode 100644 index 0000000..4c48b09 --- /dev/null +++ b/designer/lib/shared/stylesheeteditor.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stylesheeteditor_p.h" +#include "csshighlighter_p.h" +#include "iconselector_p.h" +#include "qtgradientmanager.h" +#include "qtgradientviewdialog.h" +#include "qtgradientutils.h" +#include "qdesigner_integration_p.h" +#include "qdesigner_utils_p.h" +#include "abstractsettings_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "private/qcssparser_p.h" + +QT_BEGIN_NAMESPACE + +static const char *styleSheetProperty = "styleSheet"; +static const char *StyleSheetDialogC = "StyleSheetDialog"; +static const char *Geometry = "Geometry"; + +namespace qdesigner_internal { + +StyleSheetEditor::StyleSheetEditor(QWidget *parent) + : QTextEdit(parent) +{ + setTabStopWidth(fontMetrics().width(QLatin1Char(' '))*4); + new CssHighlighter(document()); +} + +// --- StyleSheetEditorDialog +StyleSheetEditorDialog::StyleSheetEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent, Mode mode): + QDialog(parent), + m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Help)), + m_editor(new StyleSheetEditor), + m_validityLabel(new QLabel(tr("Valid Style Sheet"))), + m_core(core), + m_addResourceAction(new QAction(tr("Add Resource..."), this)), + m_addGradientAction(new QAction(tr("Add Gradient..."), this)), + m_addColorAction(new QAction(tr("Add Color..."), this)), + m_addFontAction(new QAction(tr("Add Font..."), this)) +{ + setWindowTitle(tr("Edit Style Sheet")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(m_buttonBox, SIGNAL(helpRequested()), this, SLOT(slotRequestHelp())); + m_buttonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents); + + connect(m_editor, SIGNAL(textChanged()), this, SLOT(validateStyleSheet())); + + QToolBar *toolBar = new QToolBar; + + QGridLayout *layout = new QGridLayout; + layout->addWidget(toolBar, 0, 0, 1, 2); + layout->addWidget(m_editor, 1, 0, 1, 2); + layout->addWidget(m_validityLabel, 2, 0, 1, 1); + layout->addWidget(m_buttonBox, 2, 1, 1, 1); + setLayout(layout); + + m_editor->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_editor, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotContextMenuRequested(QPoint))); + + QSignalMapper *resourceActionMapper = new QSignalMapper(this); + QSignalMapper *gradientActionMapper = new QSignalMapper(this); + QSignalMapper *colorActionMapper = new QSignalMapper(this); + + resourceActionMapper->setMapping(m_addResourceAction, QString()); + gradientActionMapper->setMapping(m_addGradientAction, QString()); + colorActionMapper->setMapping(m_addColorAction, QString()); + + connect(m_addResourceAction, SIGNAL(triggered()), resourceActionMapper, SLOT(map())); + connect(m_addGradientAction, SIGNAL(triggered()), gradientActionMapper, SLOT(map())); + connect(m_addColorAction, SIGNAL(triggered()), colorActionMapper, SLOT(map())); + connect(m_addFontAction, SIGNAL(triggered()), this, SLOT(slotAddFont())); + + m_addResourceAction->setEnabled(mode == ModePerForm); + + const char * const resourceProperties[] = { + "background-image", + "border-image", + "image", + 0 + }; + + const char * const colorProperties[] = { + "color", + "background-color", + "alternate-background-color", + "border-color", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + "gridline-color", + "selection-color", + "selection-background-color", + 0 + }; + + QMenu *resourceActionMenu = new QMenu(this); + QMenu *gradientActionMenu = new QMenu(this); + QMenu *colorActionMenu = new QMenu(this); + + for (int resourceProperty = 0; resourceProperties[resourceProperty]; ++resourceProperty) { + QAction *action = resourceActionMenu->addAction(QLatin1String(resourceProperties[resourceProperty])); + connect(action, SIGNAL(triggered()), resourceActionMapper, SLOT(map())); + resourceActionMapper->setMapping(action, QLatin1String(resourceProperties[resourceProperty])); + } + + for (int colorProperty = 0; colorProperties[colorProperty]; ++colorProperty) { + QAction *gradientAction = gradientActionMenu->addAction(QLatin1String(colorProperties[colorProperty])); + QAction *colorAction = colorActionMenu->addAction(QLatin1String(colorProperties[colorProperty])); + connect(gradientAction, SIGNAL(triggered()), gradientActionMapper, SLOT(map())); + connect(colorAction, SIGNAL(triggered()), colorActionMapper, SLOT(map())); + gradientActionMapper->setMapping(gradientAction, QLatin1String(colorProperties[colorProperty])); + colorActionMapper->setMapping(colorAction, QLatin1String(colorProperties[colorProperty])); + } + + connect(resourceActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddResource(QString))); + connect(gradientActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddGradient(QString))); + connect(colorActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddColor(QString))); + + m_addResourceAction->setMenu(resourceActionMenu); + m_addGradientAction->setMenu(gradientActionMenu); + m_addColorAction->setMenu(colorActionMenu); + + toolBar->addAction(m_addResourceAction); + toolBar->addAction(m_addGradientAction); + toolBar->addAction(m_addColorAction); + toolBar->addAction(m_addFontAction); + + m_editor->setFocus(); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(StyleSheetDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +StyleSheetEditorDialog::~StyleSheetEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(StyleSheetDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +void StyleSheetEditorDialog::setOkButtonEnabled(bool v) +{ + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(v); + if (QPushButton *applyButton = m_buttonBox->button(QDialogButtonBox::Apply)) + applyButton->setEnabled(v); +} + +void StyleSheetEditorDialog::slotContextMenuRequested(const QPoint &pos) +{ + QMenu *menu = m_editor->createStandardContextMenu(); + menu->addSeparator(); + menu->addAction(m_addResourceAction); + menu->addAction(m_addGradientAction); + menu->exec(mapToGlobal(pos)); + delete menu; +} + +void StyleSheetEditorDialog::slotAddResource(const QString &property) +{ + const QString path = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), QString(), this); + if (!path.isEmpty()) + insertCssProperty(property, QString(QLatin1String("url(%1)")).arg(path)); +} + +void StyleSheetEditorDialog::slotAddGradient(const QString &property) +{ + bool ok; + const QGradient grad = QtGradientViewDialog::getGradient(&ok, m_core->gradientManager(), this); + if (ok) + insertCssProperty(property, QtGradientUtils::styleSheetCode(grad)); +} + +void StyleSheetEditorDialog::slotAddColor(const QString &property) +{ + const QColor color = QColorDialog::getColor(0xffffffff, this, QString(), QColorDialog::ShowAlphaChannel); + if (!color.isValid()) + return; + + QString colorStr; + + if (color.alpha() == 255) { + colorStr = QString(QLatin1String("rgb(%1, %2, %3)")).arg( + color.red()).arg(color.green()).arg(color.blue()); + } else { + colorStr = QString(QLatin1String("rgba(%1, %2, %3, %4)")).arg( + color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha()); + } + + insertCssProperty(property, colorStr); +} + +void StyleSheetEditorDialog::slotAddFont() +{ + bool ok; + QFont font = QFontDialog::getFont(&ok, this); + if (ok) { + QString fontStr; + if (font.weight() != QFont::Normal) { + fontStr += QString::number(font.weight()); + fontStr += QLatin1Char(' '); + } + + switch (font.style()) { + case QFont::StyleItalic: + fontStr += QLatin1String("italic "); + break; + case QFont::StyleOblique: + fontStr += QLatin1String("oblique "); + break; + default: + break; + } + fontStr += QString::number(font.pointSize()); + fontStr += QLatin1String("pt \""); + fontStr += font.family(); + fontStr += QLatin1Char('"'); + + insertCssProperty(QLatin1String("font"), fontStr); + QString decoration; + if (font.underline()) + decoration += QLatin1String("underline"); + if (font.strikeOut()) { + if (!decoration.isEmpty()) + decoration += QLatin1Char(' '); + decoration += QLatin1String("line-through"); + } + insertCssProperty(QLatin1String("text-decoration"), decoration); + } +} + +void StyleSheetEditorDialog::insertCssProperty(const QString &name, const QString &value) +{ + if (!value.isEmpty()) { + QTextCursor cursor = m_editor->textCursor(); + if (!name.isEmpty()) { + cursor.beginEditBlock(); + cursor.removeSelectedText(); + cursor.movePosition(QTextCursor::EndOfLine); + + // Simple check to see if we're in a selector scope + const QTextDocument *doc = m_editor->document(); + const QTextCursor closing = doc->find(QLatin1String("}"), cursor, QTextDocument::FindBackward); + const QTextCursor opening = doc->find(QLatin1String("{"), cursor, QTextDocument::FindBackward); + const bool inSelector = !opening.isNull() && (closing.isNull() || + closing.position() < opening.position()); + QString insertion; + if (m_editor->textCursor().block().length() != 1) + insertion += QLatin1Char('\n'); + if (inSelector) + insertion += QLatin1Char('\t'); + insertion += name; + insertion += QLatin1String(": "); + insertion += value; + insertion += QLatin1Char(';'); + cursor.insertText(insertion); + cursor.endEditBlock(); + } else { + cursor.insertText(value); + } + } +} + +void StyleSheetEditorDialog::slotRequestHelp() +{ + QDesignerIntegration::requestHelp(m_core, QLatin1String("qt"), + QLatin1String("stylesheet-reference.html")); +} + +QDialogButtonBox * StyleSheetEditorDialog::buttonBox() const +{ + return m_buttonBox; +} + +QString StyleSheetEditorDialog::text() const +{ + return m_editor->toPlainText(); +} + +void StyleSheetEditorDialog::setText(const QString &t) +{ + m_editor->setText(t); +} + +bool StyleSheetEditorDialog::isStyleSheetValid(const QString &styleSheet) +{ + QCss::Parser parser(styleSheet); + QCss::StyleSheet sheet; + if (parser.parse(&sheet)) + return true; + QString fullSheet = QLatin1String("* { "); + fullSheet += styleSheet; + fullSheet += QLatin1Char('}'); + QCss::Parser parser2(fullSheet); + return parser2.parse(&sheet); +} + +void StyleSheetEditorDialog::validateStyleSheet() +{ + const bool valid = isStyleSheetValid(m_editor->toPlainText()); + setOkButtonEnabled(valid); + if (valid) { + m_validityLabel->setText(tr("Valid Style Sheet")); + m_validityLabel->setStyleSheet(QLatin1String("color: green")); + } else { + m_validityLabel->setText(tr("Invalid Style Sheet")); + m_validityLabel->setStyleSheet(QLatin1String("color: red")); + } +} + +// --- StyleSheetPropertyEditorDialog +StyleSheetPropertyEditorDialog::StyleSheetPropertyEditorDialog(QWidget *parent, + QDesignerFormWindowInterface *fw, + QWidget *widget): + StyleSheetEditorDialog(fw->core(), parent), + m_fw(fw), + m_widget(widget) +{ + Q_ASSERT(m_fw != 0); + + QPushButton *apply = buttonBox()->addButton(QDialogButtonBox::Apply); + QObject::connect(apply, SIGNAL(clicked()), this, SLOT(applyStyleSheet())); + QObject::connect(buttonBox(), SIGNAL(accepted()), this, SLOT(applyStyleSheet())); + + QDesignerPropertySheetExtension *sheet = + qt_extension(m_fw->core()->extensionManager(), m_widget); + Q_ASSERT(sheet != 0); + const int index = sheet->indexOf(QLatin1String(styleSheetProperty)); + const PropertySheetStringValue value = qVariantValue(sheet->property(index)); + setText(value.value()); +} + +void StyleSheetPropertyEditorDialog::applyStyleSheet() +{ + const PropertySheetStringValue value(text(), false); + m_fw->cursor()->setWidgetProperty(m_widget, QLatin1String(styleSheetProperty), qVariantFromValue(value)); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/stylesheeteditor_p.h b/designer/lib/shared/stylesheeteditor_p.h new file mode 100644 index 0000000..98a9c67 --- /dev/null +++ b/designer/lib/shared/stylesheeteditor_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef STYLESHEETEDITOR_H +#define STYLESHEETEDITOR_H + +#include +#include +#include +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QDialogButtonBox; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT StyleSheetEditor : public QTextEdit +{ + Q_OBJECT +public: + StyleSheetEditor(QWidget *parent = 0); +}; + +// Edit a style sheet. +class QDESIGNER_SHARED_EXPORT StyleSheetEditorDialog : public QDialog +{ + Q_OBJECT +public: + enum Mode { + ModeGlobal, // resources are disabled (we don't have current resource set loaded), used e.g. in configuration dialog context + ModePerForm // resources are available + }; + + StyleSheetEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent, Mode mode = ModePerForm); + ~StyleSheetEditorDialog(); + QString text() const; + void setText(const QString &t); + + static bool isStyleSheetValid(const QString &styleSheet); + + +private slots: + void validateStyleSheet(); + void slotContextMenuRequested(const QPoint &pos); + void slotAddResource(const QString &property); + void slotAddGradient(const QString &property); + void slotAddColor(const QString &property); + void slotAddFont(); + void slotRequestHelp(); + +protected: + QDialogButtonBox *buttonBox() const; + void setOkButtonEnabled(bool v); + +private: + void insertCssProperty(const QString &name, const QString &value); + + QDialogButtonBox *m_buttonBox; + StyleSheetEditor *m_editor; + QLabel *m_validityLabel; + QDesignerFormEditorInterface *m_core; + QAction *m_addResourceAction; + QAction *m_addGradientAction; + QAction *m_addColorAction; + QAction *m_addFontAction; +}; + +// Edit the style sheet property of the designer selection. +// Provides an "Apply" button. + +class QDESIGNER_SHARED_EXPORT StyleSheetPropertyEditorDialog : public StyleSheetEditorDialog +{ + Q_OBJECT +public: + StyleSheetPropertyEditorDialog(QWidget *parent, QDesignerFormWindowInterface *fw, QWidget *widget); + + static bool isStyleSheetValid(const QString &styleSheet); + +private slots: + void applyStyleSheet(); + +private: + QDesignerFormWindowInterface *m_fw; + QWidget *m_widget; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // STYLESHEETEDITOR_H diff --git a/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui b/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..7ff64b0 --- /dev/null +++ b/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 240 + 320 + + + + Dialog + + + + + 10 + 270 + 221 + 41 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui b/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..203af78 --- /dev/null +++ b/designer/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 240 + 320 + + + + Dialog + + + + + 150 + 10 + 81 + 301 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui b/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..0ac856e --- /dev/null +++ b/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 320 + 240 + + + + Dialog + + + + + 10 + 200 + 301 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui b/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..52f0f66 --- /dev/null +++ b/designer/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 320 + 240 + + + + Dialog + + + + + 230 + 10 + 81 + 221 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui b/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..0d219c9 --- /dev/null +++ b/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 480 + 640 + + + + Dialog + + + + + 10 + 600 + 461 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui b/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..c82a78e --- /dev/null +++ b/designer/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 480 + 640 + + + + Dialog + + + + + 390 + 10 + 81 + 621 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui b/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..adc5d48 --- /dev/null +++ b/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 640 + 480 + + + + Dialog + + + + + 10 + 440 + 621 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui b/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..defb42a --- /dev/null +++ b/designer/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ + + Dialog + + + + 0 + 0 + 640 + 480 + + + + Dialog + + + + + 550 + 10 + 81 + 461 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui b/designer/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..18d31ab --- /dev/null +++ b/designer/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,71 @@ + + + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui b/designer/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..703d594 --- /dev/null +++ b/designer/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui @@ -0,0 +1,71 @@ + + + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 290 + 20 + 81 + 241 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/designer/lib/shared/templates/forms/Dialog_without_Buttons.ui b/designer/lib/shared/templates/forms/Dialog_without_Buttons.ui new file mode 100644 index 0000000..1be6298 --- /dev/null +++ b/designer/lib/shared/templates/forms/Dialog_without_Buttons.ui @@ -0,0 +1,18 @@ + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + diff --git a/designer/lib/shared/templates/forms/Main_Window.ui b/designer/lib/shared/templates/forms/Main_Window.ui new file mode 100644 index 0000000..9ae3b50 --- /dev/null +++ b/designer/lib/shared/templates/forms/Main_Window.ui @@ -0,0 +1,24 @@ + + + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + + + diff --git a/designer/lib/shared/templates/forms/Widget.ui b/designer/lib/shared/templates/forms/Widget.ui new file mode 100644 index 0000000..4b7d6a4 --- /dev/null +++ b/designer/lib/shared/templates/forms/Widget.ui @@ -0,0 +1,21 @@ + + + + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/designer/lib/shared/textpropertyeditor.cpp b/designer/lib/shared/textpropertyeditor.cpp new file mode 100644 index 0000000..375e07d --- /dev/null +++ b/designer/lib/shared/textpropertyeditor.cpp @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textpropertyeditor_p.h" +#include "propertylineedit_p.h" +#include "stylesheeteditor_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + const QChar NewLineChar(QLatin1Char('\n')); + const QLatin1String EscapedNewLine("\\n"); + + // A validator that replaces offending strings + class ReplacementValidator : public QValidator { + public: + ReplacementValidator (QObject * parent, + const QString &offending, + const QString &replacement); + virtual void fixup ( QString & input ) const; + virtual State validate ( QString & input, int &pos) const; + private: + const QString m_offending; + const QString m_replacement; + }; + + ReplacementValidator::ReplacementValidator (QObject * parent, + const QString &offending, + const QString &replacement) : + QValidator(parent ), + m_offending(offending), + m_replacement(replacement) + { + } + + void ReplacementValidator::fixup ( QString & input ) const { + input.replace(m_offending, m_replacement); + } + + QValidator::State ReplacementValidator::validate ( QString & input, int &/* pos */) const { + fixup (input); + return Acceptable; + } + + // A validator for style sheets. Does newline handling and validates sheets. + class StyleSheetValidator : public ReplacementValidator { + public: + StyleSheetValidator (QObject * parent); + virtual State validate(QString & input, int &pos) const; + }; + + StyleSheetValidator::StyleSheetValidator (QObject * parent) : + ReplacementValidator(parent, NewLineChar, EscapedNewLine) + { + } + + QValidator::State StyleSheetValidator::validate ( QString & input, int &pos) const + { + // base class + const State state = ReplacementValidator:: validate(input, pos); + if (state != Acceptable) + return state; + // now check style sheet, create string with newlines + const QString styleSheet = qdesigner_internal::TextPropertyEditor::editorStringToString(input, qdesigner_internal::ValidationStyleSheet); + const bool valid = qdesigner_internal::StyleSheetEditorDialog::isStyleSheetValid(styleSheet); + return valid ? Acceptable : Intermediate; + } + + // A validator for URLs based on QUrl. Enforces complete protocol + // specification with a completer (adds a trailing slash) + class UrlValidator : public QValidator { + public: + UrlValidator(QCompleter *completer, QObject *parent); + + virtual State validate(QString &input, int &pos) const; + virtual void fixup(QString &input) const; + private: + QUrl guessUrlFromString(const QString &string) const; + QCompleter *m_completer; + }; + + UrlValidator::UrlValidator(QCompleter *completer, QObject *parent) : + QValidator(parent), + m_completer(completer) + { + } + + QValidator::State UrlValidator::validate(QString &input, int &pos) const + { + Q_UNUSED(pos); + + if (input.isEmpty()) + return Acceptable; + + const QUrl url(input, QUrl::StrictMode); + + if (!url.isValid() || url.isEmpty()) + return Intermediate; + + if (url.scheme().isEmpty()) + return Intermediate; + + if (url.host().isEmpty() && url.path().isEmpty()) + return Intermediate; + + return Acceptable; + } + + void UrlValidator::fixup(QString &input) const + { + // Don't try to fixup if the user is busy selecting a completion proposal + if (const QAbstractItemView *iv = m_completer->popup()) { + if (iv->isVisible()) + return; + } + + input = guessUrlFromString(input).toString(); + } + + QUrl UrlValidator::guessUrlFromString(const QString &string) const + { + const QString urlStr = string.trimmed(); + const QRegExp qualifiedUrl(QLatin1String("^[a-zA-Z]+\\:.*")); + + // Check if it looks like a qualified URL. Try parsing it and see. + const bool hasSchema = qualifiedUrl.exactMatch(urlStr); + if (hasSchema) { + const QUrl url(urlStr, QUrl::TolerantMode); + if (url.isValid()) + return url; + } + + // Might be a Qt resource + if (string.startsWith(QLatin1String(":/"))) + return QUrl(QLatin1String("qrc") + string); + + // Might be a file. + if (QFile::exists(urlStr)) + return QUrl::fromLocalFile(urlStr); + + // Might be a short url - try to detect the schema. + if (!hasSchema) { + const int dotIndex = urlStr.indexOf(QLatin1Char('.')); + if (dotIndex != -1) { + const QString prefix = urlStr.left(dotIndex).toLower(); + QString urlString; + if (prefix == QLatin1String("ftp")) + urlString += prefix; + else + urlString += QLatin1String("http"); + urlString += QLatin1String("://"); + urlString += urlStr; + const QUrl url(urlString, QUrl::TolerantMode); + if (url.isValid()) + return url; + } + } + + // Fall back to QUrl's own tolerant parser. + return QUrl(string, QUrl::TolerantMode); + } +} + +namespace qdesigner_internal { + // TextPropertyEditor + TextPropertyEditor::TextPropertyEditor(QWidget *parent, + EmbeddingMode embeddingMode, + TextPropertyValidationMode validationMode) : + QWidget(parent), + m_validationMode(ValidationSingleLine), + m_updateMode(UpdateAsYouType), + m_lineEdit(new PropertyLineEdit(this)), + m_textEdited(false) + { + switch (embeddingMode) { + case EmbeddingNone: + break; + case EmbeddingTreeView: + m_lineEdit->setFrame(false); + break; + case EmbeddingInPlace: + m_lineEdit->setFrame(false); + Q_ASSERT(parent); + m_lineEdit->setBackgroundRole(parent->backgroundRole()); + break; + } + + setFocusProxy(m_lineEdit); + + connect(m_lineEdit,SIGNAL(editingFinished()), this, SIGNAL(editingFinished())); + connect(m_lineEdit,SIGNAL(returnPressed()), this, SLOT(slotEditingFinished())); + connect(m_lineEdit,SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + connect(m_lineEdit,SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited())); + + setTextPropertyValidationMode(validationMode); + } + + void TextPropertyEditor::setTextPropertyValidationMode(TextPropertyValidationMode vm) { + m_validationMode = vm; + m_lineEdit->setWantNewLine(multiLine(m_validationMode)); + switch (m_validationMode) { + case ValidationStyleSheet: + m_lineEdit->setValidator(new StyleSheetValidator(m_lineEdit)); + m_lineEdit->setCompleter(0); + break; + case ValidationMultiLine: + case ValidationRichText: + // Set a validator that replaces newline characters by literal "\\n". + // While it is not possible to actually type a newline characters, + // it can be pasted into the line edit. + m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, EscapedNewLine)); + m_lineEdit->setCompleter(0); + break; + case ValidationSingleLine: + // Set a validator that replaces newline characters by a blank. + m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, QString(QLatin1Char(' ')))); + m_lineEdit->setCompleter(0); + break; + case ValidationObjectName: + setRegExpValidator(QLatin1String("[_a-zA-Z][_a-zA-Z0-9]{,1023}")); + m_lineEdit->setCompleter(0); + break; + case ValidationObjectNameScope: + setRegExpValidator(QLatin1String("[_a-zA-Z:][_a-zA-Z0-9:]{,1023}")); + m_lineEdit->setCompleter(0); + break; + case ValidationURL: { + static QStringList urlCompletions; + if (urlCompletions.empty()) { + urlCompletions.push_back(QLatin1String("about:blank")); + urlCompletions.push_back(QLatin1String("http://")); + urlCompletions.push_back(QLatin1String("http://www.")); + urlCompletions.push_back(QLatin1String("http://qt.nokia.com/")); + urlCompletions.push_back(QLatin1String("file://")); + urlCompletions.push_back(QLatin1String("ftp://")); + urlCompletions.push_back(QLatin1String("data:")); + urlCompletions.push_back(QLatin1String("data:text/html,")); + urlCompletions.push_back(QLatin1String("qrc:/")); + } + QCompleter *completer = new QCompleter(urlCompletions, m_lineEdit); + m_lineEdit->setCompleter(completer); + m_lineEdit->setValidator(new UrlValidator(completer, m_lineEdit)); + } + break; + } + + setFocusProxy(m_lineEdit); + setText(m_cachedText); + markIntermediateState(); + } + + void TextPropertyEditor::setRegExpValidator(const QString &pattern) + { + const QRegExp regExp(pattern); + Q_ASSERT(regExp.isValid()); + m_lineEdit->setValidator(new QRegExpValidator(regExp,m_lineEdit)); + } + + QString TextPropertyEditor::text() const + { + return m_cachedText; + } + + void TextPropertyEditor::markIntermediateState() + { + if (m_lineEdit->hasAcceptableInput()) { + m_lineEdit->setPalette(QPalette()); + } else { + QPalette palette = m_lineEdit->palette(); + palette.setColor(QPalette::Active, QPalette::Text, Qt::red); + m_lineEdit->setPalette(palette); + } + + } + + void TextPropertyEditor::setText(const QString &text) + { + m_cachedText = text; + m_lineEdit->setText(stringToEditorString(text, m_validationMode)); + markIntermediateState(); + m_textEdited = false; + } + + void TextPropertyEditor::slotTextEdited() + { + m_textEdited = true; + } + + void TextPropertyEditor::slotTextChanged(const QString &text) { + m_cachedText = editorStringToString(text, m_validationMode); + markIntermediateState(); + if (m_updateMode == UpdateAsYouType) + emit textChanged(m_cachedText); + } + + void TextPropertyEditor::slotEditingFinished() + { + if (m_updateMode == UpdateOnFinished && m_textEdited) { + emit textChanged(m_cachedText); + m_textEdited = false; + } + } + + void TextPropertyEditor::selectAll() { + m_lineEdit->selectAll(); + } + + void TextPropertyEditor::clear() { + m_lineEdit->clear(); + } + + void TextPropertyEditor::setAlignment(Qt::Alignment alignment) { + m_lineEdit->setAlignment(alignment); + } + + void TextPropertyEditor::installEventFilter(QObject *filterObject) + { + if (m_lineEdit) + m_lineEdit->installEventFilter(filterObject); + } + + void TextPropertyEditor::resizeEvent ( QResizeEvent * event ) { + m_lineEdit->resize( event->size()); + } + + QSize TextPropertyEditor::sizeHint () const { + return m_lineEdit->sizeHint (); + } + + QSize TextPropertyEditor::minimumSizeHint () const { + return m_lineEdit->minimumSizeHint (); + } + + // Returns whether newline characters are valid in validationMode. + bool TextPropertyEditor::multiLine(TextPropertyValidationMode validationMode) { + return validationMode == ValidationMultiLine || validationMode == ValidationStyleSheet || validationMode == ValidationRichText; + } + + // Replace newline characters literal "\n" for inline editing in mode ValidationMultiLine + QString TextPropertyEditor::stringToEditorString(const QString &s, TextPropertyValidationMode validationMode) { + if (s.isEmpty() || !multiLine(validationMode)) + return s; + + QString rc(s); + // protect backslashes + rc.replace(QLatin1Char('\\'), QLatin1String("\\\\")); + // escape newlines + rc.replace(NewLineChar, QString(EscapedNewLine)); + return rc; + + } + + // Replace literal "\n" by actual new lines for inline editing in mode ValidationMultiLine + // Note: As the properties are updated while the user types, it is important + // that trailing slashes ('bla\') are not deleted nor ignored, else this will + // cause jumping of the cursor + QString TextPropertyEditor::editorStringToString(const QString &s, TextPropertyValidationMode validationMode) { + if (s.isEmpty() || !multiLine(validationMode)) + return s; + + QString rc(s); + for (int pos = 0; (pos = rc.indexOf(QLatin1Char('\\'),pos)) >= 0 ; ) { + // found an escaped character. If not a newline or at end of string, leave as is, else insert '\n' + const int nextpos = pos + 1; + if (nextpos >= rc.length()) // trailing '\\' + break; + // Escaped NewLine + if (rc.at(nextpos) == QChar(QLatin1Char('n'))) + rc[nextpos] = NewLineChar; + // Remove escape, go past escaped + rc.remove(pos,1); + pos++; + } + return rc; + } + + bool TextPropertyEditor::hasAcceptableInput() const { + return m_lineEdit->hasAcceptableInput(); + } +} + +QT_END_NAMESPACE diff --git a/designer/lib/shared/textpropertyeditor_p.h b/designer/lib/shared/textpropertyeditor_p.h new file mode 100644 index 0000000..40fa522 --- /dev/null +++ b/designer/lib/shared/textpropertyeditor_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef TEXTPROPERTYEDITOR_H +#define TEXTPROPERTYEDITOR_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + class PropertyLineEdit; + + // Inline-Editor for text properties. Does escaping of newline characters + // to '\n' and back and provides validation modes. The interface + // corresponds to that of QLineEdit. + class QDESIGNER_SHARED_EXPORT TextPropertyEditor : public QWidget + { + TextPropertyEditor(const TextPropertyEditor &); + TextPropertyEditor& operator=(const TextPropertyEditor &); + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText USER true) + public: + enum EmbeddingMode { + // Stand-alone widget + EmbeddingNone, + // Disable frame + EmbeddingTreeView, + // For editing in forms + EmbeddingInPlace + }; + + enum UpdateMode { + // Emit textChanged() as the user types + UpdateAsYouType, + // Emit textChanged() only when the user finishes (for QUrl, etc.) + UpdateOnFinished + }; + + explicit TextPropertyEditor(QWidget *parent = 0, EmbeddingMode embeddingMode = EmbeddingNone, TextPropertyValidationMode validationMode = ValidationMultiLine); + + TextPropertyValidationMode textPropertyValidationMode() const { return m_validationMode; } + void setTextPropertyValidationMode(TextPropertyValidationMode vm); + + UpdateMode updateMode() const { return m_updateMode; } + void setUpdateMode(UpdateMode um) { m_updateMode = um; } + + QString text() const; + + virtual QSize sizeHint () const; + virtual QSize minimumSizeHint () const; + + void setAlignment(Qt::Alignment alignment); + + bool hasAcceptableInput() const; + + // installs an event filter object on the private QLineEdit + void installEventFilter(QObject *filterObject); + + // Replace newline characters by literal "\n" for inline editing + // in mode ValidationMultiLine + static QString stringToEditorString(const QString &s, TextPropertyValidationMode validationMode = ValidationMultiLine); + + // Replace literal "\n" by actual new lines in mode ValidationMultiLine + static QString editorStringToString(const QString &s, TextPropertyValidationMode validationMode = ValidationMultiLine); + + // Returns whether newline characters are valid in validationMode. + static bool multiLine(TextPropertyValidationMode validationMode); + + signals: + void textChanged(const QString &text); + void editingFinished(); + + public slots: + void setText(const QString &text); + void selectAll(); + void clear(); + + protected: + void resizeEvent(QResizeEvent * event ); + + private slots: + void slotTextChanged(const QString &text); + void slotTextEdited(); + void slotEditingFinished(); + + private: + void setRegExpValidator(const QString &pattern); + void markIntermediateState(); + + TextPropertyValidationMode m_validationMode; + UpdateMode m_updateMode; + PropertyLineEdit* m_lineEdit; + + // Cached text containing real newline characters. + QString m_cachedText; + bool m_textEdited; + }; +} + +QT_END_NAMESPACE + +#endif // TEXTPROPERTYEDITOR_H diff --git a/designer/lib/shared/widgetdatabase.cpp b/designer/lib/shared/widgetdatabase.cpp new file mode 100644 index 0000000..8752210 --- /dev/null +++ b/designer/lib/shared/widgetdatabase.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetdatabase_p.h" +#include "widgetfactory_p.h" +#include "spacer_widget_p.h" +#include "abstractlanguage.h" +#include "pluginmanager_p.h" +#include "qdesigner_widgetbox_p.h" +#include "qdesigner_utils_p.h" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + enum { debugWidgetDataBase = 0 }; +} + +namespace qdesigner_internal { + +// ---------------------------------------------------------- +WidgetDataBaseItem::WidgetDataBaseItem(const QString &name, const QString &group) + : m_name(name), + m_group(group), + m_compat(0), + m_container(0), + m_form(0), + m_custom(0), + m_promoted(0) +{ +} + +QString WidgetDataBaseItem::name() const +{ + return m_name; +} + +void WidgetDataBaseItem::setName(const QString &name) +{ + m_name = name; +} + +QString WidgetDataBaseItem::group() const +{ + return m_group; +} + +void WidgetDataBaseItem::setGroup(const QString &group) +{ + m_group = group; +} + +QString WidgetDataBaseItem::toolTip() const +{ + return m_toolTip; +} + +void WidgetDataBaseItem::setToolTip(const QString &toolTip) +{ + m_toolTip = toolTip; +} + +QString WidgetDataBaseItem::whatsThis() const +{ + return m_whatsThis; +} + +void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis) +{ + m_whatsThis = whatsThis; +} + +QString WidgetDataBaseItem::includeFile() const +{ + return m_includeFile; +} + +void WidgetDataBaseItem::setIncludeFile(const QString &includeFile) +{ + m_includeFile = includeFile; +} + +QIcon WidgetDataBaseItem::icon() const +{ + return m_icon; +} + +void WidgetDataBaseItem::setIcon(const QIcon &icon) +{ + m_icon = icon; +} + +bool WidgetDataBaseItem::isCompat() const +{ + return m_compat; +} + +void WidgetDataBaseItem::setCompat(bool b) +{ + m_compat = b; +} + +bool WidgetDataBaseItem::isContainer() const +{ + return m_container; +} + +void WidgetDataBaseItem::setContainer(bool b) +{ + m_container = b; +} + +bool WidgetDataBaseItem::isCustom() const +{ + return m_custom; +} + +void WidgetDataBaseItem::setCustom(bool b) +{ + m_custom = b; +} + +QString WidgetDataBaseItem::pluginPath() const +{ + return m_pluginPath; +} + +void WidgetDataBaseItem::setPluginPath(const QString &path) +{ + m_pluginPath = path; +} + +bool WidgetDataBaseItem::isPromoted() const +{ + return m_promoted; +} + +void WidgetDataBaseItem::setPromoted(bool b) +{ + m_promoted = b; +} + +QString WidgetDataBaseItem::extends() const +{ + return m_extends; +} + +void WidgetDataBaseItem::setExtends(const QString &s) +{ + m_extends = s; +} + +void WidgetDataBaseItem::setDefaultPropertyValues(const QList &list) +{ + m_defaultPropertyValues = list; +} + +QList WidgetDataBaseItem::defaultPropertyValues() const +{ + return m_defaultPropertyValues; +} + +QStringList WidgetDataBaseItem::fakeSlots() const +{ + return m_fakeSlots; +} + +void WidgetDataBaseItem::setFakeSlots(const QStringList &fs) +{ + m_fakeSlots = fs; +} + +QStringList WidgetDataBaseItem::fakeSignals() const +{ + return m_fakeSignals; +} + +void WidgetDataBaseItem::setFakeSignals(const QStringList &fs) +{ + m_fakeSignals = fs; +} + +QString WidgetDataBaseItem::addPageMethod() const +{ + return m_addPageMethod; +} + +void WidgetDataBaseItem::setAddPageMethod(const QString &m) +{ + m_addPageMethod = m; +} + +WidgetDataBaseItem *WidgetDataBaseItem::clone(const QDesignerWidgetDataBaseItemInterface *item) +{ + WidgetDataBaseItem *rc = new WidgetDataBaseItem(item->name(), item->group()); + + rc->setToolTip(item->toolTip()); + rc->setWhatsThis(item->whatsThis()); + rc->setIncludeFile(item->includeFile()); + rc->setIcon(item->icon()); + rc->setCompat(item->isCompat()); + rc->setContainer(item->isContainer()); + rc->setCustom(item->isCustom() ); + rc->setPluginPath(item->pluginPath()); + rc->setPromoted(item->isPromoted()); + rc->setExtends(item->extends()); + rc->setDefaultPropertyValues(item->defaultPropertyValues()); + // container page method, fake slots and signals ignored here.y + return rc; +} + +// ---------------------------------------------------------- +WidgetDataBase::WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerWidgetDataBaseInterface(parent), + m_core(core) +{ +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C) +#define DECLARE_WIDGET(W, C) append(new WidgetDataBaseItem(QString::fromUtf8(#W))); + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + append(new WidgetDataBaseItem(QString::fromUtf8("Line"))); + append(new WidgetDataBaseItem(QString::fromUtf8("Spacer"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget"))); + // QDesignerWidget is used as central widget and as container for tab widgets, etc. + WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget")); + designerWidgetItem->setContainer(true); + append(designerWidgetItem); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerQ3WidgetStack"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QAction"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QButtonGroup"))); + + // ### remove me + // ### check the casts + +#if 0 // ### enable me after 4.1 + item(indexOfClassName(QLatin1String("QToolBar")))->setContainer(true); +#endif + + item(indexOfClassName(QLatin1String("QTabWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QGroupBox")))->setContainer(true); + item(indexOfClassName(QLatin1String("QScrollArea")))->setContainer(true); + item(indexOfClassName(QLatin1String("QStackedWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QToolBox")))->setContainer(true); + item(indexOfClassName(QLatin1String("QFrame")))->setContainer(true); + item(indexOfClassName(QLatin1String("QLayoutWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerDialog")))->setContainer(true); + item(indexOfClassName(QLatin1String("QSplitter")))->setContainer(true); + item(indexOfClassName(QLatin1String("QMainWindow")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDockWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerDockWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerQ3WidgetStack")))->setContainer(true); + item(indexOfClassName(QLatin1String("QMdiArea")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWorkspace")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWizard")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWizardPage")))->setContainer(true); + + item(indexOfClassName(QLatin1String("QWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDialog")))->setContainer(true); +} + +WidgetDataBase::~WidgetDataBase() +{ +} + +QDesignerFormEditorInterface *WidgetDataBase::core() const +{ + return m_core; +} + +int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const +{ + QExtensionManager *mgr = m_core->extensionManager(); + QDesignerLanguageExtension *lang = qt_extension (mgr, m_core); + + QString id; + + if (lang) + id = lang->classNameOf(object); + + if (id.isEmpty()) + id = WidgetFactory::classNameOf(m_core,object); + + return QDesignerWidgetDataBaseInterface::indexOfClassName(id); +} + +static WidgetDataBaseItem *createCustomWidgetItem(const QDesignerCustomWidgetInterface *c, + const QDesignerCustomWidgetData &data) +{ + WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group()); + item->setContainer(c->isContainer()); + item->setCustom(true); + item->setIcon(c->icon()); + item->setIncludeFile(c->includeFile()); + item->setToolTip(c->toolTip()); + item->setWhatsThis(c->whatsThis()); + item->setPluginPath(data.pluginPath()); + item->setAddPageMethod(data.xmlAddPageMethod()); + item->setExtends(data.xmlExtends()); + return item; +} + +void WidgetDataBase::loadPlugins() +{ + typedef QMap NameIndexMap; + typedef QList ItemList; + typedef QMap NameItemMap; + typedef QSet NameSet; + // 1) create a map of existing custom classes + NameIndexMap existingCustomClasses; + NameSet nonCustomClasses; + const int count = m_items.size(); + for (int i = 0; i < count; i++) { + const QDesignerWidgetDataBaseItemInterface* item = m_items[i]; + if (item->isCustom() && !item->isPromoted()) + existingCustomClasses.insert(item->name(), i); + else + nonCustomClasses.insert(item->name()); + } + // 2) create a list plugins + ItemList pluginList; + const QDesignerPluginManager *pm = m_core->pluginManager(); + foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets()) + pluginList += createCustomWidgetItem(c, pm->customWidgetData(c)); + + // 3) replace custom classes or add new ones, remove them from existingCustomClasses, + // leaving behind deleted items + unsigned replacedPlugins = 0; + unsigned addedPlugins = 0; + unsigned removedPlugins = 0; + if (!pluginList.empty()) { + ItemList::const_iterator cend = pluginList.constEnd(); + for (ItemList::const_iterator it = pluginList.constBegin();it != cend; ++it ) { + QDesignerWidgetDataBaseItemInterface* pluginItem = *it; + const QString pluginName = pluginItem->name(); + NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName); + if (existingIt == existingCustomClasses.end()) { + // Add new class. + if (nonCustomClasses.contains(pluginName)) { + designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName)); + } else { + append(pluginItem); + addedPlugins++; + } + } else { + // replace existing info + const int existingIndex = existingIt.value(); + delete m_items[existingIndex]; + m_items[existingIndex] = pluginItem; + existingCustomClasses.erase(existingIt); + replacedPlugins++; + + } + } + } + // 4) remove classes that have not been matched. The stored indexes become invalid while deleting. + if (!existingCustomClasses.empty()) { + NameIndexMap::const_iterator cend = existingCustomClasses.constEnd(); + for (NameIndexMap::const_iterator it = existingCustomClasses.constBegin();it != cend; ++it ) { + const int index = indexOfClassName(it.key()); + if (index != -1) { + remove(index); + removedPlugins++; + } + } + } + if (debugWidgetDataBase) + qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted."; +} + +void WidgetDataBase::remove(int index) +{ + Q_ASSERT(index < m_items.size()); + delete m_items.takeAt(index); +} + +QList WidgetDataBase::defaultPropertyValues(const QString &name) +{ + WidgetFactory *factory = qobject_cast(m_core->widgetFactory()); + Q_ASSERT(factory); + // Create non-widgets, widgets in order + QObject* object = factory->createObject(name, 0); + if (!object) + object = factory->createWidget(name, 0); + if (!object) { + qDebug() << "** WARNING Factory failed to create " << name; + return QList(); + } + // Get properties from sheet. + QList result; + if (const QDesignerPropertySheetExtension *sheet = qt_extension(m_core->extensionManager(), object)) { + const int propertyCount = sheet->count(); + for (int i = 0; i < propertyCount; ++i) { + result.append(sheet->property(i)); + } + } + delete object; + return result; +} + +void WidgetDataBase::grabDefaultPropertyValues() +{ + const int itemCount = count(); + for (int i = 0; i < itemCount; ++i) { + QDesignerWidgetDataBaseItemInterface *dbItem = item(i); + const QList default_prop_values = defaultPropertyValues(dbItem->name()); + dbItem->setDefaultPropertyValues(default_prop_values); + } +} + +void WidgetDataBase::grabStandardWidgetBoxIcons() +{ + // At this point, grab the default icons for the non-custom widgets from + // the widget box. They will show up in the object inspector. + if (const QDesignerWidgetBox *wb = qobject_cast(m_core->widgetBox())) { + const QString qWidgetClass = QLatin1String("QWidget"); + const int itemCount = count(); + for (int i = 0; i < itemCount; ++i) { + QDesignerWidgetDataBaseItemInterface *dbItem = item(i); + if (!dbItem->isCustom() && dbItem->icon().isNull()) { + // Careful not to catch the layout icons when looking for + // QWidget + const QString name = dbItem->name(); + if (name == qWidgetClass) { + dbItem->setIcon(wb->iconForWidget(name, QLatin1String("Containers"))); + } else { + dbItem->setIcon(wb->iconForWidget(name)); + } + } + } + } +} + +// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates) + +enum { NewFormWidth = 400, NewFormHeight = 300 }; + +// Check if class is suitable to generate a form from +static inline bool isExistingTemplate(const QString &className) +{ + return className == QLatin1String("QWidget") || className == QLatin1String("QDialog") || className == QLatin1String("QMainWindow"); +} + +// Check if class is suitable to generate a form from +static inline bool suitableForNewForm(const QString &className) +{ + if (className.isEmpty()) // Missing custom widget information + return false; + if (className == QLatin1String("QWorkspace")) + return false; + if (className == QLatin1String("QSplitter")) + return false; + if (className.startsWith(QLatin1String("QDesigner")) || className.startsWith(QLatin1String("Q3")) || className.startsWith(QLatin1String("QLayout"))) + return false; + return true; +} + +// Return a list of widget classes from which new forms can be generated. +// Suitable for 'New form' wizards in integrations. +QStringList WidgetDataBase::formWidgetClasses(const QDesignerFormEditorInterface *core) +{ + static QStringList rc; + if (rc.empty()) { + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widgetCount = wdb->count(); + for (int i = 0; i < widgetCount; i++) { + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); + if (item->isContainer() && !item->isCustom() && !item->isPromoted()) { + const QString name = item->name(); // Standard Widgets: no existing templates + if (!isExistingTemplate(name) && suitableForNewForm(name)) + rc += name; + } + } + } + return rc; +} + +// Return a list of custom widget classes from which new forms can be generated. +// Suitable for 'New form' wizards in integrations. +QStringList WidgetDataBase::customFormWidgetClasses(const QDesignerFormEditorInterface *core) +{ + QStringList rc; + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widgetCount = wdb->count(); + for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class. + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); + if (item->isContainer() && item->isCustom() && !item->isPromoted()) { + if (suitableForNewForm(item->name()) && suitableForNewForm(item->extends())) + rc += item->name(); + } + } + return rc; +} + +// Get XML for a new form from the widget box. Change objectName/geometry +// properties to be suitable for new forms +static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) +{ + typedef QList PropertyList; + + QDesignerWidgetBoxInterface::Widget widget; + const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget); + if (!found) + return QString(); + DomUI *domUI = QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false); + domUI->setAttributeVersion(QLatin1String("4.0")); + if (!domUI) + return QString(); + DomWidget *domWidget = domUI->elementWidget(); + if (!domWidget) + return QString(); + // Properties: Remove the "objectName" property in favour of the name attribute and check geometry. + domWidget->setAttributeName(objectName); + const QString geometryProperty = QLatin1String("geometry"); + const QString objectNameProperty = QLatin1String("objectName"); + PropertyList properties = domWidget->elementProperty(); + for (PropertyList::iterator it = properties.begin(); it != properties.end(); ) { + DomProperty *property = *it; + if (property->attributeName() == objectNameProperty) { // remove "objectName" + it = properties.erase(it); + delete property; + } else { + if (property->attributeName() == geometryProperty) { // Make sure form is at least 400, 300 + if (DomRect *geom = property->elementRect()) { + if (geom->elementWidth() < NewFormWidth) + geom->setElementWidth(NewFormWidth); + if (geom->elementHeight() < NewFormHeight) + geom->setElementHeight(NewFormHeight); + } + } + ++it; + } + } + // Add a window title property + DomString *windowTitleString = new DomString; + windowTitleString->setText(objectName); + DomProperty *windowTitleProperty = new DomProperty; + windowTitleProperty->setAttributeName(QLatin1String("windowTitle")); + windowTitleProperty->setElementString(windowTitleString); + properties.push_back(windowTitleProperty); + // ------ + domWidget->setElementProperty(properties); + // Embed in in DomUI and get string. Omit the version number. + domUI->setElementClass(objectName); + + QString rc; + { // Serialize domUI + QXmlStreamWriter writer(&rc); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + domUI->write(writer); + writer.writeEndDocument(); + } + delete domUI; + return rc; +} + +// Generate default standard ui new form xml based on the class passed on as similarClassName. +static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name) +{ + QString rc; { + QTextStream str(&rc); + str << QLatin1String("\n") << name << QLatin1String("\n") + << QLatin1String("\n") + << QLatin1String("\n00") + << NewFormWidth << QLatin1String("") << NewFormHeight << QLatin1String("\n\n"); + str << QLatin1String("\n") << name << QLatin1String("\n\n"); + + if (similarClassName == QLatin1String("QMainWindow")) { + str << QLatin1String("\n"); + } else { + if (similarClassName == QLatin1String("QWizard")) + str << QLatin1String("\n"); + } + str << QLatin1String("\n\n"); + } + return rc; +} + +// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses(). +QString WidgetDataBase::formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) +{ + // How to find suitable XML for a class: + // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there). + const QString widgetBoxXml = xmlFromWidgetBox(core, className, objectName); + if (!widgetBoxXml.isEmpty()) + return widgetBoxXml; + // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should + // be left over. Generate something that is similar to the default templates. Find a similar class. + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + QString similarClass = QLatin1String("QWidget"); + const int index = wdb->indexOfClassName(className); + if (index != -1) { + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(index); + similarClass = item->isCustom() ? item->extends() : item->name(); + } + // Generate standard ui based on the class passed on as baseClassName. + const QString rc = generateNewFormXML(className, similarClass, objectName); + return rc; +} + +// Set a fixed size on a XML template +QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed) +{ + typedef QList PropertyList; + DomUI *domUI = QDesignerWidgetBox::xmlToUi(QLatin1String("Form"), xml, false); + if (!domUI) + return QString(); + DomWidget *domWidget = domUI->elementWidget(); + if (!domWidget) + return QString(); + // Properties: Find/Ensure the geometry, minimum and maximum sizes properties + const QString geometryPropertyName = QLatin1String("geometry"); + const QString minimumSizePropertyName = QLatin1String("minimumSize"); + const QString maximumSizePropertyName = QLatin1String("maximumSize"); + DomProperty *geomProperty = 0; + DomProperty *minimumSizeProperty = 0; + DomProperty *maximumSizeProperty = 0; + + PropertyList properties = domWidget->elementProperty(); + const PropertyList::const_iterator cend = properties.constEnd(); + for (PropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const QString name = (*it)->attributeName(); + if (name == geometryPropertyName) { + geomProperty = *it; + } else { + if (name == minimumSizePropertyName) { + minimumSizeProperty = *it; + } else { + if (name == maximumSizePropertyName) + maximumSizeProperty = *it; + } + } + } + if (!geomProperty) { + geomProperty = new DomProperty; + geomProperty->setAttributeName(geometryPropertyName); + geomProperty->setElementRect(new DomRect); + properties.push_front(geomProperty); + } + if (fixed) { + if (!minimumSizeProperty) { + minimumSizeProperty = new DomProperty; + minimumSizeProperty->setAttributeName(minimumSizePropertyName); + minimumSizeProperty->setElementSize(new DomSize); + properties.push_back(minimumSizeProperty); + } + if (!maximumSizeProperty) { + maximumSizeProperty = new DomProperty; + maximumSizeProperty->setAttributeName(maximumSizePropertyName); + maximumSizeProperty->setElementSize(new DomSize); + properties.push_back(maximumSizeProperty); + } + } + // Set values of geometry, minimum and maximum sizes properties + const int width = size.width(); + const int height = size.height(); + if (DomRect *geom = geomProperty->elementRect()) { + geom->setElementWidth(width); + geom->setElementHeight(height); + } + if (fixed) { + if (DomSize *s = minimumSizeProperty->elementSize()) { + s->setElementWidth(width); + s->setElementHeight(height); + } + if (DomSize *s = maximumSizeProperty->elementSize()) { + s->setElementWidth(width); + s->setElementHeight(height); + } + } + // write back + domWidget->setElementProperty(properties); + + QString rc; + { // serialize domUI + QXmlStreamWriter writer(&rc); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + domUI->write(writer); + writer.writeEndDocument(); + } + + delete domUI; + return rc; +} + +// ---- free functions +QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile) +{ + const bool global = !includeFile.isEmpty() && + includeFile[0] == QLatin1Char('<') && + includeFile[includeFile.size() - 1] == QLatin1Char('>'); + if (global) { + includeFile.remove(includeFile.size() - 1, 1); + includeFile.remove(0, 1); + } + return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal); +} + +QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) { + if (includeType == IncludeGlobal && !includeFile.isEmpty()) { + includeFile.append(QLatin1Char('>')); + includeFile.insert(0, QLatin1Char('<')); + } + return includeFile; +} + + +/* Appends a derived class to the database inheriting the data of the base class. Used + for custom and promoted widgets. + + Depending on whether an entry exists, the existing or a newly created entry is + returned. A return value of 0 indicates that the base class could not be found. */ + +QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface * + appendDerived(QDesignerWidgetDataBaseInterface *db, + const QString &className, const QString &group, + const QString &baseClassName, + const QString &includeFile, + bool promoted, bool custom) +{ + if (debugWidgetDataBase) + qDebug() << "appendDerived " << className << " derived from " << baseClassName; + // Check. + if (className.isEmpty() || baseClassName.isEmpty()) { + qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.", + Q_FUNC_INFO, className.toUtf8().constData(), baseClassName.toUtf8().constData()); + return 0; + } + // Check whether item already exists. + QDesignerWidgetDataBaseItemInterface *derivedItem = 0; + const int existingIndex = db->indexOfClassName(className); + if ( existingIndex != -1) + derivedItem = db->item(existingIndex); + if (derivedItem) { + // Check the existing item for base class mismatch. This will likely + // happen when loading a file written by an instance with missing plugins. + // In that case, just warn and ignore the file properties. + // + // An empty base class indicates that it is not known (for example, for custom plugins). + // In this case, the widget DB is later updated once the widget is created + // by DOM (by querying the metaobject). Suppress the warning. + const QString existingBaseClass = derivedItem->extends(); + if (existingBaseClass.isEmpty() || baseClassName == existingBaseClass) + return derivedItem; + + // Warn about mismatches + designerWarning(QCoreApplication::translate("WidgetDataBase", + "The file contains a custom widget '%1' whose base class (%2)" + " differs from the current entry in the widget database (%3)." + " The widget database is left unchanged."). + arg(className, baseClassName, existingBaseClass)); + return derivedItem; + } + // Create this item, inheriting its base properties + const int baseIndex = db->indexOfClassName(baseClassName); + if (baseIndex == -1) { + if (debugWidgetDataBase) + qDebug() << "appendDerived failed due to missing base class"; + return 0; + } + const QDesignerWidgetDataBaseItemInterface *baseItem = db->item(baseIndex); + derivedItem = WidgetDataBaseItem::clone(baseItem); + // Sort of hack: If base class is QWidget, we most likely + // do not want to inherit the container attribute. + static const QString qWidgetName = QLatin1String("QWidget"); + if (baseItem->name() == qWidgetName) + derivedItem->setContainer(false); + // set new props + derivedItem->setName(className); + derivedItem->setGroup(group); + derivedItem->setCustom(custom); + derivedItem->setPromoted(promoted); + derivedItem->setExtends(baseClassName); + derivedItem->setIncludeFile(includeFile); + db->append(derivedItem); + return derivedItem; +} + +/* Return a list of database items to which a class can be promoted to. */ + +QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList + promotionCandidates(const QDesignerWidgetDataBaseInterface *db, + const QString &baseClassName) +{ + WidgetDataBaseItemList rc; + // find existing promoted widgets deriving from base. + const int count = db->count(); + for (int i = 0; i < count; ++i) { + QDesignerWidgetDataBaseItemInterface *item = db->item(i); + if (item->isPromoted() && item->extends() == baseClassName) { + rc.push_back(item); + } + } + return rc; +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/widgetdatabase_p.h b/designer/lib/shared/widgetdatabase_p.h new file mode 100644 index 0000000..3d9b96c --- /dev/null +++ b/designer/lib/shared/widgetdatabase_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef WIDGETDATABASE_H +#define WIDGETDATABASE_H + +#include "shared_global_p.h" + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QDesignerCustomWidgetInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT WidgetDataBaseItem: public QDesignerWidgetDataBaseItemInterface +{ +public: + explicit WidgetDataBaseItem(const QString &name = QString(), + const QString &group = QString()); + + QString name() const; + void setName(const QString &name); + + QString group() const; + void setGroup(const QString &group); + + QString toolTip() const; + void setToolTip(const QString &toolTip); + + QString whatsThis() const; + void setWhatsThis(const QString &whatsThis); + + QString includeFile() const; + void setIncludeFile(const QString &includeFile); + + + QIcon icon() const; + void setIcon(const QIcon &icon); + + bool isCompat() const; + void setCompat(bool compat); + + bool isContainer() const; + void setContainer(bool b); + + bool isCustom() const; + void setCustom(bool b); + + QString pluginPath() const; + void setPluginPath(const QString &path); + + bool isPromoted() const; + void setPromoted(bool b); + + QString extends() const; + void setExtends(const QString &s); + + void setDefaultPropertyValues(const QList &list); + QList defaultPropertyValues() const; + + static WidgetDataBaseItem *clone(const QDesignerWidgetDataBaseItemInterface *item); + + QStringList fakeSlots() const; + void setFakeSlots(const QStringList &); + + QStringList fakeSignals() const; + void setFakeSignals(const QStringList &); + + QString addPageMethod() const; + void setAddPageMethod(const QString &m); + +private: + QString m_name; + QString m_group; + QString m_toolTip; + QString m_whatsThis; + QString m_includeFile; + QString m_pluginPath; + QString m_extends; + QString m_addPageMethod; + QIcon m_icon; + uint m_compat: 1; + uint m_container: 1; + uint m_form: 1; + uint m_custom: 1; + uint m_promoted: 1; + QList m_defaultPropertyValues; + QStringList m_fakeSlots; + QStringList m_fakeSignals; +}; + +enum IncludeType { IncludeLocal, IncludeGlobal }; + +typedef QPair IncludeSpecification; + +QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile); +QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType); + +class QDESIGNER_SHARED_EXPORT WidgetDataBase: public QDesignerWidgetDataBaseInterface +{ + Q_OBJECT +public: + explicit WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~WidgetDataBase(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual int indexOfObject(QObject *o, bool resolveName = true) const; + + void remove(int index); + + + void grabDefaultPropertyValues(); + void grabStandardWidgetBoxIcons(); + + // Helpers for 'New Form' wizards in integrations. Obtain a list of suitable classes and generate XML for them. + static QStringList formWidgetClasses(const QDesignerFormEditorInterface *core); + static QStringList customFormWidgetClasses(const QDesignerFormEditorInterface *core); + static QString formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName); + + // Helpers for 'New Form' wizards: Set a fixed size on a XML form template + static QString scaleFormTemplate(const QString &xml, const QSize &size, bool fixed); + +public slots: + void loadPlugins(); + +private: + QList defaultPropertyValues(const QString &name); + + QDesignerFormEditorInterface *m_core; +}; + +QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface + *appendDerived(QDesignerWidgetDataBaseInterface *db, + const QString &className, + const QString &group, + const QString &baseClassName, + const QString &includeFile, + bool promoted, + bool custom); + +typedef QList WidgetDataBaseItemList; + +QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList + promotionCandidates(const QDesignerWidgetDataBaseInterface *db, + const QString &baseClassName); +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETDATABASE_H diff --git a/designer/lib/shared/widgetfactory.cpp b/designer/lib/shared/widgetfactory.cpp new file mode 100644 index 0000000..0bd5dfd --- /dev/null +++ b/designer/lib/shared/widgetfactory.cpp @@ -0,0 +1,897 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetfactory_p.h" +#include "widgetdatabase_p.h" +#include "metadatabase_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_widget_p.h" +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_toolbox_p.h" +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_dockwidget_p.h" +#include "qdesigner_utils_p.h" +#include "formwindowbase_p.h" + +// shared +#include "layoutinfo_p.h" +#include "spacer_widget_p.h" +#include "layout_p.h" +#include "abstractintrospection_p.h" + +// sdk +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +static inline bool isAxWidget(const QObject *o) +{ + // Is it one of QDesignerAxWidget/QDesignerAxPluginWidget? + static const char *axWidgetName = "QDesignerAx"; + static const unsigned axWidgetNameLen = qstrlen(axWidgetName); + return qstrncmp(o->metaObject()->className(), axWidgetName, axWidgetNameLen) == 0; +} +#endif + +/* Dynamic boolean property indicating object was created by the factory + * for the form editor. */ + +static const char *formEditorDynamicProperty = "_q_formEditorObject"; + +namespace qdesigner_internal { + +// A friendly SpinBox that grants access to its QLineEdit +class FriendlySpinBox : public QAbstractSpinBox { +public: + friend class WidgetFactory; +}; + +// An event filter for form-combo boxes that prevents the embedded line edit +// from getting edit focus (and drawing blue artifacts/lines). It catches the +// ChildPolished event when the "editable" property flips to true and the +// QLineEdit is created and turns off the LineEdit's focus policy. + +class ComboEventFilter : public QObject { +public: + explicit ComboEventFilter(QComboBox *parent) : QObject(parent) {} + virtual bool eventFilter(QObject *watched, QEvent *event); +}; + +bool ComboEventFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::ChildPolished) { + QComboBox *cb = static_cast(watched); + if (QLineEdit *le = cb->lineEdit()) + le->setFocusPolicy(Qt::NoFocus); + } + return QObject::eventFilter(watched, event); +} + +/* Watch out for QWizards changing their pages and make sure that not some + * selected widget becomes invisible on a hidden page (causing the selection + * handles to shine through). Select the wizard in that case in analogy to + * the QTabWidget event filters, etc. */ + +class WizardPageChangeWatcher : public QObject { + Q_OBJECT +public: + explicit WizardPageChangeWatcher(QWizard *parent); + +public slots: + void pageChanged(); +}; + +WizardPageChangeWatcher::WizardPageChangeWatcher(QWizard *parent) : + QObject(parent) +{ + connect(parent, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged())); +} + +void WizardPageChangeWatcher::pageChanged() +{ + /* Use a bit more conservative approach than that for the QTabWidget, + * change the selection only if a selected child becomes invisible by + * changing the page. */ + QWizard *wizard = static_cast(parent()); + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(wizard); + if (!fw) + return; + QDesignerFormWindowCursorInterface *cursor = fw->cursor(); + const int selCount = cursor->selectedWidgetCount(); + for (int i = 0; i < selCount; i++) { + if (!cursor->selectedWidget(i)->isVisible()) { + fw->clearSelection(false); + fw->selectWidget(wizard, true); + break; + } + } +} + +// ---------------- WidgetFactory::Strings +WidgetFactory::Strings::Strings() : + m_alignment(QLatin1String("alignment")), + m_bottomMargin(QLatin1String("bottomMargin")), + m_geometry(QLatin1String("geometry")), + m_leftMargin(QLatin1String("leftMargin")), + m_line(QLatin1String("Line")), + m_objectName(QLatin1String("objectName")), + m_spacerName(QLatin1String("spacerName")), + m_orientation(QLatin1String("orientation")), + m_q3WidgetStack(QLatin1String("Q3WidgetStack")), + m_qAction(QLatin1String("QAction")), + m_qButtonGroup(QLatin1String("QButtonGroup")), + m_qAxWidget(QLatin1String("QAxWidget")), + m_qDialog(QLatin1String("QDialog")), + m_qDockWidget(QLatin1String("QDockWidget")), + m_qLayoutWidget(QLatin1String("QLayoutWidget")), + m_qMenu(QLatin1String("QMenu")), + m_qMenuBar(QLatin1String("QMenuBar")), + m_qWidget(QLatin1String("QWidget")), + m_rightMargin(QLatin1String("rightMargin")), + m_sizeHint(QLatin1String("sizeHint")), + m_spacer(QLatin1String("Spacer")), + m_text(QLatin1String("text")), + m_title(QLatin1String("title")), + m_topMargin(QLatin1String("topMargin")), + m_windowIcon(QLatin1String("windowIcon")), + m_windowTitle(QLatin1String("windowTitle")) +{ +} +// ---------------- WidgetFactory +QPointer *WidgetFactory::m_lastPassiveInteractor = new QPointer(); +bool WidgetFactory::m_lastWasAPassiveInteractor = false; +const char *WidgetFactory::disableStyleCustomPaintingPropertyC = "_q_custom_style_disabled"; + +WidgetFactory::WidgetFactory(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerWidgetFactoryInterface(parent), + m_core(core), + m_formWindow(0), + m_currentStyle(0) +{ +} + +WidgetFactory::~WidgetFactory() +{ +} + +QDesignerFormWindowInterface *WidgetFactory::currentFormWindow(QDesignerFormWindowInterface *fw) +{ + QDesignerFormWindowInterface *was = m_formWindow; + m_formWindow = fw; + return was; +} + +void WidgetFactory::loadPlugins() +{ + m_customFactory.clear(); + + QDesignerPluginManager *pluginManager = m_core->pluginManager(); + + QList lst = pluginManager->registeredCustomWidgets(); + foreach (QDesignerCustomWidgetInterface *c, lst) { + m_customFactory.insert(c->name(), c); + } +} + +// Convencience to create non-widget objects. Returns 0 if unknown +QObject* WidgetFactory::createObject(const QString &className, QObject* parent) const +{ + if (className.isEmpty()) { + qWarning("** WARNING %s called with an empty class name", Q_FUNC_INFO); + return 0; + } + if (className == m_strings.m_qAction) + return new QAction(parent); + if (className == m_strings.m_qButtonGroup) + return new QButtonGroup(parent); + return 0; +} + +QWidget* WidgetFactory::createCustomWidget(const QString &className, QWidget *parentWidget, bool *creationError) const +{ + *creationError = false; + CustomWidgetFactoryMap::const_iterator it = m_customFactory.constFind(className); + if (it == m_customFactory.constEnd()) + return 0; + + QDesignerCustomWidgetInterface *factory = it.value(); + QWidget *rc = factory->createWidget(parentWidget); + // shouldn't happen + if (!rc) { + *creationError = true; + designerWarning(tr("The custom widget factory registered for widgets of class %1 returned 0.").arg(className)); + return 0; + } + // Figure out the base class unless it is known + static QSet knownCustomClasses; + if (!knownCustomClasses.contains(className)) { + QDesignerWidgetDataBaseInterface *wdb = m_core->widgetDataBase(); + const int widgetInfoIndex = wdb->indexOfObject(rc, false); + if (widgetInfoIndex != -1) { + if (wdb->item(widgetInfoIndex)->extends().isEmpty()) { + const QDesignerMetaObjectInterface *mo = core()->introspection()->metaObject(rc)->superClass(); + // If we hit on a 'Q3DesignerXXWidget' that claims to be a 'Q3XXWidget', step + // over. + if (mo && mo->className() == className) + mo = mo->superClass(); + while (mo != 0) { + if (core()->widgetDataBase()->indexOfClassName(mo->className()) != -1) { + wdb->item(widgetInfoIndex)->setExtends(mo->className()); + break; + } + mo = mo->superClass(); + } + } + knownCustomClasses.insert(className); + } + } + // Since a language plugin may lie about its names, like Qt Jambi + // does, return immediately here... + QDesignerLanguageExtension *lang = + qt_extension(m_core->extensionManager(), m_core); + if (lang) + return rc; + +#ifdef Q_OS_WIN + if (isAxWidget(rc)) + return rc; +#endif + // Check for mismatched class names which is hard to track. + // Perform literal comparison first for QAxWidget, for which a meta object hack is in effect. + const char *createdClassNameC = rc->metaObject()->className(); + const QByteArray classNameB = className.toUtf8(); + const char *classNameC = classNameB.constData(); + + if (qstrcmp(createdClassNameC, classNameC) && !rc->inherits(classNameC)) + designerWarning(tr("A class name mismatch occurred when creating a widget using the custom widget factory registered for widgets of class %1." + " It returned a widget of class %2.").arg(className).arg(QString::fromUtf8(createdClassNameC))); + return rc; +} + + +QWidget *WidgetFactory::createWidget(const QString &widgetName, QWidget *parentWidget) const +{ + if (widgetName.isEmpty()) { + qWarning("** WARNING %s called with an empty class name", Q_FUNC_INFO); + return 0; + } + // Preview or for form window? + QDesignerFormWindowInterface *fw = m_formWindow; + if (! fw) + fw = QDesignerFormWindowInterface::findFormWindow(parentWidget); + + QWidget *w = 0; + do { + // 1) custom. If there is an explicit failure(factory wants to indicate something is wrong), + // return 0, do not try to find fallback, which might be worse in the case of Q3 widget. + bool customWidgetCreationError; + w = createCustomWidget(widgetName, parentWidget, &customWidgetCreationError); + if (w) { + break; + } else { + if (customWidgetCreationError) + return 0; + } + + // 2) Special widgets + if (widgetName == m_strings.m_line) { + w = new Line(parentWidget); + } else if (widgetName == m_strings.m_qDockWidget) { + w = new QDesignerDockWidget(parentWidget); + } else if (widgetName == m_strings.m_qMenuBar) { + w = new QDesignerMenuBar(parentWidget); + } else if (widgetName == m_strings.m_qMenu) { + w = new QDesignerMenu(parentWidget); + } else if (widgetName == m_strings.m_spacer) { + w = new Spacer(parentWidget); + } else if (widgetName == m_strings.m_qDockWidget) { + w = new QDesignerDockWidget(parentWidget); + } else if (widgetName == m_strings.m_qLayoutWidget) { + w = fw ? new QLayoutWidget(fw, parentWidget) : new QWidget(parentWidget); + } else if (widgetName == m_strings.m_qDialog) { + if (fw) { + w = new QDesignerDialog(fw, parentWidget); + } else { + w = new QDialog(parentWidget); + } + } else if (widgetName == m_strings.m_qWidget) { + /* We want a 'QDesignerWidget' that draws a grid only for widget + * forms and container extension pages (not for preview and not + * for normal QWidget children on forms (legacy) */ + if (fw && parentWidget) { + if (qt_extension(m_core->extensionManager(), parentWidget)) { + w = new QDesignerWidget(fw, parentWidget); + } else { + if (const FormWindowBase *fwb = qobject_cast(fw)) + if (parentWidget == fwb->formContainer()) + w = new QDesignerWidget(fw, parentWidget); + } + } + if (!w) + w = new QWidget(parentWidget); + } + if (w) + break; + + // 3) table + const QByteArray widgetNameBA = widgetName.toUtf8(); + const char *widgetNameC = widgetNameBA.constData(); + + if (w) { // symmetry for macro + } + +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) /*DECLARE_WIDGET(W, C)*/ +#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); } +#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); } + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + if (w) + break; + // 4) fallBack + const QString fallBackBaseClass = m_strings.m_qWidget; + QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfClassName(widgetName)); + if (item == 0) { + // Emergency: Create, derived from QWidget + QString includeFile = widgetName.toLower(); + includeFile += QLatin1String(".h"); + item = appendDerived(db,widgetName, tr("%1 Widget").arg(widgetName),fallBackBaseClass, + includeFile, true, true); + Q_ASSERT(item); + } + QString baseClass = item->extends(); + if (baseClass.isEmpty()) { + // Currently happens in the case of Q3-Support widgets + baseClass =fallBackBaseClass; + } + w = createWidget(baseClass, parentWidget); + promoteWidget(core(),w,widgetName); + } while (false); + + Q_ASSERT(w != 0); + if (m_currentStyle) + w->setStyle(m_currentStyle); + initializeCommon(w); + if (fw) { // form editor initialization + initialize(w); + } else { // preview-only initialization + initializePreview(w); + } + return w; +} + +QString WidgetFactory::classNameOf(QDesignerFormEditorInterface *c, const QObject* o) +{ + if (o == 0) + return QString(); + + const char *className = o->metaObject()->className(); + if (!o->isWidgetType()) + return QLatin1String(className); + const QWidget *w = static_cast(o); + // check promoted before designer special + const QString customClassName = promotedCustomClassName(c, const_cast(w)); + if (!customClassName.isEmpty()) + return customClassName; + if (qobject_cast(w)) + return QLatin1String("QMenuBar"); + else if (qobject_cast(w)) + return QLatin1String("QMenu"); + else if (qobject_cast(w)) + return QLatin1String("QDockWidget"); + else if (qobject_cast(w)) + return QLatin1String("QDialog"); + else if (qobject_cast(w)) + return QLatin1String("QWidget"); +#ifdef Q_OS_WIN + else if (isAxWidget(w)) + return QLatin1String("QAxWidget"); +#endif + else if (qstrcmp(className, "QDesignerQ3WidgetStack") == 0) + return QLatin1String("Q3WidgetStack"); + + return QLatin1String(className); +} + +QLayout *WidgetFactory::createUnmanagedLayout(QWidget *parentWidget, int type) +{ + switch (type) { + case LayoutInfo::HBox: + return new QHBoxLayout(parentWidget); + case LayoutInfo::VBox: + return new QVBoxLayout(parentWidget); + case LayoutInfo::Grid: + return new QGridLayout(parentWidget); + case LayoutInfo::Form: + return new QFormLayout(parentWidget); + default: + Q_ASSERT(0); + break; + } + return 0; +} + + +/*! Creates a layout on the widget \a widget of the type \a type + which can be \c HBox, \c VBox or \c Grid. +*/ + +QLayout *WidgetFactory::createLayout(QWidget *widget, QLayout *parentLayout, int type) const // ### (sizepolicy) +{ + QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase(); + + if (parentLayout == 0) { + QWidget *page = containerOfWidget(widget); + if (page) { + widget = page; + } else { + const QString msg = tr("The current page of the container '%1' (%2) could not be determined while creating a layout." +"This indicates an inconsistency in the ui-file, probably a layout being constructed on a container widget.").arg(widget->objectName()).arg(classNameOf(core(), widget)); + designerWarning(msg); + } + } + + Q_ASSERT(metaDataBase->item(widget) != 0); // ensure the widget is managed + + if (parentLayout == 0 && metaDataBase->item(widget->layout()) == 0) { + parentLayout = widget->layout(); + } + + QWidget *parentWidget = parentLayout != 0 ? 0 : widget; + + QLayout *layout = createUnmanagedLayout(parentWidget, type); + metaDataBase->add(layout); // add the layout in the MetaDataBase + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), layout); + + sheet->setChanged(sheet->indexOf(m_strings.m_objectName), true); + if (widget->inherits("Q3GroupBox")) { + layout->setContentsMargins(widget->style()->pixelMetric(QStyle::PM_LayoutLeftMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutTopMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutRightMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); + QGridLayout *grid = qobject_cast(layout); + if (grid) { + grid->setHorizontalSpacing(-1); + grid->setVerticalSpacing(-1); + } else { + layout->setSpacing(-1); + } + layout->setAlignment(Qt::AlignTop); + // Just to ensure; before 4.3 orientation property was always set (now only for QSplitter class). + // Calling Q3GroupBox::setOrientation() invoked in turn setSpacing(0). Below fixes that + widget->layout()->setSpacing(-1); + } else if (widget->inherits("QLayoutWidget")) { + sheet->setProperty(sheet->indexOf(m_strings.m_leftMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_topMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_rightMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_bottomMargin), 0); + } + + if (sheet) { + const int index = sheet->indexOf(m_strings.m_alignment); + if (index != -1) + sheet->setChanged(index, true); + } + + if (metaDataBase->item(widget->layout()) == 0) { + Q_ASSERT(layout->parent() == 0); + QBoxLayout *box = qobject_cast(widget->layout()); + if (!box) { // we support only unmanaged box layouts + const QString msg = tr("Attempt to add a layout to a widget '%1' (%2) which already has an unmanaged layout of type %3.\n" + "This indicates an inconsistency in the ui-file."). + arg(widget->objectName()).arg(classNameOf(core(), widget)).arg(classNameOf(core(), widget->layout())); + designerWarning(msg); + return 0; + } + box->addLayout(layout); + } + + return layout; +} + +/*! Returns the widget into which children should be inserted when \a + w is a container known to designer. + + Usually, it is \a w itself, but there are exceptions (for example, a + tabwidget is known to designer as a container, but the child + widgets should be inserted into the current page of the + tabwidget. In this case, the current page of + the tabwidget would be returned.) + */ +QWidget* WidgetFactory::containerOfWidget(QWidget *w) const +{ + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), w)) + return container->widget(container->currentIndex()); + + return w; +} + +/*! Returns the actual designer widget of the container \a w. This is + normally \a w itself, but it might be a parent or grand parent of \a w + (for example, when working with a tabwidget and \a w is the container which + contains and layouts children, but the actual widget known to + designer is the tabwidget which is the parent of \a w. In this case, + the tabwidget would be returned.) +*/ + +QWidget* WidgetFactory::widgetOfContainer(QWidget *w) const +{ + // ### cleanup + if (!w) + return 0; + if (w->parentWidget() && w->parentWidget()->parentWidget() && + w->parentWidget()->parentWidget()->parentWidget() && + qobject_cast(w->parentWidget()->parentWidget()->parentWidget())) + return w->parentWidget()->parentWidget()->parentWidget(); + + while (w != 0) { + if (core()->widgetDataBase()->isContainer(w) || + (w && qobject_cast(w->parentWidget()))) + return w; + + w = w->parentWidget(); + } + + return w; +} + +QDesignerFormEditorInterface *WidgetFactory::core() const +{ + return m_core; +} + +// Necessary initializations for form editor/preview objects +void WidgetFactory::initializeCommon(QWidget *widget) const +{ + // Apply style + if (m_currentStyle) + widget->setStyle(m_currentStyle); + // Prevent the wizard from emulating the Windows Vista Theme. + // This theme (in both Aero and Basic mode) is tricky to + // emulate properly in designer due to 1) the manipulation of the non-client area of + // the top-level window, and 2) the upper-right location of the Back button. + // The wizard falls back to QWizard::ModernStyle whenever the Vista theme + // would normally apply. + if (QWizard *wizard = qobject_cast(widget)) { + wizard->setProperty("_q_wizard_vista_off", QVariant(true)); + return; + } +} + +// Necessary initializations for preview objects +void WidgetFactory::initializePreview(QWidget *widget) const +{ + + if (QStackedWidget *stackedWidget = qobject_cast(widget)) { + QStackedWidgetPreviewEventFilter::install(stackedWidget); // Add browse button only. + return; + } +} + +// Necessary initializations for form editor objects +void WidgetFactory::initialize(QObject *object) const +{ + // Indicate that this is a form object (for QDesignerFormWindowInterface::findFormWindow) + object->setProperty(formEditorDynamicProperty, QVariant(true)); + QDesignerPropertySheetExtension *sheet = qt_extension(m_core->extensionManager(), object); + if (!sheet) + return; + + sheet->setChanged(sheet->indexOf(m_strings.m_objectName), true); + + if (!object->isWidgetType()) { + if (qobject_cast(object)) + sheet->setChanged(sheet->indexOf(m_strings.m_text), true); + return; + } + + QWidget *widget = static_cast(object); + const bool isMenu = qobject_cast(widget); + const bool isMenuBar = !isMenu && qobject_cast(widget); + + widget->setAttribute(Qt::WA_TransparentForMouseEvents, false); + widget->setFocusPolicy((isMenu || isMenuBar) ? Qt::StrongFocus : Qt::NoFocus); + + if (!isMenu) + sheet->setChanged(sheet->indexOf(m_strings.m_geometry), true); + + if (qobject_cast(widget)) { + sheet->setChanged(sheet->indexOf(m_strings.m_spacerName), true); + return; + } + + const int o = sheet->indexOf(m_strings.m_orientation); + if (o != -1 && widget->inherits("QSplitter")) + sheet->setChanged(o, true); + + if (QToolBar *toolBar = qobject_cast(widget)) { + ToolBarEventFilter::install(toolBar); + sheet->setVisible(sheet->indexOf(m_strings.m_windowTitle), true); + toolBar->setFloatable(false); // prevent toolbars from being dragged off + return; + } + + if (qobject_cast(widget)) { + sheet->setVisible(sheet->indexOf(m_strings.m_windowTitle), true); + sheet->setVisible(sheet->indexOf(m_strings.m_windowIcon), true); + return; + } + + if (isMenu) { + sheet->setChanged(sheet->indexOf(m_strings.m_title), true); + return; + } + // helpers + if (QToolBox *toolBox = qobject_cast(widget)) { + QToolBoxHelper::install(toolBox); + return; + } + if (QStackedWidget *stackedWidget = qobject_cast(widget)) { + QStackedWidgetEventFilter::install(stackedWidget); + return; + } + if (QTabWidget *tabWidget = qobject_cast(widget)) { + QTabWidgetEventFilter::install(tabWidget); + return; + } + // Prevent embedded line edits from getting focus + if (QAbstractSpinBox *asb = qobject_cast(widget)) { + if (QLineEdit *lineEdit = static_cast(asb)->lineEdit()) + lineEdit->setFocusPolicy(Qt::NoFocus); + return; + } + if (QComboBox *cb = qobject_cast(widget)) { + if (QFontComboBox *fcb = qobject_cast(widget)) { + fcb->lineEdit()->setFocusPolicy(Qt::NoFocus); // Always present + return; + } + cb->installEventFilter(new ComboEventFilter(cb)); + return; + } + if (QWizard *wz = qobject_cast(widget)) { + WizardPageChangeWatcher *pw = new WizardPageChangeWatcher(wz); + Q_UNUSED(pw); + } +} + +static inline QString classNameOfStyle(const QStyle *s) +{ + return QLatin1String(s->metaObject()->className()); +} + +QString WidgetFactory::styleName() const +{ + return classNameOfStyle(style()); +} + +static inline bool isApplicationStyle(const QString &styleName) +{ + return styleName.isEmpty() || styleName == classNameOfStyle(qApp->style()); +} + +void WidgetFactory::setStyleName(const QString &styleName) +{ + m_currentStyle = isApplicationStyle(styleName) ? static_cast(0) : getStyle(styleName); +} + +QStyle *WidgetFactory::style() const +{ + return m_currentStyle ? m_currentStyle : qApp->style(); +} + +QStyle *WidgetFactory::getStyle(const QString &styleName) +{ + if (isApplicationStyle(styleName)) + return qApp->style(); + + StyleCache::iterator it = m_styleCache.find(styleName); + if (it == m_styleCache.end()) { + QStyle *style = QStyleFactory::create(styleName); + if (!style) { + const QString msg = tr("Cannot create style '%1'.").arg(styleName); + designerWarning(msg); + return 0; + } + it = m_styleCache.insert(styleName, style); + } + return it.value(); +} + +void WidgetFactory::applyStyleTopLevel(const QString &styleName, QWidget *w) +{ + if (QStyle *style = getStyle(styleName)) + applyStyleToTopLevel(style, w); +} + +void WidgetFactory::applyStyleToTopLevel(QStyle *style, QWidget *widget) +{ + if (!style) + return; + const QPalette standardPalette = style->standardPalette(); + if (widget->style() == style && widget->palette() == standardPalette) + return; + + widget->setStyle(style); + widget->setPalette(standardPalette); + const QWidgetList lst = qFindChildren(widget); + const QWidgetList::const_iterator cend = lst.constEnd(); + for (QWidgetList::const_iterator it = lst.constBegin(); it != cend; ++it) + (*it)->setStyle(style); +} + +// Check for 'interactor' click on a tab bar, +// which can appear within a QTabWidget or as a standalone widget. + +static bool isTabBarInteractor(const QTabBar *tabBar) +{ + // Tabbar embedded in Q(Designer)TabWidget, ie, normal tab widget case + if (qobject_cast(tabBar->parentWidget())) + return true; + + // Standalone tab bar on the form. Return true for tab rect areas + // only to allow the user to select the tab bar by clicking outside the actual tabs. + const int count = tabBar->count(); + if (count == 0) + return false; + + // click into current tab: No Interaction + const int currentIndex = tabBar->currentIndex(); + const QPoint pos = tabBar->mapFromGlobal(QCursor::pos()); + if (tabBar->tabRect(currentIndex).contains(pos)) + return false; + + // click outside: No Interaction + const QRect geometry = QRect(QPoint(0, 0), tabBar->size()); + if (!geometry.contains(pos)) + return false; + // click into another tab: Let's interact, switch tabs. + for (int i = 0; i < count; i++) + if (tabBar->tabRect(i).contains(pos)) + return true; + return false; +} + +bool WidgetFactory::isPassiveInteractor(QWidget *widget) +{ + static const QString qtPassive = QLatin1String("__qt__passive_"); + if (m_lastPassiveInteractor != 0 && (QWidget*)(*m_lastPassiveInteractor) == widget) + return m_lastWasAPassiveInteractor; + + if (QApplication::activePopupWidget() || widget == 0) // if a popup is open, we have to make sure that this one is closed, else X might do funny things + return true; + + m_lastWasAPassiveInteractor = false; + (*m_lastPassiveInteractor) = widget; + + if (const QTabBar *tabBar = qobject_cast(widget)) { + if (isTabBarInteractor(tabBar)) + m_lastWasAPassiveInteractor = true; + return m_lastWasAPassiveInteractor; +#ifndef QT_NO_SIZEGRIP + } else if (qobject_cast(widget)) { + return (m_lastWasAPassiveInteractor = true); +#endif + } else if (qobject_cast(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast(widget) && (qobject_cast(widget->parent()) || qobject_cast(widget->parent()))) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast(widget)) { + // A scroll bar is an interactor on a QAbstractScrollArea only. + if (const QWidget *parent = widget->parentWidget()) { + const QString objectName = parent->objectName(); + static const QString scrollAreaVContainer = QLatin1String("qt_scrollarea_vcontainer"); + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + void formWindowAdded(QDesignerFormWindowInterface *formWindow); + static const QString scrollAreaHContainer = QLatin1String("qt_scrollarea_hcontainer"); + if (objectName == scrollAreaVContainer || objectName == scrollAreaHContainer) { + m_lastWasAPassiveInteractor = true; + return m_lastWasAPassiveInteractor; + } + } + } else if (qstrcmp(widget->metaObject()->className(), "QDockWidgetTitle") == 0) + return (m_lastWasAPassiveInteractor = true); + else if (qstrcmp(widget->metaObject()->className(), "QWorkspaceTitleBar") == 0) + return (m_lastWasAPassiveInteractor = true); + else if (widget->objectName().startsWith(qtPassive)) + return (m_lastWasAPassiveInteractor = true); + return m_lastWasAPassiveInteractor; +} + +void WidgetFactory::formWindowAdded(QDesignerFormWindowInterface *formWindow) +{ + setFormWindowStyle(formWindow); +} + +void WidgetFactory::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + setFormWindowStyle(formWindow); +} + +void WidgetFactory::setFormWindowStyle(QDesignerFormWindowInterface *formWindow) +{ + if (FormWindowBase *fwb = qobject_cast(formWindow)) + setStyleName(fwb->styleName()); +} + +bool WidgetFactory::isFormEditorObject(const QObject *object) +{ + return object->property(formEditorDynamicProperty).isValid(); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include "widgetfactory.moc" diff --git a/designer/lib/shared/widgetfactory_p.h b/designer/lib/shared/widgetfactory_p.h new file mode 100644 index 0000000..b44130f --- /dev/null +++ b/designer/lib/shared/widgetfactory_p.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef WIDGETFACTORY_H +#define WIDGETFACTORY_H + +#include "shared_global_p.h" +#include "pluginmanager_p.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QWidget; +class QLayout; +class QDesignerFormEditorInterface; +class QDesignerCustomWidgetInterface; +class QDesignerFormWindowInterface; +class QStyle; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT WidgetFactory: public QDesignerWidgetFactoryInterface +{ + Q_OBJECT +public: + explicit WidgetFactory(QDesignerFormEditorInterface *core, QObject *parent = 0); + ~WidgetFactory(); + + virtual QWidget* containerOfWidget(QWidget *widget) const; + virtual QWidget* widgetOfContainer(QWidget *widget) const; + + QObject* createObject(const QString &className, QObject* parent) const; + + virtual QWidget *createWidget(const QString &className, QWidget *parentWidget) const; + virtual QLayout *createLayout(QWidget *widget, QLayout *layout, int type) const; + + virtual bool isPassiveInteractor(QWidget *widget); + virtual void initialize(QObject *object) const; + void initializeCommon(QWidget *object) const; + void initializePreview(QWidget *object) const; + + + virtual QDesignerFormEditorInterface *core() const; + + static QString classNameOf(QDesignerFormEditorInterface *core, const QObject* o); + + QDesignerFormWindowInterface *currentFormWindow(QDesignerFormWindowInterface *fw); + + static QLayout *createUnmanagedLayout(QWidget *parentWidget, int type); + + // The widget factory maintains a cache of styles which it owns. + QString styleName() const; + void setStyleName(const QString &styleName); + + /* Return a cached style matching the name or QApplication's style if + * it is the default. */ + QStyle *getStyle(const QString &styleName); + // Return the current style used by the factory. This either a cached one + // or QApplication's style */ + QStyle *style() const; + + // Apply one of the cached styles or QApplication's style to a toplevel widget. + void applyStyleTopLevel(const QString &styleName, QWidget *w); + static void applyStyleToTopLevel(QStyle *style, QWidget *widget); + + // Return whether object was created by the factory for the form editor. + static bool isFormEditorObject(const QObject *o); + + // Boolean dynamic property to set on widgets to prevent custom + // styles from interfering + static const char *disableStyleCustomPaintingPropertyC; + +public slots: + void loadPlugins(); + +private slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + void formWindowAdded(QDesignerFormWindowInterface *formWindow); + +private: + struct Strings { // Reduce string allocations by storing predefined strings + Strings(); + const QString m_alignment; + const QString m_bottomMargin; + const QString m_geometry; + const QString m_leftMargin; + const QString m_line; + const QString m_objectName; + const QString m_spacerName; + const QString m_orientation; + const QString m_q3WidgetStack; + const QString m_qAction; + const QString m_qButtonGroup; + const QString m_qAxWidget; + const QString m_qDialog; + const QString m_qDockWidget; + const QString m_qLayoutWidget; + const QString m_qMenu; + const QString m_qMenuBar; + const QString m_qWidget; + const QString m_rightMargin; + const QString m_sizeHint; + const QString m_spacer; + const QString m_text; + const QString m_title; + const QString m_topMargin; + const QString m_windowIcon; + const QString m_windowTitle; + }; + + QWidget* createCustomWidget(const QString &className, QWidget *parentWidget, bool *creationError) const; + QDesignerFormWindowInterface *findFormWindow(QWidget *parentWidget) const; + void setFormWindowStyle(QDesignerFormWindowInterface *formWindow); + + const Strings m_strings; + QDesignerFormEditorInterface *m_core; + typedef QMap CustomWidgetFactoryMap; + CustomWidgetFactoryMap m_customFactory; + QDesignerFormWindowInterface *m_formWindow; + + // Points to the cached style or 0 if the default (qApp) is active + QStyle *m_currentStyle; + typedef QHash StyleCache; + StyleCache m_styleCache; + + static QPointer *m_lastPassiveInteractor; + static bool m_lastWasAPassiveInteractor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETFACTORY_H diff --git a/designer/lib/shared/zoomwidget.cpp b/designer/lib/shared/zoomwidget.cpp new file mode 100644 index 0000000..80261a3 --- /dev/null +++ b/designer/lib/shared/zoomwidget.cpp @@ -0,0 +1,570 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "zoomwidget_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QList ActionList; +typedef QList GraphicsItemList; + +enum { debugZoomWidget = 0 }; + +static const int menuZoomList[] = { 100, 25, 50, 75, 125, 150 , 175, 200 }; + +static inline QSize qCeiling(const QSizeF &s) +{ + return QSize(qCeil(s.width()), qCeil(s.height())); +} + +namespace qdesigner_internal { + +// ---------- ZoomMenu + +ZoomMenu::ZoomMenu(QObject *parent) : + QObject(parent), + m_menuActions(new QActionGroup(this)) +{ + connect(m_menuActions, SIGNAL(triggered(QAction*)), this, SLOT(slotZoomMenu(QAction*))); + const int nz = sizeof(menuZoomList)/sizeof(int); + for (int i = 0; i < nz; i++) { + const int zoom = menuZoomList[i]; + //: Zoom factor + QAction *a = m_menuActions->addAction(tr("%1 %").arg(zoom)); + a->setCheckable(true); + a->setData(QVariant(zoom)); + if (zoom == 100) + a->setChecked(true); + m_menuActions->addAction(a); + } +} + +int ZoomMenu::zoomOf(const QAction *a) +{ + return a->data().toInt(); +} + +void ZoomMenu::addActions(QMenu *m) +{ + const ActionList za = m_menuActions->actions(); + const ActionList::const_iterator cend = za.constEnd(); + for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) { + m->addAction(*it); + if (zoomOf(*it) == 100) + m->addSeparator(); + } +} + +int ZoomMenu::zoom() const +{ + return m_menuActions->checkedAction()->data().toInt(); +} + +void ZoomMenu::setZoom(int percent) +{ + const ActionList za = m_menuActions->actions(); + const ActionList::const_iterator cend = za.constEnd(); + for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) + if (zoomOf(*it) == percent) { + (*it)->setChecked(true); + return; + } +} + +void ZoomMenu::slotZoomMenu(QAction *a) +{ + emit zoomChanged(zoomOf(a)); +} + +QList ZoomMenu::zoomValues() +{ + QList rc; + const int nz = sizeof(menuZoomList)/sizeof(int); + for (int i = 0; i < nz; i++) + rc.push_back(menuZoomList[i]); + return rc; +} + +// --------- ZoomView +ZoomView::ZoomView(QWidget *parent) : + QGraphicsView(parent), + m_scene(new QGraphicsScene(this)), + m_zoom(100), + m_zoomFactor(1.0), + m_zoomContextMenuEnabled(false), + m_zoomMenu(0) +{ + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setFrameShape(QFrame::NoFrame); + setScene(m_scene); + if (debugZoomWidget) + qDebug() << "scene" << m_scene->sceneRect(); + +} + +int ZoomView::zoom() const +{ + return m_zoom; +} + +void ZoomView::scrollToOrigin() +{ + const QPoint origin(0 ,0); + const QPoint current = scrollPosition(); + if (current != origin) { + if (debugZoomWidget) + qDebug() << "ZoomView::scrollToOrigin from " << current; + setScrollPosition(origin); + } +} + +void ZoomView::setZoom(int percent) +{ + if (debugZoomWidget) + qDebug() << "ZoomView::setZoom" << percent; + + if (m_zoom == percent) + return; + + m_zoom = percent; + const qreal hundred = 100.0; + m_zoomFactor = static_cast(m_zoom) / hundred; + + applyZoom(); + if (m_zoomMenu) // Do not force them into existence + m_zoomMenu->setZoom(m_zoom); + + resetTransform(); + scale(m_zoomFactor, m_zoomFactor); +} + +void ZoomView::applyZoom() +{ +} + +qreal ZoomView::zoomFactor() const +{ + return m_zoomFactor; +} + +bool ZoomView::isZoomContextMenuEnabled() const +{ + return m_zoomContextMenuEnabled; +} + +void ZoomView::setZoomContextMenuEnabled(bool e) +{ + m_zoomContextMenuEnabled = e; +} + +ZoomMenu *ZoomView::zoomMenu() +{ + if (!m_zoomMenu) { + m_zoomMenu = new ZoomMenu(this); + m_zoomMenu->setZoom(m_zoom); + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoom(int))); + } + return m_zoomMenu; +} + +void ZoomView::contextMenuEvent(QContextMenuEvent *event) +{ + if (debugZoomWidget > 1) + qDebug() << "ZoomView::contextMenuEvent" << event->pos() << event->globalPos() << zoom() << '%'; + + if (m_zoomContextMenuEnabled) { + showContextMenu(event->globalPos()); + } else { + QGraphicsView::contextMenuEvent(event); + } +} + +void ZoomView::showContextMenu(const QPoint &globalPos) +{ + QMenu menu; + zoomMenu()->addActions(&menu); + if (debugZoomWidget) { + menu.addSeparator(); + QAction *da = menu.addAction(QLatin1String("Dump")); + connect(da, SIGNAL(triggered()), this, SLOT(dump())); + } + menu.exec(globalPos); +} + +QPoint ZoomView::scrollPosition() const +{ + return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); +} + +void ZoomView::setScrollPosition(const QPoint& pos) +{ + horizontalScrollBar()->setValue(pos.x()); + verticalScrollBar()->setValue(pos.y()); +} + +// -------------- ZoomProxyWidget +ZoomProxyWidget::ZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) : + QGraphicsProxyWidget(parent, wFlags) +{ +} + +QVariant ZoomProxyWidget::itemChange(GraphicsItemChange change, const QVariant &value) +{ + switch (change) { + case ItemPositionChange: { + const QPointF newPos = value.toPointF(); + const QPointF desiredPos = QPointF(0, 0); + if (newPos != desiredPos && debugZoomWidget) + qDebug() << "ZoomProxyWidget::itemChange: refusing " << newPos; + return desiredPos; + } + default: + break; + } + return QGraphicsProxyWidget::itemChange(change, value); +} + +/* ZoomedEventFilterRedirector: Event filter for the zoomed widget. + * It redirects the events to another handler of ZoomWidget as its + * base class QScrollArea also implements eventFilter() for its viewport. */ + +static const char *zoomedEventFilterRedirectorNameC = "__qt_ZoomedEventFilterRedirector"; + +class ZoomedEventFilterRedirector : public QObject { + Q_DISABLE_COPY(ZoomedEventFilterRedirector) + +public: + explicit ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent); + virtual bool eventFilter(QObject *watched, QEvent *event); + +private: + ZoomWidget *m_zw; +}; + +ZoomedEventFilterRedirector::ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent) : + QObject(parent), + m_zw(zw) +{ + setObjectName(QLatin1String(zoomedEventFilterRedirectorNameC)); +} + +bool ZoomedEventFilterRedirector::eventFilter(QObject *watched, QEvent *event) +{ + return m_zw->zoomedEventFilter(watched, event); +} + + +// --------- ZoomWidget + +ZoomWidget::ZoomWidget(QWidget *parent) : + ZoomView(parent), + m_proxy(0), + m_viewResizeBlocked(false), + m_widgetResizeBlocked(false), + m_widgetZoomContextMenuEnabled(false) +{ + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +void ZoomWidget::setWidget(QWidget *w, Qt::WindowFlags wFlags) +{ + if (debugZoomWidget) + qDebug() << "ZoomWidget::setWidget" << w << bin << wFlags; + + if (m_proxy) { + scene().removeItem(m_proxy); + if (QWidget *w = m_proxy->widget()) { + // remove the event filter + if (QObject *evf = qFindChild(w, QLatin1String(zoomedEventFilterRedirectorNameC))) + w->removeEventFilter(evf); + } + m_proxy->deleteLater(); + } + // Set window flags on the outer proxy for them to take effect + m_proxy = createProxyWidget(0, Qt::Window); + m_proxy->setWidget(w); + + m_proxy->setWindowFlags(wFlags); + scene().addItem(m_proxy); + w->installEventFilter(new ZoomedEventFilterRedirector(this, w)); + resizeToWidgetSize(); // Do manually for new widget + m_proxy->show(); +} + +bool ZoomWidget::isWidgetZoomContextMenuEnabled() const +{ + return m_widgetZoomContextMenuEnabled; +} +void ZoomWidget::setWidgetZoomContextMenuEnabled(bool e) +{ + m_widgetZoomContextMenuEnabled = e; +} + +QSize ZoomWidget::viewPortMargin() const +{ + return QSize(0, 0); +} + +QSizeF ZoomWidget::widgetDecorationSizeF() const +{ + qreal left, top, right, bottom; + m_proxy->getWindowFrameMargins (&left, &top, &right, &bottom); + const QSizeF rc = QSizeF(left + right, top + bottom); + return rc; +} + +QSize ZoomWidget::widgetSize() const +{ + if (m_proxy) + return m_proxy->widget()->size(); + return QSize(0, 0); +} + +/* Convert widget size to QGraphicsView size. + * Watch out for limits (0, QWIDGETSIZE_MAX); just pass them on */ + +QSize ZoomWidget::widgetSizeToViewSize(const QSize &s, bool *ptrToValid) const +{ + const QSize vpMargin = viewPortMargin(); + const QSizeF deco = widgetDecorationSizeF(); + const int width = s.width(); + + QSize rc = s; + bool valid = false; + if (width != 0 && width != QWIDGETSIZE_MAX) { + valid = true; + rc.setWidth(vpMargin.width() + qCeil(deco.width() + zoomFactor() * static_cast(width))); + } + + const int height = s.height(); + if (height != 0 && height != QWIDGETSIZE_MAX) { + valid = true; + rc.setHeight(vpMargin.height() + qCeil(deco.height() + zoomFactor() * static_cast(height))); + } + + if (ptrToValid) + *ptrToValid = valid; + + return rc; +} + +// On changing zoom: Make QGraphicsView big enough to hold the widget +void ZoomWidget::resizeToWidgetSize() +{ + if (!m_proxy) + return; + + m_viewResizeBlocked = true; + // Convert size, apply transformed min/max size if applicable + const QSize wsize = widgetSize(); + const QSize viewSize = widgetSizeToViewSize(wsize); + + bool hasMinimumSize = false; + const QSize minimumSize = m_proxy->widget()->minimumSize(); + const QSize viewMinimumSize = widgetSizeToViewSize(minimumSize, &hasMinimumSize); + + bool hasMaximumSize = false; + const QSize maximumSize = m_proxy->widget()->maximumSize(); + const QSize viewMaximumSize = widgetSizeToViewSize(maximumSize, &hasMaximumSize); + + if (debugZoomWidget) { + qDebug() + << "ZoomWidget::resizeToWidgetSize()\n" + << "Widget: " << wsize << "(scaled)" << (wsize * zoomFactor()) << " Min/Max" << minimumSize << maximumSize << '\n' + << " View: " << viewSize << hasMinimumSize << viewMinimumSize << hasMaximumSize << viewMaximumSize; + } + // Apply + if (hasMinimumSize) + setMinimumSize(viewMinimumSize); + if (hasMaximumSize) + setMaximumSize(viewMaximumSize); + // now resize + doResize(viewSize); + if (debugZoomWidget) + qDebug() << "ZoomWidget::resizeToWidgetSize(): resulting view size" << size(); + m_viewResizeBlocked = false; +} + +void ZoomWidget::applyZoom() +{ + resizeToWidgetSize(); +} + +/* virtual */ void ZoomWidget::doResize(const QSize &s) +{ + if (debugZoomWidget > 1) + qDebug() << ">ZoomWidget::doResize() " << s; + resize(s); +} + +void ZoomWidget::resizeEvent(QResizeEvent *event) +{ + /* QGraphicsView Resized from outside: Adapt widget. For some reason, + * the size passed in the event is not to be trusted. This might be due + * to some QScrollArea event fiddling. Have QScrollArea resize first + * and the use the size ZoomView::resizeEvent(event); */ + if (m_proxy && !m_viewResizeBlocked) { + if (debugZoomWidget > 1) + qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); + const QSizeF newViewPortSize = size() - viewPortMargin(); + const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF(); + m_widgetResizeBlocked = true; + m_proxy->widget()->resize(widgetSizeF.toSize()); + setSceneRect(QRectF(QPointF(0, 0), widgetSizeF)); + scrollToOrigin(); + m_widgetResizeBlocked = false; + if (debugZoomWidget > 1) + qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size(); + } +} + +QSize ZoomWidget::minimumSizeHint() const +{ + if (!m_proxy) + return QGraphicsView::minimumSizeHint(); + + const QSizeF wmsh = m_proxy->widget()->minimumSizeHint(); + const QSize rc = viewPortMargin() + (wmsh * zoomFactor()).toSize(); + if (debugZoomWidget > 1) + qDebug() << "minimumSizeHint()" << rc; + return rc; +} + +QSize ZoomWidget::sizeHint() const +{ + if (!m_proxy) + return QGraphicsView::sizeHint(); + + const QSizeF wsh = m_proxy->widget()->sizeHint(); + const QSize rc = viewPortMargin() + (wsh * zoomFactor()).toSize(); + if (debugZoomWidget > 1) + qDebug() << "sizeHint()" << rc; + return rc; +} + +bool ZoomWidget::zoomedEventFilter(QObject * /*watched*/, QEvent *event) +{ + switch (event->type()) { + case QEvent::KeyPress: + if (debugZoomWidget) { // Debug helper: Press 'D' on the zoomed widget + const QKeyEvent *kevent = static_cast(event); + if (kevent->key() == Qt::Key_D) + dump(); + } + break; + case QEvent::Resize: + if (debugZoomWidget > 1) { + const QResizeEvent *re = static_cast(event); + qDebug() << "ZoomWidget::zoomedEventFilter" << re->oldSize() << re->size() << " at " << m_proxy->widget()->geometry(); + } + if (!m_widgetResizeBlocked) + resizeToWidgetSize(); + + break; + case QEvent::ContextMenu: + if (m_widgetZoomContextMenuEnabled) { + // Calculate global position from scaled + QContextMenuEvent *ce = static_cast(event); + const QPointF origin = mapToGlobal(QPoint(0, 0)) - scrollPosition(); + const QPointF pos = QPointF(origin + (QPointF(ce->pos()) * zoomFactor())); + showContextMenu(pos.toPoint()); + ce->accept(); + return true; + } + break; + default: + break; + } + return false; +} + +void ZoomWidget::setItemAcceptDrops(bool) +{ + if (m_proxy) + m_proxy->setAcceptDrops(true); +} + +bool ZoomWidget::itemAcceptDrops() const +{ + return m_proxy ? m_proxy->acceptDrops() : false; +} + + // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget +QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const +{ + return new ZoomProxyWidget(parent, wFlags); +} + +void ZoomWidget::dump() const +{ + + qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry() + << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect(); + if (m_proxy) { + qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size() + << "\nProxy size hint" + << m_proxy->effectiveSizeHint(Qt::MinimumSize) + << m_proxy->effectiveSizeHint(Qt::PreferredSize) + << m_proxy->effectiveSizeHint(Qt::MaximumSize) + << "\nMatrix: " << m_proxy->matrix() + << "\nWidget: " << m_proxy->widget()->geometry() + << "scaled" << (zoomFactor() * m_proxy->widget()->size()); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/designer/lib/shared/zoomwidget_p.h b/designer/lib/shared/zoomwidget_p.h new file mode 100644 index 0000000..898824a --- /dev/null +++ b/designer/lib/shared/zoomwidget_p.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ZOOMWIDGET_H +#define ZOOMWIDGET_H + +#include "shared_global_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGraphicsScene; +class QMenu; +class QAction; +class QActionGroup; + +namespace qdesigner_internal { + +// A checkable zoom menu action group. Operates in percent. + +class QDESIGNER_SHARED_EXPORT ZoomMenu : public QObject { + Q_OBJECT + Q_DISABLE_COPY(ZoomMenu) + +public: + ZoomMenu(QObject *parent = 0); + void addActions(QMenu *m); + + int zoom() const; + + // Return a list of available zoom values. + static QList zoomValues(); + +public slots: + void setZoom(int percent); + +signals: + void zoomChanged(int); + +private slots: + void slotZoomMenu(QAction *); + +private: + static int zoomOf(const QAction *a); + + QActionGroup *m_menuActions; +}; + +/* Zoom view: A QGraphicsView with a zoom menu */ + +class QDESIGNER_SHARED_EXPORT ZoomView : public QGraphicsView +{ + Q_PROPERTY(int zoom READ zoom WRITE setZoom DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(bool zoomContextMenuEnabled READ isZoomContextMenuEnabled WRITE setZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) + Q_OBJECT + Q_DISABLE_COPY(ZoomView) +public: + ZoomView(QWidget *parent = 0); + + /* Zoom in percent (for easily implementing menus) and qreal zoomFactor + * in sync */ + int zoom() const; // in percent + qreal zoomFactor() const; + + // Zoom Menu on QGraphicsView. + bool isZoomContextMenuEnabled() const; + void setZoomContextMenuEnabled(bool e); + + QGraphicsScene &scene() { return *m_scene; } + const QGraphicsScene &scene() const { return *m_scene; } + + // Helpers for menu + ZoomMenu *zoomMenu(); + + QPoint scrollPosition() const; + void setScrollPosition(const QPoint& pos); + void scrollToOrigin(); + +public slots: + void setZoom(int percent); + void showContextMenu(const QPoint &globalPos); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + + // Overwrite for implementing additional behaviour when doing setZoom(); + virtual void applyZoom(); + +private: + QGraphicsScene *m_scene; + int m_zoom; + qreal m_zoomFactor; + + bool m_zoomContextMenuEnabled; + bool m_resizeBlocked; + ZoomMenu *m_zoomMenu; +}; + +/* The proxy widget used in ZoomWidget. It refuses to move away from 0,0, + * This behaviour is required for Windows only. */ + +class QDESIGNER_SHARED_EXPORT ZoomProxyWidget : public QGraphicsProxyWidget { + Q_DISABLE_COPY(ZoomProxyWidget) +public: + explicit ZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + +protected: + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); +}; + +/* Zoom widget: A QGraphicsView-based container for a widget that allows for + * zooming it. Communicates changes in size in the following ways: + * 1) Embedded widget wants to resize: Listen for its resize in event filter + * and resize + * 2) Zoom is changed: resize to fully display the embedded widget + * 3) Outer widget changes (by manually resizing the window: + * Pass the scaled change on to the embedded widget. + * Provides helper functions for a zoom context menu on the widget. */ + +class QDESIGNER_SHARED_EXPORT ZoomWidget : public ZoomView +{ + Q_PROPERTY(bool widgetZoomContextMenuEnabled READ isWidgetZoomContextMenuEnabled WRITE setWidgetZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(bool itemAcceptDrops READ itemAcceptDrops WRITE setItemAcceptDrops DESIGNABLE true SCRIPTABLE true) + Q_OBJECT + Q_DISABLE_COPY(ZoomWidget) + +public: + ZoomWidget(QWidget *parent = 0); + void setWidget(QWidget *w, Qt::WindowFlags wFlags = 0); + + const QGraphicsProxyWidget *proxy() const { return m_proxy; } + QGraphicsProxyWidget *proxy() { return m_proxy; } + + /* Enable the zoom Menu on the Widget (as opposed ZoomView's menu which + * is on the canvas). */ + bool isWidgetZoomContextMenuEnabled() const; + void setWidgetZoomContextMenuEnabled(bool e); + + void setItemAcceptDrops(bool); + bool itemAcceptDrops() const; + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + + bool zoomedEventFilter(QObject *watched, QEvent *event); + +public slots: + // debug state + void dump() const; + +protected: + void resizeEvent(QResizeEvent * event); + + // Overwritten from ZoomView + virtual void applyZoom(); + // Overwrite to actually perform a resize. This is required if we are in a layout. Default does resize(). + virtual void doResize(const QSize &s); + +private: + // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget + virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const; + QSize widgetSizeToViewSize(const QSize &s, bool *ptrToValid = 0) const; + + void resizeToWidgetSize(); + QSize viewPortMargin() const; + QSize widgetSize() const; + QSizeF widgetDecorationSizeF() const; + + QGraphicsProxyWidget *m_proxy; + bool m_viewResizeBlocked; + bool m_widgetResizeBlocked; + bool m_widgetZoomContextMenuEnabled; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/designer/lib/uilib/abstractformbuilder.cpp b/designer/lib/uilib/abstractformbuilder.cpp new file mode 100644 index 0000000..bcd33b7 --- /dev/null +++ b/designer/lib/uilib/abstractformbuilder.cpp @@ -0,0 +1,3069 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +**sw +****************************************************************************/ + +#include "abstractformbuilder.h" +#include "formbuilderextra_p.h" +#include "resourcebuilder_p.h" +#include "textbuilder_p.h" +#include "ui4_p.h" +#include "properties_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_FORMLAYOUT +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QFORMINTERNAL_NAMESPACE +# include // Compiling within Designer +#endif + +#include + +#include + +// containers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +Q_DECLARE_METATYPE(QWidgetList) + +static const char *buttonGroupPropertyC = "buttonGroup"; + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +using namespace QFormInternal; +#endif + +class QFriendlyLayout: public QLayout +{ +public: + inline QFriendlyLayout() { Q_ASSERT(0); } + +#ifdef QFORMINTERNAL_NAMESPACE + friend class QFormInternal::QAbstractFormBuilder; +#else + friend class QAbstractFormBuilder; +#endif +}; + +/*! + \class QAbstractFormBuilder + + \brief The QAbstractFormBuilder class provides a default + implementation for classes that create user interfaces at + run-time. + + \inmodule QtDesigner + + QAbstractFormBuilder provides a standard interface and a default + implementation for constructing forms from user interface + files. It is not intended to be instantiated directly. Use the + QFormBuilder class to create user interfaces from UI files at + run-time. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_abstractformbuilder.cpp 0 + + To override certain aspects of the form builder's behavior, + subclass QAbstractFormBuilder and reimplement the relevant virtual + functions: + + \list + \o load() handles reading of UI format files from arbitrary + QIODevices, and construction of widgets from the XML data + that they contain. + \o save() handles saving of widget details in UI format to + arbitrary QIODevices. + \o workingDirectory() and setWorkingDirectory() control the + directory in which forms are held. The form builder looks for + other resources on paths relative to this directory. + \endlist + + The QFormBuilder class is typically used by custom components and + applications that embed \QD. Standalone applications that need to + dynamically generate user interfaces at run-time use the + QUiLoader, found in the QtUiTools module. + + \sa {QtUiTools Module} +*/ + +/*! + Constructs a new form builder.*/ +QAbstractFormBuilder::QAbstractFormBuilder() : + m_defaultMargin(INT_MIN), + m_defaultSpacing(INT_MIN) +{ + setResourceBuilder(new QResourceBuilder()); + setTextBuilder(new QTextBuilder()); +} + +/*! + Destroys the form builder.*/ +QAbstractFormBuilder::~QAbstractFormBuilder() +{ + QFormBuilderExtra::removeInstance(this); +} + + +/*! + \fn QWidget *QAbstractFormBuilder::load(QIODevice *device, QWidget *parent) + + Loads an XML representation of a widget from the given \a device, + and constructs a new widget with the specified \a parent. + + \sa save() +*/ +QWidget *QAbstractFormBuilder::load(QIODevice *dev, QWidget *parentWidget) +{ + QXmlStreamReader reader; + reader.setDevice(dev); + DomUI ui; + bool initialized = false; + + const QString uiElement = QLatin1String("ui"); + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0) { + ui.read(reader); + initialized = true; + } else { + reader.raiseError(QCoreApplication::translate("QAbstractFormBuilder", "Unexpected element <%1>").arg(reader.name().toString())); + } + } + } + if (reader.hasError()) { + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "An error has occurred while reading the UI file at line %1, column %2: %3") + .arg(reader.lineNumber()).arg(reader.columnNumber()) + .arg(reader.errorString())); + return 0; + } + if (!initialized) { + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Invalid UI file: The root element is missing.")); + return 0; + } + + QWidget *widget = create(&ui, parentWidget); + return widget; +} + +/*! + \internal +*/ +QWidget *QAbstractFormBuilder::create(DomUI *ui, QWidget *parentWidget) +{ + typedef QFormBuilderExtra::ButtonGroupHash ButtonGroupHash; + + QFormBuilderExtra *formBuilderPrivate = QFormBuilderExtra::instance(this); + formBuilderPrivate->clear(); + if (const DomLayoutDefault *def = ui->elementLayoutDefault()) { + m_defaultMargin = def->hasAttributeMargin() ? def->attributeMargin() : INT_MIN; + m_defaultSpacing = def->hasAttributeSpacing() ? def->attributeSpacing() : INT_MIN; + } + + DomWidget *ui_widget = ui->elementWidget(); + if (!ui_widget) + return 0; + + initialize(ui); + + if (const DomButtonGroups *domButtonGroups = ui->elementButtonGroups()) + formBuilderPrivate->registerButtonGroups(domButtonGroups); + + if (QWidget *widget = create(ui_widget, parentWidget)) { + // Reparent button groups that were actually created to main container for them to be found in the signal/slot part + const ButtonGroupHash &buttonGroups = formBuilderPrivate->buttonGroups(); + if (!buttonGroups.empty()) { + const ButtonGroupHash::const_iterator cend = buttonGroups.constEnd(); + for (ButtonGroupHash::const_iterator it = buttonGroups.constBegin(); it != cend; ++it) + if (it.value().second) + it.value().second->setParent(widget); + } + createConnections(ui->elementConnections(), widget); + createResources(ui->elementResources()); // maybe this should go first, before create()... + applyTabStops(widget, ui->elementTabStops()); + formBuilderPrivate->applyInternalProperties(); + reset(); + formBuilderPrivate->clear(); + return widget; + } + formBuilderPrivate->clear(); + return 0; +} + +/*! + \internal + Retrieve relevant information from the custom widgets section. + Called by create(DomUI *, QWidget *); call manually if you + just use create(DomWidget *, QWidget *) on some child widget of DomUI. + */ + +void QAbstractFormBuilder::initialize(const DomUI *ui) +{ + typedef QList DomCustomWidgetList; + + DomCustomWidgets *domCustomWidgets = ui->elementCustomWidgets(); + createCustomWidgets(domCustomWidgets); + + if (domCustomWidgets) { + const DomCustomWidgetList customWidgets = domCustomWidgets->elementCustomWidget(); + if (!customWidgets.empty()) { + QFormBuilderExtra *formBuilderPrivate = QFormBuilderExtra::instance(this); + const DomCustomWidgetList::const_iterator cend = customWidgets.constEnd(); + for (DomCustomWidgetList::const_iterator it = customWidgets.constBegin(); it != cend; ++it) + formBuilderPrivate->storeCustomWidgetData((*it)->elementClass(), *it); + } + } +} + +/*! + \internal +*/ +QWidget *QAbstractFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + QWidget *w = createWidget(ui_widget->attributeClass(), parentWidget, ui_widget->attributeName()); + if (!w) + return 0; + + applyProperties(w, ui_widget->elementProperty()); + + foreach (DomAction *ui_action, ui_widget->elementAction()) { + QAction *child_action = create(ui_action, w); + Q_UNUSED( child_action ); + } + + foreach (DomActionGroup *ui_action_group, ui_widget->elementActionGroup()) { + QActionGroup *child_action_group = create(ui_action_group, w); + Q_UNUSED( child_action_group ); + } + + QWidgetList children; + foreach (DomWidget *ui_child, ui_widget->elementWidget()) { + if (QWidget *child = create(ui_child, w)) { + children += child; + } else { + const QString className = ui_child->elementClass().empty() ? QString() : ui_child->elementClass().front(); + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "The creation of a widget of the class '%1' failed.").arg(className)); + } + } + + foreach (DomLayout *ui_lay, ui_widget->elementLayout()) { + QLayout *child_lay = create(ui_lay, 0, w); + Q_UNUSED( child_lay ); + } + + const QList addActions = ui_widget->elementAddAction(); + if (!addActions.empty()) { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + foreach (DomActionRef *ui_action_ref, addActions) { + const QString name = ui_action_ref->attributeName(); + if (name == strings.separator) { + QAction *sep = new QAction(w); + sep->setSeparator(true); + w->addAction(sep); + addMenuAction(sep); + } else if (QAction *a = m_actions.value(name)) { + w->addAction(a); + } else if (QActionGroup *g = m_actionGroups.value(name)) { + w->addActions(g->actions()); + } else if (QMenu *menu = qFindChild(w, name)) { + w->addAction(menu->menuAction()); + addMenuAction(menu->menuAction()); + } + } + } + + loadExtraInfo(ui_widget, w, parentWidget); +#ifndef QT_FORMBUILDER_NO_SCRIPT + QString scriptErrorMessage; + QFormBuilderExtra *extra = QFormBuilderExtra::instance(this); + extra->formScriptRunner().run(ui_widget, + extra->customWidgetScript(ui_widget->attributeClass()), + w, children, &scriptErrorMessage); +#endif + addItem(ui_widget, w, parentWidget); + + if (qobject_cast(w) && parentWidget) + w->setAttribute(Qt::WA_Moved, false); // So that QDialog::setVisible(true) will center it + + const QStringList zOrderNames = ui_widget->elementZOrder(); + if (!zOrderNames.isEmpty()) { + QList zOrder = qVariantValue(w->property("_q_zOrder")); + foreach (const QString &widgetName, zOrderNames) { + if (QWidget *child = qFindChild(w, widgetName)) { + if (child->parentWidget() == w) { + zOrder.removeAll(child); + zOrder.append(child); + child->raise(); + } + } + } + w->setProperty("_q_zOrder", qVariantFromValue(zOrder)); + } + + return w; +} + +/*! + \internal +*/ +QAction *QAbstractFormBuilder::create(DomAction *ui_action, QObject *parent) +{ + QAction *a = createAction(parent, ui_action->attributeName()); + if (!a) + return 0; + + m_actions.insert(ui_action->attributeName(), a); + applyProperties(a, ui_action->elementProperty()); + return a; +} + +/*! + \internal +*/ +QActionGroup *QAbstractFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent) +{ + QActionGroup *a = createActionGroup(parent, ui_action_group->attributeName()); + if (!a) + return 0; + m_actionGroups.insert(ui_action_group->attributeName(), a); + applyProperties(a, ui_action_group->elementProperty()); + + foreach (DomAction *ui_action, ui_action_group->elementAction()) { + QAction *child_action = create(ui_action, a); + Q_UNUSED( child_action ); + } + + foreach (DomActionGroup *g, ui_action_group->elementActionGroup()) { + QActionGroup *child_action_group = create(g, parent); + Q_UNUSED( child_action_group ); + } + + return a; +} + +// figure out the toolbar area of a DOM attrib list. +// By legacy, it is stored as an integer. As of 4.3.0, it is the enumeration value. +Qt::ToolBarArea QAbstractFormBuilder::toolbarAreaFromDOMAttributes(const DomPropertyHash &attributes) { + const DomProperty *attr = attributes.value(QFormBuilderStrings::instance().toolBarAreaAttribute); + if (!attr) + return Qt::TopToolBarArea; + switch(attr->kind()) { + case DomProperty::Number: + return static_cast(attr->elementNumber()); + case DomProperty::Enum: + return enumKeyOfObjectToValue("toolBarArea", attr->elementEnum().toLatin1()); + default: + break; + } + return Qt::TopToolBarArea; +} + +/*! + \internal +*/ +bool QAbstractFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute()); + + if (parentWidget == 0) + return true; + // Check special cases. First: Custom container + const QString className = QLatin1String(parentWidget->metaObject()->className()); + const QString addPageMethod = QFormBuilderExtra::instance(this)->customWidgetAddPageMethod(className); + if (!addPageMethod.isEmpty()) { + // If this fails ( non-existent or non-slot), use ContainerExtension in Designer, else it can't be helped + return QMetaObject::invokeMethod(parentWidget, addPageMethod.toUtf8().constData(), Qt::DirectConnection, Q_ARG(QWidget*, widget)); + } + + if (QMainWindow *mw = qobject_cast(parentWidget)) { + +#ifndef QT_NO_MENUBAR + // the menubar + if (QMenuBar *menuBar = qobject_cast(widget)) { + mw->setMenuBar(menuBar); + return true; + } +#endif + +#ifndef QT_NO_TOOLBAR + // apply the toolbar's attributes + else if (QToolBar *toolBar = qobject_cast(widget)) { + mw->addToolBar(toolbarAreaFromDOMAttributes(attributes), toolBar); + // check break + if (const DomProperty *attr = attributes.value(strings.toolBarBreakAttribute)) + if (attr->elementBool() == strings.trueValue) + mw->insertToolBarBreak (toolBar); + + return true; + } +#endif + +#ifndef QT_NO_STATUSBAR + // statusBar + else if (QStatusBar *statusBar = qobject_cast(widget)) { + mw->setStatusBar(statusBar); + return true; + } +#endif + +#ifndef QT_NO_DOCKWIDGET + // apply the dockwidget's attributes + else if (QDockWidget *dockWidget = qobject_cast(widget)) { + if (const DomProperty *attr = attributes.value(strings.dockWidgetAreaAttribute)) { + Qt::DockWidgetArea area = static_cast(attr->elementNumber()); + if (!dockWidget->isAreaAllowed(area)) { + if (dockWidget->isAreaAllowed(Qt::LeftDockWidgetArea)) + area = Qt::LeftDockWidgetArea; + else if (dockWidget->isAreaAllowed(Qt::RightDockWidgetArea)) + area = Qt::RightDockWidgetArea; + else if (dockWidget->isAreaAllowed(Qt::TopDockWidgetArea)) + area = Qt::TopDockWidgetArea; + else if (dockWidget->isAreaAllowed(Qt::BottomDockWidgetArea)) + area = Qt::BottomDockWidgetArea; + } + mw->addDockWidget(area, dockWidget); + } else { + mw->addDockWidget(Qt::LeftDockWidgetArea, dockWidget); + } + return true; + } +#endif + + else if (! mw->centralWidget()) { + mw->setCentralWidget(widget); + return true; + } + } + +#ifndef QT_NO_TABWIDGET + else if (QTabWidget *tabWidget = qobject_cast(parentWidget)) { + widget->setParent(0); + + const int tabIndex = tabWidget->count(); + if (const DomProperty *titleP = attributes.value(strings.titleAttribute, 0)) + tabWidget->addTab(widget, toString(titleP->elementString())); + else + tabWidget->addTab(widget, strings.defaultTitle); + + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + QVariant nativeValue = resourceBuilder()->toNativeValue(v); + tabWidget->setTabIcon(tabIndex, qvariant_cast(nativeValue)); + } + +#ifndef QT_NO_TOOLTIP + if (const DomProperty *ptoolTip = attributes.value(strings.toolTipAttribute)) { + tabWidget->setTabToolTip(tabIndex, toString(ptoolTip->elementString())); + } +#endif + +#ifndef QT_NO_WHATSTHIS + if (const DomProperty *pwhatsThis = attributes.value(strings.whatsThisAttribute)) { + tabWidget->setTabWhatsThis(tabIndex, toString(pwhatsThis->elementString())); + } +#endif + + return true; + } +#endif + +#ifndef QT_NO_TOOLBOX + else if (QToolBox *toolBox = qobject_cast(parentWidget)) { + const int tabIndex = toolBox->count(); + if (const DomProperty *labelP = attributes.value(strings.labelAttribute, 0)) + toolBox->addItem(widget, toString(labelP->elementString())); + else + toolBox->addItem(widget, strings.defaultTitle); + + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + QVariant nativeValue = resourceBuilder()->toNativeValue(v); + toolBox->setItemIcon(tabIndex, qvariant_cast(nativeValue)); + } + +#ifndef QT_NO_TOOLTIP + if (const DomProperty *ptoolTip = attributes.value(strings.toolTipAttribute)) { + toolBox->setItemToolTip(tabIndex, toString(ptoolTip->elementString())); + } +#endif + + return true; + } +#endif + +#ifndef QT_NO_STACKEDWIDGET + else if (QStackedWidget *stackedWidget = qobject_cast(parentWidget)) { + stackedWidget->addWidget(widget); + return true; + } +#endif + +#ifndef QT_NO_SPLITTER + else if (QSplitter *splitter = qobject_cast(parentWidget)) { + splitter->addWidget(widget); + return true; + } +#endif + +#ifndef QT_NO_MDIAREA + else if (QMdiArea *mdiArea = qobject_cast(parentWidget)) { + mdiArea->addSubWindow(widget); + return true; + } +#endif + +#ifndef QT_NO_WORKSPACE + else if (QWorkspace *ws = qobject_cast(parentWidget)) { + ws->addWindow(widget); + return true; + } +#endif + +#ifndef QT_NO_DOCKWIDGET + else if (QDockWidget *dockWidget = qobject_cast(parentWidget)) { + dockWidget->setWidget(widget); + return true; + } +#endif + +#ifndef QT_NO_SCROLLAREA + else if (QScrollArea *scrollArea = qobject_cast(parentWidget)) { + scrollArea->setWidget(widget); + return true; + } +#endif + +#ifndef QT_NO_WIZARD + else if (QWizard *wizard = qobject_cast(parentWidget)) { + QWizardPage *page = qobject_cast(widget); + if (!page) { + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Attempt to add child that is not of class QWizardPage to QWizard.")); + return false; + } + wizard->addPage(page); + return true; + } +#endif + return false; +} + +/*! + \internal +*/ +void QAbstractFormBuilder::layoutInfo(DomLayout *ui_layout, QObject *parent, int *margin, int *spacing) +{ + Q_UNUSED(parent) + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + const DomPropertyHash properties = propertyMap(ui_layout->elementProperty()); + + int mar = INT_MIN; + int spac = INT_MIN; + if (const DomProperty *p = properties.value(strings.marginProperty, 0)) + mar = p->elementNumber(); + + if (const DomProperty *p = properties.value(strings.spacingProperty, 0)) + spac = p->elementNumber(); + +#ifdef Q_OS_MAC + // here we recognize UI file < 4.3 (no we don't store margin property) + if (mar != INT_MIN) { + const int defaultMargin = parent->inherits("QLayoutWidget") ? 0 : 9; + if (mar == defaultMargin) + mar = INT_MIN; + if (spac == 6) + spac = INT_MIN; + + if (mar == INT_MIN || spac == INT_MIN) { + QList properties = ui_layout->elementProperty(); + QMutableListIterator it(properties); + while (it.hasNext()) { + DomProperty *prop = it.next(); + if ((mar == INT_MIN && prop->attributeName() == strings.marginProperty) || + (spac == INT_MIN && prop->attributeName() == strings.spacingProperty)) { + it.remove(); + delete prop; + } + } + ui_layout->setElementProperty(properties); + } + } +#endif + if (margin) + *margin = mar; + if (spacing) + *spacing = spac; +} + +/*! + \internal +*/ +QLayout *QAbstractFormBuilder::create(DomLayout *ui_layout, QLayout *parentLayout, QWidget *parentWidget) +{ + QObject *p = parentLayout; + + if (p == 0) + p = parentWidget; + + Q_ASSERT(p != 0); + + bool tracking = false; + + if (p == parentWidget && parentWidget->layout()) { + tracking = true; + p = parentWidget->layout(); + } + + QLayout *layout = createLayout(ui_layout->attributeClass(), p, ui_layout->hasAttributeName() ? ui_layout->attributeName() : QString()); + + if (layout == 0) + return 0; + + if (tracking && layout->parent() == 0) { + QBoxLayout *box = qobject_cast(parentWidget->layout()); + if (!box) { // only QBoxLayout is supported + const QString widgetClass = QString::fromUtf8(parentWidget->metaObject()->className()); + const QString layoutClass = QString::fromUtf8(parentWidget->layout()->metaObject()->className()); + const QString msg = QCoreApplication::translate("QAbstractFormBuilder", "Attempt to add a layout to a widget '%1' (%2) which already has a layout of non-box type %3.\n" + "This indicates an inconsistency in the ui-file."). + arg(parentWidget->objectName(), widgetClass, layoutClass); + uiLibWarning(msg); + return 0; + } + box->addLayout(layout); + } + + int margin = INT_MIN, spacing = INT_MIN; + layoutInfo(ui_layout, p, &margin, &spacing); + + if (margin != INT_MIN) { + layout->setMargin(margin); + } else { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + int left, top, right, bottom; + left = top = right = bottom = -1; + layout->getContentsMargins(&left, &top, &right, &bottom); + + const DomPropertyHash properties = propertyMap(ui_layout->elementProperty()); + + if (const DomProperty *p = properties.value(strings.leftMarginProperty, 0)) + left = p->elementNumber(); + + if (const DomProperty *p = properties.value(strings.topMarginProperty, 0)) + top = p->elementNumber(); + + if (const DomProperty *p = properties.value(strings.rightMarginProperty, 0)) + right = p->elementNumber(); + + if (const DomProperty *p = properties.value(strings.bottomMarginProperty, 0)) + bottom = p->elementNumber(); + + layout->setContentsMargins(left, top, right, bottom); + } + + if (spacing != INT_MIN) { + layout->setSpacing(spacing); + } else { + QGridLayout *grid = qobject_cast(layout); + if (grid) { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + const DomPropertyHash properties = propertyMap(ui_layout->elementProperty()); + + if (const DomProperty *p = properties.value(strings.horizontalSpacingProperty, 0)) + grid->setHorizontalSpacing(p->elementNumber()); + if (const DomProperty *p = properties.value(strings.verticalSpacingProperty, 0)) + grid->setVerticalSpacing(p->elementNumber()); + } + } + + applyProperties(layout, ui_layout->elementProperty()); + + foreach (DomLayoutItem *ui_item, ui_layout->elementItem()) { + if (QLayoutItem *item = create(ui_item, layout, parentWidget)) { + addItem(ui_item, item, layout); + } + } + // Check the box stretch attributes + if (QBoxLayout *box = qobject_cast(layout)) { + const QString boxStretch = ui_layout->attributeStretch(); + if (!boxStretch.isEmpty()) + QFormBuilderExtra::setBoxLayoutStretch(boxStretch, box); + } + // Check the grid stretch/minimum size attributes + if (QGridLayout *grid = qobject_cast(layout)) { + // Stretch + const QString gridRowStretch = ui_layout->attributeRowStretch(); + if (!gridRowStretch.isEmpty()) + QFormBuilderExtra::setGridLayoutRowStretch(gridRowStretch, grid); + const QString gridColumnStretch = ui_layout->attributeColumnStretch(); + if (!gridColumnStretch.isEmpty()) + QFormBuilderExtra::setGridLayoutColumnStretch(gridColumnStretch, grid); + // Minimum size + const QString gridColumnMinimumWidth = ui_layout->attributeColumnMinimumWidth(); + if (!gridColumnMinimumWidth.isEmpty()) + QFormBuilderExtra::setGridLayoutColumnMinimumWidth(gridColumnMinimumWidth, grid); + const QString gridRowMinimumHeight = ui_layout->attributeRowMinimumHeight(); + if (!gridRowMinimumHeight.isEmpty()) + QFormBuilderExtra::setGridLayoutRowMinimumHeight(gridRowMinimumHeight, grid); + } + return layout; +} + +#ifndef QT_NO_FORMLAYOUT +static inline QFormLayout::ItemRole formLayoutRole(int column, int colspan) +{ + if (colspan > 1) + return QFormLayout::SpanningRole; + return column == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; +} +#endif + +/*! + \internal +*/ +bool QAbstractFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + // Calling addChildWidget(), etc. is required to maintain consistency of the layouts, + // see documentation of addItem(), which should ideally not be used. + if (item->widget()) { + static_cast(layout)->addChildWidget(item->widget()); + } else if (item->layout()) { + static_cast(layout)->addChildLayout(item->layout()); + } else if (item->spacerItem()) { + // nothing to do + } else { + return false; + } + + if (QGridLayout *grid = qobject_cast(layout)) { + const int rowSpan = ui_item->hasAttributeRowSpan() ? ui_item->attributeRowSpan() : 1; + const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1; + grid->addItem(item, ui_item->attributeRow(), ui_item->attributeColumn(), + rowSpan, colSpan, item->alignment()); + return true; + } +#ifndef QT_NO_FORMLAYOUT + if (QFormLayout *form = qobject_cast(layout)) { + const int row = ui_item->attributeRow(); + const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1; + form->setItem(row, formLayoutRole(ui_item->attributeColumn(), colSpan), item); + return true; + } + +#endif + layout->addItem(item); + return true; +} + +/*! + \internal +*/ +QLayoutItem *QAbstractFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget) +{ + switch (ui_layoutItem->kind()) { + case DomLayoutItem::Widget: { + if (QWidget *w = create(ui_layoutItem->elementWidget(), parentWidget)) +#ifdef QFORMINTERNAL_NAMESPACE // uilib + return new QWidgetItemV2(w); +#else // Within Designer: Use factory method that returns special items that refuse to shrink to 0,0 + return QLayoutPrivate::createWidgetItem(layout, w); +#endif + qWarning() << QCoreApplication::translate("QAbstractFormBuilder", "Empty widget item in %1 '%2'.").arg(QString::fromUtf8(layout->metaObject()->className()), layout->objectName()); + return 0; + } + case DomLayoutItem::Spacer: { + QSize size(0, 0); + QSizePolicy::Policy sizeType = QSizePolicy::Expanding; + bool isVspacer = false; + + const DomSpacer *ui_spacer = ui_layoutItem->elementSpacer(); + + const QMetaEnum sizePolicy_enum = metaEnum("sizeType"); + const QMetaEnum orientation_enum = metaEnum("orientation"); + + const QList spacerProperties = ui_spacer->elementProperty(); + if (!spacerProperties.empty()) { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + foreach (DomProperty *p, spacerProperties) { + const QVariant v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p); // ### remove me + if (v.isNull()) + continue; + if (p->attributeName() == strings.sizeHintProperty && p->kind() == DomProperty::Size) { + size = v.toSize(); // ### remove me + } else if (p->attributeName() == strings.sizeTypeProperty && p->kind() == DomProperty::Enum) { + sizeType = static_cast(v.toInt()); + } else if (p->attributeName() == strings.orientationProperty && p->kind() == DomProperty::Enum) { + const Qt::Orientation o = static_cast(v.toInt()); + isVspacer = (o == Qt::Vertical); + } + } + } + + QSpacerItem *spacer = 0; + if (isVspacer) + spacer = new QSpacerItem(size.width(), size.height(), QSizePolicy::Minimum, sizeType); + else + spacer = new QSpacerItem(size.width(), size.height(), sizeType, QSizePolicy::Minimum); + return spacer; } + + case DomLayoutItem::Layout: + return create(ui_layoutItem->elementLayout(), layout, parentWidget); + + default: + break; + } + + return 0; +} + +/*! + \internal +*/ +void QAbstractFormBuilder::applyProperties(QObject *o, const QList &properties) +{ + typedef QList DomPropertyList; + + if (properties.empty()) + return; + + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const QVariant v = toVariant(o->metaObject(), *it); + if (!v.isNull()) { + const QString attributeName = (*it)->attributeName(); + if (!fb->applyPropertyInternally(o, attributeName, v)) + o->setProperty(attributeName.toUtf8(), v); + } + } +} + + +/*! + \internal + Check whether a property is applied internally by QAbstractFormBuilder. Call this + from overwritten applyProperties(). +*/ + +bool QAbstractFormBuilder::applyPropertyInternally(QObject *o, const QString &propertyName, const QVariant &value) +{ + return QFormBuilderExtra::instance(this)->applyPropertyInternally(o,propertyName, value); +} + +/*! + \internal +*/ + +QVariant QAbstractFormBuilder::toVariant(const QMetaObject *meta, DomProperty *p) +{ + return domPropertyToVariant(this, meta, p); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::setupColorGroup(QPalette &palette, QPalette::ColorGroup colorGroup, + DomColorGroup *group) +{ + // old format + const QList colors = group->elementColor(); + for (int role = 0; role < colors.size(); ++role) { + const DomColor *color = colors.at(role); + const QColor c(color->elementRed(), color->elementGreen(), color->elementBlue()); + palette.setColor(colorGroup, QPalette::ColorRole(role), c); + } + + // new format + const QMetaEnum colorRole_enum = metaEnum("colorRole"); + + const QList colorRoles = group->elementColorRole(); + for (int role = 0; role < colorRoles.size(); ++role) { + const DomColorRole *colorRole = colorRoles.at(role); + + if (colorRole->hasAttributeRole()) { + const int r = colorRole_enum.keyToValue(colorRole->attributeRole().toLatin1()); + if (r != -1) { + const QBrush br = setupBrush(colorRole->elementBrush()); + palette.setBrush(colorGroup, static_cast(r), br); + } + } + } +} + +/*! + \internal +*/ +DomColorGroup *QAbstractFormBuilder::saveColorGroup(const QPalette &palette) +{ + + const QMetaEnum colorRole_enum = metaEnum("colorRole"); + + DomColorGroup *group = new DomColorGroup(); + QList colorRoles; + + const uint mask = palette.resolve(); + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; ++role) { + if (mask & (1 << role)) { + QBrush br = palette.brush(QPalette::ColorRole(role)); + + DomColorRole *colorRole = new DomColorRole(); + colorRole->setElementBrush(saveBrush(br)); + colorRole->setAttributeRole(QLatin1String(colorRole_enum.valueToKey(role))); + colorRoles.append(colorRole); + } + } + + group->setElementColorRole(colorRoles); + return group; +} + +/*! + \internal +*/ +QBrush QAbstractFormBuilder::setupBrush(DomBrush *brush) +{ + QBrush br; + if (!brush->hasAttributeBrushStyle()) + return br; + + const Qt::BrushStyle style = enumKeyOfObjectToValue("brushStyle", brush->attributeBrushStyle().toLatin1()); + + if (style == Qt::LinearGradientPattern || + style == Qt::RadialGradientPattern || + style == Qt::ConicalGradientPattern) { + const QMetaEnum gradientType_enum = metaEnum("gradientType"); + const QMetaEnum gradientSpread_enum = metaEnum("gradientSpread"); + const QMetaEnum gradientCoordinate_enum = metaEnum("gradientCoordinate"); + + const DomGradient *gradient = brush->elementGradient(); + const QGradient::Type type = enumKeyToValue(gradientType_enum, gradient->attributeType().toLatin1()); + + + QGradient *gr = 0; + + if (type == QGradient::LinearGradient) { + gr = new QLinearGradient(QPointF(gradient->attributeStartX(), gradient->attributeStartY()), + QPointF(gradient->attributeEndX(), gradient->attributeEndY())); + } else if (type == QGradient::RadialGradient) { + gr = new QRadialGradient(QPointF(gradient->attributeCentralX(), gradient->attributeCentralY()), + gradient->attributeRadius(), + QPointF(gradient->attributeFocalX(), gradient->attributeFocalY())); + } else if (type == QGradient::ConicalGradient) { + gr = new QConicalGradient(QPointF(gradient->attributeCentralX(), gradient->attributeCentralY()), + gradient->attributeAngle()); + } + if (!gr) + return br; + + const QGradient::Spread spread = enumKeyToValue(gradientSpread_enum, gradient->attributeSpread().toLatin1()); + gr->setSpread(spread); + + const QGradient::CoordinateMode coord = enumKeyToValue(gradientCoordinate_enum, gradient->attributeCoordinateMode().toLatin1()); + gr->setCoordinateMode(coord); + + const QList stops = gradient->elementGradientStop(); + QListIterator it(stops); + while (it.hasNext()) { + const DomGradientStop *stop = it.next(); + const DomColor *color = stop->elementColor(); + gr->setColorAt(stop->attributePosition(), QColor::fromRgb(color->elementRed(), + color->elementGreen(), color->elementBlue(), color->attributeAlpha())); + } + br = QBrush(*gr); + delete gr; + } else if (style == Qt::TexturePattern) { + const DomProperty *texture = brush->elementTexture(); + if (texture && texture->kind() == DomProperty::Pixmap) { + br.setTexture(domPropertyToPixmap(texture)); + } + } else { + const DomColor *color = brush->elementColor(); + br.setColor(QColor::fromRgb(color->elementRed(), + color->elementGreen(), color->elementBlue(), color->attributeAlpha())); + br.setStyle((Qt::BrushStyle)style); + } + return br; +} + +/*! + \internal +*/ +DomBrush *QAbstractFormBuilder::saveBrush(const QBrush &br) +{ + const QMetaEnum brushStyle_enum = metaEnum("brushStyle"); + + DomBrush *brush = new DomBrush(); + const Qt::BrushStyle style = br.style(); + brush->setAttributeBrushStyle(QLatin1String(brushStyle_enum.valueToKey(style))); + if (style == Qt::LinearGradientPattern || + style == Qt::RadialGradientPattern || + style == Qt::ConicalGradientPattern) { + const QMetaEnum gradientType_enum = metaEnum("gradientType"); + const QMetaEnum gradientSpread_enum = metaEnum("gradientSpread"); + const QMetaEnum gradientCoordinate_enum = metaEnum("gradientCoordinate"); + + DomGradient *gradient = new DomGradient(); + const QGradient *gr = br.gradient(); + const QGradient::Type type = gr->type(); + gradient->setAttributeType(QLatin1String(gradientType_enum.valueToKey(type))); + gradient->setAttributeSpread(QLatin1String(gradientSpread_enum.valueToKey(gr->spread()))); + gradient->setAttributeCoordinateMode(QLatin1String(gradientCoordinate_enum.valueToKey(gr->coordinateMode()))); + QList stops; + QGradientStops st = gr->stops(); + QVectorIterator > it(st); + while (it.hasNext()) { + const QPair pair = it.next(); + DomGradientStop *stop = new DomGradientStop(); + stop->setAttributePosition(pair.first); + DomColor *color = new DomColor(); + color->setElementRed(pair.second.red()); + color->setElementGreen(pair.second.green()); + color->setElementBlue(pair.second.blue()); + color->setAttributeAlpha(pair.second.alpha()); + stop->setElementColor(color); + stops.append(stop); + } + gradient->setElementGradientStop(stops); + if (type == QGradient::LinearGradient) { + QLinearGradient *lgr = (QLinearGradient *)(gr); + gradient->setAttributeStartX(lgr->start().x()); + gradient->setAttributeStartY(lgr->start().y()); + gradient->setAttributeEndX(lgr->finalStop().x()); + gradient->setAttributeEndY(lgr->finalStop().y()); + } else if (type == QGradient::RadialGradient) { + QRadialGradient *rgr = (QRadialGradient *)(gr); + gradient->setAttributeCentralX(rgr->center().x()); + gradient->setAttributeCentralY(rgr->center().y()); + gradient->setAttributeFocalX(rgr->focalPoint().x()); + gradient->setAttributeFocalY(rgr->focalPoint().y()); + gradient->setAttributeRadius(rgr->radius()); + } else if (type == QGradient::ConicalGradient) { + QConicalGradient *cgr = (QConicalGradient *)(gr); + gradient->setAttributeCentralX(cgr->center().x()); + gradient->setAttributeCentralY(cgr->center().y()); + gradient->setAttributeAngle(cgr->angle()); + } + + brush->setElementGradient(gradient); + } else if (style == Qt::TexturePattern) { + const QPixmap pixmap = br.texture(); + if (!pixmap.isNull()) { + DomProperty *p = new DomProperty; + setPixmapProperty(*p, pixmapPaths(pixmap)); + brush->setElementTexture(p); + } + } else { + QColor c = br.color(); + DomColor *color = new DomColor(); + color->setElementRed(c.red()); + color->setElementGreen(c.green()); + color->setElementBlue(c.blue()); + color->setAttributeAlpha(c.alpha()); + brush->setElementColor(color); + } + return brush; +} + +/*! + \internal +*/ +QWidget *QAbstractFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + Q_UNUSED(widgetName); + Q_UNUSED(parentWidget); + Q_UNUSED(name); + return 0; +} + +/*! + \internal +*/ +QLayout *QAbstractFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name) +{ + Q_UNUSED(layoutName); + Q_UNUSED(parent); + Q_UNUSED(name); + return 0; +} + +/*! + \internal +*/ +QAction *QAbstractFormBuilder::createAction(QObject *parent, const QString &name) +{ + QAction *action = new QAction(parent); + action->setObjectName(name); + return action; +} + +/*! + \internal +*/ +QActionGroup *QAbstractFormBuilder::createActionGroup(QObject *parent, const QString &name) +{ + QActionGroup *g = new QActionGroup(parent); + g->setObjectName(name); + return g; +} + +/*! + \fn void QAbstractFormBuilder::save(QIODevice *device, QWidget *widget) + + Saves an XML representation of the given \a widget to the + specified \a device in the standard UI file format. + + \sa load()*/ +void QAbstractFormBuilder::save(QIODevice *dev, QWidget *widget) +{ + DomWidget *ui_widget = createDom(widget, 0); + Q_ASSERT( ui_widget != 0 ); + + DomUI *ui = new DomUI(); + ui->setAttributeVersion(QLatin1String("4.0")); + ui->setElementWidget(ui_widget); + + saveDom(ui, widget); + + QXmlStreamWriter writer(dev); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + ui->write(writer); + writer.writeEndDocument(); + + m_laidout.clear(); + + delete ui; +} + +/*! + \internal +*/ +void QAbstractFormBuilder::saveDom(DomUI *ui, QWidget *widget) +{ + ui->setElementClass(widget->objectName()); + + if (DomConnections *ui_connections = saveConnections()) { + ui->setElementConnections(ui_connections); + } + + if (DomCustomWidgets *ui_customWidgets = saveCustomWidgets()) { + ui->setElementCustomWidgets(ui_customWidgets); + } + + if (DomTabStops *ui_tabStops = saveTabStops()) { + ui->setElementTabStops(ui_tabStops); + } + + if (DomResources *ui_resources = saveResources()) { + ui->setElementResources(ui_resources); + } + if (DomButtonGroups *ui_buttonGroups = saveButtonGroups(widget)) + ui->setElementButtonGroups(ui_buttonGroups); +} + +/*! + \internal +*/ +DomConnections *QAbstractFormBuilder::saveConnections() +{ + return new DomConnections; +} + +/*! + \internal +*/ + +DomWidget *QAbstractFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) +{ + DomWidget *ui_widget = new DomWidget(); + ui_widget->setAttributeClass(QLatin1String(widget->metaObject()->className())); + ui_widget->setElementProperty(computeProperties(widget)); + + if (recursive) { + if (QLayout *layout = widget->layout()) { + if (DomLayout *ui_layout = createDom(layout, 0, ui_parentWidget)) { + QList ui_layouts; + ui_layouts.append(ui_layout); + + ui_widget->setElementLayout(ui_layouts); + } + } + } + + // widgets, actions and action groups + QList ui_widgets; + QList ui_actions; + QList ui_action_groups; + + QList children; + + // splitters need to store their children in the order specified by child indexes, + // not the order of the child list. +#ifndef QT_NO_SPLITTER + if (const QSplitter *splitter = qobject_cast(widget)) { + const int count = splitter->count(); + for (int i = 0; i < count; ++i) + children.append(splitter->widget(i)); + } else +#endif + { + QList childObjects = widget->children(); + + const QList list = qVariantValue(widget->property("_q_widgetOrder")); + foreach (QWidget *w, list) { + if (childObjects.contains(w)) { + children.append(w); + childObjects.removeAll(w); + } + } + children += childObjects; + + const QList zOrder = qVariantValue(widget->property("_q_zOrder")); + if (list != zOrder) { + QStringList zOrderList; + QListIterator itZOrder(zOrder); + while (itZOrder.hasNext()) + zOrderList.append(itZOrder.next()->objectName()); + ui_widget->setElementZOrder(zOrderList); + } + } + + foreach (QObject *obj, children) { + if (QWidget *childWidget = qobject_cast(obj)) { + if (m_laidout.contains(childWidget) || recursive == false) + continue; + + if (QMenu *menu = qobject_cast(childWidget)) { + QList actions = menu->parentWidget()->actions(); + QListIterator it(actions); + bool found = false; + while (it.hasNext()) { + if (it.next()->menu() == menu) + found = true; + } + if (!found) + continue; + } + + if (DomWidget *ui_child = createDom(childWidget, ui_widget)) { + ui_widgets.append(ui_child); + } + } else if (QAction *childAction = qobject_cast(obj)) { + if (childAction->actionGroup() != 0) { + // it will be added later. + continue; + } + + if (DomAction *ui_action = createDom(childAction)) { + ui_actions.append(ui_action); + } + } else if (QActionGroup *childActionGroup = qobject_cast(obj)) { + if (DomActionGroup *ui_action_group = createDom(childActionGroup)) { + ui_action_groups.append(ui_action_group); + } + } + } + + // add-action + QList ui_action_refs; + foreach (QAction *action, widget->actions()) { + if (DomActionRef *ui_action_ref = createActionRefDom(action)) { + ui_action_refs.append(ui_action_ref); + } + } + + if (recursive) + ui_widget->setElementWidget(ui_widgets); + + ui_widget->setElementAction(ui_actions); + ui_widget->setElementActionGroup(ui_action_groups); + ui_widget->setElementAddAction(ui_action_refs); + + saveExtraInfo(widget, ui_widget, ui_parentWidget); + + return ui_widget; +} + +/*! + \internal +*/ +DomActionRef *QAbstractFormBuilder::createActionRefDom(QAction *action) +{ + QString name = action->objectName(); + + if (action->menu() != 0) + name = action->menu()->objectName(); + + DomActionRef *ui_action_ref = new DomActionRef(); + if (action->isSeparator()) + ui_action_ref->setAttributeName(QFormBuilderStrings::instance().separator); + else + ui_action_ref->setAttributeName(name); + + return ui_action_ref; +} + +/*! + \internal +*/ +DomLayout *QAbstractFormBuilder::createDom(QLayout *layout, DomLayout *ui_layout, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_layout) + DomLayout *lay = new DomLayout(); + lay->setAttributeClass(QLatin1String(layout->metaObject()->className())); + const QString objectName = layout->objectName(); + if (!objectName.isEmpty()) + lay->setAttributeName(objectName); + lay->setElementProperty(computeProperties(layout)); + + QList ui_items; + + QMap objectToItem; + QList spacerItems; + QList newList; + + for (int idx=0; layout->itemAt(idx); ++idx) { + QLayoutItem *item = layout->itemAt(idx); + if (item->widget()) + objectToItem[item->widget()] = item; + else if (item->layout()) + objectToItem[item->layout()] = item; + else if (item->spacerItem()) + spacerItems.append(item); + newList.append(item); + } + + if (qobject_cast(layout)) { + newList.clear(); + QList childrenList = layout->parentWidget()->children(); + foreach (QObject *o, childrenList) { + if (objectToItem.contains(o)) + newList.append(objectToItem[o]); + } + newList += spacerItems; + } + + foreach (QLayoutItem *item, newList) { + DomLayoutItem *ui_item = createDom(item, lay, ui_parentWidget); + if (ui_item) + ui_items.append(ui_item); + } + + lay->setElementItem(ui_items); + + return lay; +} + +/*! + \internal +*/ +DomLayoutItem *QAbstractFormBuilder::createDom(QLayoutItem *item, DomLayout *ui_layout, DomWidget *ui_parentWidget) +{ + DomLayoutItem *ui_item = new DomLayoutItem(); + + if (item->widget()) { + ui_item->setElementWidget(createDom(item->widget(), ui_parentWidget)); + m_laidout.insert(item->widget(), true); + } else if (item->layout()) { + ui_item->setElementLayout(createDom(item->layout(), ui_layout, ui_parentWidget)); + } else if (item->spacerItem()) { + ui_item->setElementSpacer(createDom(item->spacerItem(), ui_layout, ui_parentWidget)); + } + + return ui_item; +} + +/*! + \internal +*/ +DomSpacer *QAbstractFormBuilder::createDom(QSpacerItem *spacer, DomLayout *ui_layout, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_layout); + Q_UNUSED(ui_parentWidget); + + DomSpacer *ui_spacer = new DomSpacer(); + QList properties; + + DomProperty *prop = 0; + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + // sizeHint property + prop = new DomProperty(); + prop->setAttributeName(strings.sizeHintProperty); + prop->setElementSize(new DomSize()); + prop->elementSize()->setElementWidth(spacer->sizeHint().width()); + prop->elementSize()->setElementHeight(spacer->sizeHint().height()); + properties.append(prop); + + // orientation property + prop = new DomProperty(); // ### we don't implemented the case where expandingDirections() is both Vertical and Horizontal + prop->setAttributeName(strings.orientationProperty); + prop->setElementEnum((spacer->expandingDirections() & Qt::Horizontal) ? strings.qtHorizontal : strings.qtVertical); + properties.append(prop); + + ui_spacer->setElementProperty(properties); + return ui_spacer; +} + +/*! + \internal +*/ +DomProperty *QAbstractFormBuilder::createProperty(QObject *obj, const QString &pname, const QVariant &v) +{ + if (!checkProperty(obj, pname)) { + return 0; + } + return variantToDomProperty(this, obj->metaObject(), pname, v); +} + +/*! + \internal +*/ +QList QAbstractFormBuilder::computeProperties(QObject *obj) +{ + QList lst; + + const QMetaObject *meta = obj->metaObject(); + + QHash properties; + const int propertyCount = meta->propertyCount(); + for(int i=0; i < propertyCount; ++i) + properties.insert(meta->property(i).name(), true); + + const QList propertyNames = properties.keys(); + + const int propertyNamesCount = propertyNames.size(); + for(int i=0; iproperty(meta->indexOfProperty(pname.toUtf8())); + + if (!prop.isWritable() || !checkProperty(obj, QLatin1String(prop.name()))) + continue; + + const QVariant v = prop.read(obj); + + DomProperty *dom_prop = 0; + if (v.type() == QVariant::Int) { + dom_prop = new DomProperty(); + + if (prop.isFlagType()) + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Flags property are not supported yet.")); + + if (prop.isEnumType()) { + QString scope = QString::fromUtf8(prop.enumerator().scope()); + if (scope.size()) + scope += QString::fromUtf8("::"); + const QString e = QString::fromUtf8(prop.enumerator().valueToKey(v.toInt())); + if (e.size()) + dom_prop->setElementEnum(scope + e); + } else + dom_prop->setElementNumber(v.toInt()); + dom_prop->setAttributeName(pname); + } else { + dom_prop = createProperty(obj, pname, v); + } + + if (!dom_prop || dom_prop->kind() == DomProperty::Unknown) + delete dom_prop; + else + lst.append(dom_prop); + } + + return lst; +} + + +/*! + \internal + \typedef QAbstractFormBuilder::DomPropertyHash + \typedef QAbstractFormBuilder::IconPaths +*/ + + +/*! + \internal +*/ +QAbstractFormBuilder::DomPropertyHash QAbstractFormBuilder::propertyMap(const QList &properties) +{ + DomPropertyHash map; + + foreach (DomProperty *p, properties) + map.insert(p->attributeName(), p); + + return map; +} + +/*! + \internal +*/ +bool QAbstractFormBuilder::checkProperty(QObject *obj, const QString &prop) const +{ + Q_UNUSED(obj); + Q_UNUSED(prop); + + return true; +} + +/*! + \internal +*/ +QString QAbstractFormBuilder::toString(const DomString *str) +{ + return str ? str->text() : QString(); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::applyTabStops(QWidget *widget, DomTabStops *tabStops) +{ + if (!tabStops) + return; + + QWidget *lastWidget = 0; + + const QStringList l = tabStops->elementTabStop(); + for (int i=0; i(widget, name); + if (!child) { + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "While applying tab stops: The widget '%1' could not be found.").arg(name)); + continue; + } + + if (i == 0) { + lastWidget = qFindChild(widget, name); + continue; + } else if (!child || !lastWidget) { + continue; + } + + QWidget::setTabOrder(lastWidget, child); + + lastWidget = qFindChild(widget, name); + } +} + +/*! + \internal +*/ +DomCustomWidgets *QAbstractFormBuilder::saveCustomWidgets() +{ + return 0; +} + +/*! + \internal +*/ +DomTabStops *QAbstractFormBuilder::saveTabStops() +{ + return 0; +} + +/*! + \internal +*/ +DomResources *QAbstractFormBuilder::saveResources() +{ + return 0; +} + +/*! + \internal + \since 4.5 +*/ + +DomButtonGroups *QAbstractFormBuilder::saveButtonGroups(const QWidget *mainContainer) +{ + // Save fst order buttongroup children of maincontainer + typedef QList ButtonGroupList; + const QObjectList mchildren = mainContainer->children(); + if (mchildren.empty()) + return 0; + QList domGroups; + const QObjectList::const_iterator cend = mchildren.constEnd(); + for (QObjectList::const_iterator it = mchildren.constBegin(); it != cend; ++it) + if (QButtonGroup *bg = qobject_cast(*it)) + if (DomButtonGroup* dg = createDom(bg)) + domGroups.push_back(dg); + + if (domGroups.empty()) + return 0; + DomButtonGroups *rc = new DomButtonGroups; + rc->setElementButtonGroup(domGroups); + return rc; +} + +// VC6 would not find templated members, so we use statics and this utter hack. +class FriendlyFB : public QAbstractFormBuilder { +public: + using QAbstractFormBuilder::saveResource; + using QAbstractFormBuilder::saveText; + using QAbstractFormBuilder::resourceBuilder; + using QAbstractFormBuilder::textBuilder; + using QAbstractFormBuilder::toVariant; +}; + +template +static void storeItemFlags(const T *item, QList *properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + static const Qt::ItemFlags defaultFlags = T().flags(); + static const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + + if (item->flags() != defaultFlags) { + DomProperty *p = new DomProperty; + p->setAttributeName(strings.flagsAttribute); + p->setElementSet(QString::fromAscii(itemFlags_enum.valueToKeys(item->flags()))); + properties->append(p); + } +} + +#ifndef Q_CC_RVCT +// RVCT does not accept static inline functions if one argument is templated type +// For this reason all necessary function variants are explicityly written for it. +template +static void storeItemProps(QAbstractFormBuilder *abstractFormBuilder, const T *item, + QList *properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = formBuilder->saveText(it.second, item->data(it.first.second)))) + properties->append(p); + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((v = item->data(it.first)).isValid() && + (p = variantToDomProperty(abstractFormBuilder, + static_cast(&QAbstractFormBuilderGadget::staticMetaObject), + it.second, v))) + properties->append(p); + + if ((p = formBuilder->saveResource(item->data(Qt::DecorationPropertyRole)))) + properties->append(p); +} + +template +static void storeItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, const T *item, + QList *properties) +{ + storeItemProps(abstractFormBuilder, item, properties); + storeItemFlags(item, properties); +} + +template +static void loadItemProps(QAbstractFormBuilder *abstractFormBuilder, T *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = properties.value(it.second))) { + v = formBuilder->textBuilder()->loadText(p); + QVariant nativeValue = formBuilder->textBuilder()->toNativeValue(v); + item->setData(it.first.first, qVariantValue(nativeValue)); + item->setData(it.first.second, v); + } + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((p = properties.value(it.second)) && + (v = formBuilder->toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid()) + item->setData(it.first, v); + + if ((p = properties.value(strings.iconAttribute))) { + v = formBuilder->resourceBuilder()->loadResource(formBuilder->workingDirectory(), p); + QVariant nativeValue = formBuilder->resourceBuilder()->toNativeValue(v); + item->setIcon(qVariantValue(nativeValue)); + item->setData(Qt::DecorationPropertyRole, v); + } +} + +template +static void loadItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, T *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + static const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + + loadItemProps(abstractFormBuilder, item, properties); + + DomProperty *p; + if ((p = properties.value(strings.flagsAttribute)) && p->kind() == DomProperty::Set) + item->setFlags(enumKeysToValue(itemFlags_enum, p->elementSet().toAscii())); +} + +#else + +static void storeItemProps(QAbstractFormBuilder *abstractFormBuilder, const QTableWidgetItem *item, + QList *properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = formBuilder->saveText(it.second, item->data(it.first.second)))) + properties->append(p); + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((v = item->data(it.first)).isValid() && + (p = variantToDomProperty(abstractFormBuilder, + static_cast(&QAbstractFormBuilderGadget::staticMetaObject), + it.second, v))) + properties->append(p); + + if ((p = formBuilder->saveResource(item->data(Qt::DecorationPropertyRole)))) + properties->append(p); +} + +static void storeItemProps(QAbstractFormBuilder *abstractFormBuilder, const QListWidgetItem *item, + QList *properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = formBuilder->saveText(it.second, item->data(it.first.second)))) + properties->append(p); + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((v = item->data(it.first)).isValid() && + (p = variantToDomProperty(abstractFormBuilder, + static_cast(&QAbstractFormBuilderGadget::staticMetaObject), + it.second, v))) + properties->append(p); + + if ((p = formBuilder->saveResource(item->data(Qt::DecorationPropertyRole)))) + properties->append(p); +} + +static void storeItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, const QTableWidgetItem *item, + QList *properties) +{ + storeItemProps(abstractFormBuilder, item, properties); + storeItemFlags(item, properties); +} + +static void storeItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, const QListWidgetItem *item, + QList *properties) +{ + storeItemProps(abstractFormBuilder, item, properties); + storeItemFlags(item, properties); +} + +static void loadItemProps(QAbstractFormBuilder *abstractFormBuilder, QTableWidgetItem *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = properties.value(it.second))) { + v = formBuilder->textBuilder()->loadText(p); + QVariant nativeValue = formBuilder->textBuilder()->toNativeValue(v); + item->setData(it.first.first, qVariantValue(nativeValue)); + item->setData(it.first.second, v); + } + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((p = properties.value(it.second)) && + (v = formBuilder->toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid()) + item->setData(it.first, v); + + if ((p = properties.value(strings.iconAttribute))) { + v = formBuilder->resourceBuilder()->loadResource(formBuilder->workingDirectory(), p); + QVariant nativeValue = formBuilder->resourceBuilder()->toNativeValue(v); + item->setIcon(qVariantValue(nativeValue)); + item->setData(Qt::DecorationPropertyRole, v); + } +} + +static void loadItemProps(QAbstractFormBuilder *abstractFormBuilder, QListWidgetItem *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + FriendlyFB * const formBuilder = static_cast(abstractFormBuilder); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = properties.value(it.second))) { + v = formBuilder->textBuilder()->loadText(p); + QVariant nativeValue = formBuilder->textBuilder()->toNativeValue(v); + item->setData(it.first.first, qVariantValue(nativeValue)); + item->setData(it.first.second, v); + } + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((p = properties.value(it.second)) && + (v = formBuilder->toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid()) + item->setData(it.first, v); + + if ((p = properties.value(strings.iconAttribute))) { + v = formBuilder->resourceBuilder()->loadResource(formBuilder->workingDirectory(), p); + QVariant nativeValue = formBuilder->resourceBuilder()->toNativeValue(v); + item->setIcon(qVariantValue(nativeValue)); + item->setData(Qt::DecorationPropertyRole, v); + } +} + +static void loadItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, QTableWidgetItem *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + static const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + + loadItemProps(abstractFormBuilder, item, properties); + + DomProperty *p; + if ((p = properties.value(strings.flagsAttribute)) && p->kind() == DomProperty::Set) + item->setFlags(enumKeysToValue(itemFlags_enum, p->elementSet().toAscii())); +} + +static void loadItemPropsNFlags(QAbstractFormBuilder *abstractFormBuilder, QListWidgetItem *item, + const QHash &properties) +{ + static const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + static const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + + loadItemProps(abstractFormBuilder, item, properties); + + DomProperty *p; + if ((p = properties.value(strings.flagsAttribute)) && p->kind() == DomProperty::Set) + item->setFlags(enumKeysToValue(itemFlags_enum, p->elementSet().toAscii())); +} + +#endif + +/*! + \internal +*/ +void QAbstractFormBuilder::saveTreeWidgetExtraInfo(QTreeWidget *treeWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_parentWidget); + + QList columns; + DomProperty *p; + QVariant v; + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + // save the header + for (int c = 0; ccolumnCount(); ++c) { + DomColumn *column = new DomColumn; + + QList properties; + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) { + p = saveText(it.second, treeWidget->headerItem()->data(c, it.first.second)); + // Prevent uic 4.4.X from crashing if it cannot find a column text + if (!p && it.first.first == Qt::EditRole && it.second == QLatin1String("text")) { + DomString *defaultHeader = new DomString; + defaultHeader->setText(QString::number(c + 1)); + defaultHeader->setAttributeNotr(QLatin1String("true")); + p = new DomProperty; + p->setAttributeName(it.second); + p->setElementString(defaultHeader); + } + if (p) + properties.append(p); + } + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((v = treeWidget->headerItem()->data(c, it.first)).isValid() && + (p = variantToDomProperty(this, &QAbstractFormBuilderGadget::staticMetaObject, it.second, v))) + properties.append(p); + + if ((p = saveResource(treeWidget->headerItem()->data(c, Qt::DecorationPropertyRole)))) + properties.append(p); + + column->setElementProperty(properties); + columns.append(column); + } + + ui_widget->setElementColumn(columns); + + QList items = ui_widget->elementItem(); + + QQueue > pendingQueue; + for (int i = 0; i < treeWidget->topLevelItemCount(); i++) + pendingQueue.enqueue(qMakePair(treeWidget->topLevelItem(i), (DomItem *)0)); + + while (!pendingQueue.isEmpty()) { + const QPair pair = pendingQueue.dequeue(); + QTreeWidgetItem *item = pair.first; + DomItem *parentDomItem = pair.second; + + DomItem *currentDomItem = new DomItem; + + QList properties; + for (int c = 0; c < treeWidget->columnCount(); c++) { + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = saveText(it.second, item->data(c, it.first.second)))) + properties.append(p); + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((v = item->data(c, it.first)).isValid() && + (p = variantToDomProperty(this, &QAbstractFormBuilderGadget::staticMetaObject, it.second, v))) + properties.append(p); + + if ((p = saveResource(item->data(c, Qt::DecorationPropertyRole)))) + properties.append(p); + } + storeItemFlags(item, &properties); + currentDomItem->setElementProperty(properties); + + if (parentDomItem) { + QList childrenItems = parentDomItem->elementItem(); + childrenItems.append(currentDomItem); + parentDomItem->setElementItem(childrenItems); + } else + items.append(currentDomItem); + + for (int i = 0; i < item->childCount(); i++) + pendingQueue.enqueue(qMakePair(item->child(i), currentDomItem)); + } + + ui_widget->setElementItem(items); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::saveTableWidgetExtraInfo(QTableWidget *tableWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_parentWidget); + + // save the horizontal header + QList columns; + for (int c = 0; c < tableWidget->columnCount(); c++) { + QList properties; + QTableWidgetItem *item = tableWidget->horizontalHeaderItem(c); + if (item) + storeItemProps(this, item, &properties); + + DomColumn *column = new DomColumn; + column->setElementProperty(properties); + columns.append(column); + } + ui_widget->setElementColumn(columns); + + // save the vertical header + QList rows; + for (int r = 0; r < tableWidget->rowCount(); r++) { + QList properties; + QTableWidgetItem *item = tableWidget->verticalHeaderItem(r); + if (item) + storeItemProps(this, item, &properties); + + DomRow *row = new DomRow; + row->setElementProperty(properties); + rows.append(row); + } + ui_widget->setElementRow(rows); + + QList items = ui_widget->elementItem(); + for (int r = 0; r < tableWidget->rowCount(); r++) + for (int c = 0; c < tableWidget->columnCount(); c++) { + QTableWidgetItem *item = tableWidget->item(r, c); + if (item) { + QList properties; + storeItemPropsNFlags(this, item, &properties); + + DomItem *domItem = new DomItem; + domItem->setAttributeRow(r); + domItem->setAttributeColumn(c); + domItem->setElementProperty(properties); + items.append(domItem); + } + } + + ui_widget->setElementItem(items); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::saveListWidgetExtraInfo(QListWidget *listWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_parentWidget); + + QList ui_items = ui_widget->elementItem(); + for (int i=0; icount(); ++i) { + QList properties; + storeItemPropsNFlags(this, listWidget->item(i), &properties); + + DomItem *ui_item = new DomItem(); + ui_item->setElementProperty(properties); + ui_items.append(ui_item); + } + + ui_widget->setElementItem(ui_items); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::saveComboBoxExtraInfo(QComboBox *comboBox, DomWidget *ui_widget, DomWidget *ui_parentWidget) +{ + Q_UNUSED(ui_parentWidget); + QList ui_items = ui_widget->elementItem(); + + const int count = comboBox->count(); + for (int i=0; i < count; ++i) { + // We might encounter items for which both builders return 0 in Designer + // (indicating a custom combo adding items in the constructor). Ignore those. + DomProperty *textProperty = saveText(QFormBuilderStrings::instance().textAttribute, + comboBox->itemData(i, Qt::DisplayPropertyRole)); + DomProperty *iconProperty = saveResource(comboBox->itemData(i, Qt::DecorationPropertyRole)); + if (textProperty || iconProperty) { + QList properties; + if (textProperty) + properties.push_back(textProperty); + if (iconProperty) + properties.push_back(iconProperty); + + DomItem *ui_item = new DomItem(); + ui_item->setElementProperty(properties); + ui_items.push_back(ui_item); + } + } + + ui_widget->setElementItem(ui_items); +} + +// Return the buttongroups assigned to a button except the internal one +// (with empty object name) used by Q3ButtonGroup. +static inline const QButtonGroup *formButtonGroup(const QAbstractButton *widget) +{ + const QButtonGroup *buttonGroup = widget->group(); + if (!buttonGroup) + return 0; + if (buttonGroup->objectName().isEmpty()) { + if (const QWidget *parent = widget->parentWidget()) + if (!qstrcmp(parent->metaObject()->className(), "Q3ButtonGroup")) + return 0; + } + return buttonGroup; +} + +/*! + \internal + \since 4.5 +*/ + +void QAbstractFormBuilder::saveButtonExtraInfo(const QAbstractButton *widget, DomWidget *ui_widget, DomWidget *) +{ + typedef QList DomPropertyList; + if (const QButtonGroup *buttonGroup = formButtonGroup(widget)) { + DomPropertyList attributes = ui_widget->elementAttribute(); + DomString *domString = new DomString(); + domString->setText(buttonGroup->objectName()); + DomProperty *domProperty = new DomProperty(); + domProperty->setAttributeName(QLatin1String(buttonGroupPropertyC)); + domProperty->setElementString(domString); + attributes += domProperty; + ui_widget->setElementAttribute(attributes); + } +} + +/*! + \internal + \since 4.5 +*/ +void QAbstractFormBuilder::saveItemViewExtraInfo(const QAbstractItemView *itemView, + DomWidget *ui_widget, DomWidget *) +{ + // + // Special handling for qtableview/qtreeview fake header attributes + // + static QStringList realPropertyNames = + (QStringList() << QLatin1String("visible") + << QLatin1String("cascadingSectionResizes") + << QLatin1String("defaultSectionSize") + << QLatin1String("highlightSections") + << QLatin1String("minimumSectionSize") + << QLatin1String("showSortIndicator") + << QLatin1String("stretchLastSection")); + + if (const QTreeView *treeView = qobject_cast(itemView)) { + QList viewProperties = ui_widget->elementAttribute(); + QList headerProperties = computeProperties(treeView->header()); + foreach (const QString &realPropertyName, realPropertyNames) { + const QString upperPropertyName = realPropertyName.at(0).toUpper() + + realPropertyName.mid(1); + const QString fakePropertyName = QLatin1String("header") + upperPropertyName; + foreach (DomProperty *property, headerProperties) { + if (property->attributeName() == realPropertyName) { + property->setAttributeName(fakePropertyName); + viewProperties << property; + } + } + } + ui_widget->setElementAttribute(viewProperties); + } else if (const QTableView *tableView = qobject_cast(itemView)) { + static QStringList headerPrefixes = + (QStringList() << QLatin1String("horizontalHeader") + << QLatin1String("verticalHeader")); + + QList viewProperties = ui_widget->elementAttribute(); + foreach (const QString &headerPrefix, headerPrefixes) { + QList headerProperties; + if (headerPrefix == QLatin1String("horizontalHeader")) + headerProperties = computeProperties(tableView->horizontalHeader()); + else + headerProperties = computeProperties(tableView->verticalHeader()); + foreach (const QString &realPropertyName, realPropertyNames) { + const QString upperPropertyName = realPropertyName.at(0).toUpper() + + realPropertyName.mid(1); + const QString fakePropertyName = headerPrefix + upperPropertyName; + foreach (DomProperty *property, headerProperties) { + if (property->attributeName() == realPropertyName) { + property->setAttributeName(fakePropertyName); + viewProperties << property; + } + } + } + } + ui_widget->setElementAttribute(viewProperties); + } +} + +/*! + \internal + \since 4.4 +*/ + +void QAbstractFormBuilder::setResourceBuilder(QResourceBuilder *builder) +{ + QFormBuilderExtra::instance(this)->setResourceBuilder(builder); +} + +/*! + \internal + \since 4.4 +*/ + +QResourceBuilder *QAbstractFormBuilder::resourceBuilder() const +{ + return QFormBuilderExtra::instance(this)->resourceBuilder(); +} + +/*! + \internal + \since 4.5 +*/ + +void QAbstractFormBuilder::setTextBuilder(QTextBuilder *builder) +{ + QFormBuilderExtra::instance(this)->setTextBuilder(builder); +} + +/*! + \internal + \since 4.5 +*/ + +QTextBuilder *QAbstractFormBuilder::textBuilder() const +{ + return QFormBuilderExtra::instance(this)->textBuilder(); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::saveExtraInfo(QWidget *widget, DomWidget *ui_widget, + DomWidget *ui_parentWidget) +{ + if (QListWidget *listWidget = qobject_cast(widget)) { + saveListWidgetExtraInfo(listWidget, ui_widget, ui_parentWidget); + } else if (QTreeWidget *treeWidget = qobject_cast(widget)) { + saveTreeWidgetExtraInfo(treeWidget, ui_widget, ui_parentWidget); + } else if (QTableWidget *tableWidget = qobject_cast(widget)) { + saveTableWidgetExtraInfo(tableWidget, ui_widget, ui_parentWidget); + } else if (QComboBox *comboBox = qobject_cast(widget)) { + if (!qobject_cast(widget)) + saveComboBoxExtraInfo(comboBox, ui_widget, ui_parentWidget); + } else if(QAbstractButton *ab = qobject_cast(widget)) { + saveButtonExtraInfo(ab, ui_widget, ui_parentWidget); + } + if (QAbstractItemView *itemView = qobject_cast(widget)) { + saveItemViewExtraInfo(itemView, ui_widget, ui_parentWidget); + } +} + +/*! + \internal +*/ +void QAbstractFormBuilder::loadListWidgetExtraInfo(DomWidget *ui_widget, QListWidget *listWidget, QWidget *parentWidget) +{ + Q_UNUSED(parentWidget); + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + + foreach (DomItem *ui_item, ui_widget->elementItem()) { + const DomPropertyHash properties = propertyMap(ui_item->elementProperty()); + QListWidgetItem *item = new QListWidgetItem(listWidget); +#ifndef Q_CC_RVCT + loadItemPropsNFlags(this, item, properties); +#else + loadItemPropsNFlags(this, item, properties); +#endif + } + + DomProperty *currentRow = propertyMap(ui_widget->elementProperty()).value(strings.currentRowProperty); + if (currentRow) + listWidget->setCurrentRow(currentRow->elementNumber()); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::loadTreeWidgetExtraInfo(DomWidget *ui_widget, QTreeWidget *treeWidget, QWidget *parentWidget) +{ + Q_UNUSED(parentWidget); + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + const QMetaEnum itemFlags_enum = metaEnum("itemFlags"); + const QList columns = ui_widget->elementColumn(); + if (columns.count() > 0) + treeWidget->setColumnCount(columns.count()); + + for (int i = 0; ielementProperty()); + + DomProperty *p; + QVariant v; + + foreach (const QFormBuilderStrings::RoleNName &it, strings.itemRoles) + if ((p = properties.value(it.second)) && + (v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, p)).isValid()) + treeWidget->headerItem()->setData(i, it.first, v); + + foreach (const QFormBuilderStrings::TextRoleNName &it, strings.itemTextRoles) + if ((p = properties.value(it.second))) { + v = textBuilder()->loadText(p); + QVariant nativeValue = textBuilder()->toNativeValue(v); + treeWidget->headerItem()->setData(i, it.first.first, qVariantValue(nativeValue)); + treeWidget->headerItem()->setData(i, it.first.second, v); + } + + if ((p = properties.value(strings.iconAttribute))) { + v = resourceBuilder()->loadResource(workingDirectory(), p); + QVariant nativeValue = resourceBuilder()->toNativeValue(v); + treeWidget->headerItem()->setIcon(i, qVariantValue(nativeValue)); + treeWidget->headerItem()->setData(i, Qt::DecorationPropertyRole, v); + } + } + + QQueue > pendingQueue; + foreach (DomItem *ui_item, ui_widget->elementItem()) + pendingQueue.enqueue(qMakePair(ui_item, (QTreeWidgetItem *)0)); + + while (!pendingQueue.isEmpty()) { + const QPair pair = pendingQueue.dequeue(); + const DomItem *domItem = pair.first; + QTreeWidgetItem *parentItem = pair.second; + + QTreeWidgetItem *currentItem = 0; + + if (parentItem) + currentItem = new QTreeWidgetItem(parentItem); + else + currentItem = new QTreeWidgetItem(treeWidget); + + const QList properties = domItem->elementProperty(); + int col = -1; + foreach (DomProperty *property, properties) { + if (property->attributeName() == strings.flagsAttribute && !property->elementSet().isEmpty()) { + currentItem->setFlags(enumKeysToValue(itemFlags_enum, property->elementSet().toAscii())); + } else if (property->attributeName() == strings.textAttribute && property->elementString()) { + col++; + QVariant textV = textBuilder()->loadText(property); + QVariant nativeValue = textBuilder()->toNativeValue(textV); + currentItem->setText(col, qVariantValue(nativeValue)); + currentItem->setData(col, Qt::DisplayPropertyRole, textV); + } else if (col >= 0) { + if (property->attributeName() == strings.iconAttribute) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), property); + if (v.isValid()) { + QVariant nativeValue = resourceBuilder()->toNativeValue(v); + currentItem->setIcon(col, qVariantValue(nativeValue)); + currentItem->setData(col, Qt::DecorationPropertyRole, v); + } + } else { + QVariant v; + int role = strings.treeItemRoleHash.value(property->attributeName(), (Qt::ItemDataRole)-1); + if (role >= 0) { + if ((v = toVariant(&QAbstractFormBuilderGadget::staticMetaObject, property)).isValid()) + currentItem->setData(col, role, v); + } else { + QPair rolePair = + strings.treeItemTextRoleHash.value(property->attributeName(), + qMakePair((Qt::ItemDataRole)-1, (Qt::ItemDataRole)-1)); + if (rolePair.first >= 0) { + QVariant textV = textBuilder()->loadText(property); + QVariant nativeValue = textBuilder()->toNativeValue(textV); + currentItem->setData(col, rolePair.first, qVariantValue(nativeValue)); + currentItem->setData(col, rolePair.second, textV); + } + } + } + } + } + + foreach (DomItem *childItem, domItem->elementItem()) + pendingQueue.enqueue(qMakePair(childItem, currentItem)); + + } +} + +/*! + \internal +*/ +void QAbstractFormBuilder::loadTableWidgetExtraInfo(DomWidget *ui_widget, QTableWidget *tableWidget, QWidget *parentWidget) +{ + Q_UNUSED(parentWidget); + + const QList columns = ui_widget->elementColumn(); + if (columns.count() > 0) + tableWidget->setColumnCount(columns.count()); + for (int i = 0; i< columns.count(); i++) { + DomColumn *c = columns.at(i); + const DomPropertyHash properties = propertyMap(c->elementProperty()); + + if (!properties.isEmpty()) { + QTableWidgetItem *item = new QTableWidgetItem; + loadItemProps(this, item, properties); + tableWidget->setHorizontalHeaderItem(i, item); + } + } + + const QList rows = ui_widget->elementRow(); + if (rows.count() > 0) + tableWidget->setRowCount(rows.count()); + for (int i = 0; i< rows.count(); i++) { + const DomRow *r = rows.at(i); + const DomPropertyHash properties = propertyMap(r->elementProperty()); + + if (!properties.isEmpty()) { + QTableWidgetItem *item = new QTableWidgetItem; + loadItemProps(this, item, properties); + tableWidget->setVerticalHeaderItem(i, item); + } + } + + foreach (DomItem *ui_item, ui_widget->elementItem()) { + if (ui_item->hasAttributeRow() && ui_item->hasAttributeColumn()) { + const DomPropertyHash properties = propertyMap(ui_item->elementProperty()); + QTableWidgetItem *item = new QTableWidgetItem; + loadItemPropsNFlags(this, item, properties); + tableWidget->setItem(ui_item->attributeRow(), ui_item->attributeColumn(), item); + } + } +} + +/*! + \internal +*/ +void QAbstractFormBuilder::loadComboBoxExtraInfo(DomWidget *ui_widget, QComboBox *comboBox, QWidget *parentWidget) +{ + Q_UNUSED(parentWidget); + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + foreach (DomItem *ui_item, ui_widget->elementItem()) { + const DomPropertyHash properties = propertyMap(ui_item->elementProperty()); + QString text; + QIcon icon; + QVariant textData; + QVariant iconData; + + DomProperty *p = 0; + + p = properties.value(strings.textAttribute); + if (p && p->elementString()) { + textData = textBuilder()->loadText(p); + text = qVariantValue(textBuilder()->toNativeValue(textData)); + } + + p = properties.value(strings.iconAttribute); + if (p) { + iconData = resourceBuilder()->loadResource(workingDirectory(), p); + icon = qVariantValue(resourceBuilder()->toNativeValue(iconData)); + } + + comboBox->addItem(icon, text); + comboBox->setItemData((comboBox->count()-1), iconData, Qt::DecorationPropertyRole); + comboBox->setItemData((comboBox->count()-1), textData, Qt::DisplayPropertyRole); + } + + DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty); + if (currentIndex) + comboBox->setCurrentIndex(currentIndex->elementNumber()); +} + +// Get the button group name out of a widget's attribute list +static QString buttonGroupName(const DomWidget *ui_widget) +{ + typedef QList DomPropertyList; + const DomPropertyList attributes = ui_widget->elementAttribute(); + if (attributes.empty()) + return QString(); + const QString buttonGroupProperty = QLatin1String(buttonGroupPropertyC); + const DomPropertyList::const_iterator cend = attributes.constEnd(); + for (DomPropertyList::const_iterator it = attributes.constBegin(); it != cend; ++it) + if ( (*it)->attributeName() == buttonGroupProperty) + return (*it)->elementString()->text(); + return QString(); +} + +/*! + \internal + \since 4.5 +*/ + +void QAbstractFormBuilder::loadButtonExtraInfo(const DomWidget *ui_widget, QAbstractButton *button, QWidget *) +{ + typedef QFormBuilderExtra::ButtonGroupEntry ButtonGroupEntry; + typedef QFormBuilderExtra::ButtonGroupHash ButtonGroupHash; + + const QString groupName = buttonGroupName(ui_widget); + if (groupName.isEmpty()) + return; + // Find entry + QFormBuilderExtra *extra = QFormBuilderExtra::instance(this); + ButtonGroupHash &buttonGroups = extra->buttonGroups(); + ButtonGroupHash::iterator it = buttonGroups.find(groupName); + if (it == buttonGroups.end()) { +#ifdef QFORMINTERNAL_NAMESPACE // Suppress the warning when copying in Designer + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "Invalid QButtonGroup reference '%1' referenced by '%2'.").arg(groupName, button->objectName())); +#endif + return; + } + // Create button group on demand? + QButtonGroup *&group = it.value().second; + if (group == 0) { + group = new QButtonGroup; + group->setObjectName(groupName); + applyProperties(group, it.value().first->elementProperty()); + } + group->addButton(button); +} + +/*! + \internal + \since 4.5 +*/ +void QAbstractFormBuilder::loadItemViewExtraInfo(DomWidget *ui_widget, QAbstractItemView *itemView, + QWidget *) +{ + // + // Special handling for qtableview/qtreeview fake header attributes + // + static QStringList realPropertyNames = + (QStringList() << QLatin1String("visible") + << QLatin1String("cascadingSectionResizes") + << QLatin1String("defaultSectionSize") + << QLatin1String("highlightSections") + << QLatin1String("minimumSectionSize") + << QLatin1String("showSortIndicator") + << QLatin1String("stretchLastSection")); + + if (QTreeView *treeView = qobject_cast(itemView)) { + QList allAttributes = ui_widget->elementAttribute(); + QList headerProperties; + foreach (const QString &realPropertyName, realPropertyNames) { + const QString upperPropertyName = realPropertyName.at(0).toUpper() + + realPropertyName.mid(1); + const QString fakePropertyName = QLatin1String("header") + upperPropertyName; + foreach (DomProperty *attr, allAttributes) { + if (attr->attributeName() == fakePropertyName) { + attr->setAttributeName(realPropertyName); + headerProperties << attr; + } + } + } + applyProperties(treeView->header(), headerProperties); + } else if (QTableView *tableView = qobject_cast(itemView)) { + static QStringList headerPrefixes = + (QStringList() << QLatin1String("horizontalHeader") + << QLatin1String("verticalHeader")); + + QList allAttributes = ui_widget->elementAttribute(); + foreach (const QString &headerPrefix, headerPrefixes) { + QList headerProperties; + foreach (const QString &realPropertyName, realPropertyNames) { + const QString upperPropertyName = realPropertyName.at(0).toUpper() + + realPropertyName.mid(1); + const QString fakePropertyName = headerPrefix + upperPropertyName; + foreach (DomProperty *attr, allAttributes) { + if (attr->attributeName() == fakePropertyName) { + attr->setAttributeName(realPropertyName); + headerProperties << attr; + } + } + } + if (headerPrefix == QLatin1String("horizontalHeader")) + applyProperties(tableView->horizontalHeader(), headerProperties); + else + applyProperties(tableView->verticalHeader(), headerProperties); + } + } +} + +/*! + \internal +*/ +void QAbstractFormBuilder::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + if (0) { +#ifndef QT_NO_LISTWIDGET + } else if (QListWidget *listWidget = qobject_cast(widget)) { + loadListWidgetExtraInfo(ui_widget, listWidget, parentWidget); +#endif +#ifndef QT_NO_TREEWIDGET + } else if (QTreeWidget *treeWidget = qobject_cast(widget)) { + loadTreeWidgetExtraInfo(ui_widget, treeWidget, parentWidget); +#endif +#ifndef QT_NO_TABLEWIDGET + } else if (QTableWidget *tableWidget = qobject_cast(widget)) { + loadTableWidgetExtraInfo(ui_widget, tableWidget, parentWidget); +#endif +#ifndef QT_NO_COMBOBOX + } else if (QComboBox *comboBox = qobject_cast(widget)) { + if (!qobject_cast(widget)) + loadComboBoxExtraInfo(ui_widget, comboBox, parentWidget); +#endif +#ifndef QT_NO_TABWIDGET + } else if (QTabWidget *tabWidget = qobject_cast(widget)) { + const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty); + if (currentIndex) + tabWidget->setCurrentIndex(currentIndex->elementNumber()); +#endif +#ifndef QT_NO_STACKEDWIDGET + } else if (QStackedWidget *stackedWidget = qobject_cast(widget)) { + const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty); + if (currentIndex) + stackedWidget->setCurrentIndex(currentIndex->elementNumber()); +#endif +#ifndef QT_NO_TOOLBOX + } else if (QToolBox *toolBox = qobject_cast(widget)) { + const DomProperty *currentIndex = propertyMap(ui_widget->elementProperty()).value(strings.currentIndexProperty); + if (currentIndex) + toolBox->setCurrentIndex(currentIndex->elementNumber()); + const DomProperty *tabSpacing = propertyMap(ui_widget->elementProperty()).value(strings.tabSpacingProperty); + if (tabSpacing) + toolBox->layout()->setSpacing(tabSpacing->elementNumber()); +#endif + } else if (QAbstractButton *ab = qobject_cast(widget)) { + loadButtonExtraInfo(ui_widget, ab, parentWidget); + } + if (QAbstractItemView *itemView = qobject_cast(widget)) { + loadItemViewExtraInfo(ui_widget, itemView, parentWidget); + } +} + +/*! + \internal +*/ +QIcon QAbstractFormBuilder::nameToIcon(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QAbstractFormBuilder::nameToIcon() is obsoleted"; + return QIcon(); +} + +/*! + \internal +*/ +QString QAbstractFormBuilder::iconToFilePath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QAbstractFormBuilder::iconToFilePath() is obsoleted"; + return QString(); +} + +/*! + \internal +*/ +QString QAbstractFormBuilder::iconToQrcPath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QAbstractFormBuilder::iconToQrcPath() is obsoleted"; + return QString(); +} + +/*! + \internal +*/ +QPixmap QAbstractFormBuilder::nameToPixmap(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QAbstractFormBuilder::nameToPixmap() is obsoleted"; + return QPixmap(); +} + +/*! + \internal +*/ +QString QAbstractFormBuilder::pixmapToFilePath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QAbstractFormBuilder::pixmapToFilePath() is obsoleted"; + return QString(); +} + +/*! + \internal +*/ +QString QAbstractFormBuilder::pixmapToQrcPath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QAbstractFormBuilder::pixmapToQrcPath() is obsoleted"; + return QString(); +} + +/*! + Returns the current working directory of the form builder. + + \sa setWorkingDirectory() */ +QDir QAbstractFormBuilder::workingDirectory() const +{ + return m_workingDirectory; +} + +/*! + Sets the current working directory of the form builder to the + specified \a directory. + + \sa workingDirectory()*/ +void QAbstractFormBuilder::setWorkingDirectory(const QDir &directory) +{ + m_workingDirectory = directory; +} + +/*! + \internal +*/ +DomAction *QAbstractFormBuilder::createDom(QAction *action) +{ + if (action->parentWidget() == action->menu() || action->isSeparator()) + return 0; + + DomAction *ui_action = new DomAction; + ui_action->setAttributeName(action->objectName()); + + const QList properties = computeProperties(action); + ui_action->setElementProperty(properties); + + return ui_action; +} + +/*! + \internal + \since 4.5 +*/ + +DomButtonGroup *QAbstractFormBuilder::createDom(QButtonGroup *buttonGroup) +{ + if (buttonGroup->buttons().count() == 0) // Empty group left over on form? + return 0; + DomButtonGroup *domButtonGroup = new DomButtonGroup; + domButtonGroup->setAttributeName(buttonGroup->objectName()); + + QList properties = computeProperties(buttonGroup); + domButtonGroup->setElementProperty(properties); + return domButtonGroup; +} + +/*! + \internal +*/ +DomActionGroup *QAbstractFormBuilder::createDom(QActionGroup *actionGroup) +{ + DomActionGroup *ui_action_group = new DomActionGroup; + ui_action_group->setAttributeName(actionGroup->objectName()); + + QList properties = computeProperties(actionGroup); + ui_action_group->setElementProperty(properties); + + QList ui_actions; + + foreach (QAction *action, actionGroup->actions()) { + if (DomAction *ui_action = createDom(action)) { + ui_actions.append(ui_action); + } + } + + ui_action_group->setElementAction(ui_actions); + + return ui_action_group; +} + +/*! + \internal +*/ +void QAbstractFormBuilder::addMenuAction(QAction *action) +{ + Q_UNUSED(action); +} + +/*! + \internal +*/ +void QAbstractFormBuilder::reset() +{ + m_laidout.clear(); + m_actions.clear(); + m_actionGroups.clear(); + m_defaultMargin = INT_MIN; + m_defaultSpacing = INT_MIN; +} + +/*! + \internal + Access meta enumeration for Qt::ToolBarArea +*/ + +QMetaEnum QAbstractFormBuilder::toolBarAreaMetaEnum() +{ + return metaEnum("toolBarArea"); +} + +/*! + \internal + Return paths of an icon. +*/ + +QAbstractFormBuilder::IconPaths QAbstractFormBuilder::iconPaths(const QIcon &icon) const +{ + Q_UNUSED(icon); + qWarning() << "QAbstractFormBuilder::iconPaths() is obsoleted"; + return IconPaths(); +} + +/*! + \internal + Return paths of a pixmap. +*/ + +QAbstractFormBuilder::IconPaths QAbstractFormBuilder::pixmapPaths(const QPixmap &pixmap) const +{ + Q_UNUSED(pixmap); + qWarning() << "QAbstractFormBuilder::pixmapPaths() is obsoleted"; + return IconPaths(); +} + +/*! + \internal + Set up a DOM property with icon. +*/ + +void QAbstractFormBuilder::setIconProperty(DomProperty &p, const IconPaths &ip) const +{ + DomResourceIcon *dpi = new DomResourceIcon; + + /* TODO + if (!ip.second.isEmpty()) + pix->setAttributeResource(ip.second); +*/ + dpi->setText(ip.first); + + p.setAttributeName(QFormBuilderStrings::instance().iconAttribute); + p.setElementIconSet(dpi); +} + +/*! + \internal + Set up a DOM property with pixmap. +*/ + +void QAbstractFormBuilder::setPixmapProperty(DomProperty &p, const IconPaths &ip) const +{ + DomResourcePixmap *pix = new DomResourcePixmap; + if (!ip.second.isEmpty()) + pix->setAttributeResource(ip.second); + + pix->setText(ip.first); + + p.setAttributeName(QFormBuilderStrings::instance().pixmapAttribute); + p.setElementPixmap(pix); +} + +/*! + \internal + Convenience. Return DOM property for icon; 0 if icon.isNull(). +*/ + +DomProperty* QAbstractFormBuilder::iconToDomProperty(const QIcon &icon) const +{ + Q_UNUSED(icon); + qWarning() << "QAbstractFormBuilder::iconToDomProperty() is obsoleted"; + return 0; +} + +/*! + \internal + \since 4.4 +*/ + +DomProperty *QAbstractFormBuilder::saveResource(const QVariant &v) const +{ + if (v.isNull()) + return 0; + + DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), v); + if (p) + p->setAttributeName(QFormBuilderStrings::instance().iconAttribute); + return p; +} + +/*! + \internal + \since 4.5 +*/ + +DomProperty *QAbstractFormBuilder::saveText(const QString &attributeName, const QVariant &v) const +{ + if (v.isNull()) + return 0; + + DomProperty *p = textBuilder()->saveText(v); + if (p) + p->setAttributeName(attributeName); + return p; +} + +/*! + \internal + Return the appropriate DOM pixmap for an image dom property. + From 4.4 - unused +*/ + +const DomResourcePixmap *QAbstractFormBuilder::domPixmap(const DomProperty* p) { + switch (p->kind()) { + case DomProperty::IconSet: + qDebug() << "** WARNING QAbstractFormBuilder::domPixmap() called for icon set!"; + break; + case DomProperty::Pixmap: + return p->elementPixmap(); + default: + break; + } + return 0; +} + +/*! + \internal + Create icon from DOM. + From 4.4 - unused +*/ + +QIcon QAbstractFormBuilder::domPropertyToIcon(const DomResourcePixmap *icon) +{ + Q_UNUSED(icon); + qWarning() << "QAbstractFormBuilder::domPropertyToIcon() is obsoleted"; + return QIcon(); +} + +/*! + \internal + Create icon from DOM. Assert if !domPixmap + From 4.4 - unused +*/ + +QIcon QAbstractFormBuilder::domPropertyToIcon(const DomProperty* p) +{ + Q_UNUSED(p); + qWarning() << "QAbstractFormBuilder::domPropertyToIcon() is obsoleted"; + return QIcon(); +} + + +/*! + \internal + Create pixmap from DOM. + From 4.4 - unused +*/ + +QPixmap QAbstractFormBuilder::domPropertyToPixmap(const DomResourcePixmap* pixmap) +{ + Q_UNUSED(pixmap); + qWarning() << "QAbstractFormBuilder::domPropertyToPixmap() is obsoleted"; + return QPixmap(); +} + + +/*! + \internal + Create pixmap from DOM. Assert if !domPixmap + From 4.4 - unused +*/ + +QPixmap QAbstractFormBuilder::domPropertyToPixmap(const DomProperty* p) +{ + Q_UNUSED(p); + qWarning() << "QAbstractFormBuilder::domPropertyToPixmap() is obsoleted"; + return QPixmap(); +} + +/*! + \fn void QAbstractFormBuilder::createConnections ( DomConnections *, QWidget * ) + \internal +*/ + +/*! + \fn void QAbstractFormBuilder::createCustomWidgets ( DomCustomWidgets * ) + \internal +*/ + +/*! + \fn void QAbstractFormBuilder::createResources ( DomResources * ) + \internal +*/ + +/*! + \fn QFormScriptRunner *QAbstractFormBuilder::formScriptRunner() const + \internal + \since 4.3 +*/ +#ifndef QT_FORMBUILDER_NO_SCRIPT +QFormScriptRunner *QAbstractFormBuilder::formScriptRunner() const +{ + return &(QFormBuilderExtra::instance(this)->formScriptRunner()); +} +#endif + +/*! + Sets whether the execution of scripts is enabled to \a enabled. + \since 4.3 + \internal +*/ + +void QAbstractFormBuilder::setScriptingEnabled(bool enabled) +{ +#ifdef QT_FORMBUILDER_NO_SCRIPT + if (enabled) + uiLibWarning(QCoreApplication::translate("QAbstractFormBuilder", "This version of the uitools library is linked without script support.")); +#else + QFormScriptRunner::Options options = formScriptRunner()->options(); + if (enabled) + options &= ~QFormScriptRunner::DisableScripts; + else + options |= QFormScriptRunner::DisableScripts; + formScriptRunner()->setOptions(options); +#endif +} + +/*! + Returns whether the execution of scripts is enabled. + \sa setScriptingEnabled() + \since 4.3 + \internal +*/ + +bool QAbstractFormBuilder::isScriptingEnabled() const +{ +#ifdef QT_FORMBUILDER_NO_SCRIPT + return false; +#else + return !(formScriptRunner()->options() & QFormScriptRunner::DisableScripts); +#endif +} + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/abstractformbuilder.h b/designer/lib/uilib/abstractformbuilder.h new file mode 100644 index 0000000..da354cd --- /dev/null +++ b/designer/lib/uilib/abstractformbuilder.h @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMBUILDER_H +#define ABSTRACTFORMBUILDER_H + +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +#if 0 +// pragma for syncqt, don't remove. + +#pragma qt_class(QAbstractFormBuilder) +#endif + +class QAction; +class QButtonGroup; +class QActionGroup; +class QComboBox; +class QIODevice; +class QIcon; +class QLayout; +class QLayoutItem; +class QListWidget; +class QObject; +class QSpacerItem; +class QTreeWidget; +class QTableWidget; +class QVariant; +class QWidget; +class QAbstractButton; +class QAbstractItemView; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class DomAction; +class DomActionGroup; +class DomButtonGroup; +class DomButtonGroups; +class DomActionRef; +class DomBrush; +class DomColorGroup; +class DomConnections; +class DomCustomWidgets; +class DomLayout; +class DomLayoutItem; +class DomProperty; +class DomResources; +class DomSpacer; +class DomString; +class DomTabStops; +class DomUI; +class DomWidget; +class DomResourcePixmap; + +class QResourceBuilder; +class QTextBuilder; + +#ifndef QT_FORMBUILDER_NO_SCRIPT +class QFormScriptRunner; +#endif + +class QDESIGNER_UILIB_EXPORT QAbstractFormBuilder +{ +public: + QAbstractFormBuilder(); + virtual ~QAbstractFormBuilder(); + + QDir workingDirectory() const; + void setWorkingDirectory(const QDir &directory); + + virtual QWidget *load(QIODevice *dev, QWidget *parentWidget=0); + virtual void save(QIODevice *dev, QWidget *widget); + + void setScriptingEnabled(bool enabled); + bool isScriptingEnabled() const; + +protected: +// +// load +// + virtual void loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + virtual QWidget *create(DomUI *ui, QWidget *parentWidget); + virtual QWidget *create(DomWidget *ui_widget, QWidget *parentWidget); + virtual QLayout *create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget); + virtual QLayoutItem *create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget); + + virtual QAction *create(DomAction *ui_action, QObject *parent); + virtual QActionGroup *create(DomActionGroup *ui_action_group, QObject *parent); + virtual void addMenuAction(QAction *action); + + virtual void applyProperties(QObject *o, const QList &properties); + bool applyPropertyInternally(QObject *o, const QString &propertyName, const QVariant &value); + + virtual void applyTabStops(QWidget *widget, DomTabStops *tabStops); + + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual QLayout *createLayout(const QString &layoutName, QObject *parent, const QString &name); + virtual QAction *createAction(QObject *parent, const QString &name); + virtual QActionGroup *createActionGroup(QObject *parent, const QString &name); + + virtual void createCustomWidgets(DomCustomWidgets *) {} + virtual void createConnections(DomConnections *, QWidget *) {} + virtual void createResources(DomResources*) {} + + virtual bool addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout); + virtual bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + +// +// save +// + virtual void saveExtraInfo(QWidget *widget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + + virtual void saveDom(DomUI *ui, QWidget *widget); + + virtual DomActionRef *createActionRefDom(QAction *action); + + virtual DomWidget *createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive = true); + virtual DomLayout *createDom(QLayout *layout, DomLayout *ui_layout, DomWidget *ui_parentWidget); + virtual DomLayoutItem *createDom(QLayoutItem *item, DomLayout *ui_parentLayout, DomWidget *ui_parentWidget); + virtual DomSpacer *createDom(QSpacerItem *spacer, DomLayout *ui_parentLayout, DomWidget *ui_parentWidget); + + virtual DomAction *createDom(QAction *action); + virtual DomActionGroup *createDom(QActionGroup *actionGroup); + DomButtonGroup *createDom(QButtonGroup *buttonGroup); + + virtual DomConnections *saveConnections(); + virtual DomCustomWidgets *saveCustomWidgets(); + virtual DomTabStops *saveTabStops(); + virtual DomResources *saveResources(); + DomButtonGroups *saveButtonGroups(const QWidget *mainContainer); + virtual QList computeProperties(QObject *obj); + virtual bool checkProperty(QObject *obj, const QString &prop) const; + virtual DomProperty *createProperty(QObject *object, const QString &propertyName, const QVariant &value); + + virtual void layoutInfo(DomLayout *layout, QObject *parent, int *margin, int *spacing); + + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath); + virtual QString iconToFilePath(const QIcon &pm) const; + virtual QString iconToQrcPath(const QIcon &pm) const; + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath); + virtual QString pixmapToFilePath(const QPixmap &pm) const; + virtual QString pixmapToQrcPath(const QPixmap &pm) const; + + void loadListWidgetExtraInfo(DomWidget *ui_widget, QListWidget *listWidget, QWidget *parentWidget); + void loadTreeWidgetExtraInfo(DomWidget *ui_widget, QTreeWidget *treeWidget, QWidget *parentWidget); + void loadTableWidgetExtraInfo(DomWidget *ui_widget, QTableWidget *tableWidget, QWidget *parentWidget); + void loadComboBoxExtraInfo(DomWidget *ui_widget, QComboBox *comboBox, QWidget *parentWidget); + void loadButtonExtraInfo(const DomWidget *ui_widget, QAbstractButton *button, QWidget *parentWidget); + void loadItemViewExtraInfo(DomWidget *ui_widget, QAbstractItemView *itemView, QWidget *parentWidget); + + void saveListWidgetExtraInfo(QListWidget *widget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + void saveTreeWidgetExtraInfo(QTreeWidget *treeWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + void saveTableWidgetExtraInfo(QTableWidget *tablWidget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + void saveComboBoxExtraInfo(QComboBox *widget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + void saveButtonExtraInfo(const QAbstractButton *widget, DomWidget *ui_widget, DomWidget *ui_parentWidget); + void saveItemViewExtraInfo(const QAbstractItemView *itemView, DomWidget *ui_widget, DomWidget *ui_parentWidget); + + void setResourceBuilder(QResourceBuilder *builder); + QResourceBuilder *resourceBuilder() const; + DomProperty *saveResource(const QVariant &v) const; + + void setTextBuilder(QTextBuilder *builder); + QTextBuilder *textBuilder() const; + DomProperty *saveText(const QString &attributeName, const QVariant &v) const; +// +// utils +// + + QVariant toVariant(const QMetaObject *meta, DomProperty *property); + static QString toString(const DomString *str); + + typedef QHash DomPropertyHash; + static DomPropertyHash propertyMap(const QList &properties); + + void setupColorGroup(QPalette &palette, QPalette::ColorGroup colorGroup, DomColorGroup *group); + DomColorGroup *saveColorGroup(const QPalette &palette); + QBrush setupBrush(DomBrush *brush); + DomBrush *saveBrush(const QBrush &brush); + + void reset(); + void initialize(const DomUI *ui); + +#ifndef QT_FORMBUILDER_NO_SCRIPT + QFormScriptRunner *formScriptRunner() const; +#endif +// +// utils +// + + static QMetaEnum toolBarAreaMetaEnum(); + +// +// Icon/pixmap stuff +// + // A Pair of icon path/qrc path. + typedef QPair IconPaths; + + IconPaths iconPaths(const QIcon &) const; + IconPaths pixmapPaths(const QPixmap &) const; + void setIconProperty(DomProperty &, const IconPaths &) const; + void setPixmapProperty(DomProperty &, const IconPaths &) const; + DomProperty* iconToDomProperty(const QIcon &) const; + + static const DomResourcePixmap *domPixmap(const DomProperty* p); + QIcon domPropertyToIcon(const DomResourcePixmap *); + QIcon domPropertyToIcon(const DomProperty* p); + QPixmap domPropertyToPixmap(const DomResourcePixmap* p); + QPixmap domPropertyToPixmap(const DomProperty* p); + + QHash m_laidout; + QHash m_actions; + QHash m_actionGroups; + int m_defaultMargin; + int m_defaultSpacing; + QDir m_workingDirectory; + +private: +// +// utils +// + static Qt::ToolBarArea toolbarAreaFromDOMAttributes(const DomPropertyHash &attributeMap); + + QAbstractFormBuilder(const QAbstractFormBuilder &other); + void operator = (const QAbstractFormBuilder &other); + + friend QDESIGNER_UILIB_EXPORT DomProperty *variantToDomProperty(QAbstractFormBuilder *abstractFormBuilder, const QMetaObject *meta, const QString &propertyName, const QVariant &value); + friend QDESIGNER_UILIB_EXPORT QVariant domPropertyToVariant(QAbstractFormBuilder *abstractFormBuilder,const QMetaObject *meta, const DomProperty *property); +}; + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // ABSTRACTFORMBUILDER_H diff --git a/designer/lib/uilib/container.h b/designer/lib/uilib/container.h new file mode 100644 index 0000000..478ddf6 --- /dev/null +++ b/designer/lib/uilib/container.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTAINER_H +#define CONTAINER_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWidget; + +class QDesignerContainerExtension +{ +public: + virtual ~QDesignerContainerExtension() {} + + virtual int count() const = 0; + virtual QWidget *widget(int index) const = 0; + + virtual int currentIndex() const = 0; + virtual void setCurrentIndex(int index) = 0; + + virtual void addWidget(QWidget *widget) = 0; + virtual void insertWidget(int index, QWidget *widget) = 0; + virtual void remove(int index) = 0; +}; +Q_DECLARE_EXTENSION_INTERFACE(QDesignerContainerExtension, "com.trolltech.Qt.Designer.Container") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // CONTAINER_H diff --git a/designer/lib/uilib/container.qdoc b/designer/lib/uilib/container.qdoc new file mode 100644 index 0000000..a3bbcc6 --- /dev/null +++ b/designer/lib/uilib/container.qdoc @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerContainerExtension + \brief The QDesignerContainerExtension class allows you to add pages to + a custom multi-page container in Qt Designer's workspace. + \inmodule QtDesigner + + QDesignerContainerExtension provide an interface for creating + custom container extensions. A container extension consists of a + collection of functions that \QD needs to manage a multi-page + container plugin, and a list of the container's pages. + + \image containerextension-example.png + + \warning This is \e not an extension for container plugins in + general, only custom \e multi-page containers. + + To create a container extension, your extension class must inherit + from both QObject and QDesignerContainerExtension. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 6 + + Since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + You must reimplement several functions to enable \QD to manage a + custom multi-page container widget: \QD uses count() to keep track + of the number pages in your container, widget() to return the page + at a given index in the list of the container's pages, and + currentIndex() to return the list index of the selected page. \QD + uses the addWidget() function to add a given page to the + container, expecting it to be appended to the list of pages, while + it expects the insertWidget() function to add a given page to the + container by inserting it at a given index. + + In \QD the extensions are not created until they are + required. For that reason you must also create a + QExtensionFactory, i.e a class that is able to make an instance of + your extension, and register it using \QD's \l + {QExtensionManager}{extension manager}. + + When a container extension is required, \QD's \l + {QExtensionManager}{extension manager} will run through all its + registered factories calling QExtensionFactory::createExtension() + for each until the first one that is able to create a container + extension, is found. This factory will then create the extension + for the plugin. + + There are four available types of extensions in \QD: + QDesignerContainerExtension , QDesignerMemberSheetExtension, + QDesignerPropertySheetExtension and QDesignerTaskMenuExtension. + \QD's behavior is the same whether the requested extension is + associated with a multi page container, a member sheet, a property + sheet or a task menu. + + The QExtensionFactory class provides a standard extension factory, + and can also be used as an interface for custom extension + factories. You can either create a new QExtensionFactory and + reimplement the QExtensionFactory::createExtension() function. For + example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 7 + + Or you can use an existing factory, expanding the + QExtensionFactory::createExtension() function to make the factory + able to create a container extension as well. For example: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 8 + + For a complete example using the QDesignerContainerExtension + class, see the \l {designer/containerextension}{Container + Extension example}. The example shows how to create a custom + multi-page plugin for \QD. + + \sa QExtensionFactory, QExtensionManager, {Creating Custom Widget + Extensions} +*/ + +/*! + \fn QDesignerContainerExtension::~QDesignerContainerExtension() + + Destroys the extension. +*/ + +/*! + \fn int QDesignerContainerExtension::count() const + + Returns the number of pages in the container. +*/ + +/*! + \fn QWidget *QDesignerContainerExtension::widget(int index) const + + Returns the page at the given \a index in the extension's list of + pages. + + \sa addWidget(), insertWidget() +*/ + +/*! + \fn int QDesignerContainerExtension::currentIndex() const + + Returns the index of the currently selected page in the + container. + + \sa setCurrentIndex() +*/ + +/*! + \fn void QDesignerContainerExtension::setCurrentIndex(int index) + + Sets the currently selected page in the container to be the + page at the given \a index in the extension's list of pages. + + \sa currentIndex() +*/ + +/*! + \fn void QDesignerContainerExtension::addWidget(QWidget *page) + + Adds the given \a page to the container by appending it to the + extension's list of pages. + + \sa insertWidget(), remove(), widget() +*/ + +/*! + \fn void QDesignerContainerExtension::insertWidget(int index, QWidget *page) + + Adds the given \a page to the container by inserting it at the + given \a index in the extension's list of pages. + + \sa addWidget(), remove(), widget() +*/ + +/*! + \fn void QDesignerContainerExtension::remove(int index) + + Removes the page at the given \a index from the extension's list + of pages. + + \sa addWidget(), insertWidget() +*/ diff --git a/designer/lib/uilib/customwidget.h b/designer/lib/uilib/customwidget.h new file mode 100644 index 0000000..40410f0 --- /dev/null +++ b/designer/lib/uilib/customwidget.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CUSTOMWIDGET_H +#define CUSTOMWIDGET_H + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QWidget; +class QDesignerFormEditorInterface; + +class QDesignerCustomWidgetInterface +{ +public: + virtual ~QDesignerCustomWidgetInterface() {} + + virtual QString name() const = 0; + virtual QString group() const = 0; + virtual QString toolTip() const = 0; + virtual QString whatsThis() const = 0; + virtual QString includeFile() const = 0; + virtual QIcon icon() const = 0; + + virtual bool isContainer() const = 0; + + virtual QWidget *createWidget(QWidget *parent) = 0; + + virtual bool isInitialized() const { return false; } + virtual void initialize(QDesignerFormEditorInterface *core) { Q_UNUSED(core); } + + virtual QString domXml() const + { + return QString::fromUtf8("") + .arg(name()).arg(name().toLower()); + } + + virtual QString codeTemplate() const { return QString(); } +}; +Q_DECLARE_INTERFACE(QDesignerCustomWidgetInterface, "com.trolltech.Qt.Designer.CustomWidget") + + +class QDesignerCustomWidgetCollectionInterface +{ +public: + virtual ~QDesignerCustomWidgetCollectionInterface() {} + + virtual QList customWidgets() const = 0; +}; +Q_DECLARE_INTERFACE(QDesignerCustomWidgetCollectionInterface, + "com.trolltech.Qt.Designer.CustomWidgetCollection") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // CUSTOMWIDGET_H diff --git a/designer/lib/uilib/customwidget.qdoc b/designer/lib/uilib/customwidget.qdoc new file mode 100644 index 0000000..cdbdfa2 --- /dev/null +++ b/designer/lib/uilib/customwidget.qdoc @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QDesignerCustomWidgetInterface + + \brief The QDesignerCustomWidgetInterface class enables Qt Designer + to access and construct custom widgets. + + \inmodule QtDesigner + + QDesignerCustomWidgetInterface provides a custom widget with an + interface. The class contains a set of functions that must be subclassed + to return basic information about the widget, such as its class name and + the name of its header file. Other functions must be implemented to + initialize the plugin when it is loaded, and to construct instances of + the custom widget for \QD to use. + + When implementing a custom widget you must subclass + QDesignerCustomWidgetInterface to expose your widget to \QD. For + example, this is the declaration for the plugin used in the + \l{Custom Widget Plugin Example}{Custom Widget Plugin example} that + enables an analog clock custom widget to be used by \QD: + + \snippet examples/designer/customwidgetplugin/customwidgetplugin.h 0 + + Note that the only part of the class definition that is specific + to this particular custom widget is the class name. In addition, + since we are implementing an interface, we must ensure that it's + made known to the meta object system using the Q_INTERFACES() + macro. This enables \QD to use the qobject_cast() function to + query for supported interfaces using nothing but a QObject + pointer. + + After \QD loads a custom widget plugin, it calls the interface's + initialize() function to enable it to set up any resources that it + may need. This function is called with a QDesignerFormEditorInterface + parameter that provides the plugin with a gateway to all of \QD's API. + + \QD constructs instances of the custom widget by calling the plugin's + createWidget() function with a suitable parent widget. Plugins must + construct and return an instance of a custom widget with the specified + parent widget. + + In the implementation of the class you must remember to export + your custom widget plugin to \QD using the Q_EXPORT_PLUGIN2() + macro. For example, if a library called \c libcustomwidgetplugin.so + (on Unix) or \c libcustomwidget.dll (on Windows) contains a widget + class called \c MyCustomWidget, we can export it by adding the + following line to the file containing the plugin implementation: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 14 + + This macro ensures that \QD can access and construct the custom widget. + Without this macro, there is no way for \QD to use it. + + When implementing a custom widget plugin, you build it as a + separate library. If you want to include several custom widget + plugins in the same library, you must in addition subclass + QDesignerCustomWidgetCollectionInterface. + + \warning If your custom widget plugin contains QVariant + properties, be aware that only the following \l + {QVariant::Type}{types} are supported: + + \list + \o QVariant::ByteArray + \o QVariant::Bool + \o QVariant::Color + \o QVariant::Cursor + \o QVariant::Date + \o QVariant::DateTime + \o QVariant::Double + \o QVariant::Int + \o QVariant::Point + \o QVariant::Rect + \o QVariant::Size + \o QVariant::SizePolicy + \o QVariant::String + \o QVariant::Time + \o QVariant::UInt + \endlist + + For a complete example using the QDesignerCustomWidgetInterface + class, see the \l {designer/customwidgetplugin}{Custom Widget + Example}. The example shows how to create a custom widget plugin + for \QD. + + \sa QDesignerCustomWidgetCollectionInterface {Creating Custom + Widgets for Qt Designer} +*/ + +/*! + \fn QDesignerCustomWidgetInterface::~QDesignerCustomWidgetInterface() + + Destroys the custom widget interface. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::name() const + + Returns the class name of the custom widget supplied by the interface. + + The name returned \e must be identical to the class name used for the + custom widget. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::group() const + + Returns the name of the group to which the custom widget belongs. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::toolTip() const + + Returns a short description of the widget that can be used by \QD + in a tool tip. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::whatsThis() const + + Returns a description of the widget that can be used by \QD in + "What's This?" help for the widget. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::includeFile() const + + Returns the path to the include file that \l uic uses when + creating code for the custom widget. +*/ + +/*! + \fn QIcon QDesignerCustomWidgetInterface::icon() const + + Returns the icon used to represent the custom widget in \QD's + widget box. +*/ + +/*! + \fn bool QDesignerCustomWidgetInterface::isContainer() const + + Returns true if the custom widget is intended to be used as a + container; otherwise returns false. + + Most custom widgets are not used to hold other widgets, so their + implementations of this function will return false, but custom + containers will return true to ensure that they behave correctly + in \QD. +*/ + +/*! + \fn QWidget *QDesignerCustomWidgetInterface::createWidget(QWidget *parent) + + Returns a new instance of the custom widget, with the given \a + parent. +*/ + +/*! + \fn bool QDesignerCustomWidgetInterface::isInitialized() const + + Returns true if the widget has been initialized; otherwise returns + false. + + \sa initialize() +*/ + +/*! + \fn void QDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface *formEditor) + + Initializes the widget for use with the specified \a formEditor + interface. + + \sa isInitialized() +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::domXml() const + + Returns the XML that is used to describe the custom widget's + properties to \QD. +*/ + +/*! + \fn QString QDesignerCustomWidgetInterface::codeTemplate() const + + This function is reserved for future use by \QD. + + \omit + Returns the code template that \QD includes in forms that contain + the custom widget when they are saved. + \endomit +*/ + +/*! + \macro QDESIGNER_WIDGET_EXPORT + \relates QDesignerCustomWidgetInterface + \since 4.1 + + This macro is used when defining custom widgets to ensure that they are + correctly exported from plugins for use with \QD. + + On some platforms, the symbols required by \QD to create new widgets + are removed from plugins by the build system, making them unusable. + Using this macro ensures that the symbols are retained on those platforms, + and has no side effects on other platforms. + + For example, the \l{designer/worldtimeclockplugin}{World Time Clock Plugin} + example exports a custom widget class with the following declaration: + + \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 0 + \dots + \snippet examples/designer/worldtimeclockplugin/worldtimeclock.h 2 + + \sa {Creating Custom Widgets for Qt Designer} +*/ + + + + + +/*! + \class QDesignerCustomWidgetCollectionInterface + + \brief The QDesignerCustomWidgetCollectionInterface class allows + you to include several custom widgets in one single library. + + \inmodule QtDesigner + + When implementing a custom widget plugin, you build it as a + separate library. If you want to include several custom widget + plugins in the same library, you must in addition subclass + QDesignerCustomWidgetCollectionInterface. + + QDesignerCustomWidgetCollectionInterface contains one single + function returning a list of the collection's + QDesignerCustomWidgetInterface objects. For example, if you have + several custom widgets \c CustomWidgetOne, \c CustomWidgetTwo and + \c CustomWidgetThree, the class definition may look like this: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 12 + + In the class constructor you add the interfaces to your custom + widgets to the list which you return in the customWidgets() + function: + + \snippet doc/src/snippets/code/doc_src_qtdesigner.qdoc 13 + + Note that instead of exporting each custom widget plugin using the + Q_EXPORT_PLUGIN2() macro, you export the entire collection. The + Q_EXPORT_PLUGIN2() macro ensures that \QD can access and construct + the custom widgets. Without this macro, there is no way for \QD to + use them. + + \sa QDesignerCustomWidgetInterface, {Creating Custom Widgets for + Qt Designer} +*/ + +/*! + \fn QDesignerCustomWidgetCollectionInterface::~QDesignerCustomWidgetCollectionInterface() { + + Destroys the custom widget collection interface. +*/ + +/*! + \fn QList QDesignerCustomWidgetCollectionInterface::customWidgets() const + + Returns a list of interfaces to the collection's custom widgets. +*/ diff --git a/designer/lib/uilib/formbuilder.cpp b/designer/lib/uilib/formbuilder.cpp new file mode 100644 index 0000000..e9a7b95 --- /dev/null +++ b/designer/lib/uilib/formbuilder.cpp @@ -0,0 +1,570 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "customwidget.h" +#include "formbuilder.h" +#include "formbuilderextra_p.h" +#include "ui4_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +/*! + \class QFormBuilder + + \brief The QFormBuilder class is used to dynamically construct + user interfaces from UI files at run-time. + + \inmodule QtDesigner + + The QFormBuilder class provides a mechanism for dynamically + creating user interfaces at run-time, based on UI files + created with \QD. For example: + + \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 0 + + By including the user interface in the example's resources (\c + myForm.qrc), we ensure that it will be present when the example is + run: + + \snippet doc/src/snippets/code/tools_designer_src_lib_uilib_formbuilder.cpp 1 + + QFormBuilder extends the QAbstractFormBuilder base class with a + number of functions that are used to support custom widget + plugins: + + \list + \o pluginPaths() returns the list of paths that the form builder + searches when loading custom widget plugins. + \o addPluginPath() allows additional paths to be registered with + the form builder. + \o setPluginPath() is used to replace the existing list of paths + with a list obtained from some other source. + \o clearPluginPaths() removes all paths registered with the form + builder. + \o customWidgets() returns a list of interfaces to plugins that + can be used to create new instances of registered custom widgets. + \endlist + + The QFormBuilder class is typically used by custom components and + applications that embed \QD. Standalone applications that need to + dynamically generate user interfaces at run-time use the + QUiLoader class, found in the QtUiTools module. + + \sa QAbstractFormBuilder, {QtUiTools Module} +*/ + +/*! + \fn QFormBuilder::QFormBuilder() + + Constructs a new form builder. +*/ + +QFormBuilder::QFormBuilder() : QAbstractFormBuilder() +{ +} + +/*! + Destroys the form builder. +*/ +QFormBuilder::~QFormBuilder() +{ +} + +/*! + \internal +*/ +QWidget *QFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + if (!fb->parentWidgetIsSet()) + fb->setParentWidget(parentWidget); + // Is this a QLayoutWidget with a margin of 0: Not a known page-based + // container and no method for adding pages registered. + fb->setProcessingLayoutWidget(false); + if (ui_widget->attributeClass() == QFormBuilderStrings::instance().qWidgetClass && !ui_widget->hasAttributeNative() + && parentWidget +#ifndef QT_NO_MAINWINDOW + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_TOOLBOX + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_STACKEDWIDGET + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_STACKEDWIDGET + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_SCROLLAREA + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_MDIAREA + && !qobject_cast(parentWidget) +#endif +#ifndef QT_NO_DOCKWIDGET + && !qobject_cast(parentWidget) +#endif + ) { + const QString parentClassName = QLatin1String(parentWidget->metaObject()->className()); + if (!fb->isCustomWidgetContainer(parentClassName)) + fb->setProcessingLayoutWidget(true); + } + return QAbstractFormBuilder::create(ui_widget, parentWidget); +} + + +/*! + \internal +*/ +QWidget *QFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + if (widgetName.isEmpty()) { + //: Empty class name passed to widget factory method + qWarning() << QCoreApplication::translate("QFormBuilder", "An empty class name was passed on to %1 (object name: '%2').").arg(QString::fromUtf8(Q_FUNC_INFO), name); + return 0; + } + + QWidget *w = 0; + +#ifndef QT_NO_TABWIDGET + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif +#ifndef QT_NO_STACKEDWIDGET + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif +#ifndef QT_NO_TOOLBOX + if (qobject_cast(parentWidget)) + parentWidget = 0; +#endif + + // ### special-casing for Line (QFrame) -- fix for 4.2 + do { + if (widgetName == QFormBuilderStrings::instance().lineClass) { + w = new QFrame(parentWidget); + static_cast(w)->setFrameStyle(QFrame::HLine | QFrame::Sunken); + break; + } + const QByteArray widgetNameBA = widgetName.toUtf8(); + const char *widgetNameC = widgetNameBA.constData(); + if (w) { // symmetry for macro + } + +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) +#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); } +#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); } + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + if (w) + break; + + // try with a registered custom widget + QDesignerCustomWidgetInterface *factory = m_customWidgets.value(widgetName); + if (factory != 0) + w = factory->createWidget(parentWidget); + } while(false); + + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + if (w == 0) { // Attempt to instantiate base class of promoted/custom widgets + const QString baseClassName = fb->customWidgetBaseClass(widgetName); + if (!baseClassName.isEmpty()) { + qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a custom widget of the class '%1'; defaulting to base class '%2'.").arg(widgetName, baseClassName); + return createWidget(baseClassName, parentWidget, name); + } + } + + if (w == 0) { // nothing to do + qWarning() << QCoreApplication::translate("QFormBuilder", "QFormBuilder was unable to create a widget of the class '%1'.").arg(widgetName); + return 0; + } + + w->setObjectName(name); + + if (qobject_cast(w)) + w->setParent(parentWidget); + + return w; +} + +/*! + \internal +*/ +QLayout *QFormBuilder::createLayout(const QString &layoutName, QObject *parent, const QString &name) +{ + QLayout *l = 0; + + QWidget *parentWidget = qobject_cast(parent); + QLayout *parentLayout = qobject_cast(parent); + + Q_ASSERT(parentWidget || parentLayout); + +#define DECLARE_WIDGET(W, C) +#define DECLARE_COMPAT_WIDGET(W, C) + +#define DECLARE_LAYOUT(L, C) \ + if (layoutName == QLatin1String(#L)) { \ + Q_ASSERT(l == 0); \ + l = parentLayout \ + ? new L() \ + : new L(parentWidget); \ + } + +#include "widgets.table" + +#undef DECLARE_LAYOUT +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_WIDGET + + if (l) { + l->setObjectName(name); + if (parentLayout) { + QWidget *w = qobject_cast(parentLayout->parent()); + if (w && w->inherits("Q3GroupBox")) { + l->setContentsMargins(w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin), + w->style()->pixelMetric(QStyle::PM_LayoutTopMargin), + w->style()->pixelMetric(QStyle::PM_LayoutRightMargin), + w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); + QGridLayout *grid = qobject_cast(l); + if (grid) { + grid->setHorizontalSpacing(-1); + grid->setVerticalSpacing(-1); + } else { + l->setSpacing(-1); + } + l->setAlignment(Qt::AlignTop); + } + } + } else { + qWarning() << QCoreApplication::translate("QFormBuilder", "The layout type `%1' is not supported.").arg(layoutName); + } + + return l; +} + +/*! + \internal +*/ +bool QFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + return QAbstractFormBuilder::addItem(ui_item, item, layout); +} + +/*! + \internal +*/ +bool QFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + return QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget); +} + +/*! + \internal +*/ +QWidget *QFormBuilder::widgetByName(QWidget *topLevel, const QString &name) +{ + Q_ASSERT(topLevel); + if (topLevel->objectName() == name) + return topLevel; + + return qFindChild(topLevel, name); +} + +static QObject *objectByName(QWidget *topLevel, const QString &name) +{ + Q_ASSERT(topLevel); + if (topLevel->objectName() == name) + return topLevel; + + return qFindChild(topLevel, name); +} + +/*! + \internal +*/ +void QFormBuilder::createConnections(DomConnections *ui_connections, QWidget *widget) +{ + typedef QList DomConnectionList; + Q_ASSERT(widget != 0); + + if (ui_connections == 0) + return; + + const DomConnectionList connections = ui_connections->elementConnection(); + if (!connections.empty()) { + const DomConnectionList::const_iterator cend = connections.constEnd(); + for (DomConnectionList::const_iterator it = connections.constBegin(); it != cend; ++it) { + + QObject *sender = objectByName(widget, (*it)->elementSender()); + QObject *receiver = objectByName(widget, (*it)->elementReceiver()); + if (!sender || !receiver) + continue; + + QByteArray sig = (*it)->elementSignal().toUtf8(); + sig.prepend("2"); + QByteArray sl = (*it)->elementSlot().toUtf8(); + sl.prepend("1"); + QObject::connect(sender, sig, receiver, sl); + } + } +} + +/*! + \internal +*/ +QWidget *QFormBuilder::create(DomUI *ui, QWidget *parentWidget) +{ + return QAbstractFormBuilder::create(ui, parentWidget); +} + +/*! + \internal +*/ +QLayout *QFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + // Is this a temporary layout widget used to represent QLayout hierarchies in Designer? + // Set its margins to 0. + bool layoutWidget = fb->processingLayoutWidget(); + QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget); + if (layoutWidget) { + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + int left, top, right, bottom; + left = top = right = bottom = 0; + const DomPropertyHash properties = propertyMap(ui_layout->elementProperty()); + + if (DomProperty *prop = properties.value(strings.leftMarginProperty)) + left = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.topMarginProperty)) + top = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.rightMarginProperty)) + right = prop->elementNumber(); + + if (DomProperty *prop = properties.value(strings.bottomMarginProperty)) + bottom = prop->elementNumber(); + + l->setContentsMargins(left, top, right, bottom); + fb->setProcessingLayoutWidget(false); + } + return l; +} + +/*! + \internal +*/ +QLayoutItem *QFormBuilder::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget) +{ + return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget); +} + +/*! + \internal +*/ +QAction *QFormBuilder::create(DomAction *ui_action, QObject *parent) +{ + return QAbstractFormBuilder::create(ui_action, parent); +} + +/*! + \internal +*/ +QActionGroup *QFormBuilder::create(DomActionGroup *ui_action_group, QObject *parent) +{ + return QAbstractFormBuilder::create(ui_action_group, parent); +} + +/*! + Returns the list of paths the form builder searches for plugins. + + \sa addPluginPath() +*/ +QStringList QFormBuilder::pluginPaths() const +{ + return m_pluginPaths; +} + +/*! + Clears the list of paths that the form builder uses to search for + custom widget plugins. + + \sa pluginPaths() +*/ +void QFormBuilder::clearPluginPaths() +{ + m_pluginPaths.clear(); + updateCustomWidgets(); +} + +/*! + Adds a new plugin path specified by \a pluginPath to the list of + paths that will be searched by the form builder when loading a + custom widget plugin. + + \sa setPluginPath(), clearPluginPaths() +*/ +void QFormBuilder::addPluginPath(const QString &pluginPath) +{ + m_pluginPaths.append(pluginPath); + updateCustomWidgets(); +} + +/*! + Sets the list of plugin paths to the list specified by \a pluginPaths. + + \sa addPluginPath() +*/ +void QFormBuilder::setPluginPath(const QStringList &pluginPaths) +{ + m_pluginPaths = pluginPaths; + updateCustomWidgets(); +} + +static void insertPlugins(QObject *o, QMap *customWidgets) +{ + // step 1) try with a normal plugin + if (QDesignerCustomWidgetInterface *iface = qobject_cast(o)) { + customWidgets->insert(iface->name(), iface); + return; + } + // step 2) try with a collection of plugins + if (QDesignerCustomWidgetCollectionInterface *c = qobject_cast(o)) { + foreach (QDesignerCustomWidgetInterface *iface, c->customWidgets()) + customWidgets->insert(iface->name(), iface); + } +} + +/*! + \internal +*/ +void QFormBuilder::updateCustomWidgets() +{ + m_customWidgets.clear(); + + foreach (const QString &path, m_pluginPaths) { + const QDir dir(path); + const QStringList candidates = dir.entryList(QDir::Files); + + foreach (const QString &plugin, candidates) { + if (!QLibrary::isLibrary(plugin)) + continue; + + QString loaderPath = path; + loaderPath += QLatin1Char('/'); + loaderPath += plugin; + + QPluginLoader loader(loaderPath); + if (loader.load()) + insertPlugins(loader.instance(), &m_customWidgets); + } + } + // Check statically linked plugins + const QObjectList staticPlugins = QPluginLoader::staticInstances(); + if (!staticPlugins.empty()) + foreach (QObject *o, staticPlugins) + insertPlugins(o, &m_customWidgets); +} + +/*! + \fn QList QFormBuilder::customWidgets() const + + Returns a list of the available plugins. +*/ +QList QFormBuilder::customWidgets() const +{ + return m_customWidgets.values(); +} + +/*! + \internal +*/ + +void QFormBuilder::applyProperties(QObject *o, const QList &properties) +{ + typedef QList DomPropertyList; + + if (properties.empty()) + return; + + QFormBuilderExtra *fb = QFormBuilderExtra::instance(this); + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const QVariant v = toVariant(o->metaObject(), *it); + if (v.isNull()) + continue; + + const QString attributeName = (*it)->attributeName(); + const bool isWidget = o->isWidgetType(); + if (isWidget && o->parent() == fb->parentWidget() && attributeName == strings.geometryProperty) { + // apply only the size part of a geometry for the root widget + static_cast(o)->resize(qvariant_cast(v).size()); + } else if (fb->applyPropertyInternally(o, attributeName, v)) { + } else if (isWidget && !qstrcmp("QFrame", o->metaObject()->className ()) && attributeName == strings.orientationProperty) { + // ### special-casing for Line (QFrame) -- try to fix me + o->setProperty("frameShape", v); // v is of QFrame::Shape enum + } else { + o->setProperty(attributeName.toUtf8(), v); + } + } +} + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/formbuilder.h b/designer/lib/uilib/formbuilder.h new file mode 100644 index 0000000..d2ac9e6 --- /dev/null +++ b/designer/lib/uilib/formbuilder.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMBUILDER_H +#define FORMBUILDER_H + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +#if 0 +// pragma for syncqt, don't remove. + +#pragma qt_class(QFormBuilder) +#endif + +class QDesignerCustomWidgetInterface; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class QDESIGNER_UILIB_EXPORT QFormBuilder: public QAbstractFormBuilder +{ +public: + QFormBuilder(); + virtual ~QFormBuilder(); + + QStringList pluginPaths() const; + + void clearPluginPaths(); + void addPluginPath(const QString &pluginPath); + void setPluginPath(const QStringList &pluginPaths); + + QList customWidgets() const; + +protected: + virtual QWidget *create(DomUI *ui, QWidget *parentWidget); + virtual QWidget *create(DomWidget *ui_widget, QWidget *parentWidget); + virtual QLayout *create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget); + virtual QLayoutItem *create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget); + virtual QAction *create(DomAction *ui_action, QObject *parent); + virtual QActionGroup *create(DomActionGroup *ui_action_group, QObject *parent); + + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual QLayout *createLayout(const QString &layoutName, QObject *parent, const QString &name); + + virtual void createConnections(DomConnections *connections, QWidget *widget); + + virtual bool addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout); + virtual bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + virtual void updateCustomWidgets(); + virtual void applyProperties(QObject *o, const QList &properties); + + static QWidget *widgetByName(QWidget *topLevel, const QString &name); + +private: + QStringList m_pluginPaths; + QMap m_customWidgets; +}; + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // FORMBUILDER_H diff --git a/designer/lib/uilib/formbuilderextra.cpp b/designer/lib/uilib/formbuilderextra.cpp new file mode 100644 index 0000000..fdea1c3 --- /dev/null +++ b/designer/lib/uilib/formbuilderextra.cpp @@ -0,0 +1,555 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formbuilderextra_p.h" +#include "abstractformbuilder.h" +#include "resourcebuilder_p.h" +#include "textbuilder_p.h" +#include "ui4_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +void uiLibWarning(const QString &message) { + qWarning("Designer: %s", qPrintable(message)); +} + + +QFormBuilderExtra::CustomWidgetData::CustomWidgetData() : + isContainer(false) +{ +} + +QFormBuilderExtra::CustomWidgetData::CustomWidgetData(const DomCustomWidget *dcw) : + addPageMethod(dcw->elementAddPageMethod()), + baseClass(dcw->elementExtends()), + isContainer(dcw->hasElementContainer() && dcw->elementContainer() != 0) +{ +#ifndef QT_FORMBUILDER_NO_SCRIPT + if (const DomScript *domScript = dcw->elementScript()) + script = domScript->text(); +#endif +} + +QFormBuilderExtra::QFormBuilderExtra() : + m_layoutWidget(false), + m_resourceBuilder(0), + m_textBuilder(0) +{ +} + +QFormBuilderExtra::~QFormBuilderExtra() +{ + clearResourceBuilder(); + clearTextBuilder(); +} + +void QFormBuilderExtra::clear() +{ + m_buddies.clear(); + m_parentWidget = 0; + m_parentWidgetIsSet = false; +#ifndef QT_FORMBUILDER_NO_SCRIPT + m_FormScriptRunner.clearErrors(); +#endif + m_customWidgetDataHash.clear(); + m_buttonGroups.clear(); +} + + +bool QFormBuilderExtra::applyPropertyInternally(QObject *o, const QString &propertyName, const QVariant &value) +{ + // Store buddies and apply them later on as the widgets might not exist yet. + QLabel *label = qobject_cast(o); + if (!label || propertyName != QFormBuilderStrings::instance().buddyProperty) + return false; + + m_buddies.insert(label, value.toString()); + return true; +} + +void QFormBuilderExtra::applyInternalProperties() const +{ + if (m_buddies.empty()) + return; + + const BuddyHash::const_iterator cend = m_buddies.constEnd(); + for (BuddyHash::const_iterator it = m_buddies.constBegin(); it != cend; ++it ) + applyBuddy(it.value(), BuddyApplyAll, it.key()); +} + +bool QFormBuilderExtra::applyBuddy(const QString &buddyName, BuddyMode applyMode, QLabel *label) +{ + if (buddyName.isEmpty()) { + label->setBuddy(0); + return false; + } + + const QWidgetList widgets = qFindChildren(label->topLevelWidget(), buddyName); + if (widgets.empty()) { + label->setBuddy(0); + return false; + } + + const QWidgetList::const_iterator cend = widgets.constEnd(); + for ( QWidgetList::const_iterator it = widgets.constBegin(); it != cend; ++it) { + if (applyMode == BuddyApplyAll || !(*it)->isHidden()) { + label->setBuddy(*it); + return true; + } + } + + label->setBuddy(0); + return false; +} + +const QPointer &QFormBuilderExtra::parentWidget() const +{ + return m_parentWidget; +} + +bool QFormBuilderExtra::parentWidgetIsSet() const +{ + return m_parentWidgetIsSet; +} + +void QFormBuilderExtra::setParentWidget(const QPointer &w) +{ + // Parent widget requires special handling of the geometry property. + m_parentWidget = w; + m_parentWidgetIsSet = true; +} + +#ifndef QT_FORMBUILDER_NO_SCRIPT +QFormScriptRunner &QFormBuilderExtra::formScriptRunner() +{ + return m_FormScriptRunner; +} + +QString QFormBuilderExtra::customWidgetScript(const QString &className) const +{ + const QHash::const_iterator it = m_customWidgetDataHash.constFind(className); + if (it != m_customWidgetDataHash.constEnd()) + return it.value().script; + return QString(); +} + +#endif + +void QFormBuilderExtra::storeCustomWidgetData(const QString &className, const DomCustomWidget *d) +{ + if (d) + m_customWidgetDataHash.insert(className, CustomWidgetData(d)); +} + +QString QFormBuilderExtra::customWidgetBaseClass(const QString &className) const +{ + const QHash::const_iterator it = m_customWidgetDataHash.constFind(className); + if (it != m_customWidgetDataHash.constEnd()) + return it.value().baseClass; + return QString(); +} + +QString QFormBuilderExtra::customWidgetAddPageMethod(const QString &className) const +{ + const QHash::const_iterator it = m_customWidgetDataHash.constFind(className); + if (it != m_customWidgetDataHash.constEnd()) + return it.value().addPageMethod; + return QString(); +} + +bool QFormBuilderExtra::isCustomWidgetContainer(const QString &className) const +{ + const QHash::const_iterator it = m_customWidgetDataHash.constFind(className); + if (it != m_customWidgetDataHash.constEnd()) + return it.value().isContainer; + return false; +} + +namespace { + typedef QHash FormBuilderPrivateHash; +} + +Q_GLOBAL_STATIC(FormBuilderPrivateHash, g_FormBuilderPrivateHash) + +QFormBuilderExtra *QFormBuilderExtra::instance(const QAbstractFormBuilder *afb) +{ + FormBuilderPrivateHash &fbHash = *g_FormBuilderPrivateHash(); + + FormBuilderPrivateHash::iterator it = fbHash.find(afb); + if (it == fbHash.end()) + it = fbHash.insert(afb, new QFormBuilderExtra); + return it.value(); +} + +void QFormBuilderExtra::removeInstance(const QAbstractFormBuilder *afb) +{ + FormBuilderPrivateHash &fbHash = *g_FormBuilderPrivateHash(); + + FormBuilderPrivateHash::iterator it = fbHash.find(afb); + if (it != fbHash.end()) { + delete it.value(); + fbHash.erase(it); + } +} + +void QFormBuilderExtra::setProcessingLayoutWidget(bool processing) +{ + m_layoutWidget = processing; +} + + bool QFormBuilderExtra::processingLayoutWidget() const +{ + return m_layoutWidget; +} +void QFormBuilderExtra::setResourceBuilder(QResourceBuilder *builder) +{ + if (m_resourceBuilder == builder) + return; + clearResourceBuilder(); + m_resourceBuilder = builder; +} + +QResourceBuilder *QFormBuilderExtra::resourceBuilder() const +{ + return m_resourceBuilder; +} + +void QFormBuilderExtra::clearResourceBuilder() +{ + if (m_resourceBuilder) { + delete m_resourceBuilder; + m_resourceBuilder = 0; + } +} + +void QFormBuilderExtra::setTextBuilder(QTextBuilder *builder) +{ + if (m_textBuilder == builder) + return; + clearTextBuilder(); + m_textBuilder = builder; +} + +QTextBuilder *QFormBuilderExtra::textBuilder() const +{ + return m_textBuilder; +} + +void QFormBuilderExtra::clearTextBuilder() +{ + if (m_textBuilder) { + delete m_textBuilder; + m_textBuilder = 0; + } +} + +void QFormBuilderExtra::registerButtonGroups(const DomButtonGroups *domGroups) +{ + typedef QList DomButtonGroupList; + const DomButtonGroupList domGroupList = domGroups->elementButtonGroup(); + const DomButtonGroupList::const_iterator cend = domGroupList.constEnd(); + for (DomButtonGroupList::const_iterator it = domGroupList.constBegin(); it != cend; ++it) { + DomButtonGroup *domGroup = *it; + m_buttonGroups.insert(domGroup->attributeName(), ButtonGroupEntry(domGroup, 0)); + } +} + +// Utilities for parsing per-cell integer properties that have setters and +// getters of the form 'setX(int idx, int value)' and 'x(int index)' +// (converting them to comma-separated string lists and back). +// Used for layout stretch and grid per-row/column properties. + +// Format a list of cell-properties of one dimension as a ','-separated list +template +inline QString perCellPropertyToString(const Layout *l, int count, int (Layout::*getter)(int) const) +{ + if (count == 0) + return QString(); + QString rc; + { + QTextStream str(&rc); + for (int i = 0; i < count; i++) { + if (i) + str << QLatin1Char(','); + str << (l->*getter)(i); + } + } + return rc; +} + +// Clear the property, set all cells to 0 + +template +inline void clearPerCellValue(Layout *l, int count, void (Layout::*setter)(int,int), int value = 0) +{ + for (int i = 0; i < count; i++) + (l->*setter)(i, value); +} + +// Parse and set the property from a comma-separated list + +template +inline bool parsePerCellProperty(Layout *l, int count, void (Layout::*setter)(int,int), const QString &s, int defaultValue = 0) +{ + if (s.isEmpty()) { + clearPerCellValue(l, count, setter, defaultValue); + return true; + } + const QStringList list = s.split(QLatin1Char(',')); + if (list.empty()) { + clearPerCellValue(l, count, setter, defaultValue); + return true; + } + // Apply all values contained in list + const int ac = qMin(count, list.size()); + bool ok; + int i = 0; + for ( ; i < ac; i++) { + const int value = list.at(i).toInt(&ok); + if (!ok || value < 0) + return false; + (l->*setter)(i, value); + } + // Clear rest + for ( ; i < count; i++) + (l->*setter)(i, defaultValue); + return true; +} + +// Read and write stretch +static QString msgInvalidStretch(const QString &objectName, const QString &stretch) +{ + //: Parsing layout stretch values + return QCoreApplication::translate("FormBuilder", "Invalid stretch value for '%1': '%2'").arg(objectName, stretch); +} + +QString QFormBuilderExtra::boxLayoutStretch(const QBoxLayout *box) +{ + return perCellPropertyToString(box, box->count(), &QBoxLayout::stretch); +} + +bool QFormBuilderExtra::setBoxLayoutStretch(const QString &s, QBoxLayout *box) +{ + const bool rc = parsePerCellProperty(box, box->count(), &QBoxLayout::setStretch, s); + if (!rc) + uiLibWarning(msgInvalidStretch(box->objectName(), s)); + return rc; +} + +void QFormBuilderExtra::clearBoxLayoutStretch(QBoxLayout *box) +{ + clearPerCellValue(box, box->count(), &QBoxLayout::setStretch); +} + +QString QFormBuilderExtra::gridLayoutRowStretch(const QGridLayout *grid) +{ + return perCellPropertyToString(grid, grid->rowCount(), &QGridLayout::rowStretch); +} + +bool QFormBuilderExtra::setGridLayoutRowStretch(const QString &s, QGridLayout *grid) +{ + const bool rc = parsePerCellProperty(grid, grid->rowCount(), &QGridLayout::setRowStretch, s); + if (!rc) + uiLibWarning(msgInvalidStretch(grid->objectName(), s)); + return rc; +} + +void QFormBuilderExtra::clearGridLayoutRowStretch(QGridLayout *grid) +{ + clearPerCellValue(grid, grid->rowCount(), &QGridLayout::setRowStretch); +} + +QString QFormBuilderExtra::gridLayoutColumnStretch(const QGridLayout *grid) +{ + return perCellPropertyToString(grid, grid->columnCount(), &QGridLayout::columnStretch); +} + +bool QFormBuilderExtra::setGridLayoutColumnStretch(const QString &s, QGridLayout *grid) +{ + const bool rc = parsePerCellProperty(grid, grid->columnCount(), &QGridLayout::setColumnStretch, s); + if (!rc) + uiLibWarning(msgInvalidStretch(grid->objectName(), s)); + return rc; +} + +void QFormBuilderExtra::clearGridLayoutColumnStretch(QGridLayout *grid) +{ + clearPerCellValue(grid, grid->columnCount(), &QGridLayout::setColumnStretch); +} + +// Read and write grid layout row/column size limits + +static QString msgInvalidMinimumSize(const QString &objectName, const QString &ms) +{ + //: Parsing grid layout minimum size values + return QCoreApplication::translate("FormBuilder", "Invalid minimum size for '%1': '%2'").arg(objectName, ms); +} + +QString QFormBuilderExtra::gridLayoutRowMinimumHeight(const QGridLayout *grid) +{ + return perCellPropertyToString(grid, grid->rowCount(), &QGridLayout::rowMinimumHeight); +} + +bool QFormBuilderExtra::setGridLayoutRowMinimumHeight(const QString &s, QGridLayout *grid) +{ + const bool rc = parsePerCellProperty(grid, grid->rowCount(), &QGridLayout::setRowMinimumHeight, s); + if (!rc) + uiLibWarning(msgInvalidMinimumSize(grid->objectName(), s)); + return rc; +} + +void QFormBuilderExtra::clearGridLayoutRowMinimumHeight(QGridLayout *grid) +{ + clearPerCellValue(grid, grid->rowCount(), &QGridLayout::setRowMinimumHeight); +} + +QString QFormBuilderExtra::gridLayoutColumnMinimumWidth(const QGridLayout *grid) +{ + return perCellPropertyToString(grid, grid->columnCount(), &QGridLayout::columnMinimumWidth); +} + +bool QFormBuilderExtra::setGridLayoutColumnMinimumWidth(const QString &s, QGridLayout *grid) +{ + const bool rc = parsePerCellProperty(grid, grid->columnCount(), &QGridLayout::setColumnMinimumWidth, s); + if (!rc) + uiLibWarning(msgInvalidMinimumSize(grid->objectName(), s)); + return rc; +} + +void QFormBuilderExtra::clearGridLayoutColumnMinimumWidth(QGridLayout *grid) +{ + clearPerCellValue(grid, grid->columnCount(), &QGridLayout::setColumnMinimumWidth); +} + +// ------------ QFormBuilderStrings + +QFormBuilderStrings::QFormBuilderStrings() : + buddyProperty(QLatin1String("buddy")), + cursorProperty(QLatin1String("cursor")), + objectNameProperty(QLatin1String("objectName")), + trueValue(QLatin1String("true")), + falseValue(QLatin1String("false")), + horizontalPostFix(QLatin1String("Horizontal")), + separator(QLatin1String("separator")), + defaultTitle(QLatin1String("Page")), + titleAttribute(QLatin1String("title")), + labelAttribute(QLatin1String("label")), + toolTipAttribute(QLatin1String("toolTip")), + whatsThisAttribute(QLatin1String("whatsThis")), + flagsAttribute(QLatin1String("flags")), + iconAttribute(QLatin1String("icon")), + pixmapAttribute(QLatin1String("pixmap")), + textAttribute(QLatin1String("text")), + currentIndexProperty(QLatin1String("currentIndex")), + toolBarAreaAttribute(QLatin1String("toolBarArea")), + toolBarBreakAttribute(QLatin1String("toolBarBreak")), + dockWidgetAreaAttribute(QLatin1String("dockWidgetArea")), + marginProperty(QLatin1String("margin")), + spacingProperty(QLatin1String("spacing")), + leftMarginProperty(QLatin1String("leftMargin")), + topMarginProperty(QLatin1String("topMargin")), + rightMarginProperty(QLatin1String("rightMargin")), + bottomMarginProperty(QLatin1String("bottomMargin")), + horizontalSpacingProperty(QLatin1String("horizontalSpacing")), + verticalSpacingProperty(QLatin1String("verticalSpacing")), + sizeHintProperty(QLatin1String("sizeHint")), + sizeTypeProperty(QLatin1String("sizeType")), + orientationProperty(QLatin1String("orientation")), + styleSheetProperty(QLatin1String("styleSheet")), + qtHorizontal(QLatin1String("Qt::Horizontal")), + qtVertical(QLatin1String("Qt::Vertical")), + currentRowProperty(QLatin1String("currentRow")), + tabSpacingProperty(QLatin1String("tabSpacing")), + qWidgetClass(QLatin1String("QWidget")), + lineClass(QLatin1String("Line")), + geometryProperty(QLatin1String("geometry")), + scriptWidgetVariable(QLatin1String("widget")), + scriptChildWidgetsVariable(QLatin1String("childWidgets")) +{ + itemRoles.append(qMakePair(Qt::FontRole, QString::fromLatin1("font"))); + itemRoles.append(qMakePair(Qt::TextAlignmentRole, QString::fromLatin1("textAlignment"))); + itemRoles.append(qMakePair(Qt::BackgroundRole, QString::fromLatin1("background"))); + itemRoles.append(qMakePair(Qt::ForegroundRole, QString::fromLatin1("foreground"))); + itemRoles.append(qMakePair(Qt::CheckStateRole, QString::fromLatin1("checkState"))); + + foreach (const RoleNName &it, itemRoles) + treeItemRoleHash.insert(it.second, it.first); + + itemTextRoles.append(qMakePair(qMakePair(Qt::EditRole, Qt::DisplayPropertyRole), + textAttribute)); // This must be first for the loop below + itemTextRoles.append(qMakePair(qMakePair(Qt::ToolTipRole, Qt::ToolTipPropertyRole), + toolTipAttribute)); + itemTextRoles.append(qMakePair(qMakePair(Qt::StatusTipRole, Qt::StatusTipPropertyRole), + QString::fromLatin1("statusTip"))); + itemTextRoles.append(qMakePair(qMakePair(Qt::WhatsThisRole, Qt::WhatsThisPropertyRole), + whatsThisAttribute)); + + // Note: this skips the first item! + QList::const_iterator it = itemTextRoles.constBegin(), end = itemTextRoles.constEnd(); + while (++it != end) + treeItemTextRoleHash.insert(it->second, it->first); +} + +const QFormBuilderStrings &QFormBuilderStrings::instance() +{ + static const QFormBuilderStrings rc; + return rc; +} + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/formbuilderextra_p.h b/designer/lib/uilib/formbuilderextra_p.h new file mode 100644 index 0000000..8dd2009 --- /dev/null +++ b/designer/lib/uilib/formbuilderextra_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABSTRACTFORMBUILDERPRIVATE_H +#define ABSTRACTFORMBUILDERPRIVATE_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 "uilib_global.h" + +#ifndef QT_FORMBUILDER_NO_SCRIPT +# include "formscriptrunner_p.h" +#endif + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QVariant; +class QWidget; +class QObject; +class QLabel; +class QButtonGroup; + +class QBoxLayout; +class QGridLayout; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class DomButtonGroups; +class DomButtonGroup; +class DomCustomWidget; + +class QAbstractFormBuilder; +class QResourceBuilder; +class QTextBuilder; + +class QDESIGNER_UILIB_EXPORT QFormBuilderExtra +{ + QFormBuilderExtra(); + ~QFormBuilderExtra(); +public: + struct CustomWidgetData { + CustomWidgetData(); + explicit CustomWidgetData(const DomCustomWidget *dc); + + QString addPageMethod; + QString script; + QString baseClass; + bool isContainer; + }; + + void clear(); + + bool applyPropertyInternally(QObject *o, const QString &propertyName, const QVariant &value); + + enum BuddyMode { BuddyApplyAll, BuddyApplyVisibleOnly }; + + void applyInternalProperties() const; + static bool applyBuddy(const QString &buddyName, BuddyMode applyMode, QLabel *label); + + const QPointer &parentWidget() const; + bool parentWidgetIsSet() const; + void setParentWidget(const QPointer &w); + +#ifndef QT_FORMBUILDER_NO_SCRIPT + QFormScriptRunner &formScriptRunner(); + QString customWidgetScript(const QString &className) const; +#endif + + void setProcessingLayoutWidget(bool processing); + bool processingLayoutWidget() const; + + void setResourceBuilder(QResourceBuilder *builder); + QResourceBuilder *resourceBuilder() const; + + void setTextBuilder(QTextBuilder *builder); + QTextBuilder *textBuilder() const; + + static QFormBuilderExtra *instance(const QAbstractFormBuilder *afb); + static void removeInstance(const QAbstractFormBuilder *afb); + + void storeCustomWidgetData(const QString &className, const DomCustomWidget *d); + QString customWidgetAddPageMethod(const QString &className) const; + QString customWidgetBaseClass(const QString &className) const; + bool isCustomWidgetContainer(const QString &className) const; + + // --- Hash used in creating button groups on demand. Store a map of name and pair of dom group and real group + void registerButtonGroups(const DomButtonGroups *groups); + + typedef QPair ButtonGroupEntry; + typedef QHash ButtonGroupHash; + const ButtonGroupHash &buttonGroups() const { return m_buttonGroups; } + ButtonGroupHash &buttonGroups() { return m_buttonGroups; } + + // return stretch as a comma-separated list + static QString boxLayoutStretch(const QBoxLayout*); + // apply stretch + static bool setBoxLayoutStretch(const QString &, QBoxLayout*); + static void clearBoxLayoutStretch(QBoxLayout*); + + static QString gridLayoutRowStretch(const QGridLayout *); + static bool setGridLayoutRowStretch(const QString &, QGridLayout *); + static void clearGridLayoutRowStretch(QGridLayout *); + + static QString gridLayoutColumnStretch(const QGridLayout *); + static bool setGridLayoutColumnStretch(const QString &, QGridLayout *); + static void clearGridLayoutColumnStretch(QGridLayout *); + + // return the row/column sizes as comma-separated lists + static QString gridLayoutRowMinimumHeight(const QGridLayout *); + static bool setGridLayoutRowMinimumHeight(const QString &, QGridLayout *); + static void clearGridLayoutRowMinimumHeight(QGridLayout *); + + static QString gridLayoutColumnMinimumWidth(const QGridLayout *); + static bool setGridLayoutColumnMinimumWidth(const QString &, QGridLayout *); + static void clearGridLayoutColumnMinimumWidth(QGridLayout *); + +private: + void clearResourceBuilder(); + void clearTextBuilder(); + + typedef QHash BuddyHash; + BuddyHash m_buddies; + +#ifndef QT_FORMBUILDER_NO_SCRIPT + QFormScriptRunner m_FormScriptRunner; +#endif + + QHash m_customWidgetDataHash; + + ButtonGroupHash m_buttonGroups; + + bool m_layoutWidget; + QResourceBuilder *m_resourceBuilder; + QTextBuilder *m_textBuilder; + + QPointer m_parentWidget; + bool m_parentWidgetIsSet; +}; + +void uiLibWarning(const QString &message); + +// Struct with static accessor that provides most strings used in the form builder. +struct QDESIGNER_UILIB_EXPORT QFormBuilderStrings { + QFormBuilderStrings(); + + static const QFormBuilderStrings &instance(); + + const QString buddyProperty; + const QString cursorProperty; + const QString objectNameProperty; + const QString trueValue; + const QString falseValue; + const QString horizontalPostFix; + const QString separator; + const QString defaultTitle; + const QString titleAttribute; + const QString labelAttribute; + const QString toolTipAttribute; + const QString whatsThisAttribute; + const QString flagsAttribute; + const QString iconAttribute; + const QString pixmapAttribute; + const QString textAttribute; + const QString currentIndexProperty; + const QString toolBarAreaAttribute; + const QString toolBarBreakAttribute; + const QString dockWidgetAreaAttribute; + const QString marginProperty; + const QString spacingProperty; + const QString leftMarginProperty; + const QString topMarginProperty; + const QString rightMarginProperty; + const QString bottomMarginProperty; + const QString horizontalSpacingProperty; + const QString verticalSpacingProperty; + const QString sizeHintProperty; + const QString sizeTypeProperty; + const QString orientationProperty; + const QString styleSheetProperty; + const QString qtHorizontal; + const QString qtVertical; + const QString currentRowProperty; + const QString tabSpacingProperty; + const QString qWidgetClass; + const QString lineClass; + const QString geometryProperty; + const QString scriptWidgetVariable; + const QString scriptChildWidgetsVariable; + + typedef QPair RoleNName; + QList itemRoles; + QHash treeItemRoleHash; + + // first.first is primary role, first.second is shadow role. + // Shadow is used for either the translation source or the designer + // representation of the string value. + typedef QPair, QString> TextRoleNName; + QList itemTextRoles; + QHash > treeItemTextRoleHash; +}; +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // ABSTRACTFORMBUILDERPRIVATE_H diff --git a/designer/lib/uilib/formscriptrunner.cpp b/designer/lib/uilib/formscriptrunner.cpp new file mode 100644 index 0000000..21315ac --- /dev/null +++ b/designer/lib/uilib/formscriptrunner.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formscriptrunner_p.h" +#include "formbuilderextra_p.h" +#include "ui4_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + enum { debugFormScriptRunner = 0 }; +} + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +class QFormScriptRunner::QFormScriptRunnerPrivate { +public: + QFormScriptRunnerPrivate() : m_options(DisableScripts) {} + void clearErrors() { m_errors.clear(); } + + bool run(const QString &script, QWidget *widget, const WidgetList &children, QString *errorMessage); + + static void initializeEngine(QWidget *w, const WidgetList &children, QScriptEngine &scriptEngine); + static QString engineError(QScriptEngine &scriptEngine); + + Options options() const { return m_options; } + void setOptions(Options options) { m_options = options; } + + Errors errors() const { return m_errors; } +private: + QScriptEngine m_scriptEngine; + Options m_options; + Errors m_errors; +}; + +bool QFormScriptRunner::QFormScriptRunnerPrivate::run(const QString &script, QWidget *widget, const WidgetList &children, QString *errorMessage) { + bool rc = false; + initializeEngine(widget, children, m_scriptEngine); + + do { + m_scriptEngine.evaluate(script); + if (m_scriptEngine.hasUncaughtException ()) { + *errorMessage = QCoreApplication::tr("Exception at line %1: %2").arg(m_scriptEngine.uncaughtExceptionLineNumber()).arg(engineError(m_scriptEngine)); + break; + } + rc = true; + } while (false); + m_scriptEngine.popContext(); + + if (!rc) { + Error error; + error.objectName = widget->objectName(); + error.script = script; + error.errorMessage = *errorMessage; + m_errors.push_back(error); + } + return rc; +} + +void QFormScriptRunner::QFormScriptRunnerPrivate::initializeEngine(QWidget *w, const WidgetList &children, QScriptEngine &scriptEngine) { + // Populate the script variables. This pushes a context which must be popped. + QScriptContext *ctx = scriptEngine.pushContext(); + QScriptValue widgetObject = scriptEngine.newQObject(w); + QScriptValue childrenArray = scriptEngine.newArray (children.size()); + + for(int i = 0; i < children.size(); i++) { + childrenArray.setProperty(i, scriptEngine.newQObject(children[i])); + } + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + ctx ->activationObject().setProperty(strings.scriptWidgetVariable, widgetObject); + ctx ->activationObject().setProperty(strings.scriptChildWidgetsVariable, childrenArray); +} + +QString QFormScriptRunner::QFormScriptRunnerPrivate::engineError(QScriptEngine &scriptEngine) { + QScriptValue error = scriptEngine.evaluate(QLatin1String("Error")); + if (error.isValid()) + return error.toString(); + return QCoreApplication::tr("Unknown error"); +} +// -- QFormScriptRunner + +QFormScriptRunner::QFormScriptRunner() : m_impl(new QFormScriptRunnerPrivate) +{ +} + +QFormScriptRunner::~QFormScriptRunner() +{ + delete m_impl; +} + +bool QFormScriptRunner::run(const DomWidget *domWidget, + const QString &customWidgetScript, + QWidget *widget, const WidgetList &children, + QString *errorMessage) +{ + typedef QList DomScripts; + + const Options scriptOptions = m_impl->options(); + if (scriptOptions & DisableScripts) + return true; + // get list + const DomScripts domScripts = domWidget->elementScript(); + // Concatenate snippets, starting with custom widget script + QString script = customWidgetScript; + if (script.isEmpty() && domScripts.empty()) + return true; + + foreach (const DomScript *scriptSnippet, domScripts) { + // Ensure new line + if (!script.isEmpty() && !script.endsWith(QLatin1Char('\n'))) + script += QLatin1Char('\n'); + script += scriptSnippet->text(); + } + + if (script.isEmpty()) + return true; + + const bool rc = m_impl->run(script, widget, children, errorMessage); + + if (debugFormScriptRunner) { + qDebug() << "For " << widget << " with " << children.size() << " children, ran: " << script; + if (!rc) + qDebug() << *errorMessage; + } + + if (!rc) { + if (!(scriptOptions & DisableWarnings)) { + const QString message = QCoreApplication::tr("An error occurred while running the script for %1: %2\nScript: %3"). + arg(widget->objectName()).arg(*errorMessage).arg(script); + qWarning() << message; + } + } + return rc; +} + +QFormScriptRunner::Options QFormScriptRunner::options() const +{ + return m_impl->options(); +} + +void QFormScriptRunner::setOptions(Options options) +{ + m_impl->setOptions(options); +} + + +QFormScriptRunner::Errors QFormScriptRunner::errors() const +{ + return m_impl->errors(); +} + +void QFormScriptRunner::clearErrors() +{ + m_impl->clearErrors(); +} + + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/formscriptrunner_p.h b/designer/lib/uilib/formscriptrunner_p.h new file mode 100644 index 0000000..605ca07 --- /dev/null +++ b/designer/lib/uilib/formscriptrunner_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMSCRIPTRUNNER_H +#define FORMSCRIPTRUNNER_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 +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QWidget; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class DomWidget; + +class QDESIGNER_UILIB_EXPORT QFormScriptRunner +{ +public: + QFormScriptRunner(); + ~QFormScriptRunner(); + + typedef QList WidgetList; + + bool run(const DomWidget *domWidget, + const QString &customWidgetScript, + QWidget *widget, const WidgetList &children, + QString *errorMessage); + + struct Error { + QString objectName; + QString script; + QString errorMessage; + }; + typedef QList Errors; + Errors errors() const; + void clearErrors(); + + enum Option { + NoOptions = 0x0, + DisableWarnings = 0x1, + DisableScripts = 02 + }; + Q_DECLARE_FLAGS(Options, Option) + + Options options() const; + void setOptions(Options options); + +private: + class QFormScriptRunnerPrivate; + QFormScriptRunnerPrivate *m_impl; + + QFormScriptRunner(const QFormScriptRunner &); + void operator = (const QFormScriptRunner &); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QFormScriptRunner::Options) + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // FORMSCRIPTRUNNER_H diff --git a/designer/lib/uilib/properties.cpp b/designer/lib/uilib/properties.cpp new file mode 100644 index 0000000..c1b6890 --- /dev/null +++ b/designer/lib/uilib/properties.cpp @@ -0,0 +1,681 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "properties_p.h" +#include "ui4_p.h" +#include "abstractformbuilder.h" +#include "formbuilderextra_p.h" +#include "resourcebuilder_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +static inline void fixEnum(QString &s) +{ + int qualifierIndex = s.lastIndexOf(QLatin1Char(':')); + if (qualifierIndex == -1) + qualifierIndex = s.lastIndexOf(QLatin1Char('.')); + if (qualifierIndex != -1) + s.remove(0, qualifierIndex + 1); +} +// Convert complex DOM types with the help of QAbstractFormBuilder +QVariant domPropertyToVariant(QAbstractFormBuilder *afb,const QMetaObject *meta,const DomProperty *p) +{ + // Complex types that need functions from QAbstractFormBuilder + switch(p->kind()) { + case DomProperty::String: { + const int index = meta->indexOfProperty(p->attributeName().toUtf8()); + if (index != -1 && meta->property(index).type() == QVariant::KeySequence) + return qVariantFromValue(QKeySequence(p->elementString()->text())); + } + break; + + case DomProperty::Palette: { + const DomPalette *dom = p->elementPalette(); + QPalette palette; + + if (dom->elementActive()) + afb->setupColorGroup(palette, QPalette::Active, dom->elementActive()); + + if (dom->elementInactive()) + afb->setupColorGroup(palette, QPalette::Inactive, dom->elementInactive()); + + if (dom->elementDisabled()) + afb->setupColorGroup(palette, QPalette::Disabled, dom->elementDisabled()); + + palette.setCurrentColorGroup(QPalette::Active); + return qVariantFromValue(palette); + } + + case DomProperty::Set: { + const QByteArray pname = p->attributeName().toUtf8(); + const int index = meta->indexOfProperty(pname); + if (index == -1) { + uiLibWarning(QCoreApplication::translate("QFormBuilder", "The set-type property %1 could not be read.").arg(p->attributeName())); + return QVariant(); + } + + const QMetaEnum e = meta->property(index).enumerator(); + Q_ASSERT(e.isFlag() == true); + return QVariant(e.keysToValue(p->elementSet().toUtf8())); + } + + case DomProperty::Enum: { + const QByteArray pname = p->attributeName().toUtf8(); + const int index = meta->indexOfProperty(pname); + QString enumValue = p->elementEnum(); + // Triggers in case of objects in Designer like Spacer/Line for which properties + // are serialized using language introspection. On preview, however, these objects are + // emulated by hacks in the formbuilder (size policy/orientation) + fixEnum(enumValue); + if (index == -1) { + // ### special-casing for Line (QFrame) -- fix for 4.2. Jambi hack for enumerations + if (!qstrcmp(meta->className(), "QFrame") + && (pname == QByteArray("orientation"))) { + return QVariant(enumValue == QFormBuilderStrings::instance().horizontalPostFix ? QFrame::HLine : QFrame::VLine); + } else { + uiLibWarning(QCoreApplication::translate("QFormBuilder", "The enumeration-type property %1 could not be read.").arg(p->attributeName())); + return QVariant(); + } + } + + const QMetaEnum e = meta->property(index).enumerator(); + return QVariant(e.keyToValue(enumValue.toUtf8())); + } + case DomProperty::Brush: + return qVariantFromValue(afb->setupBrush(p->elementBrush())); + default: + if (afb->resourceBuilder()->isResourceProperty(p)) { + return afb->resourceBuilder()->loadResource(afb->workingDirectory(), p); + } + + break; + } + + // simple type + return domPropertyToVariant(p); +} + +// Convert simple DOM types +QVariant domPropertyToVariant(const DomProperty *p) +{ + // requires non-const virtual nameToIcon, etc. + switch(p->kind()) { + case DomProperty::Bool: + return QVariant(p->elementBool() == QFormBuilderStrings::instance().trueValue); + + case DomProperty::Cstring: + return QVariant(p->elementCstring().toUtf8()); + + case DomProperty::Point: { + const DomPoint *point = p->elementPoint(); + return QVariant(QPoint(point->elementX(), point->elementY())); + } + + case DomProperty::PointF: { + const DomPointF *pointf = p->elementPointF(); + return QVariant(QPointF(pointf->elementX(), pointf->elementY())); + } + + case DomProperty::Size: { + const DomSize *size = p->elementSize(); + return QVariant(QSize(size->elementWidth(), size->elementHeight())); + } + + case DomProperty::SizeF: { + const DomSizeF *sizef = p->elementSizeF(); + return QVariant(QSizeF(sizef->elementWidth(), sizef->elementHeight())); + } + + case DomProperty::Rect: { + const DomRect *rc = p->elementRect(); + const QRect g(rc->elementX(), rc->elementY(), rc->elementWidth(), rc->elementHeight()); + return QVariant(g); + } + + case DomProperty::RectF: { + const DomRectF *rcf = p->elementRectF(); + const QRectF g(rcf->elementX(), rcf->elementY(), rcf->elementWidth(), rcf->elementHeight()); + return QVariant(g); + } + + case DomProperty::String: + return QVariant(p->elementString()->text()); + + case DomProperty::Number: + return QVariant(p->elementNumber()); + + case DomProperty::UInt: + return QVariant(p->elementUInt()); + + case DomProperty::LongLong: + return QVariant(p->elementLongLong()); + + case DomProperty::ULongLong: + return QVariant(p->elementULongLong()); + + case DomProperty::Double: + return QVariant(p->elementDouble()); + + case DomProperty::Char: { + const DomChar *character = p->elementChar(); + const QChar c(character->elementUnicode()); + return qVariantFromValue(c); + } + + case DomProperty::Color: { + const DomColor *color = p->elementColor(); + QColor c(color->elementRed(), color->elementGreen(), color->elementBlue()); + if (color->hasAttributeAlpha()) + c.setAlpha(color->attributeAlpha()); + return qVariantFromValue(c); + } + + case DomProperty::Font: { + const DomFont *font = p->elementFont(); + + QFont f; + if (font->hasElementFamily() && !font->elementFamily().isEmpty()) + f.setFamily(font->elementFamily()); + if (font->hasElementPointSize() && font->elementPointSize() > 0) + f.setPointSize(font->elementPointSize()); + if (font->hasElementWeight() && font->elementWeight() > 0) + f.setWeight(font->elementWeight()); + if (font->hasElementItalic()) + f.setItalic(font->elementItalic()); + if (font->hasElementBold()) + f.setBold(font->elementBold()); + if (font->hasElementUnderline()) + f.setUnderline(font->elementUnderline()); + if (font->hasElementStrikeOut()) + f.setStrikeOut(font->elementStrikeOut()); + if (font->hasElementKerning()) + f.setKerning(font->elementKerning()); + if (font->hasElementAntialiasing()) + f.setStyleStrategy(font->elementAntialiasing() ? QFont::PreferDefault : QFont::NoAntialias); + if (font->hasElementStyleStrategy()) { + f.setStyleStrategy(enumKeyOfObjectToValue("styleStrategy", font->elementStyleStrategy().toLatin1())); + } + return qVariantFromValue(f); + } + + case DomProperty::Date: { + const DomDate *date = p->elementDate(); + return QVariant(QDate(date->elementYear(), date->elementMonth(), date->elementDay())); + } + + case DomProperty::Time: { + const DomTime *t = p->elementTime(); + return QVariant(QTime(t->elementHour(), t->elementMinute(), t->elementSecond())); + } + + case DomProperty::DateTime: { + const DomDateTime *dateTime = p->elementDateTime(); + const QDate d(dateTime->elementYear(), dateTime->elementMonth(), dateTime->elementDay()); + const QTime tm(dateTime->elementHour(), dateTime->elementMinute(), dateTime->elementSecond()); + return QVariant(QDateTime(d, tm)); + } + + case DomProperty::Url: { + const DomUrl *url = p->elementUrl(); + return QVariant(QUrl(url->elementString()->text())); + } + +#ifndef QT_NO_CURSOR + case DomProperty::Cursor: + return qVariantFromValue(QCursor(static_cast(p->elementCursor()))); + + case DomProperty::CursorShape: + return qVariantFromValue(QCursor(enumKeyOfObjectToValue("cursorShape", p->elementCursorShape().toLatin1()))); +#endif + + case DomProperty::Locale: { + const DomLocale *locale = p->elementLocale(); + return qVariantFromValue(QLocale(enumKeyOfObjectToValue("language", locale->attributeLanguage().toLatin1()), + enumKeyOfObjectToValue("country", locale->attributeCountry().toLatin1()))); + } + case DomProperty::SizePolicy: { + const DomSizePolicy *sizep = p->elementSizePolicy(); + + QSizePolicy sizePolicy; + sizePolicy.setHorizontalStretch(sizep->elementHorStretch()); + sizePolicy.setVerticalStretch(sizep->elementVerStretch()); + + const QMetaEnum sizeType_enum = metaEnum("sizeType"); + + if (sizep->hasElementHSizeType()) { + sizePolicy.setHorizontalPolicy((QSizePolicy::Policy) sizep->elementHSizeType()); + } else if (sizep->hasAttributeHSizeType()) { + const QSizePolicy::Policy sp = enumKeyToValue(sizeType_enum, sizep->attributeHSizeType().toLatin1()); + sizePolicy.setHorizontalPolicy(sp); + } + + if (sizep->hasElementVSizeType()) { + sizePolicy.setVerticalPolicy((QSizePolicy::Policy) sizep->elementVSizeType()); + } else if (sizep->hasAttributeVSizeType()) { + const QSizePolicy::Policy sp = enumKeyToValue(sizeType_enum, sizep->attributeVSizeType().toLatin1()); + sizePolicy.setVerticalPolicy(sp); + } + + return qVariantFromValue(sizePolicy); + } + + case DomProperty::StringList: + return QVariant(p->elementStringList()->elementString()); + + default: + uiLibWarning(QCoreApplication::translate("QFormBuilder", "Reading properties of the type %1 is not supported yet.").arg(p->kind())); + break; + } + + return QVariant(); +} + +// Apply a simple variant type to a DOM property +static bool applySimpleProperty(const QVariant &v, bool translateString, DomProperty *dom_prop) +{ + switch (v.type()) { + case QVariant::String: { + DomString *str = new DomString(); + str->setText(v.toString()); + if (!translateString) + str->setAttributeNotr(QLatin1String("true")); + dom_prop->setElementString(str); + } + return true; + + case QVariant::ByteArray: + dom_prop->setElementCstring(QString::fromUtf8(v.toByteArray())); + return true; + + case QVariant::Int: + dom_prop->setElementNumber(v.toInt()); + return true; + + case QVariant::UInt: + dom_prop->setElementUInt(v.toUInt()); + return true; + + case QVariant::LongLong: + dom_prop->setElementLongLong(v.toLongLong()); + return true; + + case QVariant::ULongLong: + dom_prop->setElementULongLong(v.toULongLong()); + return true; + + case QVariant::Double: + dom_prop->setElementDouble(v.toDouble()); + return true; + + case QVariant::Bool: + dom_prop->setElementBool(v.toBool() ? QFormBuilderStrings::instance().trueValue : QFormBuilderStrings::instance().falseValue); + return true; + + case QVariant::Char: { + DomChar *ch = new DomChar(); + const QChar character = v.toChar(); + ch->setElementUnicode(character.unicode()); + dom_prop->setElementChar(ch); + } + return true; + + case QVariant::Point: { + DomPoint *pt = new DomPoint(); + const QPoint point = v.toPoint(); + pt->setElementX(point.x()); + pt->setElementY(point.y()); + dom_prop->setElementPoint(pt); + } + return true; + + case QVariant::PointF: { + DomPointF *ptf = new DomPointF(); + const QPointF pointf = v.toPointF(); + ptf->setElementX(pointf.x()); + ptf->setElementY(pointf.y()); + dom_prop->setElementPointF(ptf); + } + return true; + + case QVariant::Color: { + DomColor *clr = new DomColor(); + const QColor color = qvariant_cast(v); + clr->setElementRed(color.red()); + clr->setElementGreen(color.green()); + clr->setElementBlue(color.blue()); + const int alphaChannel = color.alpha(); + if (alphaChannel != 255) + clr->setAttributeAlpha(alphaChannel); + dom_prop->setElementColor(clr); + } + return true; + + case QVariant::Size: { + DomSize *sz = new DomSize(); + const QSize size = v.toSize(); + sz->setElementWidth(size.width()); + sz->setElementHeight(size.height()); + dom_prop->setElementSize(sz); + } + return true; + + case QVariant::SizeF: { + DomSizeF *szf = new DomSizeF(); + const QSizeF sizef = v.toSizeF(); + szf->setElementWidth(sizef.width()); + szf->setElementHeight(sizef.height()); + dom_prop->setElementSizeF(szf); + } + return true; + + case QVariant::Rect: { + DomRect *rc = new DomRect(); + const QRect rect = v.toRect(); + rc->setElementX(rect.x()); + rc->setElementY(rect.y()); + rc->setElementWidth(rect.width()); + rc->setElementHeight(rect.height()); + dom_prop->setElementRect(rc); + } + return true; + + case QVariant::RectF: { + DomRectF *rcf = new DomRectF(); + const QRectF rectf = v.toRectF(); + rcf->setElementX(rectf.x()); + rcf->setElementY(rectf.y()); + rcf->setElementWidth(rectf.width()); + rcf->setElementHeight(rectf.height()); + dom_prop->setElementRectF(rcf); + } + return true; + + case QVariant::Font: { + DomFont *fnt = new DomFont(); + const QFont font = qvariant_cast(v); + const uint mask = font.resolve(); + if (mask & QFont::WeightResolved) { + fnt->setElementBold(font.bold()); + fnt->setElementWeight(font.weight()); + } + if (mask & QFont::FamilyResolved) + fnt->setElementFamily(font.family()); + if (mask & QFont::StyleResolved) + fnt->setElementItalic(font.italic()); + if (mask & QFont::SizeResolved) + fnt->setElementPointSize(font.pointSize()); + if (mask & QFont::StrikeOutResolved) + fnt->setElementStrikeOut(font.strikeOut()); + if (mask & QFont::UnderlineResolved) + fnt->setElementUnderline(font.underline()); + if (mask & QFont::KerningResolved) + fnt->setElementKerning(font.kerning()); + if (mask & QFont::StyleStrategyResolved) { + const QMetaEnum styleStrategy_enum = metaEnum("styleStrategy"); + fnt->setElementStyleStrategy(QLatin1String(styleStrategy_enum.valueToKey(font.styleStrategy()))); + } + dom_prop->setElementFont(fnt); + } + return true; + +#ifndef QT_NO_CURSOR + case QVariant::Cursor: { + const QMetaEnum cursorShape_enum = metaEnum("cursorShape"); + dom_prop->setElementCursorShape(QLatin1String(cursorShape_enum.valueToKey(qvariant_cast(v).shape()))); + } + return true; +#endif + + case QVariant::KeySequence: { + DomString *s = new DomString(); + s->setText(qvariant_cast(v).toString(QKeySequence::PortableText)); + dom_prop->setElementString(s); + } + return true; + + case QVariant::Locale: { + DomLocale *dom = new DomLocale(); + const QLocale locale = qvariant_cast(v); + + const QMetaEnum language_enum = metaEnum("language"); + const QMetaEnum country_enum = metaEnum("country"); + + dom->setAttributeLanguage(QLatin1String(language_enum.valueToKey(locale.language()))); + dom->setAttributeCountry(QLatin1String(country_enum.valueToKey(locale.country()))); + + dom_prop->setElementLocale(dom); + } + return true; + + case QVariant::SizePolicy: { + DomSizePolicy *dom = new DomSizePolicy(); + const QSizePolicy sizePolicy = qvariant_cast(v); + + dom->setElementHorStretch(sizePolicy.horizontalStretch()); + dom->setElementVerStretch(sizePolicy.verticalStretch()); + + const QMetaEnum sizeType_enum = metaEnum("sizeType"); + + dom->setAttributeHSizeType(QLatin1String(sizeType_enum.valueToKey(sizePolicy.horizontalPolicy()))); + dom->setAttributeVSizeType(QLatin1String(sizeType_enum.valueToKey(sizePolicy.verticalPolicy()))); + + dom_prop->setElementSizePolicy(dom); + } + return true; + + case QVariant::Date: { + DomDate *dom = new DomDate(); + const QDate date = qvariant_cast(v); + + dom->setElementYear(date.year()); + dom->setElementMonth(date.month()); + dom->setElementDay(date.day()); + + dom_prop->setElementDate(dom); + } + return true; + + case QVariant::Time: { + DomTime *dom = new DomTime(); + const QTime time = qvariant_cast(v); + + dom->setElementHour(time.hour()); + dom->setElementMinute(time.minute()); + dom->setElementSecond(time.second()); + + dom_prop->setElementTime(dom); + } + return true; + + case QVariant::DateTime: { + DomDateTime *dom = new DomDateTime(); + const QDateTime dateTime = qvariant_cast(v); + + dom->setElementHour(dateTime.time().hour()); + dom->setElementMinute(dateTime.time().minute()); + dom->setElementSecond(dateTime.time().second()); + dom->setElementYear(dateTime.date().year()); + dom->setElementMonth(dateTime.date().month()); + dom->setElementDay(dateTime.date().day()); + + dom_prop->setElementDateTime(dom); + } + return true; + + case QVariant::Url: { + DomUrl *dom = new DomUrl(); + const QUrl url = v.toUrl(); + + DomString *str = new DomString(); + str->setText(url.toString()); + dom->setElementString(str); + + dom_prop->setElementUrl(dom); + } + return true; + + case QVariant::StringList: { + DomStringList *sl = new DomStringList; + sl->setElementString(qvariant_cast(v)); + dom_prop->setElementStringList(sl); + } + return true; + + default: + break; + } + + return false; +} +static QString msgCannotWriteProperty(const QString &pname, const QVariant &v) +{ + return QCoreApplication::translate("QFormBuilder", "The property %1 could not be written. The type %2 is not supported yet."). + arg(pname).arg(QLatin1String(v.typeName())); + +} + +static bool isOfType(const QMetaObject *what, const QMetaObject *type) +{ + do { + if (what == type) + return true; + } while ((what = what->superClass())); + return false; +} + +static bool isTranslatable(const QString &pname, const QVariant &v, const QMetaObject *meta) +{ + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + if (pname == strings.objectNameProperty) + return false; + if (pname == strings.styleSheetProperty && v.type() == QVariant::String && isOfType(meta, &QWidget::staticMetaObject)) + return false; + return true; +} + +// Convert complex variant types to DOM properties with the help of QAbstractFormBuilder +// Does not perform a check using QAbstractFormBuilder::checkProperty(). +DomProperty *variantToDomProperty(QAbstractFormBuilder *afb, const QMetaObject *meta, + const QString &pname, const QVariant &v) +{ + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + DomProperty *dom_prop = new DomProperty(); + dom_prop->setAttributeName(pname); + + const int pindex = meta->indexOfProperty(pname.toLatin1()); + if (pindex != -1) { + QMetaProperty meta_property = meta->property(pindex); + if ((v.type() == QVariant::Int || v.type() == QVariant::UInt) && meta_property.isEnumType()) { + const QMetaEnum e = meta_property.enumerator(); + if (e.isFlag()) + dom_prop->setElementSet(QString::fromAscii(e.valueToKeys(v.toInt()))); + else + dom_prop->setElementEnum(QString::fromAscii(e.valueToKey(v.toInt()))); + return dom_prop; + } + if (!meta_property.hasStdCppSet() || (isOfType(meta, &QAbstractScrollArea::staticMetaObject) && pname == strings.cursorProperty)) + dom_prop->setAttributeStdset(0); + } + + // Try simple properties + if (applySimpleProperty(v, isTranslatable(pname, v, meta), dom_prop)) + return dom_prop; + + // Complex properties + switch (v.type()) { + case QVariant::Palette: { + DomPalette *dom = new DomPalette(); + QPalette palette = qvariant_cast(v); + + palette.setCurrentColorGroup(QPalette::Active); + dom->setElementActive(afb->saveColorGroup(palette)); + + palette.setCurrentColorGroup(QPalette::Inactive); + dom->setElementInactive(afb->saveColorGroup(palette)); + + palette.setCurrentColorGroup(QPalette::Disabled); + dom->setElementDisabled(afb->saveColorGroup(palette)); + + dom_prop->setElementPalette(dom); + } break; + case QVariant::Brush: + dom_prop->setElementBrush(afb->saveBrush(qvariant_cast(v))); + break; + default: { + const bool hadAttributeStdset = dom_prop->hasAttributeStdset(); + const bool attributeStdset = dom_prop->attributeStdset(); + delete dom_prop; + if (afb->resourceBuilder()->isResourceType(v)) { + dom_prop = afb->resourceBuilder()->saveResource(afb->workingDirectory(), v); + if (dom_prop) { + dom_prop->setAttributeName(pname); + if (hadAttributeStdset) + dom_prop->setAttributeStdset(attributeStdset); + } + break; + } + uiLibWarning(msgCannotWriteProperty(pname, v)); + } return 0; + } + return dom_prop; +} + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/properties_p.h b/designer/lib/uilib/properties_p.h new file mode 100644 index 0000000..ea697dc --- /dev/null +++ b/designer/lib/uilib/properties_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef UILIBPROPERTIES_H +#define UILIBPROPERTIES_H + +#include + +#include +#include +#include +#include + +#include + +#include "formbuilderextra_p.h" + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class QAbstractFormBuilder; +class DomProperty; + +QDESIGNER_UILIB_EXPORT DomProperty *variantToDomProperty(QAbstractFormBuilder *abstractFormBuilder, const QMetaObject *meta, const QString &propertyName, const QVariant &value); + + +QDESIGNER_UILIB_EXPORT QVariant domPropertyToVariant(const DomProperty *property); +QDESIGNER_UILIB_EXPORT QVariant domPropertyToVariant(QAbstractFormBuilder *abstractFormBuilder, const QMetaObject *meta, const DomProperty *property); + +// This class exists to provide meta information +// for enumerations only. +class QAbstractFormBuilderGadget: public QWidget +{ + Q_OBJECT + Q_PROPERTY(Qt::ItemFlags itemFlags READ fakeItemFlags) + Q_PROPERTY(Qt::CheckState checkState READ fakeCheckState) + Q_PROPERTY(Qt::Alignment textAlignment READ fakeAlignment) + Q_PROPERTY(Qt::Orientation orientation READ fakeOrientation) + Q_PROPERTY(QSizePolicy::Policy sizeType READ fakeSizeType) + Q_PROPERTY(QPalette::ColorRole colorRole READ fakeColorRole) + Q_PROPERTY(QPalette::ColorGroup colorGroup READ fakeColorGroup) + Q_PROPERTY(QFont::StyleStrategy styleStrategy READ fakeStyleStrategy) + Q_PROPERTY(Qt::CursorShape cursorShape READ fakeCursorShape) + Q_PROPERTY(Qt::BrushStyle brushStyle READ fakeBrushStyle) + Q_PROPERTY(Qt::ToolBarArea toolBarArea READ fakeToolBarArea) + Q_PROPERTY(QGradient::Type gradientType READ fakeGradientType) + Q_PROPERTY(QGradient::Spread gradientSpread READ fakeGradientSpread) + Q_PROPERTY(QGradient::CoordinateMode gradientCoordinate READ fakeGradientCoordinate) + Q_PROPERTY(QLocale::Language language READ fakeLanguage) + Q_PROPERTY(QLocale::Country country READ fakeCountry) +public: + QAbstractFormBuilderGadget() { Q_ASSERT(0); } + + Qt::Orientation fakeOrientation() const { Q_ASSERT(0); return Qt::Horizontal; } + QSizePolicy::Policy fakeSizeType() const { Q_ASSERT(0); return QSizePolicy::Expanding; } + QPalette::ColorGroup fakeColorGroup() const { Q_ASSERT(0); return static_cast(0); } + QPalette::ColorRole fakeColorRole() const { Q_ASSERT(0); return static_cast(0); } + QFont::StyleStrategy fakeStyleStrategy() const { Q_ASSERT(0); return QFont::PreferDefault; } + Qt::CursorShape fakeCursorShape() const { Q_ASSERT(0); return Qt::ArrowCursor; } + Qt::BrushStyle fakeBrushStyle() const { Q_ASSERT(0); return Qt::NoBrush; } + Qt::ToolBarArea fakeToolBarArea() const { Q_ASSERT(0); return Qt::NoToolBarArea; } + QGradient::Type fakeGradientType() const { Q_ASSERT(0); return QGradient::NoGradient; } + QGradient::Spread fakeGradientSpread() const { Q_ASSERT(0); return QGradient::PadSpread; } + QGradient::CoordinateMode fakeGradientCoordinate() const { Q_ASSERT(0); return QGradient::LogicalMode; } + QLocale::Language fakeLanguage() const { Q_ASSERT(0); return QLocale::C; } + QLocale::Country fakeCountry() const { Q_ASSERT(0); return QLocale::AnyCountry; } + Qt::ItemFlags fakeItemFlags() const { Q_ASSERT(0); return Qt::NoItemFlags; } + Qt::CheckState fakeCheckState() const { Q_ASSERT(0); return Qt::Unchecked; } + Qt::Alignment fakeAlignment() const { Q_ASSERT(0); return Qt::AlignLeft; } +}; + +// Convert key to value for a given QMetaEnum +template +inline EnumType enumKeyToValue(const QMetaEnum &metaEnum,const char *key, const EnumType* = 0) +{ + int val = metaEnum.keyToValue(key); + if (val == -1) { + + uiLibWarning(QCoreApplication::translate("QFormBuilder", "The enumeration-value '%1' is invalid. The default value '%2' will be used instead.") + .arg(QString::fromUtf8(key)).arg(QString::fromUtf8(metaEnum.key(0)))); + val = metaEnum.value(0); + } + return static_cast(val); +} + +// Convert keys to value for a given QMetaEnum +template +inline EnumType enumKeysToValue(const QMetaEnum &metaEnum,const char *keys, const EnumType* = 0) +{ + int val = metaEnum.keysToValue(keys); + if (val == -1) { + + uiLibWarning(QCoreApplication::translate("QFormBuilder", "The flag-value '%1' is invalid. Zero will be used instead.") + .arg(QString::fromUtf8(keys))); + val = 0; + } + return static_cast(QFlag(val)); +} + +// Access meta enumeration object of a qobject +template +inline QMetaEnum metaEnum(const char *name, const QObjectType* = 0) +{ + const int e_index = QObjectType::staticMetaObject.indexOfProperty(name); + Q_ASSERT(e_index != -1); + return QObjectType::staticMetaObject.property(e_index).enumerator(); +} + +// Convert key to value for enumeration by name +template +inline EnumType enumKeyOfObjectToValue(const char *enumName, const char *key, const QObjectType* = 0, const EnumType* = 0) +{ + const QMetaEnum me = metaEnum(enumName); + return enumKeyToValue(me, key); +} + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // UILIBPROPERTIES_H diff --git a/designer/lib/uilib/qdesignerexportwidget.h b/designer/lib/uilib/qdesignerexportwidget.h new file mode 100644 index 0000000..444d690 --- /dev/null +++ b/designer/lib/uilib/qdesignerexportwidget.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNEREXPORTWIDGET_H +#define QDESIGNEREXPORTWIDGET_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +#if 0 +// pragma for syncqt, don't remove. +#pragma qt_class(QDesignerExportWidget) +#endif + +#if defined(QDESIGNER_EXPORT_WIDGETS) +# define QDESIGNER_WIDGET_EXPORT Q_DECL_EXPORT +#else +# define QDESIGNER_WIDGET_EXPORT Q_DECL_IMPORT +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QDESIGNEREXPORTWIDGET_H diff --git a/designer/lib/uilib/resourcebuilder.cpp b/designer/lib/uilib/resourcebuilder.cpp new file mode 100644 index 0000000..32e84f0 --- /dev/null +++ b/designer/lib/uilib/resourcebuilder.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "resourcebuilder_p.h" +#include "ui4_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +QResourceBuilder::QResourceBuilder() +{ + +} + +QResourceBuilder::~QResourceBuilder() +{ + +} + +int QResourceBuilder::iconStateFlags(const DomResourceIcon *dpi) +{ + int rc = 0; + if (dpi->hasElementNormalOff()) + rc |= NormalOff; + if (dpi->hasElementNormalOn()) + rc |= NormalOn; + if (dpi->hasElementDisabledOff()) + rc |= DisabledOff; + if (dpi->hasElementDisabledOn()) + rc |= DisabledOn; + if (dpi->hasElementActiveOff()) + rc |= ActiveOff; + if (dpi->hasElementActiveOn()) + rc |= ActiveOn; + if (dpi->hasElementSelectedOff()) + rc |= SelectedOff; + if (dpi->hasElementSelectedOn()) + rc |= SelectedOn; + return rc; +} + +QVariant QResourceBuilder::loadResource(const QDir &workingDirectory, const DomProperty *property) const +{ + switch (property->kind()) { + case DomProperty::Pixmap: { + const DomResourcePixmap *dpx = property->elementPixmap(); + QPixmap pixmap(QFileInfo(workingDirectory, dpx->text()).absoluteFilePath()); + return qVariantFromValue(pixmap); + } + case DomProperty::IconSet: { + const DomResourceIcon *dpi = property->elementIconSet(); + if (const int flags = iconStateFlags(dpi)) { // new, post 4.4 format + QIcon icon; + if (flags & NormalOff) + icon.addFile(QFileInfo(workingDirectory, dpi->elementNormalOff()->text()).absoluteFilePath(), QSize(), QIcon::Normal, QIcon::Off); + if (flags & NormalOn) + icon.addFile(QFileInfo(workingDirectory, dpi->elementNormalOn()->text()).absoluteFilePath(), QSize(), QIcon::Normal, QIcon::On); + if (flags & DisabledOff) + icon.addFile(QFileInfo(workingDirectory, dpi->elementDisabledOff()->text()).absoluteFilePath(), QSize(), QIcon::Disabled, QIcon::Off); + if (flags & DisabledOn) + icon.addFile(QFileInfo(workingDirectory, dpi->elementDisabledOn()->text()).absoluteFilePath(), QSize(), QIcon::Disabled, QIcon::On); + if (flags & ActiveOff) + icon.addFile(QFileInfo(workingDirectory, dpi->elementActiveOff()->text()).absoluteFilePath(), QSize(), QIcon::Active, QIcon::Off); + if (flags & ActiveOn) + icon.addFile(QFileInfo(workingDirectory, dpi->elementActiveOn()->text()).absoluteFilePath(), QSize(), QIcon::Active, QIcon::On); + if (flags & SelectedOff) + icon.addFile(QFileInfo(workingDirectory, dpi->elementSelectedOff()->text()).absoluteFilePath(), QSize(), QIcon::Selected, QIcon::Off); + if (flags & SelectedOn) + icon.addFile(QFileInfo(workingDirectory, dpi->elementSelectedOn()->text()).absoluteFilePath(), QSize(), QIcon::Selected, QIcon::On); + return qVariantFromValue(icon); + } else { // 4.3 legacy + const QIcon icon(QFileInfo(workingDirectory, dpi->text()).absoluteFilePath()); + return qVariantFromValue(icon); + } + } + break; + default: + break; + } + return QVariant(); +} + +QVariant QResourceBuilder::toNativeValue(const QVariant &value) const +{ + return value; +} + +DomProperty *QResourceBuilder::saveResource(const QDir &workingDirectory, const QVariant &value) const +{ + Q_UNUSED(workingDirectory) + Q_UNUSED(value) + return 0; +} + +bool QResourceBuilder::isResourceProperty(const DomProperty *p) const +{ + switch (p->kind()) { + case DomProperty::Pixmap: + case DomProperty::IconSet: + return true; + default: + break; + } + return false; +} + +bool QResourceBuilder::isResourceType(const QVariant &value) const +{ + switch (value.type()) { + case QVariant::Pixmap: + case QVariant::Icon: + return true; + default: + break; + } + return false; +} + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/resourcebuilder_p.h b/designer/lib/uilib/resourcebuilder_p.h new file mode 100644 index 0000000..8eb4304 --- /dev/null +++ b/designer/lib/uilib/resourcebuilder_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RESOURCEBUILDER_H +#define RESOURCEBUILDER_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 +#include +#include + +QT_BEGIN_NAMESPACE + +class QDir; +class QVariant; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class DomProperty; +class DomResourceIcon; + +class QDESIGNER_UILIB_EXPORT QResourceBuilder +{ +public: + enum IconStateFlags { + NormalOff = 0x1, NormalOn = 0x2, DisabledOff = 0x4, DisabledOn = 0x8, + ActiveOff = 0x10, ActiveOn = 0x20, SelectedOff = 0x40, SelectedOn = 0x80 + }; + + QResourceBuilder(); + virtual ~QResourceBuilder(); + + virtual QVariant loadResource(const QDir &workingDirectory, const DomProperty *property) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveResource(const QDir &workingDirectory, const QVariant &value) const; + + virtual bool isResourceProperty(const DomProperty *p) const; + + virtual bool isResourceType(const QVariant &value) const; + + static int iconStateFlags(const DomResourceIcon *resIcon); +}; + + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // RESOURCEBUILDER_H diff --git a/designer/lib/uilib/textbuilder.cpp b/designer/lib/uilib/textbuilder.cpp new file mode 100644 index 0000000..64cdb47 --- /dev/null +++ b/designer/lib/uilib/textbuilder.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textbuilder_p.h" +#include "ui4_p.h" +#include + +QT_BEGIN_NAMESPACE + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal { +#endif + +QTextBuilder::QTextBuilder() +{ + +} + +QTextBuilder::~QTextBuilder() +{ + +} + +QVariant QTextBuilder::loadText(const DomProperty *property) const +{ + if (property->kind() == DomProperty::String) + return property->elementString()->text(); + return QVariant(); +} + +QVariant QTextBuilder::toNativeValue(const QVariant &value) const +{ + return value; +} + +DomProperty *QTextBuilder::saveText(const QVariant &value) const +{ + Q_UNUSED(value) + return 0; +} + +#ifdef QFORMINTERNAL_NAMESPACE +} // namespace QFormInternal +#endif + +QT_END_NAMESPACE diff --git a/designer/lib/uilib/textbuilder_p.h b/designer/lib/uilib/textbuilder_p.h new file mode 100644 index 0000000..dcceea2 --- /dev/null +++ b/designer/lib/uilib/textbuilder_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTBUILDER_H +#define TEXTBUILDER_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 +#include +#include + +QT_BEGIN_NAMESPACE + +class QDir; +class QVariant; + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + +class DomProperty; +class DomString; + +class QDESIGNER_UILIB_EXPORT QTextBuilder +{ +public: + QTextBuilder(); + virtual ~QTextBuilder(); + + virtual QVariant loadText(const DomProperty *property) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveText(const QVariant &value) const; +}; + + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // TEXTBUILDER_H diff --git a/designer/lib/uilib/ui4.cpp b/designer/lib/uilib/ui4.cpp new file mode 100644 index 0000000..d9bee39 --- /dev/null +++ b/designer/lib/uilib/ui4.cpp @@ -0,0 +1,11132 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "ui4_p.h" + +#ifdef QUILOADER_QDOM_READ +#include +#endif + +QT_BEGIN_NAMESPACE +#ifdef QFORMINTERNAL_NAMESPACE +using namespace QFormInternal; +#endif + +/******************************************************************************* +** Implementations +*/ + +void DomUI::clear(bool clear_all) +{ + delete m_widget; + delete m_layoutDefault; + delete m_layoutFunction; + delete m_customWidgets; + delete m_tabStops; + delete m_images; + delete m_includes; + delete m_resources; + delete m_connections; + delete m_designerdata; + delete m_slots; + delete m_buttonGroups; + + if (clear_all) { + m_text.clear(); + m_has_attr_version = false; + m_has_attr_language = false; + m_has_attr_displayname = false; + m_has_attr_stdsetdef = false; + m_attr_stdsetdef = 0; + m_has_attr_stdSetDef = false; + m_attr_stdSetDef = 0; + } + + m_children = 0; + m_widget = 0; + m_layoutDefault = 0; + m_layoutFunction = 0; + m_customWidgets = 0; + m_tabStops = 0; + m_images = 0; + m_includes = 0; + m_resources = 0; + m_connections = 0; + m_designerdata = 0; + m_slots = 0; + m_buttonGroups = 0; +} + +DomUI::DomUI() +{ + m_children = 0; + m_has_attr_version = false; + m_has_attr_language = false; + m_has_attr_displayname = false; + m_has_attr_stdsetdef = false; + m_attr_stdsetdef = 0; + m_has_attr_stdSetDef = false; + m_attr_stdSetDef = 0; + m_widget = 0; + m_layoutDefault = 0; + m_layoutFunction = 0; + m_customWidgets = 0; + m_tabStops = 0; + m_images = 0; + m_includes = 0; + m_resources = 0; + m_connections = 0; + m_designerdata = 0; + m_slots = 0; + m_buttonGroups = 0; +} + +DomUI::~DomUI() +{ + delete m_widget; + delete m_layoutDefault; + delete m_layoutFunction; + delete m_customWidgets; + delete m_tabStops; + delete m_images; + delete m_includes; + delete m_resources; + delete m_connections; + delete m_designerdata; + delete m_slots; + delete m_buttonGroups; +} + +void DomUI::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("version")) { + setAttributeVersion(attribute.value().toString()); + continue; + } + if (name == QLatin1String("language")) { + setAttributeLanguage(attribute.value().toString()); + continue; + } + if (name == QLatin1String("displayname")) { + setAttributeDisplayname(attribute.value().toString()); + continue; + } + if (name == QLatin1String("stdsetdef")) { + setAttributeStdsetdef(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("stdSetDef")) { + setAttributeStdSetDef(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("author")) { + setElementAuthor(reader.readElementText()); + continue; + } + if (tag == QLatin1String("comment")) { + setElementComment(reader.readElementText()); + continue; + } + if (tag == QLatin1String("exportmacro")) { + setElementExportMacro(reader.readElementText()); + continue; + } + if (tag == QLatin1String("class")) { + setElementClass(reader.readElementText()); + continue; + } + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(reader); + setElementWidget(v); + continue; + } + if (tag == QLatin1String("layoutdefault")) { + DomLayoutDefault *v = new DomLayoutDefault(); + v->read(reader); + setElementLayoutDefault(v); + continue; + } + if (tag == QLatin1String("layoutfunction")) { + DomLayoutFunction *v = new DomLayoutFunction(); + v->read(reader); + setElementLayoutFunction(v); + continue; + } + if (tag == QLatin1String("pixmapfunction")) { + setElementPixmapFunction(reader.readElementText()); + continue; + } + if (tag == QLatin1String("customwidgets")) { + DomCustomWidgets *v = new DomCustomWidgets(); + v->read(reader); + setElementCustomWidgets(v); + continue; + } + if (tag == QLatin1String("tabstops")) { + DomTabStops *v = new DomTabStops(); + v->read(reader); + setElementTabStops(v); + continue; + } + if (tag == QLatin1String("images")) { + DomImages *v = new DomImages(); + v->read(reader); + setElementImages(v); + continue; + } + if (tag == QLatin1String("includes")) { + DomIncludes *v = new DomIncludes(); + v->read(reader); + setElementIncludes(v); + continue; + } + if (tag == QLatin1String("resources")) { + DomResources *v = new DomResources(); + v->read(reader); + setElementResources(v); + continue; + } + if (tag == QLatin1String("connections")) { + DomConnections *v = new DomConnections(); + v->read(reader); + setElementConnections(v); + continue; + } + if (tag == QLatin1String("designerdata")) { + DomDesignerData *v = new DomDesignerData(); + v->read(reader); + setElementDesignerdata(v); + continue; + } + if (tag == QLatin1String("slots")) { + DomSlots *v = new DomSlots(); + v->read(reader); + setElementSlots(v); + continue; + } + if (tag == QLatin1String("buttongroups")) { + DomButtonGroups *v = new DomButtonGroups(); + v->read(reader); + setElementButtonGroups(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomUI::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("version"))) + setAttributeVersion(node.attribute(QLatin1String("version"))); + if (node.hasAttribute(QLatin1String("language"))) + setAttributeLanguage(node.attribute(QLatin1String("language"))); + if (node.hasAttribute(QLatin1String("displayname"))) + setAttributeDisplayname(node.attribute(QLatin1String("displayname"))); + if (node.hasAttribute(QLatin1String("stdsetdef"))) + setAttributeStdsetdef(node.attribute(QLatin1String("stdsetdef")).toInt()); + if (node.hasAttribute(QLatin1String("stdSetDef"))) + setAttributeStdSetDef(node.attribute(QLatin1String("stdSetDef")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("author")) { + setElementAuthor(e.text()); + continue; + } + if (tag == QLatin1String("comment")) { + setElementComment(e.text()); + continue; + } + if (tag == QLatin1String("exportmacro")) { + setElementExportMacro(e.text()); + continue; + } + if (tag == QLatin1String("class")) { + setElementClass(e.text()); + continue; + } + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(e); + setElementWidget(v); + continue; + } + if (tag == QLatin1String("layoutdefault")) { + DomLayoutDefault *v = new DomLayoutDefault(); + v->read(e); + setElementLayoutDefault(v); + continue; + } + if (tag == QLatin1String("layoutfunction")) { + DomLayoutFunction *v = new DomLayoutFunction(); + v->read(e); + setElementLayoutFunction(v); + continue; + } + if (tag == QLatin1String("pixmapfunction")) { + setElementPixmapFunction(e.text()); + continue; + } + if (tag == QLatin1String("customwidgets")) { + DomCustomWidgets *v = new DomCustomWidgets(); + v->read(e); + setElementCustomWidgets(v); + continue; + } + if (tag == QLatin1String("tabstops")) { + DomTabStops *v = new DomTabStops(); + v->read(e); + setElementTabStops(v); + continue; + } + if (tag == QLatin1String("images")) { + DomImages *v = new DomImages(); + v->read(e); + setElementImages(v); + continue; + } + if (tag == QLatin1String("includes")) { + DomIncludes *v = new DomIncludes(); + v->read(e); + setElementIncludes(v); + continue; + } + if (tag == QLatin1String("resources")) { + DomResources *v = new DomResources(); + v->read(e); + setElementResources(v); + continue; + } + if (tag == QLatin1String("connections")) { + DomConnections *v = new DomConnections(); + v->read(e); + setElementConnections(v); + continue; + } + if (tag == QLatin1String("designerdata")) { + DomDesignerData *v = new DomDesignerData(); + v->read(e); + setElementDesignerdata(v); + continue; + } + if (tag == QLatin1String("slots")) { + DomSlots *v = new DomSlots(); + v->read(e); + setElementSlots(v); + continue; + } + if (tag == QLatin1String("buttongroups")) { + DomButtonGroups *v = new DomButtonGroups(); + v->read(e); + setElementButtonGroups(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomUI::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("ui") : tagName.toLower()); + + if (hasAttributeVersion()) + writer.writeAttribute(QLatin1String("version"), attributeVersion()); + + if (hasAttributeLanguage()) + writer.writeAttribute(QLatin1String("language"), attributeLanguage()); + + if (hasAttributeDisplayname()) + writer.writeAttribute(QLatin1String("displayname"), attributeDisplayname()); + + if (hasAttributeStdsetdef()) + writer.writeAttribute(QLatin1String("stdsetdef"), QString::number(attributeStdsetdef())); + + if (hasAttributeStdSetDef()) + writer.writeAttribute(QLatin1String("stdsetdef"), QString::number(attributeStdSetDef())); + + if (m_children & Author) { + writer.writeTextElement(QLatin1String("author"), m_author); + } + + if (m_children & Comment) { + writer.writeTextElement(QLatin1String("comment"), m_comment); + } + + if (m_children & ExportMacro) { + writer.writeTextElement(QLatin1String("exportmacro"), m_exportMacro); + } + + if (m_children & Class) { + writer.writeTextElement(QLatin1String("class"), m_class); + } + + if (m_children & Widget) { + m_widget->write(writer, QLatin1String("widget")); + } + + if (m_children & LayoutDefault) { + m_layoutDefault->write(writer, QLatin1String("layoutdefault")); + } + + if (m_children & LayoutFunction) { + m_layoutFunction->write(writer, QLatin1String("layoutfunction")); + } + + if (m_children & PixmapFunction) { + writer.writeTextElement(QLatin1String("pixmapfunction"), m_pixmapFunction); + } + + if (m_children & CustomWidgets) { + m_customWidgets->write(writer, QLatin1String("customwidgets")); + } + + if (m_children & TabStops) { + m_tabStops->write(writer, QLatin1String("tabstops")); + } + + if (m_children & Images) { + m_images->write(writer, QLatin1String("images")); + } + + if (m_children & Includes) { + m_includes->write(writer, QLatin1String("includes")); + } + + if (m_children & Resources) { + m_resources->write(writer, QLatin1String("resources")); + } + + if (m_children & Connections) { + m_connections->write(writer, QLatin1String("connections")); + } + + if (m_children & Designerdata) { + m_designerdata->write(writer, QLatin1String("designerdata")); + } + + if (m_children & Slots) { + m_slots->write(writer, QLatin1String("slots")); + } + + if (m_children & ButtonGroups) { + m_buttonGroups->write(writer, QLatin1String("buttongroups")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomUI::setElementAuthor(const QString& a) +{ + m_children |= Author; + m_author = a; +} + +void DomUI::setElementComment(const QString& a) +{ + m_children |= Comment; + m_comment = a; +} + +void DomUI::setElementExportMacro(const QString& a) +{ + m_children |= ExportMacro; + m_exportMacro = a; +} + +void DomUI::setElementClass(const QString& a) +{ + m_children |= Class; + m_class = a; +} + +DomWidget* DomUI::takeElementWidget() +{ + DomWidget* a = m_widget; + m_widget = 0; + m_children ^= Widget; + return a; +} + +void DomUI::setElementWidget(DomWidget* a) +{ + delete m_widget; + m_children |= Widget; + m_widget = a; +} + +DomLayoutDefault* DomUI::takeElementLayoutDefault() +{ + DomLayoutDefault* a = m_layoutDefault; + m_layoutDefault = 0; + m_children ^= LayoutDefault; + return a; +} + +void DomUI::setElementLayoutDefault(DomLayoutDefault* a) +{ + delete m_layoutDefault; + m_children |= LayoutDefault; + m_layoutDefault = a; +} + +DomLayoutFunction* DomUI::takeElementLayoutFunction() +{ + DomLayoutFunction* a = m_layoutFunction; + m_layoutFunction = 0; + m_children ^= LayoutFunction; + return a; +} + +void DomUI::setElementLayoutFunction(DomLayoutFunction* a) +{ + delete m_layoutFunction; + m_children |= LayoutFunction; + m_layoutFunction = a; +} + +void DomUI::setElementPixmapFunction(const QString& a) +{ + m_children |= PixmapFunction; + m_pixmapFunction = a; +} + +DomCustomWidgets* DomUI::takeElementCustomWidgets() +{ + DomCustomWidgets* a = m_customWidgets; + m_customWidgets = 0; + m_children ^= CustomWidgets; + return a; +} + +void DomUI::setElementCustomWidgets(DomCustomWidgets* a) +{ + delete m_customWidgets; + m_children |= CustomWidgets; + m_customWidgets = a; +} + +DomTabStops* DomUI::takeElementTabStops() +{ + DomTabStops* a = m_tabStops; + m_tabStops = 0; + m_children ^= TabStops; + return a; +} + +void DomUI::setElementTabStops(DomTabStops* a) +{ + delete m_tabStops; + m_children |= TabStops; + m_tabStops = a; +} + +DomImages* DomUI::takeElementImages() +{ + DomImages* a = m_images; + m_images = 0; + m_children ^= Images; + return a; +} + +void DomUI::setElementImages(DomImages* a) +{ + delete m_images; + m_children |= Images; + m_images = a; +} + +DomIncludes* DomUI::takeElementIncludes() +{ + DomIncludes* a = m_includes; + m_includes = 0; + m_children ^= Includes; + return a; +} + +void DomUI::setElementIncludes(DomIncludes* a) +{ + delete m_includes; + m_children |= Includes; + m_includes = a; +} + +DomResources* DomUI::takeElementResources() +{ + DomResources* a = m_resources; + m_resources = 0; + m_children ^= Resources; + return a; +} + +void DomUI::setElementResources(DomResources* a) +{ + delete m_resources; + m_children |= Resources; + m_resources = a; +} + +DomConnections* DomUI::takeElementConnections() +{ + DomConnections* a = m_connections; + m_connections = 0; + m_children ^= Connections; + return a; +} + +void DomUI::setElementConnections(DomConnections* a) +{ + delete m_connections; + m_children |= Connections; + m_connections = a; +} + +DomDesignerData* DomUI::takeElementDesignerdata() +{ + DomDesignerData* a = m_designerdata; + m_designerdata = 0; + m_children ^= Designerdata; + return a; +} + +void DomUI::setElementDesignerdata(DomDesignerData* a) +{ + delete m_designerdata; + m_children |= Designerdata; + m_designerdata = a; +} + +DomSlots* DomUI::takeElementSlots() +{ + DomSlots* a = m_slots; + m_slots = 0; + m_children ^= Slots; + return a; +} + +void DomUI::setElementSlots(DomSlots* a) +{ + delete m_slots; + m_children |= Slots; + m_slots = a; +} + +DomButtonGroups* DomUI::takeElementButtonGroups() +{ + DomButtonGroups* a = m_buttonGroups; + m_buttonGroups = 0; + m_children ^= ButtonGroups; + return a; +} + +void DomUI::setElementButtonGroups(DomButtonGroups* a) +{ + delete m_buttonGroups; + m_children |= ButtonGroups; + m_buttonGroups = a; +} + +void DomUI::clearElementAuthor() +{ + m_children &= ~Author; +} + +void DomUI::clearElementComment() +{ + m_children &= ~Comment; +} + +void DomUI::clearElementExportMacro() +{ + m_children &= ~ExportMacro; +} + +void DomUI::clearElementClass() +{ + m_children &= ~Class; +} + +void DomUI::clearElementWidget() +{ + delete m_widget; + m_widget = 0; + m_children &= ~Widget; +} + +void DomUI::clearElementLayoutDefault() +{ + delete m_layoutDefault; + m_layoutDefault = 0; + m_children &= ~LayoutDefault; +} + +void DomUI::clearElementLayoutFunction() +{ + delete m_layoutFunction; + m_layoutFunction = 0; + m_children &= ~LayoutFunction; +} + +void DomUI::clearElementPixmapFunction() +{ + m_children &= ~PixmapFunction; +} + +void DomUI::clearElementCustomWidgets() +{ + delete m_customWidgets; + m_customWidgets = 0; + m_children &= ~CustomWidgets; +} + +void DomUI::clearElementTabStops() +{ + delete m_tabStops; + m_tabStops = 0; + m_children &= ~TabStops; +} + +void DomUI::clearElementImages() +{ + delete m_images; + m_images = 0; + m_children &= ~Images; +} + +void DomUI::clearElementIncludes() +{ + delete m_includes; + m_includes = 0; + m_children &= ~Includes; +} + +void DomUI::clearElementResources() +{ + delete m_resources; + m_resources = 0; + m_children &= ~Resources; +} + +void DomUI::clearElementConnections() +{ + delete m_connections; + m_connections = 0; + m_children &= ~Connections; +} + +void DomUI::clearElementDesignerdata() +{ + delete m_designerdata; + m_designerdata = 0; + m_children &= ~Designerdata; +} + +void DomUI::clearElementSlots() +{ + delete m_slots; + m_slots = 0; + m_children &= ~Slots; +} + +void DomUI::clearElementButtonGroups() +{ + delete m_buttonGroups; + m_buttonGroups = 0; + m_children &= ~ButtonGroups; +} + +void DomIncludes::clear(bool clear_all) +{ + qDeleteAll(m_include); + m_include.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomIncludes::DomIncludes() +{ + m_children = 0; +} + +DomIncludes::~DomIncludes() +{ + qDeleteAll(m_include); + m_include.clear(); +} + +void DomIncludes::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("include")) { + DomInclude *v = new DomInclude(); + v->read(reader); + m_include.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomIncludes::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("include")) { + DomInclude *v = new DomInclude(); + v->read(e); + m_include.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomIncludes::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("includes") : tagName.toLower()); + + for (int i = 0; i < m_include.size(); ++i) { + DomInclude* v = m_include[i]; + v->write(writer, QLatin1String("include")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomIncludes::setElementInclude(const QList& a) +{ + m_children |= Include; + m_include = a; +} + +void DomInclude::clear(bool clear_all) +{ + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_location = false; + m_has_attr_impldecl = false; + } + + m_children = 0; +} + +DomInclude::DomInclude() +{ + m_children = 0; + m_has_attr_location = false; + m_has_attr_impldecl = false; + m_text = QLatin1String(""); +} + +DomInclude::~DomInclude() +{ +} + +void DomInclude::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("location")) { + setAttributeLocation(attribute.value().toString()); + continue; + } + if (name == QLatin1String("impldecl")) { + setAttributeImpldecl(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomInclude::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("location"))) + setAttributeLocation(node.attribute(QLatin1String("location"))); + if (node.hasAttribute(QLatin1String("impldecl"))) + setAttributeImpldecl(node.attribute(QLatin1String("impldecl"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomInclude::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("include") : tagName.toLower()); + + if (hasAttributeLocation()) + writer.writeAttribute(QLatin1String("location"), attributeLocation()); + + if (hasAttributeImpldecl()) + writer.writeAttribute(QLatin1String("impldecl"), attributeImpldecl()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomResources::clear(bool clear_all) +{ + qDeleteAll(m_include); + m_include.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomResources::DomResources() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomResources::~DomResources() +{ + qDeleteAll(m_include); + m_include.clear(); +} + +void DomResources::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("include")) { + DomResource *v = new DomResource(); + v->read(reader); + m_include.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomResources::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("include")) { + DomResource *v = new DomResource(); + v->read(e); + m_include.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomResources::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("resources") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + for (int i = 0; i < m_include.size(); ++i) { + DomResource* v = m_include[i]; + v->write(writer, QLatin1String("include")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomResources::setElementInclude(const QList& a) +{ + m_children |= Include; + m_include = a; +} + +void DomResource::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_location = false; + } + + m_children = 0; +} + +DomResource::DomResource() +{ + m_children = 0; + m_has_attr_location = false; +} + +DomResource::~DomResource() +{ +} + +void DomResource::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("location")) { + setAttributeLocation(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomResource::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("location"))) + setAttributeLocation(node.attribute(QLatin1String("location"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomResource::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("resource") : tagName.toLower()); + + if (hasAttributeLocation()) + writer.writeAttribute(QLatin1String("location"), attributeLocation()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomActionGroup::clear(bool clear_all) +{ + qDeleteAll(m_action); + m_action.clear(); + qDeleteAll(m_actionGroup); + m_actionGroup.clear(); + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomActionGroup::DomActionGroup() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomActionGroup::~DomActionGroup() +{ + qDeleteAll(m_action); + m_action.clear(); + qDeleteAll(m_actionGroup); + m_actionGroup.clear(); + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); +} + +void DomActionGroup::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("action")) { + DomAction *v = new DomAction(); + v->read(reader); + m_action.append(v); + continue; + } + if (tag == QLatin1String("actiongroup")) { + DomActionGroup *v = new DomActionGroup(); + v->read(reader); + m_actionGroup.append(v); + continue; + } + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_attribute.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomActionGroup::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("action")) { + DomAction *v = new DomAction(); + v->read(e); + m_action.append(v); + continue; + } + if (tag == QLatin1String("actiongroup")) { + DomActionGroup *v = new DomActionGroup(); + v->read(e); + m_actionGroup.append(v); + continue; + } + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_attribute.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomActionGroup::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("actiongroup") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + for (int i = 0; i < m_action.size(); ++i) { + DomAction* v = m_action[i]; + v->write(writer, QLatin1String("action")); + } + for (int i = 0; i < m_actionGroup.size(); ++i) { + DomActionGroup* v = m_actionGroup[i]; + v->write(writer, QLatin1String("actiongroup")); + } + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_attribute.size(); ++i) { + DomProperty* v = m_attribute[i]; + v->write(writer, QLatin1String("attribute")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomActionGroup::setElementAction(const QList& a) +{ + m_children |= Action; + m_action = a; +} + +void DomActionGroup::setElementActionGroup(const QList& a) +{ + m_children |= ActionGroup; + m_actionGroup = a; +} + +void DomActionGroup::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomActionGroup::setElementAttribute(const QList& a) +{ + m_children |= Attribute; + m_attribute = a; +} + +void DomAction::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_menu = false; + } + + m_children = 0; +} + +DomAction::DomAction() +{ + m_children = 0; + m_has_attr_name = false; + m_has_attr_menu = false; +} + +DomAction::~DomAction() +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); +} + +void DomAction::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("menu")) { + setAttributeMenu(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_attribute.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomAction::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("menu"))) + setAttributeMenu(node.attribute(QLatin1String("menu"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_attribute.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomAction::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("action") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeMenu()) + writer.writeAttribute(QLatin1String("menu"), attributeMenu()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_attribute.size(); ++i) { + DomProperty* v = m_attribute[i]; + v->write(writer, QLatin1String("attribute")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomAction::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomAction::setElementAttribute(const QList& a) +{ + m_children |= Attribute; + m_attribute = a; +} + +void DomActionRef::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomActionRef::DomActionRef() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomActionRef::~DomActionRef() +{ +} + +void DomActionRef::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomActionRef::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomActionRef::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("actionref") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomButtonGroup::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomButtonGroup::DomButtonGroup() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomButtonGroup::~DomButtonGroup() +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); +} + +void DomButtonGroup::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_attribute.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomButtonGroup::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_attribute.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomButtonGroup::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("buttongroup") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_attribute.size(); ++i) { + DomProperty* v = m_attribute[i]; + v->write(writer, QLatin1String("attribute")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomButtonGroup::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomButtonGroup::setElementAttribute(const QList& a) +{ + m_children |= Attribute; + m_attribute = a; +} + +void DomButtonGroups::clear(bool clear_all) +{ + qDeleteAll(m_buttonGroup); + m_buttonGroup.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomButtonGroups::DomButtonGroups() +{ + m_children = 0; +} + +DomButtonGroups::~DomButtonGroups() +{ + qDeleteAll(m_buttonGroup); + m_buttonGroup.clear(); +} + +void DomButtonGroups::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("buttongroup")) { + DomButtonGroup *v = new DomButtonGroup(); + v->read(reader); + m_buttonGroup.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomButtonGroups::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("buttongroup")) { + DomButtonGroup *v = new DomButtonGroup(); + v->read(e); + m_buttonGroup.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomButtonGroups::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("buttongroups") : tagName.toLower()); + + for (int i = 0; i < m_buttonGroup.size(); ++i) { + DomButtonGroup* v = m_buttonGroup[i]; + v->write(writer, QLatin1String("buttongroup")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomButtonGroups::setElementButtonGroup(const QList& a) +{ + m_children |= ButtonGroup; + m_buttonGroup = a; +} + +void DomImages::clear(bool clear_all) +{ + qDeleteAll(m_image); + m_image.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomImages::DomImages() +{ + m_children = 0; +} + +DomImages::~DomImages() +{ + qDeleteAll(m_image); + m_image.clear(); +} + +void DomImages::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("image")) { + DomImage *v = new DomImage(); + v->read(reader); + m_image.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomImages::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("image")) { + DomImage *v = new DomImage(); + v->read(e); + m_image.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomImages::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("images") : tagName.toLower()); + + for (int i = 0; i < m_image.size(); ++i) { + DomImage* v = m_image[i]; + v->write(writer, QLatin1String("image")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomImages::setElementImage(const QList& a) +{ + m_children |= Image; + m_image = a; +} + +void DomImage::clear(bool clear_all) +{ + delete m_data; + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; + m_data = 0; +} + +DomImage::DomImage() +{ + m_children = 0; + m_has_attr_name = false; + m_data = 0; +} + +DomImage::~DomImage() +{ + delete m_data; +} + +void DomImage::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("data")) { + DomImageData *v = new DomImageData(); + v->read(reader); + setElementData(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomImage::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("data")) { + DomImageData *v = new DomImageData(); + v->read(e); + setElementData(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomImage::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("image") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (m_children & Data) { + m_data->write(writer, QLatin1String("data")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomImageData* DomImage::takeElementData() +{ + DomImageData* a = m_data; + m_data = 0; + m_children ^= Data; + return a; +} + +void DomImage::setElementData(DomImageData* a) +{ + delete m_data; + m_children |= Data; + m_data = a; +} + +void DomImage::clearElementData() +{ + delete m_data; + m_data = 0; + m_children &= ~Data; +} + +void DomImageData::clear(bool clear_all) +{ + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_format = false; + m_has_attr_length = false; + m_attr_length = 0; + } + + m_children = 0; +} + +DomImageData::DomImageData() +{ + m_children = 0; + m_has_attr_format = false; + m_has_attr_length = false; + m_attr_length = 0; + m_text = QLatin1String(""); +} + +DomImageData::~DomImageData() +{ +} + +void DomImageData::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("format")) { + setAttributeFormat(attribute.value().toString()); + continue; + } + if (name == QLatin1String("length")) { + setAttributeLength(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomImageData::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("format"))) + setAttributeFormat(node.attribute(QLatin1String("format"))); + if (node.hasAttribute(QLatin1String("length"))) + setAttributeLength(node.attribute(QLatin1String("length")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomImageData::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("imagedata") : tagName.toLower()); + + if (hasAttributeFormat()) + writer.writeAttribute(QLatin1String("format"), attributeFormat()); + + if (hasAttributeLength()) + writer.writeAttribute(QLatin1String("length"), QString::number(attributeLength())); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomCustomWidgets::clear(bool clear_all) +{ + qDeleteAll(m_customWidget); + m_customWidget.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomCustomWidgets::DomCustomWidgets() +{ + m_children = 0; +} + +DomCustomWidgets::~DomCustomWidgets() +{ + qDeleteAll(m_customWidget); + m_customWidget.clear(); +} + +void DomCustomWidgets::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("customwidget")) { + DomCustomWidget *v = new DomCustomWidget(); + v->read(reader); + m_customWidget.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomCustomWidgets::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("customwidget")) { + DomCustomWidget *v = new DomCustomWidget(); + v->read(e); + m_customWidget.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomCustomWidgets::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("customwidgets") : tagName.toLower()); + + for (int i = 0; i < m_customWidget.size(); ++i) { + DomCustomWidget* v = m_customWidget[i]; + v->write(writer, QLatin1String("customwidget")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomCustomWidgets::setElementCustomWidget(const QList& a) +{ + m_children |= CustomWidget; + m_customWidget = a; +} + +void DomHeader::clear(bool clear_all) +{ + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_location = false; + } + + m_children = 0; +} + +DomHeader::DomHeader() +{ + m_children = 0; + m_has_attr_location = false; + m_text = QLatin1String(""); +} + +DomHeader::~DomHeader() +{ +} + +void DomHeader::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("location")) { + setAttributeLocation(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomHeader::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("location"))) + setAttributeLocation(node.attribute(QLatin1String("location"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomHeader::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("header") : tagName.toLower()); + + if (hasAttributeLocation()) + writer.writeAttribute(QLatin1String("location"), attributeLocation()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomCustomWidget::clear(bool clear_all) +{ + delete m_header; + delete m_sizeHint; + delete m_sizePolicy; + delete m_script; + delete m_properties; + delete m_slots; + delete m_propertyspecifications; + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_header = 0; + m_sizeHint = 0; + m_container = 0; + m_sizePolicy = 0; + m_script = 0; + m_properties = 0; + m_slots = 0; + m_propertyspecifications = 0; +} + +DomCustomWidget::DomCustomWidget() +{ + m_children = 0; + m_header = 0; + m_sizeHint = 0; + m_container = 0; + m_sizePolicy = 0; + m_script = 0; + m_properties = 0; + m_slots = 0; + m_propertyspecifications = 0; +} + +DomCustomWidget::~DomCustomWidget() +{ + delete m_header; + delete m_sizeHint; + delete m_sizePolicy; + delete m_script; + delete m_properties; + delete m_slots; + delete m_propertyspecifications; +} + +void DomCustomWidget::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("class")) { + setElementClass(reader.readElementText()); + continue; + } + if (tag == QLatin1String("extends")) { + setElementExtends(reader.readElementText()); + continue; + } + if (tag == QLatin1String("header")) { + DomHeader *v = new DomHeader(); + v->read(reader); + setElementHeader(v); + continue; + } + if (tag == QLatin1String("sizehint")) { + DomSize *v = new DomSize(); + v->read(reader); + setElementSizeHint(v); + continue; + } + if (tag == QLatin1String("addpagemethod")) { + setElementAddPageMethod(reader.readElementText()); + continue; + } + if (tag == QLatin1String("container")) { + setElementContainer(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("sizepolicy")) { + DomSizePolicyData *v = new DomSizePolicyData(); + v->read(reader); + setElementSizePolicy(v); + continue; + } + if (tag == QLatin1String("pixmap")) { + setElementPixmap(reader.readElementText()); + continue; + } + if (tag == QLatin1String("script")) { + DomScript *v = new DomScript(); + v->read(reader); + setElementScript(v); + continue; + } + if (tag == QLatin1String("properties")) { + DomProperties *v = new DomProperties(); + v->read(reader); + setElementProperties(v); + continue; + } + if (tag == QLatin1String("slots")) { + DomSlots *v = new DomSlots(); + v->read(reader); + setElementSlots(v); + continue; + } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(reader); + setElementPropertyspecifications(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomCustomWidget::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("class")) { + setElementClass(e.text()); + continue; + } + if (tag == QLatin1String("extends")) { + setElementExtends(e.text()); + continue; + } + if (tag == QLatin1String("header")) { + DomHeader *v = new DomHeader(); + v->read(e); + setElementHeader(v); + continue; + } + if (tag == QLatin1String("sizehint")) { + DomSize *v = new DomSize(); + v->read(e); + setElementSizeHint(v); + continue; + } + if (tag == QLatin1String("addpagemethod")) { + setElementAddPageMethod(e.text()); + continue; + } + if (tag == QLatin1String("container")) { + setElementContainer(e.text().toInt()); + continue; + } + if (tag == QLatin1String("sizepolicy")) { + DomSizePolicyData *v = new DomSizePolicyData(); + v->read(e); + setElementSizePolicy(v); + continue; + } + if (tag == QLatin1String("pixmap")) { + setElementPixmap(e.text()); + continue; + } + if (tag == QLatin1String("script")) { + DomScript *v = new DomScript(); + v->read(e); + setElementScript(v); + continue; + } + if (tag == QLatin1String("properties")) { + DomProperties *v = new DomProperties(); + v->read(e); + setElementProperties(v); + continue; + } + if (tag == QLatin1String("slots")) { + DomSlots *v = new DomSlots(); + v->read(e); + setElementSlots(v); + continue; + } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(e); + setElementPropertyspecifications(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomCustomWidget::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("customwidget") : tagName.toLower()); + + if (m_children & Class) { + writer.writeTextElement(QLatin1String("class"), m_class); + } + + if (m_children & Extends) { + writer.writeTextElement(QLatin1String("extends"), m_extends); + } + + if (m_children & Header) { + m_header->write(writer, QLatin1String("header")); + } + + if (m_children & SizeHint) { + m_sizeHint->write(writer, QLatin1String("sizehint")); + } + + if (m_children & AddPageMethod) { + writer.writeTextElement(QLatin1String("addpagemethod"), m_addPageMethod); + } + + if (m_children & Container) { + writer.writeTextElement(QLatin1String("container"), QString::number(m_container)); + } + + if (m_children & SizePolicy) { + m_sizePolicy->write(writer, QLatin1String("sizepolicy")); + } + + if (m_children & Pixmap) { + writer.writeTextElement(QLatin1String("pixmap"), m_pixmap); + } + + if (m_children & Script) { + m_script->write(writer, QLatin1String("script")); + } + + if (m_children & Properties) { + m_properties->write(writer, QLatin1String("properties")); + } + + if (m_children & Slots) { + m_slots->write(writer, QLatin1String("slots")); + } + + if (m_children & Propertyspecifications) { + m_propertyspecifications->write(writer, QLatin1String("propertyspecifications")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomCustomWidget::setElementClass(const QString& a) +{ + m_children |= Class; + m_class = a; +} + +void DomCustomWidget::setElementExtends(const QString& a) +{ + m_children |= Extends; + m_extends = a; +} + +DomHeader* DomCustomWidget::takeElementHeader() +{ + DomHeader* a = m_header; + m_header = 0; + m_children ^= Header; + return a; +} + +void DomCustomWidget::setElementHeader(DomHeader* a) +{ + delete m_header; + m_children |= Header; + m_header = a; +} + +DomSize* DomCustomWidget::takeElementSizeHint() +{ + DomSize* a = m_sizeHint; + m_sizeHint = 0; + m_children ^= SizeHint; + return a; +} + +void DomCustomWidget::setElementSizeHint(DomSize* a) +{ + delete m_sizeHint; + m_children |= SizeHint; + m_sizeHint = a; +} + +void DomCustomWidget::setElementAddPageMethod(const QString& a) +{ + m_children |= AddPageMethod; + m_addPageMethod = a; +} + +void DomCustomWidget::setElementContainer(int a) +{ + m_children |= Container; + m_container = a; +} + +DomSizePolicyData* DomCustomWidget::takeElementSizePolicy() +{ + DomSizePolicyData* a = m_sizePolicy; + m_sizePolicy = 0; + m_children ^= SizePolicy; + return a; +} + +void DomCustomWidget::setElementSizePolicy(DomSizePolicyData* a) +{ + delete m_sizePolicy; + m_children |= SizePolicy; + m_sizePolicy = a; +} + +void DomCustomWidget::setElementPixmap(const QString& a) +{ + m_children |= Pixmap; + m_pixmap = a; +} + +DomScript* DomCustomWidget::takeElementScript() +{ + DomScript* a = m_script; + m_script = 0; + m_children ^= Script; + return a; +} + +void DomCustomWidget::setElementScript(DomScript* a) +{ + delete m_script; + m_children |= Script; + m_script = a; +} + +DomProperties* DomCustomWidget::takeElementProperties() +{ + DomProperties* a = m_properties; + m_properties = 0; + m_children ^= Properties; + return a; +} + +void DomCustomWidget::setElementProperties(DomProperties* a) +{ + delete m_properties; + m_children |= Properties; + m_properties = a; +} + +DomSlots* DomCustomWidget::takeElementSlots() +{ + DomSlots* a = m_slots; + m_slots = 0; + m_children ^= Slots; + return a; +} + +void DomCustomWidget::setElementSlots(DomSlots* a) +{ + delete m_slots; + m_children |= Slots; + m_slots = a; +} + +DomPropertySpecifications* DomCustomWidget::takeElementPropertyspecifications() +{ + DomPropertySpecifications* a = m_propertyspecifications; + m_propertyspecifications = 0; + m_children ^= Propertyspecifications; + return a; +} + +void DomCustomWidget::setElementPropertyspecifications(DomPropertySpecifications* a) +{ + delete m_propertyspecifications; + m_children |= Propertyspecifications; + m_propertyspecifications = a; +} + +void DomCustomWidget::clearElementClass() +{ + m_children &= ~Class; +} + +void DomCustomWidget::clearElementExtends() +{ + m_children &= ~Extends; +} + +void DomCustomWidget::clearElementHeader() +{ + delete m_header; + m_header = 0; + m_children &= ~Header; +} + +void DomCustomWidget::clearElementSizeHint() +{ + delete m_sizeHint; + m_sizeHint = 0; + m_children &= ~SizeHint; +} + +void DomCustomWidget::clearElementAddPageMethod() +{ + m_children &= ~AddPageMethod; +} + +void DomCustomWidget::clearElementContainer() +{ + m_children &= ~Container; +} + +void DomCustomWidget::clearElementSizePolicy() +{ + delete m_sizePolicy; + m_sizePolicy = 0; + m_children &= ~SizePolicy; +} + +void DomCustomWidget::clearElementPixmap() +{ + m_children &= ~Pixmap; +} + +void DomCustomWidget::clearElementScript() +{ + delete m_script; + m_script = 0; + m_children &= ~Script; +} + +void DomCustomWidget::clearElementProperties() +{ + delete m_properties; + m_properties = 0; + m_children &= ~Properties; +} + +void DomCustomWidget::clearElementSlots() +{ + delete m_slots; + m_slots = 0; + m_children &= ~Slots; +} + +void DomCustomWidget::clearElementPropertyspecifications() +{ + delete m_propertyspecifications; + m_propertyspecifications = 0; + m_children &= ~Propertyspecifications; +} + +void DomProperties::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomProperties::DomProperties() +{ + m_children = 0; +} + +DomProperties::~DomProperties() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomProperties::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomPropertyData *v = new DomPropertyData(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomProperties::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomPropertyData *v = new DomPropertyData(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomProperties::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("properties") : tagName.toLower()); + + for (int i = 0; i < m_property.size(); ++i) { + DomPropertyData* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomProperties::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomPropertyData::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_type = false; + } + + m_children = 0; +} + +DomPropertyData::DomPropertyData() +{ + m_children = 0; + m_has_attr_type = false; +} + +DomPropertyData::~DomPropertyData() +{ +} + +void DomPropertyData::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPropertyData::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPropertyData::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertydata") : tagName.toLower()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSizePolicyData::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_horData = 0; + m_verData = 0; +} + +DomSizePolicyData::DomSizePolicyData() +{ + m_children = 0; + m_horData = 0; + m_verData = 0; +} + +DomSizePolicyData::~DomSizePolicyData() +{ +} + +void DomSizePolicyData::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("hordata")) { + setElementHorData(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("verdata")) { + setElementVerData(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSizePolicyData::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("hordata")) { + setElementHorData(e.text().toInt()); + continue; + } + if (tag == QLatin1String("verdata")) { + setElementVerData(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSizePolicyData::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("sizepolicydata") : tagName.toLower()); + + if (m_children & HorData) { + writer.writeTextElement(QLatin1String("hordata"), QString::number(m_horData)); + } + + if (m_children & VerData) { + writer.writeTextElement(QLatin1String("verdata"), QString::number(m_verData)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSizePolicyData::setElementHorData(int a) +{ + m_children |= HorData; + m_horData = a; +} + +void DomSizePolicyData::setElementVerData(int a) +{ + m_children |= VerData; + m_verData = a; +} + +void DomSizePolicyData::clearElementHorData() +{ + m_children &= ~HorData; +} + +void DomSizePolicyData::clearElementVerData() +{ + m_children &= ~VerData; +} + +void DomLayoutDefault::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_spacing = false; + m_attr_spacing = 0; + m_has_attr_margin = false; + m_attr_margin = 0; + } + + m_children = 0; +} + +DomLayoutDefault::DomLayoutDefault() +{ + m_children = 0; + m_has_attr_spacing = false; + m_attr_spacing = 0; + m_has_attr_margin = false; + m_attr_margin = 0; +} + +DomLayoutDefault::~DomLayoutDefault() +{ +} + +void DomLayoutDefault::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("spacing")) { + setAttributeSpacing(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("margin")) { + setAttributeMargin(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomLayoutDefault::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("spacing"))) + setAttributeSpacing(node.attribute(QLatin1String("spacing")).toInt()); + if (node.hasAttribute(QLatin1String("margin"))) + setAttributeMargin(node.attribute(QLatin1String("margin")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomLayoutDefault::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("layoutdefault") : tagName.toLower()); + + if (hasAttributeSpacing()) + writer.writeAttribute(QLatin1String("spacing"), QString::number(attributeSpacing())); + + if (hasAttributeMargin()) + writer.writeAttribute(QLatin1String("margin"), QString::number(attributeMargin())); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomLayoutFunction::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_spacing = false; + m_has_attr_margin = false; + } + + m_children = 0; +} + +DomLayoutFunction::DomLayoutFunction() +{ + m_children = 0; + m_has_attr_spacing = false; + m_has_attr_margin = false; +} + +DomLayoutFunction::~DomLayoutFunction() +{ +} + +void DomLayoutFunction::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("spacing")) { + setAttributeSpacing(attribute.value().toString()); + continue; + } + if (name == QLatin1String("margin")) { + setAttributeMargin(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomLayoutFunction::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("spacing"))) + setAttributeSpacing(node.attribute(QLatin1String("spacing"))); + if (node.hasAttribute(QLatin1String("margin"))) + setAttributeMargin(node.attribute(QLatin1String("margin"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomLayoutFunction::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("layoutfunction") : tagName.toLower()); + + if (hasAttributeSpacing()) + writer.writeAttribute(QLatin1String("spacing"), attributeSpacing()); + + if (hasAttributeMargin()) + writer.writeAttribute(QLatin1String("margin"), attributeMargin()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomTabStops::clear(bool clear_all) +{ + m_tabStop.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomTabStops::DomTabStops() +{ + m_children = 0; +} + +DomTabStops::~DomTabStops() +{ + m_tabStop.clear(); +} + +void DomTabStops::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("tabstop")) { + m_tabStop.append(reader.readElementText()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomTabStops::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("tabstop")) { + m_tabStop.append(e.text()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomTabStops::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("tabstops") : tagName.toLower()); + + for (int i = 0; i < m_tabStop.size(); ++i) { + QString v = m_tabStop[i]; + writer.writeTextElement(QLatin1String("tabstop"), v); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomTabStops::setElementTabStop(const QStringList& a) +{ + m_children |= TabStop; + m_tabStop = a; +} + +void DomLayout::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + qDeleteAll(m_item); + m_item.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_class = false; + m_has_attr_name = false; + m_has_attr_stretch = false; + m_has_attr_rowStretch = false; + m_has_attr_columnStretch = false; + m_has_attr_rowMinimumHeight = false; + m_has_attr_columnMinimumWidth = false; + } + + m_children = 0; +} + +DomLayout::DomLayout() +{ + m_children = 0; + m_has_attr_class = false; + m_has_attr_name = false; + m_has_attr_stretch = false; + m_has_attr_rowStretch = false; + m_has_attr_columnStretch = false; + m_has_attr_rowMinimumHeight = false; + m_has_attr_columnMinimumWidth = false; +} + +DomLayout::~DomLayout() +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + qDeleteAll(m_item); + m_item.clear(); +} + +void DomLayout::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("class")) { + setAttributeClass(attribute.value().toString()); + continue; + } + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("stretch")) { + setAttributeStretch(attribute.value().toString()); + continue; + } + if (name == QLatin1String("rowstretch")) { + setAttributeRowStretch(attribute.value().toString()); + continue; + } + if (name == QLatin1String("columnstretch")) { + setAttributeColumnStretch(attribute.value().toString()); + continue; + } + if (name == QLatin1String("rowminimumheight")) { + setAttributeRowMinimumHeight(attribute.value().toString()); + continue; + } + if (name == QLatin1String("columnminimumwidth")) { + setAttributeColumnMinimumWidth(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_attribute.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomLayoutItem *v = new DomLayoutItem(); + v->read(reader); + m_item.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomLayout::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("class"))) + setAttributeClass(node.attribute(QLatin1String("class"))); + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("stretch"))) + setAttributeStretch(node.attribute(QLatin1String("stretch"))); + if (node.hasAttribute(QLatin1String("rowstretch"))) + setAttributeRowStretch(node.attribute(QLatin1String("rowstretch"))); + if (node.hasAttribute(QLatin1String("columnstretch"))) + setAttributeColumnStretch(node.attribute(QLatin1String("columnstretch"))); + if (node.hasAttribute(QLatin1String("rowminimumheight"))) + setAttributeRowMinimumHeight(node.attribute(QLatin1String("rowminimumheight"))); + if (node.hasAttribute(QLatin1String("columnminimumwidth"))) + setAttributeColumnMinimumWidth(node.attribute(QLatin1String("columnminimumwidth"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_attribute.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomLayoutItem *v = new DomLayoutItem(); + v->read(e); + m_item.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomLayout::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("layout") : tagName.toLower()); + + if (hasAttributeClass()) + writer.writeAttribute(QLatin1String("class"), attributeClass()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeStretch()) + writer.writeAttribute(QLatin1String("stretch"), attributeStretch()); + + if (hasAttributeRowStretch()) + writer.writeAttribute(QLatin1String("rowstretch"), attributeRowStretch()); + + if (hasAttributeColumnStretch()) + writer.writeAttribute(QLatin1String("columnstretch"), attributeColumnStretch()); + + if (hasAttributeRowMinimumHeight()) + writer.writeAttribute(QLatin1String("rowminimumheight"), attributeRowMinimumHeight()); + + if (hasAttributeColumnMinimumWidth()) + writer.writeAttribute(QLatin1String("columnminimumwidth"), attributeColumnMinimumWidth()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_attribute.size(); ++i) { + DomProperty* v = m_attribute[i]; + v->write(writer, QLatin1String("attribute")); + } + for (int i = 0; i < m_item.size(); ++i) { + DomLayoutItem* v = m_item[i]; + v->write(writer, QLatin1String("item")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomLayout::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomLayout::setElementAttribute(const QList& a) +{ + m_children |= Attribute; + m_attribute = a; +} + +void DomLayout::setElementItem(const QList& a) +{ + m_children |= Item; + m_item = a; +} + +void DomLayoutItem::clear(bool clear_all) +{ + delete m_widget; + delete m_layout; + delete m_spacer; + + if (clear_all) { + m_text.clear(); + m_has_attr_row = false; + m_attr_row = 0; + m_has_attr_column = false; + m_attr_column = 0; + m_has_attr_rowSpan = false; + m_attr_rowSpan = 0; + m_has_attr_colSpan = false; + m_attr_colSpan = 0; + } + + m_kind = Unknown; + + m_widget = 0; + m_layout = 0; + m_spacer = 0; +} + +DomLayoutItem::DomLayoutItem() +{ + m_kind = Unknown; + + m_has_attr_row = false; + m_attr_row = 0; + m_has_attr_column = false; + m_attr_column = 0; + m_has_attr_rowSpan = false; + m_attr_rowSpan = 0; + m_has_attr_colSpan = false; + m_attr_colSpan = 0; + m_widget = 0; + m_layout = 0; + m_spacer = 0; +} + +DomLayoutItem::~DomLayoutItem() +{ + delete m_widget; + delete m_layout; + delete m_spacer; +} + +void DomLayoutItem::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("row")) { + setAttributeRow(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("column")) { + setAttributeColumn(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("rowspan")) { + setAttributeRowSpan(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("colspan")) { + setAttributeColSpan(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(reader); + setElementWidget(v); + continue; + } + if (tag == QLatin1String("layout")) { + DomLayout *v = new DomLayout(); + v->read(reader); + setElementLayout(v); + continue; + } + if (tag == QLatin1String("spacer")) { + DomSpacer *v = new DomSpacer(); + v->read(reader); + setElementSpacer(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomLayoutItem::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("row"))) + setAttributeRow(node.attribute(QLatin1String("row")).toInt()); + if (node.hasAttribute(QLatin1String("column"))) + setAttributeColumn(node.attribute(QLatin1String("column")).toInt()); + if (node.hasAttribute(QLatin1String("rowspan"))) + setAttributeRowSpan(node.attribute(QLatin1String("rowspan")).toInt()); + if (node.hasAttribute(QLatin1String("colspan"))) + setAttributeColSpan(node.attribute(QLatin1String("colspan")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(e); + setElementWidget(v); + continue; + } + if (tag == QLatin1String("layout")) { + DomLayout *v = new DomLayout(); + v->read(e); + setElementLayout(v); + continue; + } + if (tag == QLatin1String("spacer")) { + DomSpacer *v = new DomSpacer(); + v->read(e); + setElementSpacer(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomLayoutItem::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("layoutitem") : tagName.toLower()); + + if (hasAttributeRow()) + writer.writeAttribute(QLatin1String("row"), QString::number(attributeRow())); + + if (hasAttributeColumn()) + writer.writeAttribute(QLatin1String("column"), QString::number(attributeColumn())); + + if (hasAttributeRowSpan()) + writer.writeAttribute(QLatin1String("rowspan"), QString::number(attributeRowSpan())); + + if (hasAttributeColSpan()) + writer.writeAttribute(QLatin1String("colspan"), QString::number(attributeColSpan())); + + switch (kind()) { + case Widget: { + DomWidget* v = elementWidget(); + if (v != 0) { + v->write(writer, QLatin1String("widget")); + } + break; + } + case Layout: { + DomLayout* v = elementLayout(); + if (v != 0) { + v->write(writer, QLatin1String("layout")); + } + break; + } + case Spacer: { + DomSpacer* v = elementSpacer(); + if (v != 0) { + v->write(writer, QLatin1String("spacer")); + } + break; + } + default: + break; + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomWidget* DomLayoutItem::takeElementWidget() +{ + DomWidget* a = m_widget; + m_widget = 0; + return a; +} + +void DomLayoutItem::setElementWidget(DomWidget* a) +{ + clear(false); + m_kind = Widget; + m_widget = a; +} + +DomLayout* DomLayoutItem::takeElementLayout() +{ + DomLayout* a = m_layout; + m_layout = 0; + return a; +} + +void DomLayoutItem::setElementLayout(DomLayout* a) +{ + clear(false); + m_kind = Layout; + m_layout = a; +} + +DomSpacer* DomLayoutItem::takeElementSpacer() +{ + DomSpacer* a = m_spacer; + m_spacer = 0; + return a; +} + +void DomLayoutItem::setElementSpacer(DomSpacer* a) +{ + clear(false); + m_kind = Spacer; + m_spacer = a; +} + +void DomRow::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomRow::DomRow() +{ + m_children = 0; +} + +DomRow::~DomRow() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomRow::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomRow::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomRow::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("row") : tagName.toLower()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomRow::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomColumn::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomColumn::DomColumn() +{ + m_children = 0; +} + +DomColumn::~DomColumn() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomColumn::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomColumn::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomColumn::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("column") : tagName.toLower()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomColumn::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomItem::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_item); + m_item.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_row = false; + m_attr_row = 0; + m_has_attr_column = false; + m_attr_column = 0; + } + + m_children = 0; +} + +DomItem::DomItem() +{ + m_children = 0; + m_has_attr_row = false; + m_attr_row = 0; + m_has_attr_column = false; + m_attr_column = 0; +} + +DomItem::~DomItem() +{ + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_item); + m_item.clear(); +} + +void DomItem::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("row")) { + setAttributeRow(attribute.value().toString().toInt()); + continue; + } + if (name == QLatin1String("column")) { + setAttributeColumn(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomItem *v = new DomItem(); + v->read(reader); + m_item.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomItem::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("row"))) + setAttributeRow(node.attribute(QLatin1String("row")).toInt()); + if (node.hasAttribute(QLatin1String("column"))) + setAttributeColumn(node.attribute(QLatin1String("column")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomItem *v = new DomItem(); + v->read(e); + m_item.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomItem::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("item") : tagName.toLower()); + + if (hasAttributeRow()) + writer.writeAttribute(QLatin1String("row"), QString::number(attributeRow())); + + if (hasAttributeColumn()) + writer.writeAttribute(QLatin1String("column"), QString::number(attributeColumn())); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_item.size(); ++i) { + DomItem* v = m_item[i]; + v->write(writer, QLatin1String("item")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomItem::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomItem::setElementItem(const QList& a) +{ + m_children |= Item; + m_item = a; +} + +void DomWidget::clear(bool clear_all) +{ + m_class.clear(); + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_script); + m_script.clear(); + qDeleteAll(m_widgetData); + m_widgetData.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + qDeleteAll(m_row); + m_row.clear(); + qDeleteAll(m_column); + m_column.clear(); + qDeleteAll(m_item); + m_item.clear(); + qDeleteAll(m_layout); + m_layout.clear(); + qDeleteAll(m_widget); + m_widget.clear(); + qDeleteAll(m_action); + m_action.clear(); + qDeleteAll(m_actionGroup); + m_actionGroup.clear(); + qDeleteAll(m_addAction); + m_addAction.clear(); + m_zOrder.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_class = false; + m_has_attr_name = false; + m_has_attr_native = false; + m_attr_native = false; + } + + m_children = 0; +} + +DomWidget::DomWidget() +{ + m_children = 0; + m_has_attr_class = false; + m_has_attr_name = false; + m_has_attr_native = false; + m_attr_native = false; +} + +DomWidget::~DomWidget() +{ + m_class.clear(); + qDeleteAll(m_property); + m_property.clear(); + qDeleteAll(m_script); + m_script.clear(); + qDeleteAll(m_widgetData); + m_widgetData.clear(); + qDeleteAll(m_attribute); + m_attribute.clear(); + qDeleteAll(m_row); + m_row.clear(); + qDeleteAll(m_column); + m_column.clear(); + qDeleteAll(m_item); + m_item.clear(); + qDeleteAll(m_layout); + m_layout.clear(); + qDeleteAll(m_widget); + m_widget.clear(); + qDeleteAll(m_action); + m_action.clear(); + qDeleteAll(m_actionGroup); + m_actionGroup.clear(); + qDeleteAll(m_addAction); + m_addAction.clear(); + m_zOrder.clear(); +} + +void DomWidget::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("class")) { + setAttributeClass(attribute.value().toString()); + continue; + } + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("native")) { + setAttributeNative((attribute.value().toString() == QLatin1String("true") ? true : false)); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("class")) { + m_class.append(reader.readElementText()); + continue; + } + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + if (tag == QLatin1String("script")) { + DomScript *v = new DomScript(); + v->read(reader); + m_script.append(v); + continue; + } + if (tag == QLatin1String("widgetdata")) { + DomWidgetData *v = new DomWidgetData(); + v->read(reader); + m_widgetData.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_attribute.append(v); + continue; + } + if (tag == QLatin1String("row")) { + DomRow *v = new DomRow(); + v->read(reader); + m_row.append(v); + continue; + } + if (tag == QLatin1String("column")) { + DomColumn *v = new DomColumn(); + v->read(reader); + m_column.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomItem *v = new DomItem(); + v->read(reader); + m_item.append(v); + continue; + } + if (tag == QLatin1String("layout")) { + DomLayout *v = new DomLayout(); + v->read(reader); + m_layout.append(v); + continue; + } + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(reader); + m_widget.append(v); + continue; + } + if (tag == QLatin1String("action")) { + DomAction *v = new DomAction(); + v->read(reader); + m_action.append(v); + continue; + } + if (tag == QLatin1String("actiongroup")) { + DomActionGroup *v = new DomActionGroup(); + v->read(reader); + m_actionGroup.append(v); + continue; + } + if (tag == QLatin1String("addaction")) { + DomActionRef *v = new DomActionRef(); + v->read(reader); + m_addAction.append(v); + continue; + } + if (tag == QLatin1String("zorder")) { + m_zOrder.append(reader.readElementText()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomWidget::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("class"))) + setAttributeClass(node.attribute(QLatin1String("class"))); + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("native"))) + setAttributeNative((node.attribute(QLatin1String("native")) == QLatin1String("true") ? true : false)); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("class")) { + m_class.append(e.text()); + continue; + } + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + if (tag == QLatin1String("script")) { + DomScript *v = new DomScript(); + v->read(e); + m_script.append(v); + continue; + } + if (tag == QLatin1String("widgetdata")) { + DomWidgetData *v = new DomWidgetData(); + v->read(e); + m_widgetData.append(v); + continue; + } + if (tag == QLatin1String("attribute")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_attribute.append(v); + continue; + } + if (tag == QLatin1String("row")) { + DomRow *v = new DomRow(); + v->read(e); + m_row.append(v); + continue; + } + if (tag == QLatin1String("column")) { + DomColumn *v = new DomColumn(); + v->read(e); + m_column.append(v); + continue; + } + if (tag == QLatin1String("item")) { + DomItem *v = new DomItem(); + v->read(e); + m_item.append(v); + continue; + } + if (tag == QLatin1String("layout")) { + DomLayout *v = new DomLayout(); + v->read(e); + m_layout.append(v); + continue; + } + if (tag == QLatin1String("widget")) { + DomWidget *v = new DomWidget(); + v->read(e); + m_widget.append(v); + continue; + } + if (tag == QLatin1String("action")) { + DomAction *v = new DomAction(); + v->read(e); + m_action.append(v); + continue; + } + if (tag == QLatin1String("actiongroup")) { + DomActionGroup *v = new DomActionGroup(); + v->read(e); + m_actionGroup.append(v); + continue; + } + if (tag == QLatin1String("addaction")) { + DomActionRef *v = new DomActionRef(); + v->read(e); + m_addAction.append(v); + continue; + } + if (tag == QLatin1String("zorder")) { + m_zOrder.append(e.text()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomWidget::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("widget") : tagName.toLower()); + + if (hasAttributeClass()) + writer.writeAttribute(QLatin1String("class"), attributeClass()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeNative()) + writer.writeAttribute(QLatin1String("native"), (attributeNative() ? QLatin1String("true") : QLatin1String("false"))); + + for (int i = 0; i < m_class.size(); ++i) { + QString v = m_class[i]; + writer.writeTextElement(QLatin1String("class"), v); + } + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + for (int i = 0; i < m_script.size(); ++i) { + DomScript* v = m_script[i]; + v->write(writer, QLatin1String("script")); + } + for (int i = 0; i < m_widgetData.size(); ++i) { + DomWidgetData* v = m_widgetData[i]; + v->write(writer, QLatin1String("widgetdata")); + } + for (int i = 0; i < m_attribute.size(); ++i) { + DomProperty* v = m_attribute[i]; + v->write(writer, QLatin1String("attribute")); + } + for (int i = 0; i < m_row.size(); ++i) { + DomRow* v = m_row[i]; + v->write(writer, QLatin1String("row")); + } + for (int i = 0; i < m_column.size(); ++i) { + DomColumn* v = m_column[i]; + v->write(writer, QLatin1String("column")); + } + for (int i = 0; i < m_item.size(); ++i) { + DomItem* v = m_item[i]; + v->write(writer, QLatin1String("item")); + } + for (int i = 0; i < m_layout.size(); ++i) { + DomLayout* v = m_layout[i]; + v->write(writer, QLatin1String("layout")); + } + for (int i = 0; i < m_widget.size(); ++i) { + DomWidget* v = m_widget[i]; + v->write(writer, QLatin1String("widget")); + } + for (int i = 0; i < m_action.size(); ++i) { + DomAction* v = m_action[i]; + v->write(writer, QLatin1String("action")); + } + for (int i = 0; i < m_actionGroup.size(); ++i) { + DomActionGroup* v = m_actionGroup[i]; + v->write(writer, QLatin1String("actiongroup")); + } + for (int i = 0; i < m_addAction.size(); ++i) { + DomActionRef* v = m_addAction[i]; + v->write(writer, QLatin1String("addaction")); + } + for (int i = 0; i < m_zOrder.size(); ++i) { + QString v = m_zOrder[i]; + writer.writeTextElement(QLatin1String("zorder"), v); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomWidget::setElementClass(const QStringList& a) +{ + m_children |= Class; + m_class = a; +} + +void DomWidget::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomWidget::setElementScript(const QList& a) +{ + m_children |= Script; + m_script = a; +} + +void DomWidget::setElementWidgetData(const QList& a) +{ + m_children |= WidgetData; + m_widgetData = a; +} + +void DomWidget::setElementAttribute(const QList& a) +{ + m_children |= Attribute; + m_attribute = a; +} + +void DomWidget::setElementRow(const QList& a) +{ + m_children |= Row; + m_row = a; +} + +void DomWidget::setElementColumn(const QList& a) +{ + m_children |= Column; + m_column = a; +} + +void DomWidget::setElementItem(const QList& a) +{ + m_children |= Item; + m_item = a; +} + +void DomWidget::setElementLayout(const QList& a) +{ + m_children |= Layout; + m_layout = a; +} + +void DomWidget::setElementWidget(const QList& a) +{ + m_children |= Widget; + m_widget = a; +} + +void DomWidget::setElementAction(const QList& a) +{ + m_children |= Action; + m_action = a; +} + +void DomWidget::setElementActionGroup(const QList& a) +{ + m_children |= ActionGroup; + m_actionGroup = a; +} + +void DomWidget::setElementAddAction(const QList& a) +{ + m_children |= AddAction; + m_addAction = a; +} + +void DomWidget::setElementZOrder(const QStringList& a) +{ + m_children |= ZOrder; + m_zOrder = a; +} + +void DomSpacer::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomSpacer::DomSpacer() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomSpacer::~DomSpacer() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomSpacer::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSpacer::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSpacer::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("spacer") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSpacer::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomColor::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_alpha = false; + m_attr_alpha = 0; + } + + m_children = 0; + m_red = 0; + m_green = 0; + m_blue = 0; +} + +DomColor::DomColor() +{ + m_children = 0; + m_has_attr_alpha = false; + m_attr_alpha = 0; + m_red = 0; + m_green = 0; + m_blue = 0; +} + +DomColor::~DomColor() +{ +} + +void DomColor::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("alpha")) { + setAttributeAlpha(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("red")) { + setElementRed(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("green")) { + setElementGreen(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("blue")) { + setElementBlue(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomColor::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("alpha"))) + setAttributeAlpha(node.attribute(QLatin1String("alpha")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("red")) { + setElementRed(e.text().toInt()); + continue; + } + if (tag == QLatin1String("green")) { + setElementGreen(e.text().toInt()); + continue; + } + if (tag == QLatin1String("blue")) { + setElementBlue(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomColor::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("color") : tagName.toLower()); + + if (hasAttributeAlpha()) + writer.writeAttribute(QLatin1String("alpha"), QString::number(attributeAlpha())); + + if (m_children & Red) { + writer.writeTextElement(QLatin1String("red"), QString::number(m_red)); + } + + if (m_children & Green) { + writer.writeTextElement(QLatin1String("green"), QString::number(m_green)); + } + + if (m_children & Blue) { + writer.writeTextElement(QLatin1String("blue"), QString::number(m_blue)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomColor::setElementRed(int a) +{ + m_children |= Red; + m_red = a; +} + +void DomColor::setElementGreen(int a) +{ + m_children |= Green; + m_green = a; +} + +void DomColor::setElementBlue(int a) +{ + m_children |= Blue; + m_blue = a; +} + +void DomColor::clearElementRed() +{ + m_children &= ~Red; +} + +void DomColor::clearElementGreen() +{ + m_children &= ~Green; +} + +void DomColor::clearElementBlue() +{ + m_children &= ~Blue; +} + +void DomGradientStop::clear(bool clear_all) +{ + delete m_color; + + if (clear_all) { + m_text.clear(); + m_has_attr_position = false; + m_attr_position = 0.0; + } + + m_children = 0; + m_color = 0; +} + +DomGradientStop::DomGradientStop() +{ + m_children = 0; + m_has_attr_position = false; + m_attr_position = 0.0; + m_color = 0; +} + +DomGradientStop::~DomGradientStop() +{ + delete m_color; +} + +void DomGradientStop::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("position")) { + setAttributePosition(attribute.value().toString().toDouble()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(reader); + setElementColor(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomGradientStop::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("position"))) + setAttributePosition(node.attribute(QLatin1String("position")).toDouble()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(e); + setElementColor(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomGradientStop::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("gradientstop") : tagName.toLower()); + + if (hasAttributePosition()) + writer.writeAttribute(QLatin1String("position"), QString::number(attributePosition(), 'f', 15)); + + if (m_children & Color) { + m_color->write(writer, QLatin1String("color")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomColor* DomGradientStop::takeElementColor() +{ + DomColor* a = m_color; + m_color = 0; + m_children ^= Color; + return a; +} + +void DomGradientStop::setElementColor(DomColor* a) +{ + delete m_color; + m_children |= Color; + m_color = a; +} + +void DomGradientStop::clearElementColor() +{ + delete m_color; + m_color = 0; + m_children &= ~Color; +} + +void DomGradient::clear(bool clear_all) +{ + qDeleteAll(m_gradientStop); + m_gradientStop.clear(); + + if (clear_all) { + m_text.clear(); + m_has_attr_startX = false; + m_attr_startX = 0.0; + m_has_attr_startY = false; + m_attr_startY = 0.0; + m_has_attr_endX = false; + m_attr_endX = 0.0; + m_has_attr_endY = false; + m_attr_endY = 0.0; + m_has_attr_centralX = false; + m_attr_centralX = 0.0; + m_has_attr_centralY = false; + m_attr_centralY = 0.0; + m_has_attr_focalX = false; + m_attr_focalX = 0.0; + m_has_attr_focalY = false; + m_attr_focalY = 0.0; + m_has_attr_radius = false; + m_attr_radius = 0.0; + m_has_attr_angle = false; + m_attr_angle = 0.0; + m_has_attr_type = false; + m_has_attr_spread = false; + m_has_attr_coordinateMode = false; + } + + m_children = 0; +} + +DomGradient::DomGradient() +{ + m_children = 0; + m_has_attr_startX = false; + m_attr_startX = 0.0; + m_has_attr_startY = false; + m_attr_startY = 0.0; + m_has_attr_endX = false; + m_attr_endX = 0.0; + m_has_attr_endY = false; + m_attr_endY = 0.0; + m_has_attr_centralX = false; + m_attr_centralX = 0.0; + m_has_attr_centralY = false; + m_attr_centralY = 0.0; + m_has_attr_focalX = false; + m_attr_focalX = 0.0; + m_has_attr_focalY = false; + m_attr_focalY = 0.0; + m_has_attr_radius = false; + m_attr_radius = 0.0; + m_has_attr_angle = false; + m_attr_angle = 0.0; + m_has_attr_type = false; + m_has_attr_spread = false; + m_has_attr_coordinateMode = false; +} + +DomGradient::~DomGradient() +{ + qDeleteAll(m_gradientStop); + m_gradientStop.clear(); +} + +void DomGradient::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("startx")) { + setAttributeStartX(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("starty")) { + setAttributeStartY(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("endx")) { + setAttributeEndX(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("endy")) { + setAttributeEndY(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("centralx")) { + setAttributeCentralX(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("centraly")) { + setAttributeCentralY(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("focalx")) { + setAttributeFocalX(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("focaly")) { + setAttributeFocalY(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("radius")) { + setAttributeRadius(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("angle")) { + setAttributeAngle(attribute.value().toString().toDouble()); + continue; + } + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("spread")) { + setAttributeSpread(attribute.value().toString()); + continue; + } + if (name == QLatin1String("coordinatemode")) { + setAttributeCoordinateMode(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("gradientstop")) { + DomGradientStop *v = new DomGradientStop(); + v->read(reader); + m_gradientStop.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomGradient::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("startx"))) + setAttributeStartX(node.attribute(QLatin1String("startx")).toDouble()); + if (node.hasAttribute(QLatin1String("starty"))) + setAttributeStartY(node.attribute(QLatin1String("starty")).toDouble()); + if (node.hasAttribute(QLatin1String("endx"))) + setAttributeEndX(node.attribute(QLatin1String("endx")).toDouble()); + if (node.hasAttribute(QLatin1String("endy"))) + setAttributeEndY(node.attribute(QLatin1String("endy")).toDouble()); + if (node.hasAttribute(QLatin1String("centralx"))) + setAttributeCentralX(node.attribute(QLatin1String("centralx")).toDouble()); + if (node.hasAttribute(QLatin1String("centraly"))) + setAttributeCentralY(node.attribute(QLatin1String("centraly")).toDouble()); + if (node.hasAttribute(QLatin1String("focalx"))) + setAttributeFocalX(node.attribute(QLatin1String("focalx")).toDouble()); + if (node.hasAttribute(QLatin1String("focaly"))) + setAttributeFocalY(node.attribute(QLatin1String("focaly")).toDouble()); + if (node.hasAttribute(QLatin1String("radius"))) + setAttributeRadius(node.attribute(QLatin1String("radius")).toDouble()); + if (node.hasAttribute(QLatin1String("angle"))) + setAttributeAngle(node.attribute(QLatin1String("angle")).toDouble()); + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + if (node.hasAttribute(QLatin1String("spread"))) + setAttributeSpread(node.attribute(QLatin1String("spread"))); + if (node.hasAttribute(QLatin1String("coordinatemode"))) + setAttributeCoordinateMode(node.attribute(QLatin1String("coordinatemode"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("gradientstop")) { + DomGradientStop *v = new DomGradientStop(); + v->read(e); + m_gradientStop.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomGradient::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("gradient") : tagName.toLower()); + + if (hasAttributeStartX()) + writer.writeAttribute(QLatin1String("startx"), QString::number(attributeStartX(), 'f', 15)); + + if (hasAttributeStartY()) + writer.writeAttribute(QLatin1String("starty"), QString::number(attributeStartY(), 'f', 15)); + + if (hasAttributeEndX()) + writer.writeAttribute(QLatin1String("endx"), QString::number(attributeEndX(), 'f', 15)); + + if (hasAttributeEndY()) + writer.writeAttribute(QLatin1String("endy"), QString::number(attributeEndY(), 'f', 15)); + + if (hasAttributeCentralX()) + writer.writeAttribute(QLatin1String("centralx"), QString::number(attributeCentralX(), 'f', 15)); + + if (hasAttributeCentralY()) + writer.writeAttribute(QLatin1String("centraly"), QString::number(attributeCentralY(), 'f', 15)); + + if (hasAttributeFocalX()) + writer.writeAttribute(QLatin1String("focalx"), QString::number(attributeFocalX(), 'f', 15)); + + if (hasAttributeFocalY()) + writer.writeAttribute(QLatin1String("focaly"), QString::number(attributeFocalY(), 'f', 15)); + + if (hasAttributeRadius()) + writer.writeAttribute(QLatin1String("radius"), QString::number(attributeRadius(), 'f', 15)); + + if (hasAttributeAngle()) + writer.writeAttribute(QLatin1String("angle"), QString::number(attributeAngle(), 'f', 15)); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (hasAttributeSpread()) + writer.writeAttribute(QLatin1String("spread"), attributeSpread()); + + if (hasAttributeCoordinateMode()) + writer.writeAttribute(QLatin1String("coordinatemode"), attributeCoordinateMode()); + + for (int i = 0; i < m_gradientStop.size(); ++i) { + DomGradientStop* v = m_gradientStop[i]; + v->write(writer, QLatin1String("gradientstop")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomGradient::setElementGradientStop(const QList& a) +{ + m_children |= GradientStop; + m_gradientStop = a; +} + +void DomBrush::clear(bool clear_all) +{ + delete m_color; + delete m_texture; + delete m_gradient; + + if (clear_all) { + m_text.clear(); + m_has_attr_brushStyle = false; + } + + m_kind = Unknown; + + m_color = 0; + m_texture = 0; + m_gradient = 0; +} + +DomBrush::DomBrush() +{ + m_kind = Unknown; + + m_has_attr_brushStyle = false; + m_color = 0; + m_texture = 0; + m_gradient = 0; +} + +DomBrush::~DomBrush() +{ + delete m_color; + delete m_texture; + delete m_gradient; +} + +void DomBrush::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("brushstyle")) { + setAttributeBrushStyle(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(reader); + setElementColor(v); + continue; + } + if (tag == QLatin1String("texture")) { + DomProperty *v = new DomProperty(); + v->read(reader); + setElementTexture(v); + continue; + } + if (tag == QLatin1String("gradient")) { + DomGradient *v = new DomGradient(); + v->read(reader); + setElementGradient(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomBrush::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("brushstyle"))) + setAttributeBrushStyle(node.attribute(QLatin1String("brushstyle"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(e); + setElementColor(v); + continue; + } + if (tag == QLatin1String("texture")) { + DomProperty *v = new DomProperty(); + v->read(e); + setElementTexture(v); + continue; + } + if (tag == QLatin1String("gradient")) { + DomGradient *v = new DomGradient(); + v->read(e); + setElementGradient(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomBrush::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("brush") : tagName.toLower()); + + if (hasAttributeBrushStyle()) + writer.writeAttribute(QLatin1String("brushstyle"), attributeBrushStyle()); + + switch (kind()) { + case Color: { + DomColor* v = elementColor(); + if (v != 0) { + v->write(writer, QLatin1String("color")); + } + break; + } + case Texture: { + DomProperty* v = elementTexture(); + if (v != 0) { + v->write(writer, QLatin1String("texture")); + } + break; + } + case Gradient: { + DomGradient* v = elementGradient(); + if (v != 0) { + v->write(writer, QLatin1String("gradient")); + } + break; + } + default: + break; + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomColor* DomBrush::takeElementColor() +{ + DomColor* a = m_color; + m_color = 0; + return a; +} + +void DomBrush::setElementColor(DomColor* a) +{ + clear(false); + m_kind = Color; + m_color = a; +} + +DomProperty* DomBrush::takeElementTexture() +{ + DomProperty* a = m_texture; + m_texture = 0; + return a; +} + +void DomBrush::setElementTexture(DomProperty* a) +{ + clear(false); + m_kind = Texture; + m_texture = a; +} + +DomGradient* DomBrush::takeElementGradient() +{ + DomGradient* a = m_gradient; + m_gradient = 0; + return a; +} + +void DomBrush::setElementGradient(DomGradient* a) +{ + clear(false); + m_kind = Gradient; + m_gradient = a; +} + +void DomColorRole::clear(bool clear_all) +{ + delete m_brush; + + if (clear_all) { + m_text.clear(); + m_has_attr_role = false; + } + + m_children = 0; + m_brush = 0; +} + +DomColorRole::DomColorRole() +{ + m_children = 0; + m_has_attr_role = false; + m_brush = 0; +} + +DomColorRole::~DomColorRole() +{ + delete m_brush; +} + +void DomColorRole::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("role")) { + setAttributeRole(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("brush")) { + DomBrush *v = new DomBrush(); + v->read(reader); + setElementBrush(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomColorRole::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("role"))) + setAttributeRole(node.attribute(QLatin1String("role"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("brush")) { + DomBrush *v = new DomBrush(); + v->read(e); + setElementBrush(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomColorRole::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("colorrole") : tagName.toLower()); + + if (hasAttributeRole()) + writer.writeAttribute(QLatin1String("role"), attributeRole()); + + if (m_children & Brush) { + m_brush->write(writer, QLatin1String("brush")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomBrush* DomColorRole::takeElementBrush() +{ + DomBrush* a = m_brush; + m_brush = 0; + m_children ^= Brush; + return a; +} + +void DomColorRole::setElementBrush(DomBrush* a) +{ + delete m_brush; + m_children |= Brush; + m_brush = a; +} + +void DomColorRole::clearElementBrush() +{ + delete m_brush; + m_brush = 0; + m_children &= ~Brush; +} + +void DomColorGroup::clear(bool clear_all) +{ + qDeleteAll(m_colorRole); + m_colorRole.clear(); + qDeleteAll(m_color); + m_color.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomColorGroup::DomColorGroup() +{ + m_children = 0; +} + +DomColorGroup::~DomColorGroup() +{ + qDeleteAll(m_colorRole); + m_colorRole.clear(); + qDeleteAll(m_color); + m_color.clear(); +} + +void DomColorGroup::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("colorrole")) { + DomColorRole *v = new DomColorRole(); + v->read(reader); + m_colorRole.append(v); + continue; + } + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(reader); + m_color.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomColorGroup::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("colorrole")) { + DomColorRole *v = new DomColorRole(); + v->read(e); + m_colorRole.append(v); + continue; + } + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(e); + m_color.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomColorGroup::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("colorgroup") : tagName.toLower()); + + for (int i = 0; i < m_colorRole.size(); ++i) { + DomColorRole* v = m_colorRole[i]; + v->write(writer, QLatin1String("colorrole")); + } + for (int i = 0; i < m_color.size(); ++i) { + DomColor* v = m_color[i]; + v->write(writer, QLatin1String("color")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomColorGroup::setElementColorRole(const QList& a) +{ + m_children |= ColorRole; + m_colorRole = a; +} + +void DomColorGroup::setElementColor(const QList& a) +{ + m_children |= Color; + m_color = a; +} + +void DomPalette::clear(bool clear_all) +{ + delete m_active; + delete m_inactive; + delete m_disabled; + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_active = 0; + m_inactive = 0; + m_disabled = 0; +} + +DomPalette::DomPalette() +{ + m_children = 0; + m_active = 0; + m_inactive = 0; + m_disabled = 0; +} + +DomPalette::~DomPalette() +{ + delete m_active; + delete m_inactive; + delete m_disabled; +} + +void DomPalette::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("active")) { + DomColorGroup *v = new DomColorGroup(); + v->read(reader); + setElementActive(v); + continue; + } + if (tag == QLatin1String("inactive")) { + DomColorGroup *v = new DomColorGroup(); + v->read(reader); + setElementInactive(v); + continue; + } + if (tag == QLatin1String("disabled")) { + DomColorGroup *v = new DomColorGroup(); + v->read(reader); + setElementDisabled(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPalette::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("active")) { + DomColorGroup *v = new DomColorGroup(); + v->read(e); + setElementActive(v); + continue; + } + if (tag == QLatin1String("inactive")) { + DomColorGroup *v = new DomColorGroup(); + v->read(e); + setElementInactive(v); + continue; + } + if (tag == QLatin1String("disabled")) { + DomColorGroup *v = new DomColorGroup(); + v->read(e); + setElementDisabled(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPalette::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("palette") : tagName.toLower()); + + if (m_children & Active) { + m_active->write(writer, QLatin1String("active")); + } + + if (m_children & Inactive) { + m_inactive->write(writer, QLatin1String("inactive")); + } + + if (m_children & Disabled) { + m_disabled->write(writer, QLatin1String("disabled")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomColorGroup* DomPalette::takeElementActive() +{ + DomColorGroup* a = m_active; + m_active = 0; + m_children ^= Active; + return a; +} + +void DomPalette::setElementActive(DomColorGroup* a) +{ + delete m_active; + m_children |= Active; + m_active = a; +} + +DomColorGroup* DomPalette::takeElementInactive() +{ + DomColorGroup* a = m_inactive; + m_inactive = 0; + m_children ^= Inactive; + return a; +} + +void DomPalette::setElementInactive(DomColorGroup* a) +{ + delete m_inactive; + m_children |= Inactive; + m_inactive = a; +} + +DomColorGroup* DomPalette::takeElementDisabled() +{ + DomColorGroup* a = m_disabled; + m_disabled = 0; + m_children ^= Disabled; + return a; +} + +void DomPalette::setElementDisabled(DomColorGroup* a) +{ + delete m_disabled; + m_children |= Disabled; + m_disabled = a; +} + +void DomPalette::clearElementActive() +{ + delete m_active; + m_active = 0; + m_children &= ~Active; +} + +void DomPalette::clearElementInactive() +{ + delete m_inactive; + m_inactive = 0; + m_children &= ~Inactive; +} + +void DomPalette::clearElementDisabled() +{ + delete m_disabled; + m_disabled = 0; + m_children &= ~Disabled; +} + +void DomFont::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_pointSize = 0; + m_weight = 0; + m_italic = false; + m_bold = false; + m_underline = false; + m_strikeOut = false; + m_antialiasing = false; + m_kerning = false; +} + +DomFont::DomFont() +{ + m_children = 0; + m_pointSize = 0; + m_weight = 0; + m_italic = false; + m_bold = false; + m_underline = false; + m_strikeOut = false; + m_antialiasing = false; + m_kerning = false; +} + +DomFont::~DomFont() +{ +} + +void DomFont::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("family")) { + setElementFamily(reader.readElementText()); + continue; + } + if (tag == QLatin1String("pointsize")) { + setElementPointSize(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("weight")) { + setElementWeight(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("italic")) { + setElementItalic((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("bold")) { + setElementBold((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("underline")) { + setElementUnderline((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("strikeout")) { + setElementStrikeOut((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("antialiasing")) { + setElementAntialiasing((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("stylestrategy")) { + setElementStyleStrategy(reader.readElementText()); + continue; + } + if (tag == QLatin1String("kerning")) { + setElementKerning((reader.readElementText() == QLatin1String("true") ? true : false)); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomFont::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("family")) { + setElementFamily(e.text()); + continue; + } + if (tag == QLatin1String("pointsize")) { + setElementPointSize(e.text().toInt()); + continue; + } + if (tag == QLatin1String("weight")) { + setElementWeight(e.text().toInt()); + continue; + } + if (tag == QLatin1String("italic")) { + setElementItalic((e.text() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("bold")) { + setElementBold((e.text() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("underline")) { + setElementUnderline((e.text() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("strikeout")) { + setElementStrikeOut((e.text() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("antialiasing")) { + setElementAntialiasing((e.text() == QLatin1String("true") ? true : false)); + continue; + } + if (tag == QLatin1String("stylestrategy")) { + setElementStyleStrategy(e.text()); + continue; + } + if (tag == QLatin1String("kerning")) { + setElementKerning((e.text() == QLatin1String("true") ? true : false)); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomFont::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("font") : tagName.toLower()); + + if (m_children & Family) { + writer.writeTextElement(QLatin1String("family"), m_family); + } + + if (m_children & PointSize) { + writer.writeTextElement(QLatin1String("pointsize"), QString::number(m_pointSize)); + } + + if (m_children & Weight) { + writer.writeTextElement(QLatin1String("weight"), QString::number(m_weight)); + } + + if (m_children & Italic) { + writer.writeTextElement(QLatin1String("italic"), (m_italic ? QLatin1String("true") : QLatin1String("false"))); + } + + if (m_children & Bold) { + writer.writeTextElement(QLatin1String("bold"), (m_bold ? QLatin1String("true") : QLatin1String("false"))); + } + + if (m_children & Underline) { + writer.writeTextElement(QLatin1String("underline"), (m_underline ? QLatin1String("true") : QLatin1String("false"))); + } + + if (m_children & StrikeOut) { + writer.writeTextElement(QLatin1String("strikeout"), (m_strikeOut ? QLatin1String("true") : QLatin1String("false"))); + } + + if (m_children & Antialiasing) { + writer.writeTextElement(QLatin1String("antialiasing"), (m_antialiasing ? QLatin1String("true") : QLatin1String("false"))); + } + + if (m_children & StyleStrategy) { + writer.writeTextElement(QLatin1String("stylestrategy"), m_styleStrategy); + } + + if (m_children & Kerning) { + writer.writeTextElement(QLatin1String("kerning"), (m_kerning ? QLatin1String("true") : QLatin1String("false"))); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomFont::setElementFamily(const QString& a) +{ + m_children |= Family; + m_family = a; +} + +void DomFont::setElementPointSize(int a) +{ + m_children |= PointSize; + m_pointSize = a; +} + +void DomFont::setElementWeight(int a) +{ + m_children |= Weight; + m_weight = a; +} + +void DomFont::setElementItalic(bool a) +{ + m_children |= Italic; + m_italic = a; +} + +void DomFont::setElementBold(bool a) +{ + m_children |= Bold; + m_bold = a; +} + +void DomFont::setElementUnderline(bool a) +{ + m_children |= Underline; + m_underline = a; +} + +void DomFont::setElementStrikeOut(bool a) +{ + m_children |= StrikeOut; + m_strikeOut = a; +} + +void DomFont::setElementAntialiasing(bool a) +{ + m_children |= Antialiasing; + m_antialiasing = a; +} + +void DomFont::setElementStyleStrategy(const QString& a) +{ + m_children |= StyleStrategy; + m_styleStrategy = a; +} + +void DomFont::setElementKerning(bool a) +{ + m_children |= Kerning; + m_kerning = a; +} + +void DomFont::clearElementFamily() +{ + m_children &= ~Family; +} + +void DomFont::clearElementPointSize() +{ + m_children &= ~PointSize; +} + +void DomFont::clearElementWeight() +{ + m_children &= ~Weight; +} + +void DomFont::clearElementItalic() +{ + m_children &= ~Italic; +} + +void DomFont::clearElementBold() +{ + m_children &= ~Bold; +} + +void DomFont::clearElementUnderline() +{ + m_children &= ~Underline; +} + +void DomFont::clearElementStrikeOut() +{ + m_children &= ~StrikeOut; +} + +void DomFont::clearElementAntialiasing() +{ + m_children &= ~Antialiasing; +} + +void DomFont::clearElementStyleStrategy() +{ + m_children &= ~StyleStrategy; +} + +void DomFont::clearElementKerning() +{ + m_children &= ~Kerning; +} + +void DomPoint::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_x = 0; + m_y = 0; +} + +DomPoint::DomPoint() +{ + m_children = 0; + m_x = 0; + m_y = 0; +} + +DomPoint::~DomPoint() +{ +} + +void DomPoint::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(reader.readElementText().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPoint::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(e.text().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPoint::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("point") : tagName.toLower()); + + if (m_children & X) { + writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x)); + } + + if (m_children & Y) { + writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPoint::setElementX(int a) +{ + m_children |= X; + m_x = a; +} + +void DomPoint::setElementY(int a) +{ + m_children |= Y; + m_y = a; +} + +void DomPoint::clearElementX() +{ + m_children &= ~X; +} + +void DomPoint::clearElementY() +{ + m_children &= ~Y; +} + +void DomRect::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; +} + +DomRect::DomRect() +{ + m_children = 0; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; +} + +DomRect::~DomRect() +{ +} + +void DomRect::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(reader.readElementText().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("width")) { + setElementWidth(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomRect::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(e.text().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(e.text().toInt()); + continue; + } + if (tag == QLatin1String("width")) { + setElementWidth(e.text().toInt()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomRect::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("rect") : tagName.toLower()); + + if (m_children & X) { + writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x)); + } + + if (m_children & Y) { + writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y)); + } + + if (m_children & Width) { + writer.writeTextElement(QLatin1String("width"), QString::number(m_width)); + } + + if (m_children & Height) { + writer.writeTextElement(QLatin1String("height"), QString::number(m_height)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomRect::setElementX(int a) +{ + m_children |= X; + m_x = a; +} + +void DomRect::setElementY(int a) +{ + m_children |= Y; + m_y = a; +} + +void DomRect::setElementWidth(int a) +{ + m_children |= Width; + m_width = a; +} + +void DomRect::setElementHeight(int a) +{ + m_children |= Height; + m_height = a; +} + +void DomRect::clearElementX() +{ + m_children &= ~X; +} + +void DomRect::clearElementY() +{ + m_children &= ~Y; +} + +void DomRect::clearElementWidth() +{ + m_children &= ~Width; +} + +void DomRect::clearElementHeight() +{ + m_children &= ~Height; +} + +void DomLocale::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_language = false; + m_has_attr_country = false; + } + + m_children = 0; +} + +DomLocale::DomLocale() +{ + m_children = 0; + m_has_attr_language = false; + m_has_attr_country = false; +} + +DomLocale::~DomLocale() +{ +} + +void DomLocale::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("language")) { + setAttributeLanguage(attribute.value().toString()); + continue; + } + if (name == QLatin1String("country")) { + setAttributeCountry(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomLocale::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("language"))) + setAttributeLanguage(node.attribute(QLatin1String("language"))); + if (node.hasAttribute(QLatin1String("country"))) + setAttributeCountry(node.attribute(QLatin1String("country"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomLocale::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("locale") : tagName.toLower()); + + if (hasAttributeLanguage()) + writer.writeAttribute(QLatin1String("language"), attributeLanguage()); + + if (hasAttributeCountry()) + writer.writeAttribute(QLatin1String("country"), attributeCountry()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSizePolicy::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_hSizeType = false; + m_has_attr_vSizeType = false; + } + + m_children = 0; + m_hSizeType = 0; + m_vSizeType = 0; + m_horStretch = 0; + m_verStretch = 0; +} + +DomSizePolicy::DomSizePolicy() +{ + m_children = 0; + m_has_attr_hSizeType = false; + m_has_attr_vSizeType = false; + m_hSizeType = 0; + m_vSizeType = 0; + m_horStretch = 0; + m_verStretch = 0; +} + +DomSizePolicy::~DomSizePolicy() +{ +} + +void DomSizePolicy::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("hsizetype")) { + setAttributeHSizeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("vsizetype")) { + setAttributeVSizeType(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("hsizetype")) { + setElementHSizeType(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("vsizetype")) { + setElementVSizeType(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("horstretch")) { + setElementHorStretch(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("verstretch")) { + setElementVerStretch(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSizePolicy::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("hsizetype"))) + setAttributeHSizeType(node.attribute(QLatin1String("hsizetype"))); + if (node.hasAttribute(QLatin1String("vsizetype"))) + setAttributeVSizeType(node.attribute(QLatin1String("vsizetype"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("hsizetype")) { + setElementHSizeType(e.text().toInt()); + continue; + } + if (tag == QLatin1String("vsizetype")) { + setElementVSizeType(e.text().toInt()); + continue; + } + if (tag == QLatin1String("horstretch")) { + setElementHorStretch(e.text().toInt()); + continue; + } + if (tag == QLatin1String("verstretch")) { + setElementVerStretch(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSizePolicy::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("sizepolicy") : tagName.toLower()); + + if (hasAttributeHSizeType()) + writer.writeAttribute(QLatin1String("hsizetype"), attributeHSizeType()); + + if (hasAttributeVSizeType()) + writer.writeAttribute(QLatin1String("vsizetype"), attributeVSizeType()); + + if (m_children & HSizeType) { + writer.writeTextElement(QLatin1String("hsizetype"), QString::number(m_hSizeType)); + } + + if (m_children & VSizeType) { + writer.writeTextElement(QLatin1String("vsizetype"), QString::number(m_vSizeType)); + } + + if (m_children & HorStretch) { + writer.writeTextElement(QLatin1String("horstretch"), QString::number(m_horStretch)); + } + + if (m_children & VerStretch) { + writer.writeTextElement(QLatin1String("verstretch"), QString::number(m_verStretch)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSizePolicy::setElementHSizeType(int a) +{ + m_children |= HSizeType; + m_hSizeType = a; +} + +void DomSizePolicy::setElementVSizeType(int a) +{ + m_children |= VSizeType; + m_vSizeType = a; +} + +void DomSizePolicy::setElementHorStretch(int a) +{ + m_children |= HorStretch; + m_horStretch = a; +} + +void DomSizePolicy::setElementVerStretch(int a) +{ + m_children |= VerStretch; + m_verStretch = a; +} + +void DomSizePolicy::clearElementHSizeType() +{ + m_children &= ~HSizeType; +} + +void DomSizePolicy::clearElementVSizeType() +{ + m_children &= ~VSizeType; +} + +void DomSizePolicy::clearElementHorStretch() +{ + m_children &= ~HorStretch; +} + +void DomSizePolicy::clearElementVerStretch() +{ + m_children &= ~VerStretch; +} + +void DomSize::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_width = 0; + m_height = 0; +} + +DomSize::DomSize() +{ + m_children = 0; + m_width = 0; + m_height = 0; +} + +DomSize::~DomSize() +{ +} + +void DomSize::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("width")) { + setElementWidth(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSize::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("width")) { + setElementWidth(e.text().toInt()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSize::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("size") : tagName.toLower()); + + if (m_children & Width) { + writer.writeTextElement(QLatin1String("width"), QString::number(m_width)); + } + + if (m_children & Height) { + writer.writeTextElement(QLatin1String("height"), QString::number(m_height)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSize::setElementWidth(int a) +{ + m_children |= Width; + m_width = a; +} + +void DomSize::setElementHeight(int a) +{ + m_children |= Height; + m_height = a; +} + +void DomSize::clearElementWidth() +{ + m_children &= ~Width; +} + +void DomSize::clearElementHeight() +{ + m_children &= ~Height; +} + +void DomDate::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_year = 0; + m_month = 0; + m_day = 0; +} + +DomDate::DomDate() +{ + m_children = 0; + m_year = 0; + m_month = 0; + m_day = 0; +} + +DomDate::~DomDate() +{ +} + +void DomDate::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("year")) { + setElementYear(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("month")) { + setElementMonth(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("day")) { + setElementDay(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomDate::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("year")) { + setElementYear(e.text().toInt()); + continue; + } + if (tag == QLatin1String("month")) { + setElementMonth(e.text().toInt()); + continue; + } + if (tag == QLatin1String("day")) { + setElementDay(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomDate::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("date") : tagName.toLower()); + + if (m_children & Year) { + writer.writeTextElement(QLatin1String("year"), QString::number(m_year)); + } + + if (m_children & Month) { + writer.writeTextElement(QLatin1String("month"), QString::number(m_month)); + } + + if (m_children & Day) { + writer.writeTextElement(QLatin1String("day"), QString::number(m_day)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomDate::setElementYear(int a) +{ + m_children |= Year; + m_year = a; +} + +void DomDate::setElementMonth(int a) +{ + m_children |= Month; + m_month = a; +} + +void DomDate::setElementDay(int a) +{ + m_children |= Day; + m_day = a; +} + +void DomDate::clearElementYear() +{ + m_children &= ~Year; +} + +void DomDate::clearElementMonth() +{ + m_children &= ~Month; +} + +void DomDate::clearElementDay() +{ + m_children &= ~Day; +} + +void DomTime::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_hour = 0; + m_minute = 0; + m_second = 0; +} + +DomTime::DomTime() +{ + m_children = 0; + m_hour = 0; + m_minute = 0; + m_second = 0; +} + +DomTime::~DomTime() +{ +} + +void DomTime::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("hour")) { + setElementHour(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("minute")) { + setElementMinute(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("second")) { + setElementSecond(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomTime::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("hour")) { + setElementHour(e.text().toInt()); + continue; + } + if (tag == QLatin1String("minute")) { + setElementMinute(e.text().toInt()); + continue; + } + if (tag == QLatin1String("second")) { + setElementSecond(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomTime::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("time") : tagName.toLower()); + + if (m_children & Hour) { + writer.writeTextElement(QLatin1String("hour"), QString::number(m_hour)); + } + + if (m_children & Minute) { + writer.writeTextElement(QLatin1String("minute"), QString::number(m_minute)); + } + + if (m_children & Second) { + writer.writeTextElement(QLatin1String("second"), QString::number(m_second)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomTime::setElementHour(int a) +{ + m_children |= Hour; + m_hour = a; +} + +void DomTime::setElementMinute(int a) +{ + m_children |= Minute; + m_minute = a; +} + +void DomTime::setElementSecond(int a) +{ + m_children |= Second; + m_second = a; +} + +void DomTime::clearElementHour() +{ + m_children &= ~Hour; +} + +void DomTime::clearElementMinute() +{ + m_children &= ~Minute; +} + +void DomTime::clearElementSecond() +{ + m_children &= ~Second; +} + +void DomDateTime::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_hour = 0; + m_minute = 0; + m_second = 0; + m_year = 0; + m_month = 0; + m_day = 0; +} + +DomDateTime::DomDateTime() +{ + m_children = 0; + m_hour = 0; + m_minute = 0; + m_second = 0; + m_year = 0; + m_month = 0; + m_day = 0; +} + +DomDateTime::~DomDateTime() +{ +} + +void DomDateTime::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("hour")) { + setElementHour(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("minute")) { + setElementMinute(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("second")) { + setElementSecond(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("year")) { + setElementYear(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("month")) { + setElementMonth(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("day")) { + setElementDay(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomDateTime::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("hour")) { + setElementHour(e.text().toInt()); + continue; + } + if (tag == QLatin1String("minute")) { + setElementMinute(e.text().toInt()); + continue; + } + if (tag == QLatin1String("second")) { + setElementSecond(e.text().toInt()); + continue; + } + if (tag == QLatin1String("year")) { + setElementYear(e.text().toInt()); + continue; + } + if (tag == QLatin1String("month")) { + setElementMonth(e.text().toInt()); + continue; + } + if (tag == QLatin1String("day")) { + setElementDay(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomDateTime::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("datetime") : tagName.toLower()); + + if (m_children & Hour) { + writer.writeTextElement(QLatin1String("hour"), QString::number(m_hour)); + } + + if (m_children & Minute) { + writer.writeTextElement(QLatin1String("minute"), QString::number(m_minute)); + } + + if (m_children & Second) { + writer.writeTextElement(QLatin1String("second"), QString::number(m_second)); + } + + if (m_children & Year) { + writer.writeTextElement(QLatin1String("year"), QString::number(m_year)); + } + + if (m_children & Month) { + writer.writeTextElement(QLatin1String("month"), QString::number(m_month)); + } + + if (m_children & Day) { + writer.writeTextElement(QLatin1String("day"), QString::number(m_day)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomDateTime::setElementHour(int a) +{ + m_children |= Hour; + m_hour = a; +} + +void DomDateTime::setElementMinute(int a) +{ + m_children |= Minute; + m_minute = a; +} + +void DomDateTime::setElementSecond(int a) +{ + m_children |= Second; + m_second = a; +} + +void DomDateTime::setElementYear(int a) +{ + m_children |= Year; + m_year = a; +} + +void DomDateTime::setElementMonth(int a) +{ + m_children |= Month; + m_month = a; +} + +void DomDateTime::setElementDay(int a) +{ + m_children |= Day; + m_day = a; +} + +void DomDateTime::clearElementHour() +{ + m_children &= ~Hour; +} + +void DomDateTime::clearElementMinute() +{ + m_children &= ~Minute; +} + +void DomDateTime::clearElementSecond() +{ + m_children &= ~Second; +} + +void DomDateTime::clearElementYear() +{ + m_children &= ~Year; +} + +void DomDateTime::clearElementMonth() +{ + m_children &= ~Month; +} + +void DomDateTime::clearElementDay() +{ + m_children &= ~Day; +} + +void DomStringList::clear(bool clear_all) +{ + m_string.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomStringList::DomStringList() +{ + m_children = 0; +} + +DomStringList::~DomStringList() +{ + m_string.clear(); +} + +void DomStringList::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("string")) { + m_string.append(reader.readElementText()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomStringList::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("string")) { + m_string.append(e.text()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomStringList::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("stringlist") : tagName.toLower()); + + for (int i = 0; i < m_string.size(); ++i) { + QString v = m_string[i]; + writer.writeTextElement(QLatin1String("string"), v); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomStringList::setElementString(const QStringList& a) +{ + m_children |= String; + m_string = a; +} + +void DomResourcePixmap::clear(bool clear_all) +{ + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_resource = false; + m_has_attr_alias = false; + } + + m_children = 0; +} + +DomResourcePixmap::DomResourcePixmap() +{ + m_children = 0; + m_has_attr_resource = false; + m_has_attr_alias = false; + m_text = QLatin1String(""); +} + +DomResourcePixmap::~DomResourcePixmap() +{ +} + +void DomResourcePixmap::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("resource")) { + setAttributeResource(attribute.value().toString()); + continue; + } + if (name == QLatin1String("alias")) { + setAttributeAlias(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomResourcePixmap::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("resource"))) + setAttributeResource(node.attribute(QLatin1String("resource"))); + if (node.hasAttribute(QLatin1String("alias"))) + setAttributeAlias(node.attribute(QLatin1String("alias"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomResourcePixmap::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("resourcepixmap") : tagName.toLower()); + + if (hasAttributeResource()) + writer.writeAttribute(QLatin1String("resource"), attributeResource()); + + if (hasAttributeAlias()) + writer.writeAttribute(QLatin1String("alias"), attributeAlias()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomResourceIcon::clear(bool clear_all) +{ + delete m_normalOff; + delete m_normalOn; + delete m_disabledOff; + delete m_disabledOn; + delete m_activeOff; + delete m_activeOn; + delete m_selectedOff; + delete m_selectedOn; + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_resource = false; + } + + m_children = 0; + m_normalOff = 0; + m_normalOn = 0; + m_disabledOff = 0; + m_disabledOn = 0; + m_activeOff = 0; + m_activeOn = 0; + m_selectedOff = 0; + m_selectedOn = 0; +} + +DomResourceIcon::DomResourceIcon() +{ + m_children = 0; + m_has_attr_resource = false; + m_text = QLatin1String(""); + m_normalOff = 0; + m_normalOn = 0; + m_disabledOff = 0; + m_disabledOn = 0; + m_activeOff = 0; + m_activeOn = 0; + m_selectedOff = 0; + m_selectedOn = 0; +} + +DomResourceIcon::~DomResourceIcon() +{ + delete m_normalOff; + delete m_normalOn; + delete m_disabledOff; + delete m_disabledOn; + delete m_activeOff; + delete m_activeOn; + delete m_selectedOff; + delete m_selectedOn; +} + +void DomResourceIcon::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("resource")) { + setAttributeResource(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("normaloff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementNormalOff(v); + continue; + } + if (tag == QLatin1String("normalon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementNormalOn(v); + continue; + } + if (tag == QLatin1String("disabledoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementDisabledOff(v); + continue; + } + if (tag == QLatin1String("disabledon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementDisabledOn(v); + continue; + } + if (tag == QLatin1String("activeoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementActiveOff(v); + continue; + } + if (tag == QLatin1String("activeon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementActiveOn(v); + continue; + } + if (tag == QLatin1String("selectedoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementSelectedOff(v); + continue; + } + if (tag == QLatin1String("selectedon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementSelectedOn(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomResourceIcon::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("resource"))) + setAttributeResource(node.attribute(QLatin1String("resource"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("normaloff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementNormalOff(v); + continue; + } + if (tag == QLatin1String("normalon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementNormalOn(v); + continue; + } + if (tag == QLatin1String("disabledoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementDisabledOff(v); + continue; + } + if (tag == QLatin1String("disabledon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementDisabledOn(v); + continue; + } + if (tag == QLatin1String("activeoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementActiveOff(v); + continue; + } + if (tag == QLatin1String("activeon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementActiveOn(v); + continue; + } + if (tag == QLatin1String("selectedoff")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementSelectedOff(v); + continue; + } + if (tag == QLatin1String("selectedon")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementSelectedOn(v); + continue; + } + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomResourceIcon::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("resourceicon") : tagName.toLower()); + + if (hasAttributeResource()) + writer.writeAttribute(QLatin1String("resource"), attributeResource()); + + if (m_children & NormalOff) { + m_normalOff->write(writer, QLatin1String("normaloff")); + } + + if (m_children & NormalOn) { + m_normalOn->write(writer, QLatin1String("normalon")); + } + + if (m_children & DisabledOff) { + m_disabledOff->write(writer, QLatin1String("disabledoff")); + } + + if (m_children & DisabledOn) { + m_disabledOn->write(writer, QLatin1String("disabledon")); + } + + if (m_children & ActiveOff) { + m_activeOff->write(writer, QLatin1String("activeoff")); + } + + if (m_children & ActiveOn) { + m_activeOn->write(writer, QLatin1String("activeon")); + } + + if (m_children & SelectedOff) { + m_selectedOff->write(writer, QLatin1String("selectedoff")); + } + + if (m_children & SelectedOn) { + m_selectedOn->write(writer, QLatin1String("selectedon")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomResourcePixmap* DomResourceIcon::takeElementNormalOff() +{ + DomResourcePixmap* a = m_normalOff; + m_normalOff = 0; + m_children ^= NormalOff; + return a; +} + +void DomResourceIcon::setElementNormalOff(DomResourcePixmap* a) +{ + delete m_normalOff; + m_children |= NormalOff; + m_normalOff = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementNormalOn() +{ + DomResourcePixmap* a = m_normalOn; + m_normalOn = 0; + m_children ^= NormalOn; + return a; +} + +void DomResourceIcon::setElementNormalOn(DomResourcePixmap* a) +{ + delete m_normalOn; + m_children |= NormalOn; + m_normalOn = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementDisabledOff() +{ + DomResourcePixmap* a = m_disabledOff; + m_disabledOff = 0; + m_children ^= DisabledOff; + return a; +} + +void DomResourceIcon::setElementDisabledOff(DomResourcePixmap* a) +{ + delete m_disabledOff; + m_children |= DisabledOff; + m_disabledOff = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementDisabledOn() +{ + DomResourcePixmap* a = m_disabledOn; + m_disabledOn = 0; + m_children ^= DisabledOn; + return a; +} + +void DomResourceIcon::setElementDisabledOn(DomResourcePixmap* a) +{ + delete m_disabledOn; + m_children |= DisabledOn; + m_disabledOn = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementActiveOff() +{ + DomResourcePixmap* a = m_activeOff; + m_activeOff = 0; + m_children ^= ActiveOff; + return a; +} + +void DomResourceIcon::setElementActiveOff(DomResourcePixmap* a) +{ + delete m_activeOff; + m_children |= ActiveOff; + m_activeOff = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementActiveOn() +{ + DomResourcePixmap* a = m_activeOn; + m_activeOn = 0; + m_children ^= ActiveOn; + return a; +} + +void DomResourceIcon::setElementActiveOn(DomResourcePixmap* a) +{ + delete m_activeOn; + m_children |= ActiveOn; + m_activeOn = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementSelectedOff() +{ + DomResourcePixmap* a = m_selectedOff; + m_selectedOff = 0; + m_children ^= SelectedOff; + return a; +} + +void DomResourceIcon::setElementSelectedOff(DomResourcePixmap* a) +{ + delete m_selectedOff; + m_children |= SelectedOff; + m_selectedOff = a; +} + +DomResourcePixmap* DomResourceIcon::takeElementSelectedOn() +{ + DomResourcePixmap* a = m_selectedOn; + m_selectedOn = 0; + m_children ^= SelectedOn; + return a; +} + +void DomResourceIcon::setElementSelectedOn(DomResourcePixmap* a) +{ + delete m_selectedOn; + m_children |= SelectedOn; + m_selectedOn = a; +} + +void DomResourceIcon::clearElementNormalOff() +{ + delete m_normalOff; + m_normalOff = 0; + m_children &= ~NormalOff; +} + +void DomResourceIcon::clearElementNormalOn() +{ + delete m_normalOn; + m_normalOn = 0; + m_children &= ~NormalOn; +} + +void DomResourceIcon::clearElementDisabledOff() +{ + delete m_disabledOff; + m_disabledOff = 0; + m_children &= ~DisabledOff; +} + +void DomResourceIcon::clearElementDisabledOn() +{ + delete m_disabledOn; + m_disabledOn = 0; + m_children &= ~DisabledOn; +} + +void DomResourceIcon::clearElementActiveOff() +{ + delete m_activeOff; + m_activeOff = 0; + m_children &= ~ActiveOff; +} + +void DomResourceIcon::clearElementActiveOn() +{ + delete m_activeOn; + m_activeOn = 0; + m_children &= ~ActiveOn; +} + +void DomResourceIcon::clearElementSelectedOff() +{ + delete m_selectedOff; + m_selectedOff = 0; + m_children &= ~SelectedOff; +} + +void DomResourceIcon::clearElementSelectedOn() +{ + delete m_selectedOn; + m_selectedOn = 0; + m_children &= ~SelectedOn; +} + +void DomString::clear(bool clear_all) +{ + + if (clear_all) { + m_text = QLatin1String(""); + m_has_attr_notr = false; + m_has_attr_comment = false; + m_has_attr_extraComment = false; + } + + m_children = 0; +} + +DomString::DomString() +{ + m_children = 0; + m_has_attr_notr = false; + m_has_attr_comment = false; + m_has_attr_extraComment = false; + m_text = QLatin1String(""); +} + +DomString::~DomString() +{ +} + +void DomString::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("notr")) { + setAttributeNotr(attribute.value().toString()); + continue; + } + if (name == QLatin1String("comment")) { + setAttributeComment(attribute.value().toString()); + continue; + } + if (name == QLatin1String("extracomment")) { + setAttributeExtraComment(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomString::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("notr"))) + setAttributeNotr(node.attribute(QLatin1String("notr"))); + if (node.hasAttribute(QLatin1String("comment"))) + setAttributeComment(node.attribute(QLatin1String("comment"))); + if (node.hasAttribute(QLatin1String("extracomment"))) + setAttributeExtraComment(node.attribute(QLatin1String("extracomment"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text = QLatin1String(""); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomString::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("string") : tagName.toLower()); + + if (hasAttributeNotr()) + writer.writeAttribute(QLatin1String("notr"), attributeNotr()); + + if (hasAttributeComment()) + writer.writeAttribute(QLatin1String("comment"), attributeComment()); + + if (hasAttributeExtraComment()) + writer.writeAttribute(QLatin1String("extracomment"), attributeExtraComment()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPointF::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_x = 0; + m_y = 0; +} + +DomPointF::DomPointF() +{ + m_children = 0; + m_x = 0; + m_y = 0; +} + +DomPointF::~DomPointF() +{ +} + +void DomPointF::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(reader.readElementText().toDouble()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(reader.readElementText().toDouble()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPointF::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(e.text().toDouble()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(e.text().toDouble()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPointF::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("pointf") : tagName.toLower()); + + if (m_children & X) { + writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x, 'f', 15)); + } + + if (m_children & Y) { + writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y, 'f', 15)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPointF::setElementX(double a) +{ + m_children |= X; + m_x = a; +} + +void DomPointF::setElementY(double a) +{ + m_children |= Y; + m_y = a; +} + +void DomPointF::clearElementX() +{ + m_children &= ~X; +} + +void DomPointF::clearElementY() +{ + m_children &= ~Y; +} + +void DomRectF::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; +} + +DomRectF::DomRectF() +{ + m_children = 0; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; +} + +DomRectF::~DomRectF() +{ +} + +void DomRectF::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(reader.readElementText().toDouble()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(reader.readElementText().toDouble()); + continue; + } + if (tag == QLatin1String("width")) { + setElementWidth(reader.readElementText().toDouble()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(reader.readElementText().toDouble()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomRectF::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(e.text().toDouble()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(e.text().toDouble()); + continue; + } + if (tag == QLatin1String("width")) { + setElementWidth(e.text().toDouble()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(e.text().toDouble()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomRectF::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("rectf") : tagName.toLower()); + + if (m_children & X) { + writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x, 'f', 15)); + } + + if (m_children & Y) { + writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y, 'f', 15)); + } + + if (m_children & Width) { + writer.writeTextElement(QLatin1String("width"), QString::number(m_width, 'f', 15)); + } + + if (m_children & Height) { + writer.writeTextElement(QLatin1String("height"), QString::number(m_height, 'f', 15)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomRectF::setElementX(double a) +{ + m_children |= X; + m_x = a; +} + +void DomRectF::setElementY(double a) +{ + m_children |= Y; + m_y = a; +} + +void DomRectF::setElementWidth(double a) +{ + m_children |= Width; + m_width = a; +} + +void DomRectF::setElementHeight(double a) +{ + m_children |= Height; + m_height = a; +} + +void DomRectF::clearElementX() +{ + m_children &= ~X; +} + +void DomRectF::clearElementY() +{ + m_children &= ~Y; +} + +void DomRectF::clearElementWidth() +{ + m_children &= ~Width; +} + +void DomRectF::clearElementHeight() +{ + m_children &= ~Height; +} + +void DomSizeF::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_width = 0; + m_height = 0; +} + +DomSizeF::DomSizeF() +{ + m_children = 0; + m_width = 0; + m_height = 0; +} + +DomSizeF::~DomSizeF() +{ +} + +void DomSizeF::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("width")) { + setElementWidth(reader.readElementText().toDouble()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(reader.readElementText().toDouble()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSizeF::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("width")) { + setElementWidth(e.text().toDouble()); + continue; + } + if (tag == QLatin1String("height")) { + setElementHeight(e.text().toDouble()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSizeF::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("sizef") : tagName.toLower()); + + if (m_children & Width) { + writer.writeTextElement(QLatin1String("width"), QString::number(m_width, 'f', 15)); + } + + if (m_children & Height) { + writer.writeTextElement(QLatin1String("height"), QString::number(m_height, 'f', 15)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSizeF::setElementWidth(double a) +{ + m_children |= Width; + m_width = a; +} + +void DomSizeF::setElementHeight(double a) +{ + m_children |= Height; + m_height = a; +} + +void DomSizeF::clearElementWidth() +{ + m_children &= ~Width; +} + +void DomSizeF::clearElementHeight() +{ + m_children &= ~Height; +} + +void DomChar::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_unicode = 0; +} + +DomChar::DomChar() +{ + m_children = 0; + m_unicode = 0; +} + +DomChar::~DomChar() +{ +} + +void DomChar::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("unicode")) { + setElementUnicode(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomChar::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("unicode")) { + setElementUnicode(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomChar::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("char") : tagName.toLower()); + + if (m_children & Unicode) { + writer.writeTextElement(QLatin1String("unicode"), QString::number(m_unicode)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomChar::setElementUnicode(int a) +{ + m_children |= Unicode; + m_unicode = a; +} + +void DomChar::clearElementUnicode() +{ + m_children &= ~Unicode; +} + +void DomUrl::clear(bool clear_all) +{ + delete m_string; + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_string = 0; +} + +DomUrl::DomUrl() +{ + m_children = 0; + m_string = 0; +} + +DomUrl::~DomUrl() +{ + delete m_string; +} + +void DomUrl::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("string")) { + DomString *v = new DomString(); + v->read(reader); + setElementString(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomUrl::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("string")) { + DomString *v = new DomString(); + v->read(e); + setElementString(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomUrl::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("url") : tagName.toLower()); + + if (m_children & String) { + m_string->write(writer, QLatin1String("string")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +DomString* DomUrl::takeElementString() +{ + DomString* a = m_string; + m_string = 0; + m_children ^= String; + return a; +} + +void DomUrl::setElementString(DomString* a) +{ + delete m_string; + m_children |= String; + m_string = a; +} + +void DomUrl::clearElementString() +{ + delete m_string; + m_string = 0; + m_children &= ~String; +} + +void DomProperty::clear(bool clear_all) +{ + delete m_color; + delete m_font; + delete m_iconSet; + delete m_pixmap; + delete m_palette; + delete m_point; + delete m_rect; + delete m_locale; + delete m_sizePolicy; + delete m_size; + delete m_string; + delete m_stringList; + delete m_date; + delete m_time; + delete m_dateTime; + delete m_pointF; + delete m_rectF; + delete m_sizeF; + delete m_char; + delete m_url; + delete m_brush; + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_stdset = false; + m_attr_stdset = 0; + } + + m_kind = Unknown; + + m_color = 0; + m_cursor = 0; + m_font = 0; + m_iconSet = 0; + m_pixmap = 0; + m_palette = 0; + m_point = 0; + m_rect = 0; + m_locale = 0; + m_sizePolicy = 0; + m_size = 0; + m_string = 0; + m_stringList = 0; + m_number = 0; + m_float = 0.0; + m_double = 0; + m_date = 0; + m_time = 0; + m_dateTime = 0; + m_pointF = 0; + m_rectF = 0; + m_sizeF = 0; + m_longLong = 0; + m_char = 0; + m_url = 0; + m_UInt = 0; + m_uLongLong = 0; + m_brush = 0; +} + +DomProperty::DomProperty() +{ + m_kind = Unknown; + + m_has_attr_name = false; + m_has_attr_stdset = false; + m_attr_stdset = 0; + m_color = 0; + m_cursor = 0; + m_font = 0; + m_iconSet = 0; + m_pixmap = 0; + m_palette = 0; + m_point = 0; + m_rect = 0; + m_locale = 0; + m_sizePolicy = 0; + m_size = 0; + m_string = 0; + m_stringList = 0; + m_number = 0; + m_float = 0.0; + m_double = 0; + m_date = 0; + m_time = 0; + m_dateTime = 0; + m_pointF = 0; + m_rectF = 0; + m_sizeF = 0; + m_longLong = 0; + m_char = 0; + m_url = 0; + m_UInt = 0; + m_uLongLong = 0; + m_brush = 0; +} + +DomProperty::~DomProperty() +{ + delete m_color; + delete m_font; + delete m_iconSet; + delete m_pixmap; + delete m_palette; + delete m_point; + delete m_rect; + delete m_locale; + delete m_sizePolicy; + delete m_size; + delete m_string; + delete m_stringList; + delete m_date; + delete m_time; + delete m_dateTime; + delete m_pointF; + delete m_rectF; + delete m_sizeF; + delete m_char; + delete m_url; + delete m_brush; +} + +void DomProperty::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("stdset")) { + setAttributeStdset(attribute.value().toString().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("bool")) { + setElementBool(reader.readElementText()); + continue; + } + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(reader); + setElementColor(v); + continue; + } + if (tag == QLatin1String("cstring")) { + setElementCstring(reader.readElementText()); + continue; + } + if (tag == QLatin1String("cursor")) { + setElementCursor(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("cursorshape")) { + setElementCursorShape(reader.readElementText()); + continue; + } + if (tag == QLatin1String("enum")) { + setElementEnum(reader.readElementText()); + continue; + } + if (tag == QLatin1String("font")) { + DomFont *v = new DomFont(); + v->read(reader); + setElementFont(v); + continue; + } + if (tag == QLatin1String("iconset")) { + DomResourceIcon *v = new DomResourceIcon(); + v->read(reader); + setElementIconSet(v); + continue; + } + if (tag == QLatin1String("pixmap")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(reader); + setElementPixmap(v); + continue; + } + if (tag == QLatin1String("palette")) { + DomPalette *v = new DomPalette(); + v->read(reader); + setElementPalette(v); + continue; + } + if (tag == QLatin1String("point")) { + DomPoint *v = new DomPoint(); + v->read(reader); + setElementPoint(v); + continue; + } + if (tag == QLatin1String("rect")) { + DomRect *v = new DomRect(); + v->read(reader); + setElementRect(v); + continue; + } + if (tag == QLatin1String("set")) { + setElementSet(reader.readElementText()); + continue; + } + if (tag == QLatin1String("locale")) { + DomLocale *v = new DomLocale(); + v->read(reader); + setElementLocale(v); + continue; + } + if (tag == QLatin1String("sizepolicy")) { + DomSizePolicy *v = new DomSizePolicy(); + v->read(reader); + setElementSizePolicy(v); + continue; + } + if (tag == QLatin1String("size")) { + DomSize *v = new DomSize(); + v->read(reader); + setElementSize(v); + continue; + } + if (tag == QLatin1String("string")) { + DomString *v = new DomString(); + v->read(reader); + setElementString(v); + continue; + } + if (tag == QLatin1String("stringlist")) { + DomStringList *v = new DomStringList(); + v->read(reader); + setElementStringList(v); + continue; + } + if (tag == QLatin1String("number")) { + setElementNumber(reader.readElementText().toInt()); + continue; + } + if (tag == QLatin1String("float")) { + setElementFloat(reader.readElementText().toFloat()); + continue; + } + if (tag == QLatin1String("double")) { + setElementDouble(reader.readElementText().toDouble()); + continue; + } + if (tag == QLatin1String("date")) { + DomDate *v = new DomDate(); + v->read(reader); + setElementDate(v); + continue; + } + if (tag == QLatin1String("time")) { + DomTime *v = new DomTime(); + v->read(reader); + setElementTime(v); + continue; + } + if (tag == QLatin1String("datetime")) { + DomDateTime *v = new DomDateTime(); + v->read(reader); + setElementDateTime(v); + continue; + } + if (tag == QLatin1String("pointf")) { + DomPointF *v = new DomPointF(); + v->read(reader); + setElementPointF(v); + continue; + } + if (tag == QLatin1String("rectf")) { + DomRectF *v = new DomRectF(); + v->read(reader); + setElementRectF(v); + continue; + } + if (tag == QLatin1String("sizef")) { + DomSizeF *v = new DomSizeF(); + v->read(reader); + setElementSizeF(v); + continue; + } + if (tag == QLatin1String("longlong")) { + setElementLongLong(reader.readElementText().toLongLong()); + continue; + } + if (tag == QLatin1String("char")) { + DomChar *v = new DomChar(); + v->read(reader); + setElementChar(v); + continue; + } + if (tag == QLatin1String("url")) { + DomUrl *v = new DomUrl(); + v->read(reader); + setElementUrl(v); + continue; + } + if (tag == QLatin1String("uint")) { + setElementUInt(reader.readElementText().toUInt()); + continue; + } + if (tag == QLatin1String("ulonglong")) { + setElementULongLong(reader.readElementText().toULongLong()); + continue; + } + if (tag == QLatin1String("brush")) { + DomBrush *v = new DomBrush(); + v->read(reader); + setElementBrush(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomProperty::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("stdset"))) + setAttributeStdset(node.attribute(QLatin1String("stdset")).toInt()); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("bool")) { + setElementBool(e.text()); + continue; + } + if (tag == QLatin1String("color")) { + DomColor *v = new DomColor(); + v->read(e); + setElementColor(v); + continue; + } + if (tag == QLatin1String("cstring")) { + setElementCstring(e.text()); + continue; + } + if (tag == QLatin1String("cursor")) { + setElementCursor(e.text().toInt()); + continue; + } + if (tag == QLatin1String("cursorshape")) { + setElementCursorShape(e.text()); + continue; + } + if (tag == QLatin1String("enum")) { + setElementEnum(e.text()); + continue; + } + if (tag == QLatin1String("font")) { + DomFont *v = new DomFont(); + v->read(e); + setElementFont(v); + continue; + } + if (tag == QLatin1String("iconset")) { + DomResourceIcon *v = new DomResourceIcon(); + v->read(e); + setElementIconSet(v); + continue; + } + if (tag == QLatin1String("pixmap")) { + DomResourcePixmap *v = new DomResourcePixmap(); + v->read(e); + setElementPixmap(v); + continue; + } + if (tag == QLatin1String("palette")) { + DomPalette *v = new DomPalette(); + v->read(e); + setElementPalette(v); + continue; + } + if (tag == QLatin1String("point")) { + DomPoint *v = new DomPoint(); + v->read(e); + setElementPoint(v); + continue; + } + if (tag == QLatin1String("rect")) { + DomRect *v = new DomRect(); + v->read(e); + setElementRect(v); + continue; + } + if (tag == QLatin1String("set")) { + setElementSet(e.text()); + continue; + } + if (tag == QLatin1String("locale")) { + DomLocale *v = new DomLocale(); + v->read(e); + setElementLocale(v); + continue; + } + if (tag == QLatin1String("sizepolicy")) { + DomSizePolicy *v = new DomSizePolicy(); + v->read(e); + setElementSizePolicy(v); + continue; + } + if (tag == QLatin1String("size")) { + DomSize *v = new DomSize(); + v->read(e); + setElementSize(v); + continue; + } + if (tag == QLatin1String("string")) { + DomString *v = new DomString(); + v->read(e); + setElementString(v); + continue; + } + if (tag == QLatin1String("stringlist")) { + DomStringList *v = new DomStringList(); + v->read(e); + setElementStringList(v); + continue; + } + if (tag == QLatin1String("number")) { + setElementNumber(e.text().toInt()); + continue; + } + if (tag == QLatin1String("float")) { + setElementFloat(e.text().toFloat()); + continue; + } + if (tag == QLatin1String("double")) { + setElementDouble(e.text().toDouble()); + continue; + } + if (tag == QLatin1String("date")) { + DomDate *v = new DomDate(); + v->read(e); + setElementDate(v); + continue; + } + if (tag == QLatin1String("time")) { + DomTime *v = new DomTime(); + v->read(e); + setElementTime(v); + continue; + } + if (tag == QLatin1String("datetime")) { + DomDateTime *v = new DomDateTime(); + v->read(e); + setElementDateTime(v); + continue; + } + if (tag == QLatin1String("pointf")) { + DomPointF *v = new DomPointF(); + v->read(e); + setElementPointF(v); + continue; + } + if (tag == QLatin1String("rectf")) { + DomRectF *v = new DomRectF(); + v->read(e); + setElementRectF(v); + continue; + } + if (tag == QLatin1String("sizef")) { + DomSizeF *v = new DomSizeF(); + v->read(e); + setElementSizeF(v); + continue; + } + if (tag == QLatin1String("longlong")) { + setElementLongLong(e.text().toLongLong()); + continue; + } + if (tag == QLatin1String("char")) { + DomChar *v = new DomChar(); + v->read(e); + setElementChar(v); + continue; + } + if (tag == QLatin1String("url")) { + DomUrl *v = new DomUrl(); + v->read(e); + setElementUrl(v); + continue; + } + if (tag == QLatin1String("uint")) { + setElementUInt(e.text().toUInt()); + continue; + } + if (tag == QLatin1String("ulonglong")) { + setElementULongLong(e.text().toULongLong()); + continue; + } + if (tag == QLatin1String("brush")) { + DomBrush *v = new DomBrush(); + v->read(e); + setElementBrush(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomProperty::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("property") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeStdset()) + writer.writeAttribute(QLatin1String("stdset"), QString::number(attributeStdset())); + + switch (kind()) { + case Bool: { + writer.writeTextElement(QLatin1String("bool"), elementBool()); + break; + } + case Color: { + DomColor* v = elementColor(); + if (v != 0) { + v->write(writer, QLatin1String("color")); + } + break; + } + case Cstring: { + writer.writeTextElement(QLatin1String("cstring"), elementCstring()); + break; + } + case Cursor: { + writer.writeTextElement(QLatin1String("cursor"), QString::number(elementCursor())); + break; + } + case CursorShape: { + writer.writeTextElement(QLatin1String("cursorShape"), elementCursorShape()); + break; + } + case Enum: { + writer.writeTextElement(QLatin1String("enum"), elementEnum()); + break; + } + case Font: { + DomFont* v = elementFont(); + if (v != 0) { + v->write(writer, QLatin1String("font")); + } + break; + } + case IconSet: { + DomResourceIcon* v = elementIconSet(); + if (v != 0) { + v->write(writer, QLatin1String("iconset")); + } + break; + } + case Pixmap: { + DomResourcePixmap* v = elementPixmap(); + if (v != 0) { + v->write(writer, QLatin1String("pixmap")); + } + break; + } + case Palette: { + DomPalette* v = elementPalette(); + if (v != 0) { + v->write(writer, QLatin1String("palette")); + } + break; + } + case Point: { + DomPoint* v = elementPoint(); + if (v != 0) { + v->write(writer, QLatin1String("point")); + } + break; + } + case Rect: { + DomRect* v = elementRect(); + if (v != 0) { + v->write(writer, QLatin1String("rect")); + } + break; + } + case Set: { + writer.writeTextElement(QLatin1String("set"), elementSet()); + break; + } + case Locale: { + DomLocale* v = elementLocale(); + if (v != 0) { + v->write(writer, QLatin1String("locale")); + } + break; + } + case SizePolicy: { + DomSizePolicy* v = elementSizePolicy(); + if (v != 0) { + v->write(writer, QLatin1String("sizepolicy")); + } + break; + } + case Size: { + DomSize* v = elementSize(); + if (v != 0) { + v->write(writer, QLatin1String("size")); + } + break; + } + case String: { + DomString* v = elementString(); + if (v != 0) { + v->write(writer, QLatin1String("string")); + } + break; + } + case StringList: { + DomStringList* v = elementStringList(); + if (v != 0) { + v->write(writer, QLatin1String("stringlist")); + } + break; + } + case Number: { + writer.writeTextElement(QLatin1String("number"), QString::number(elementNumber())); + break; + } + case Float: { + writer.writeTextElement(QLatin1String("float"), QString::number(elementFloat(), 'f', 8)); + break; + } + case Double: { + writer.writeTextElement(QLatin1String("double"), QString::number(elementDouble(), 'f', 15)); + break; + } + case Date: { + DomDate* v = elementDate(); + if (v != 0) { + v->write(writer, QLatin1String("date")); + } + break; + } + case Time: { + DomTime* v = elementTime(); + if (v != 0) { + v->write(writer, QLatin1String("time")); + } + break; + } + case DateTime: { + DomDateTime* v = elementDateTime(); + if (v != 0) { + v->write(writer, QLatin1String("datetime")); + } + break; + } + case PointF: { + DomPointF* v = elementPointF(); + if (v != 0) { + v->write(writer, QLatin1String("pointf")); + } + break; + } + case RectF: { + DomRectF* v = elementRectF(); + if (v != 0) { + v->write(writer, QLatin1String("rectf")); + } + break; + } + case SizeF: { + DomSizeF* v = elementSizeF(); + if (v != 0) { + v->write(writer, QLatin1String("sizef")); + } + break; + } + case LongLong: { + writer.writeTextElement(QLatin1String("longLong"), QString::number(elementLongLong())); + break; + } + case Char: { + DomChar* v = elementChar(); + if (v != 0) { + v->write(writer, QLatin1String("char")); + } + break; + } + case Url: { + DomUrl* v = elementUrl(); + if (v != 0) { + v->write(writer, QLatin1String("url")); + } + break; + } + case UInt: { + writer.writeTextElement(QLatin1String("UInt"), QString::number(elementUInt())); + break; + } + case ULongLong: { + writer.writeTextElement(QLatin1String("uLongLong"), QString::number(elementULongLong())); + break; + } + case Brush: { + DomBrush* v = elementBrush(); + if (v != 0) { + v->write(writer, QLatin1String("brush")); + } + break; + } + default: + break; + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomProperty::setElementBool(const QString& a) +{ + clear(false); + m_kind = Bool; + m_bool = a; +} + +DomColor* DomProperty::takeElementColor() +{ + DomColor* a = m_color; + m_color = 0; + return a; +} + +void DomProperty::setElementColor(DomColor* a) +{ + clear(false); + m_kind = Color; + m_color = a; +} + +void DomProperty::setElementCstring(const QString& a) +{ + clear(false); + m_kind = Cstring; + m_cstring = a; +} + +void DomProperty::setElementCursor(int a) +{ + clear(false); + m_kind = Cursor; + m_cursor = a; +} + +void DomProperty::setElementCursorShape(const QString& a) +{ + clear(false); + m_kind = CursorShape; + m_cursorShape = a; +} + +void DomProperty::setElementEnum(const QString& a) +{ + clear(false); + m_kind = Enum; + m_enum = a; +} + +DomFont* DomProperty::takeElementFont() +{ + DomFont* a = m_font; + m_font = 0; + return a; +} + +void DomProperty::setElementFont(DomFont* a) +{ + clear(false); + m_kind = Font; + m_font = a; +} + +DomResourceIcon* DomProperty::takeElementIconSet() +{ + DomResourceIcon* a = m_iconSet; + m_iconSet = 0; + return a; +} + +void DomProperty::setElementIconSet(DomResourceIcon* a) +{ + clear(false); + m_kind = IconSet; + m_iconSet = a; +} + +DomResourcePixmap* DomProperty::takeElementPixmap() +{ + DomResourcePixmap* a = m_pixmap; + m_pixmap = 0; + return a; +} + +void DomProperty::setElementPixmap(DomResourcePixmap* a) +{ + clear(false); + m_kind = Pixmap; + m_pixmap = a; +} + +DomPalette* DomProperty::takeElementPalette() +{ + DomPalette* a = m_palette; + m_palette = 0; + return a; +} + +void DomProperty::setElementPalette(DomPalette* a) +{ + clear(false); + m_kind = Palette; + m_palette = a; +} + +DomPoint* DomProperty::takeElementPoint() +{ + DomPoint* a = m_point; + m_point = 0; + return a; +} + +void DomProperty::setElementPoint(DomPoint* a) +{ + clear(false); + m_kind = Point; + m_point = a; +} + +DomRect* DomProperty::takeElementRect() +{ + DomRect* a = m_rect; + m_rect = 0; + return a; +} + +void DomProperty::setElementRect(DomRect* a) +{ + clear(false); + m_kind = Rect; + m_rect = a; +} + +void DomProperty::setElementSet(const QString& a) +{ + clear(false); + m_kind = Set; + m_set = a; +} + +DomLocale* DomProperty::takeElementLocale() +{ + DomLocale* a = m_locale; + m_locale = 0; + return a; +} + +void DomProperty::setElementLocale(DomLocale* a) +{ + clear(false); + m_kind = Locale; + m_locale = a; +} + +DomSizePolicy* DomProperty::takeElementSizePolicy() +{ + DomSizePolicy* a = m_sizePolicy; + m_sizePolicy = 0; + return a; +} + +void DomProperty::setElementSizePolicy(DomSizePolicy* a) +{ + clear(false); + m_kind = SizePolicy; + m_sizePolicy = a; +} + +DomSize* DomProperty::takeElementSize() +{ + DomSize* a = m_size; + m_size = 0; + return a; +} + +void DomProperty::setElementSize(DomSize* a) +{ + clear(false); + m_kind = Size; + m_size = a; +} + +DomString* DomProperty::takeElementString() +{ + DomString* a = m_string; + m_string = 0; + return a; +} + +void DomProperty::setElementString(DomString* a) +{ + clear(false); + m_kind = String; + m_string = a; +} + +DomStringList* DomProperty::takeElementStringList() +{ + DomStringList* a = m_stringList; + m_stringList = 0; + return a; +} + +void DomProperty::setElementStringList(DomStringList* a) +{ + clear(false); + m_kind = StringList; + m_stringList = a; +} + +void DomProperty::setElementNumber(int a) +{ + clear(false); + m_kind = Number; + m_number = a; +} + +void DomProperty::setElementFloat(float a) +{ + clear(false); + m_kind = Float; + m_float = a; +} + +void DomProperty::setElementDouble(double a) +{ + clear(false); + m_kind = Double; + m_double = a; +} + +DomDate* DomProperty::takeElementDate() +{ + DomDate* a = m_date; + m_date = 0; + return a; +} + +void DomProperty::setElementDate(DomDate* a) +{ + clear(false); + m_kind = Date; + m_date = a; +} + +DomTime* DomProperty::takeElementTime() +{ + DomTime* a = m_time; + m_time = 0; + return a; +} + +void DomProperty::setElementTime(DomTime* a) +{ + clear(false); + m_kind = Time; + m_time = a; +} + +DomDateTime* DomProperty::takeElementDateTime() +{ + DomDateTime* a = m_dateTime; + m_dateTime = 0; + return a; +} + +void DomProperty::setElementDateTime(DomDateTime* a) +{ + clear(false); + m_kind = DateTime; + m_dateTime = a; +} + +DomPointF* DomProperty::takeElementPointF() +{ + DomPointF* a = m_pointF; + m_pointF = 0; + return a; +} + +void DomProperty::setElementPointF(DomPointF* a) +{ + clear(false); + m_kind = PointF; + m_pointF = a; +} + +DomRectF* DomProperty::takeElementRectF() +{ + DomRectF* a = m_rectF; + m_rectF = 0; + return a; +} + +void DomProperty::setElementRectF(DomRectF* a) +{ + clear(false); + m_kind = RectF; + m_rectF = a; +} + +DomSizeF* DomProperty::takeElementSizeF() +{ + DomSizeF* a = m_sizeF; + m_sizeF = 0; + return a; +} + +void DomProperty::setElementSizeF(DomSizeF* a) +{ + clear(false); + m_kind = SizeF; + m_sizeF = a; +} + +void DomProperty::setElementLongLong(qlonglong a) +{ + clear(false); + m_kind = LongLong; + m_longLong = a; +} + +DomChar* DomProperty::takeElementChar() +{ + DomChar* a = m_char; + m_char = 0; + return a; +} + +void DomProperty::setElementChar(DomChar* a) +{ + clear(false); + m_kind = Char; + m_char = a; +} + +DomUrl* DomProperty::takeElementUrl() +{ + DomUrl* a = m_url; + m_url = 0; + return a; +} + +void DomProperty::setElementUrl(DomUrl* a) +{ + clear(false); + m_kind = Url; + m_url = a; +} + +void DomProperty::setElementUInt(uint a) +{ + clear(false); + m_kind = UInt; + m_UInt = a; +} + +void DomProperty::setElementULongLong(qulonglong a) +{ + clear(false); + m_kind = ULongLong; + m_uLongLong = a; +} + +DomBrush* DomProperty::takeElementBrush() +{ + DomBrush* a = m_brush; + m_brush = 0; + return a; +} + +void DomProperty::setElementBrush(DomBrush* a) +{ + clear(false); + m_kind = Brush; + m_brush = a; +} + +void DomConnections::clear(bool clear_all) +{ + qDeleteAll(m_connection); + m_connection.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomConnections::DomConnections() +{ + m_children = 0; +} + +DomConnections::~DomConnections() +{ + qDeleteAll(m_connection); + m_connection.clear(); +} + +void DomConnections::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("connection")) { + DomConnection *v = new DomConnection(); + v->read(reader); + m_connection.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomConnections::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("connection")) { + DomConnection *v = new DomConnection(); + v->read(e); + m_connection.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomConnections::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("connections") : tagName.toLower()); + + for (int i = 0; i < m_connection.size(); ++i) { + DomConnection* v = m_connection[i]; + v->write(writer, QLatin1String("connection")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomConnections::setElementConnection(const QList& a) +{ + m_children |= Connection; + m_connection = a; +} + +void DomConnection::clear(bool clear_all) +{ + delete m_hints; + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; + m_hints = 0; +} + +DomConnection::DomConnection() +{ + m_children = 0; + m_hints = 0; +} + +DomConnection::~DomConnection() +{ + delete m_hints; +} + +void DomConnection::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("sender")) { + setElementSender(reader.readElementText()); + continue; + } + if (tag == QLatin1String("signal")) { + setElementSignal(reader.readElementText()); + continue; + } + if (tag == QLatin1String("receiver")) { + setElementReceiver(reader.readElementText()); + continue; + } + if (tag == QLatin1String("slot")) { + setElementSlot(reader.readElementText()); + continue; + } + if (tag == QLatin1String("hints")) { + DomConnectionHints *v = new DomConnectionHints(); + v->read(reader); + setElementHints(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomConnection::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("sender")) { + setElementSender(e.text()); + continue; + } + if (tag == QLatin1String("signal")) { + setElementSignal(e.text()); + continue; + } + if (tag == QLatin1String("receiver")) { + setElementReceiver(e.text()); + continue; + } + if (tag == QLatin1String("slot")) { + setElementSlot(e.text()); + continue; + } + if (tag == QLatin1String("hints")) { + DomConnectionHints *v = new DomConnectionHints(); + v->read(e); + setElementHints(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomConnection::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("connection") : tagName.toLower()); + + if (m_children & Sender) { + writer.writeTextElement(QLatin1String("sender"), m_sender); + } + + if (m_children & Signal) { + writer.writeTextElement(QLatin1String("signal"), m_signal); + } + + if (m_children & Receiver) { + writer.writeTextElement(QLatin1String("receiver"), m_receiver); + } + + if (m_children & Slot) { + writer.writeTextElement(QLatin1String("slot"), m_slot); + } + + if (m_children & Hints) { + m_hints->write(writer, QLatin1String("hints")); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomConnection::setElementSender(const QString& a) +{ + m_children |= Sender; + m_sender = a; +} + +void DomConnection::setElementSignal(const QString& a) +{ + m_children |= Signal; + m_signal = a; +} + +void DomConnection::setElementReceiver(const QString& a) +{ + m_children |= Receiver; + m_receiver = a; +} + +void DomConnection::setElementSlot(const QString& a) +{ + m_children |= Slot; + m_slot = a; +} + +DomConnectionHints* DomConnection::takeElementHints() +{ + DomConnectionHints* a = m_hints; + m_hints = 0; + m_children ^= Hints; + return a; +} + +void DomConnection::setElementHints(DomConnectionHints* a) +{ + delete m_hints; + m_children |= Hints; + m_hints = a; +} + +void DomConnection::clearElementSender() +{ + m_children &= ~Sender; +} + +void DomConnection::clearElementSignal() +{ + m_children &= ~Signal; +} + +void DomConnection::clearElementReceiver() +{ + m_children &= ~Receiver; +} + +void DomConnection::clearElementSlot() +{ + m_children &= ~Slot; +} + +void DomConnection::clearElementHints() +{ + delete m_hints; + m_hints = 0; + m_children &= ~Hints; +} + +void DomConnectionHints::clear(bool clear_all) +{ + qDeleteAll(m_hint); + m_hint.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomConnectionHints::DomConnectionHints() +{ + m_children = 0; +} + +DomConnectionHints::~DomConnectionHints() +{ + qDeleteAll(m_hint); + m_hint.clear(); +} + +void DomConnectionHints::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("hint")) { + DomConnectionHint *v = new DomConnectionHint(); + v->read(reader); + m_hint.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomConnectionHints::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("hint")) { + DomConnectionHint *v = new DomConnectionHint(); + v->read(e); + m_hint.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomConnectionHints::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("connectionhints") : tagName.toLower()); + + for (int i = 0; i < m_hint.size(); ++i) { + DomConnectionHint* v = m_hint[i]; + v->write(writer, QLatin1String("hint")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomConnectionHints::setElementHint(const QList& a) +{ + m_children |= Hint; + m_hint = a; +} + +void DomConnectionHint::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_type = false; + } + + m_children = 0; + m_x = 0; + m_y = 0; +} + +DomConnectionHint::DomConnectionHint() +{ + m_children = 0; + m_has_attr_type = false; + m_x = 0; + m_y = 0; +} + +DomConnectionHint::~DomConnectionHint() +{ +} + +void DomConnectionHint::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(reader.readElementText().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(reader.readElementText().toInt()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomConnectionHint::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QString(QLatin1Char('x'))) { + setElementX(e.text().toInt()); + continue; + } + if (tag == QString(QLatin1Char('y'))) { + setElementY(e.text().toInt()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomConnectionHint::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("connectionhint") : tagName.toLower()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (m_children & X) { + writer.writeTextElement(QString(QLatin1Char('x')), QString::number(m_x)); + } + + if (m_children & Y) { + writer.writeTextElement(QString(QLatin1Char('y')), QString::number(m_y)); + } + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomConnectionHint::setElementX(int a) +{ + m_children |= X; + m_x = a; +} + +void DomConnectionHint::setElementY(int a) +{ + m_children |= Y; + m_y = a; +} + +void DomConnectionHint::clearElementX() +{ + m_children &= ~X; +} + +void DomConnectionHint::clearElementY() +{ + m_children &= ~Y; +} + +void DomScript::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_source = false; + m_has_attr_language = false; + } + + m_children = 0; +} + +DomScript::DomScript() +{ + m_children = 0; + m_has_attr_source = false; + m_has_attr_language = false; +} + +DomScript::~DomScript() +{ +} + +void DomScript::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("source")) { + setAttributeSource(attribute.value().toString()); + continue; + } + if (name == QLatin1String("language")) { + setAttributeLanguage(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomScript::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("source"))) + setAttributeSource(node.attribute(QLatin1String("source"))); + if (node.hasAttribute(QLatin1String("language"))) + setAttributeLanguage(node.attribute(QLatin1String("language"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomScript::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("script") : tagName.toLower()); + + if (hasAttributeSource()) + writer.writeAttribute(QLatin1String("source"), attributeSource()); + + if (hasAttributeLanguage()) + writer.writeAttribute(QLatin1String("language"), attributeLanguage()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomWidgetData::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomWidgetData::DomWidgetData() +{ + m_children = 0; +} + +DomWidgetData::~DomWidgetData() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomWidgetData::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomWidgetData::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomWidgetData::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("widgetdata") : tagName.toLower()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomWidgetData::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomDesignerData::clear(bool clear_all) +{ + qDeleteAll(m_property); + m_property.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomDesignerData::DomDesignerData() +{ + m_children = 0; +} + +DomDesignerData::~DomDesignerData() +{ + qDeleteAll(m_property); + m_property.clear(); +} + +void DomDesignerData::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(reader); + m_property.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomDesignerData::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("property")) { + DomProperty *v = new DomProperty(); + v->read(e); + m_property.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomDesignerData::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("designerdata") : tagName.toLower()); + + for (int i = 0; i < m_property.size(); ++i) { + DomProperty* v = m_property[i]; + v->write(writer, QLatin1String("property")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomDesignerData::setElementProperty(const QList& a) +{ + m_children |= Property; + m_property = a; +} + +void DomSlots::clear(bool clear_all) +{ + m_signal.clear(); + m_slot.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomSlots::DomSlots() +{ + m_children = 0; +} + +DomSlots::~DomSlots() +{ + m_signal.clear(); + m_slot.clear(); +} + +void DomSlots::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("signal")) { + m_signal.append(reader.readElementText()); + continue; + } + if (tag == QLatin1String("slot")) { + m_slot.append(reader.readElementText()); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomSlots::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("signal")) { + m_signal.append(e.text()); + continue; + } + if (tag == QLatin1String("slot")) { + m_slot.append(e.text()); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomSlots::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("slots") : tagName.toLower()); + + for (int i = 0; i < m_signal.size(); ++i) { + QString v = m_signal[i]; + writer.writeTextElement(QLatin1String("signal"), v); + } + for (int i = 0; i < m_slot.size(); ++i) { + QString v = m_slot[i]; + writer.writeTextElement(QLatin1String("slot"), v); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomSlots::setElementSignal(const QStringList& a) +{ + m_children |= Signal; + m_signal = a; +} + +void DomSlots::setElementSlot(const QStringList& a) +{ + m_children |= Slot; + m_slot = a; +} + +void DomPropertySpecifications::clear(bool clear_all) +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomPropertySpecifications::DomPropertySpecifications() +{ + m_children = 0; +} + +DomPropertySpecifications::~DomPropertySpecifications() +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); +} + +void DomPropertySpecifications::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(reader); + m_stringpropertyspecification.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPropertySpecifications::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(e); + m_stringpropertyspecification.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertyspecifications") : tagName.toLower()); + + for (int i = 0; i < m_stringpropertyspecification.size(); ++i) { + DomStringPropertySpecification* v = m_stringpropertyspecification[i]; + v->write(writer, QLatin1String("stringpropertyspecification")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPropertySpecifications::setElementStringpropertyspecification(const QList& a) +{ + m_children |= Stringpropertyspecification; + m_stringpropertyspecification = a; +} + +void DomStringPropertySpecification::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; + } + + m_children = 0; +} + +DomStringPropertySpecification::DomStringPropertySpecification() +{ + m_children = 0; + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; +} + +DomStringPropertySpecification::~DomStringPropertySpecification() +{ +} + +void DomStringPropertySpecification::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("notr")) { + setAttributeNotr(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomStringPropertySpecification::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + if (node.hasAttribute(QLatin1String("notr"))) + setAttributeNotr(node.attribute(QLatin1String("notr"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomStringPropertySpecification::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("stringpropertyspecification") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (hasAttributeNotr()) + writer.writeAttribute(QLatin1String("notr"), attributeNotr()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +QT_END_NAMESPACE + diff --git a/designer/lib/uilib/ui4_p.h b/designer/lib/uilib/ui4_p.h new file mode 100644 index 0000000..413d54f --- /dev/null +++ b/designer/lib/uilib/ui4_p.h @@ -0,0 +1,3791 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +// THIS FILE IS AUTOMATICALLY GENERATED + +#ifndef UI4_H +#define UI4_H + +#include +#include +#include +#include +#include +#include + +#if defined(QT_UIC3) + #define QUILOADER_QDOM_READ +#endif + +QT_BEGIN_NAMESPACE + +#ifdef QUILOADER_QDOM_READ + class QDomElement; +#endif + + +#define QDESIGNER_UILIB_EXTERN Q_DECL_EXPORT +#define QDESIGNER_UILIB_IMPORT Q_DECL_IMPORT + +#if defined(QT_DESIGNER_STATIC) || defined(QT_UIC) || defined(QT_UIC3) +# define QDESIGNER_UILIB_EXPORT +#elif defined(QDESIGNER_UILIB_LIBRARY) +# define QDESIGNER_UILIB_EXPORT QDESIGNER_UILIB_EXTERN +#else +# define QDESIGNER_UILIB_EXPORT QDESIGNER_UILIB_IMPORT +#endif + +#ifndef QDESIGNER_UILIB_EXPORT +# define QDESIGNER_UILIB_EXPORT +#endif + +#ifdef QFORMINTERNAL_NAMESPACE +namespace QFormInternal +{ +#endif + + +/******************************************************************************* +** Forward declarations +*/ + +class DomUI; +class DomIncludes; +class DomInclude; +class DomResources; +class DomResource; +class DomActionGroup; +class DomAction; +class DomActionRef; +class DomButtonGroup; +class DomButtonGroups; +class DomImages; +class DomImage; +class DomImageData; +class DomCustomWidgets; +class DomHeader; +class DomCustomWidget; +class DomProperties; +class DomPropertyData; +class DomSizePolicyData; +class DomLayoutDefault; +class DomLayoutFunction; +class DomTabStops; +class DomLayout; +class DomLayoutItem; +class DomRow; +class DomColumn; +class DomItem; +class DomWidget; +class DomSpacer; +class DomColor; +class DomGradientStop; +class DomGradient; +class DomBrush; +class DomColorRole; +class DomColorGroup; +class DomPalette; +class DomFont; +class DomPoint; +class DomRect; +class DomLocale; +class DomSizePolicy; +class DomSize; +class DomDate; +class DomTime; +class DomDateTime; +class DomStringList; +class DomResourcePixmap; +class DomResourceIcon; +class DomString; +class DomPointF; +class DomRectF; +class DomSizeF; +class DomChar; +class DomUrl; +class DomProperty; +class DomConnections; +class DomConnection; +class DomConnectionHints; +class DomConnectionHint; +class DomScript; +class DomWidgetData; +class DomDesignerData; +class DomSlots; +class DomPropertySpecifications; +class DomStringPropertySpecification; + +/******************************************************************************* +** Declarations +*/ + +class QDESIGNER_UILIB_EXPORT DomUI { +public: + DomUI(); + ~DomUI(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeVersion() const { return m_has_attr_version; } + inline QString attributeVersion() const { return m_attr_version; } + inline void setAttributeVersion(const QString& a) { m_attr_version = a; m_has_attr_version = true; } + inline void clearAttributeVersion() { m_has_attr_version = false; } + + inline bool hasAttributeLanguage() const { return m_has_attr_language; } + inline QString attributeLanguage() const { return m_attr_language; } + inline void setAttributeLanguage(const QString& a) { m_attr_language = a; m_has_attr_language = true; } + inline void clearAttributeLanguage() { m_has_attr_language = false; } + + inline bool hasAttributeDisplayname() const { return m_has_attr_displayname; } + inline QString attributeDisplayname() const { return m_attr_displayname; } + inline void setAttributeDisplayname(const QString& a) { m_attr_displayname = a; m_has_attr_displayname = true; } + inline void clearAttributeDisplayname() { m_has_attr_displayname = false; } + + inline bool hasAttributeStdsetdef() const { return m_has_attr_stdsetdef; } + inline int attributeStdsetdef() const { return m_attr_stdsetdef; } + inline void setAttributeStdsetdef(int a) { m_attr_stdsetdef = a; m_has_attr_stdsetdef = true; } + inline void clearAttributeStdsetdef() { m_has_attr_stdsetdef = false; } + + inline bool hasAttributeStdSetDef() const { return m_has_attr_stdSetDef; } + inline int attributeStdSetDef() const { return m_attr_stdSetDef; } + inline void setAttributeStdSetDef(int a) { m_attr_stdSetDef = a; m_has_attr_stdSetDef = true; } + inline void clearAttributeStdSetDef() { m_has_attr_stdSetDef = false; } + + // child element accessors + inline QString elementAuthor() const { return m_author; } + void setElementAuthor(const QString& a); + inline bool hasElementAuthor() const { return m_children & Author; } + void clearElementAuthor(); + + inline QString elementComment() const { return m_comment; } + void setElementComment(const QString& a); + inline bool hasElementComment() const { return m_children & Comment; } + void clearElementComment(); + + inline QString elementExportMacro() const { return m_exportMacro; } + void setElementExportMacro(const QString& a); + inline bool hasElementExportMacro() const { return m_children & ExportMacro; } + void clearElementExportMacro(); + + inline QString elementClass() const { return m_class; } + void setElementClass(const QString& a); + inline bool hasElementClass() const { return m_children & Class; } + void clearElementClass(); + + inline DomWidget* elementWidget() const { return m_widget; } + DomWidget* takeElementWidget(); + void setElementWidget(DomWidget* a); + inline bool hasElementWidget() const { return m_children & Widget; } + void clearElementWidget(); + + inline DomLayoutDefault* elementLayoutDefault() const { return m_layoutDefault; } + DomLayoutDefault* takeElementLayoutDefault(); + void setElementLayoutDefault(DomLayoutDefault* a); + inline bool hasElementLayoutDefault() const { return m_children & LayoutDefault; } + void clearElementLayoutDefault(); + + inline DomLayoutFunction* elementLayoutFunction() const { return m_layoutFunction; } + DomLayoutFunction* takeElementLayoutFunction(); + void setElementLayoutFunction(DomLayoutFunction* a); + inline bool hasElementLayoutFunction() const { return m_children & LayoutFunction; } + void clearElementLayoutFunction(); + + inline QString elementPixmapFunction() const { return m_pixmapFunction; } + void setElementPixmapFunction(const QString& a); + inline bool hasElementPixmapFunction() const { return m_children & PixmapFunction; } + void clearElementPixmapFunction(); + + inline DomCustomWidgets* elementCustomWidgets() const { return m_customWidgets; } + DomCustomWidgets* takeElementCustomWidgets(); + void setElementCustomWidgets(DomCustomWidgets* a); + inline bool hasElementCustomWidgets() const { return m_children & CustomWidgets; } + void clearElementCustomWidgets(); + + inline DomTabStops* elementTabStops() const { return m_tabStops; } + DomTabStops* takeElementTabStops(); + void setElementTabStops(DomTabStops* a); + inline bool hasElementTabStops() const { return m_children & TabStops; } + void clearElementTabStops(); + + inline DomImages* elementImages() const { return m_images; } + DomImages* takeElementImages(); + void setElementImages(DomImages* a); + inline bool hasElementImages() const { return m_children & Images; } + void clearElementImages(); + + inline DomIncludes* elementIncludes() const { return m_includes; } + DomIncludes* takeElementIncludes(); + void setElementIncludes(DomIncludes* a); + inline bool hasElementIncludes() const { return m_children & Includes; } + void clearElementIncludes(); + + inline DomResources* elementResources() const { return m_resources; } + DomResources* takeElementResources(); + void setElementResources(DomResources* a); + inline bool hasElementResources() const { return m_children & Resources; } + void clearElementResources(); + + inline DomConnections* elementConnections() const { return m_connections; } + DomConnections* takeElementConnections(); + void setElementConnections(DomConnections* a); + inline bool hasElementConnections() const { return m_children & Connections; } + void clearElementConnections(); + + inline DomDesignerData* elementDesignerdata() const { return m_designerdata; } + DomDesignerData* takeElementDesignerdata(); + void setElementDesignerdata(DomDesignerData* a); + inline bool hasElementDesignerdata() const { return m_children & Designerdata; } + void clearElementDesignerdata(); + + inline DomSlots* elementSlots() const { return m_slots; } + DomSlots* takeElementSlots(); + void setElementSlots(DomSlots* a); + inline bool hasElementSlots() const { return m_children & Slots; } + void clearElementSlots(); + + inline DomButtonGroups* elementButtonGroups() const { return m_buttonGroups; } + DomButtonGroups* takeElementButtonGroups(); + void setElementButtonGroups(DomButtonGroups* a); + inline bool hasElementButtonGroups() const { return m_children & ButtonGroups; } + void clearElementButtonGroups(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_version; + bool m_has_attr_version; + + QString m_attr_language; + bool m_has_attr_language; + + QString m_attr_displayname; + bool m_has_attr_displayname; + + int m_attr_stdsetdef; + bool m_has_attr_stdsetdef; + + int m_attr_stdSetDef; + bool m_has_attr_stdSetDef; + + // child element data + uint m_children; + QString m_author; + QString m_comment; + QString m_exportMacro; + QString m_class; + DomWidget* m_widget; + DomLayoutDefault* m_layoutDefault; + DomLayoutFunction* m_layoutFunction; + QString m_pixmapFunction; + DomCustomWidgets* m_customWidgets; + DomTabStops* m_tabStops; + DomImages* m_images; + DomIncludes* m_includes; + DomResources* m_resources; + DomConnections* m_connections; + DomDesignerData* m_designerdata; + DomSlots* m_slots; + DomButtonGroups* m_buttonGroups; + enum Child { + Author = 1, + Comment = 2, + ExportMacro = 4, + Class = 8, + Widget = 16, + LayoutDefault = 32, + LayoutFunction = 64, + PixmapFunction = 128, + CustomWidgets = 256, + TabStops = 512, + Images = 1024, + Includes = 2048, + Resources = 4096, + Connections = 8192, + Designerdata = 16384, + Slots = 32768, + ButtonGroups = 65536 + }; + + DomUI(const DomUI &other); + void operator = (const DomUI&other); +}; + +class QDESIGNER_UILIB_EXPORT DomIncludes { +public: + DomIncludes(); + ~DomIncludes(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementInclude() const { return m_include; } + void setElementInclude(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_include; + enum Child { + Include = 1 + }; + + DomIncludes(const DomIncludes &other); + void operator = (const DomIncludes&other); +}; + +class QDESIGNER_UILIB_EXPORT DomInclude { +public: + DomInclude(); + ~DomInclude(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeLocation() const { return m_has_attr_location; } + inline QString attributeLocation() const { return m_attr_location; } + inline void setAttributeLocation(const QString& a) { m_attr_location = a; m_has_attr_location = true; } + inline void clearAttributeLocation() { m_has_attr_location = false; } + + inline bool hasAttributeImpldecl() const { return m_has_attr_impldecl; } + inline QString attributeImpldecl() const { return m_attr_impldecl; } + inline void setAttributeImpldecl(const QString& a) { m_attr_impldecl = a; m_has_attr_impldecl = true; } + inline void clearAttributeImpldecl() { m_has_attr_impldecl = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_location; + bool m_has_attr_location; + + QString m_attr_impldecl; + bool m_has_attr_impldecl; + + // child element data + uint m_children; + + DomInclude(const DomInclude &other); + void operator = (const DomInclude&other); +}; + +class QDESIGNER_UILIB_EXPORT DomResources { +public: + DomResources(); + ~DomResources(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors + inline QList elementInclude() const { return m_include; } + void setElementInclude(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + QList m_include; + enum Child { + Include = 1 + }; + + DomResources(const DomResources &other); + void operator = (const DomResources&other); +}; + +class QDESIGNER_UILIB_EXPORT DomResource { +public: + DomResource(); + ~DomResource(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeLocation() const { return m_has_attr_location; } + inline QString attributeLocation() const { return m_attr_location; } + inline void setAttributeLocation(const QString& a) { m_attr_location = a; m_has_attr_location = true; } + inline void clearAttributeLocation() { m_has_attr_location = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_location; + bool m_has_attr_location; + + // child element data + uint m_children; + + DomResource(const DomResource &other); + void operator = (const DomResource&other); +}; + +class QDESIGNER_UILIB_EXPORT DomActionGroup { +public: + DomActionGroup(); + ~DomActionGroup(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors + inline QList elementAction() const { return m_action; } + void setElementAction(const QList& a); + + inline QList elementActionGroup() const { return m_actionGroup; } + void setElementActionGroup(const QList& a); + + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementAttribute() const { return m_attribute; } + void setElementAttribute(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + QList m_action; + QList m_actionGroup; + QList m_property; + QList m_attribute; + enum Child { + Action = 1, + ActionGroup = 2, + Property = 4, + Attribute = 8 + }; + + DomActionGroup(const DomActionGroup &other); + void operator = (const DomActionGroup&other); +}; + +class QDESIGNER_UILIB_EXPORT DomAction { +public: + DomAction(); + ~DomAction(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeMenu() const { return m_has_attr_menu; } + inline QString attributeMenu() const { return m_attr_menu; } + inline void setAttributeMenu(const QString& a) { m_attr_menu = a; m_has_attr_menu = true; } + inline void clearAttributeMenu() { m_has_attr_menu = false; } + + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementAttribute() const { return m_attribute; } + void setElementAttribute(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_menu; + bool m_has_attr_menu; + + // child element data + uint m_children; + QList m_property; + QList m_attribute; + enum Child { + Property = 1, + Attribute = 2 + }; + + DomAction(const DomAction &other); + void operator = (const DomAction&other); +}; + +class QDESIGNER_UILIB_EXPORT DomActionRef { +public: + DomActionRef(); + ~DomActionRef(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + + DomActionRef(const DomActionRef &other); + void operator = (const DomActionRef&other); +}; + +class QDESIGNER_UILIB_EXPORT DomButtonGroup { +public: + DomButtonGroup(); + ~DomButtonGroup(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementAttribute() const { return m_attribute; } + void setElementAttribute(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + QList m_property; + QList m_attribute; + enum Child { + Property = 1, + Attribute = 2 + }; + + DomButtonGroup(const DomButtonGroup &other); + void operator = (const DomButtonGroup&other); +}; + +class QDESIGNER_UILIB_EXPORT DomButtonGroups { +public: + DomButtonGroups(); + ~DomButtonGroups(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementButtonGroup() const { return m_buttonGroup; } + void setElementButtonGroup(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_buttonGroup; + enum Child { + ButtonGroup = 1 + }; + + DomButtonGroups(const DomButtonGroups &other); + void operator = (const DomButtonGroups&other); +}; + +class QDESIGNER_UILIB_EXPORT DomImages { +public: + DomImages(); + ~DomImages(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementImage() const { return m_image; } + void setElementImage(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_image; + enum Child { + Image = 1 + }; + + DomImages(const DomImages &other); + void operator = (const DomImages&other); +}; + +class QDESIGNER_UILIB_EXPORT DomImage { +public: + DomImage(); + ~DomImage(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors + inline DomImageData* elementData() const { return m_data; } + DomImageData* takeElementData(); + void setElementData(DomImageData* a); + inline bool hasElementData() const { return m_children & Data; } + void clearElementData(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + DomImageData* m_data; + enum Child { + Data = 1 + }; + + DomImage(const DomImage &other); + void operator = (const DomImage&other); +}; + +class QDESIGNER_UILIB_EXPORT DomImageData { +public: + DomImageData(); + ~DomImageData(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeFormat() const { return m_has_attr_format; } + inline QString attributeFormat() const { return m_attr_format; } + inline void setAttributeFormat(const QString& a) { m_attr_format = a; m_has_attr_format = true; } + inline void clearAttributeFormat() { m_has_attr_format = false; } + + inline bool hasAttributeLength() const { return m_has_attr_length; } + inline int attributeLength() const { return m_attr_length; } + inline void setAttributeLength(int a) { m_attr_length = a; m_has_attr_length = true; } + inline void clearAttributeLength() { m_has_attr_length = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_format; + bool m_has_attr_format; + + int m_attr_length; + bool m_has_attr_length; + + // child element data + uint m_children; + + DomImageData(const DomImageData &other); + void operator = (const DomImageData&other); +}; + +class QDESIGNER_UILIB_EXPORT DomCustomWidgets { +public: + DomCustomWidgets(); + ~DomCustomWidgets(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementCustomWidget() const { return m_customWidget; } + void setElementCustomWidget(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_customWidget; + enum Child { + CustomWidget = 1 + }; + + DomCustomWidgets(const DomCustomWidgets &other); + void operator = (const DomCustomWidgets&other); +}; + +class QDESIGNER_UILIB_EXPORT DomHeader { +public: + DomHeader(); + ~DomHeader(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeLocation() const { return m_has_attr_location; } + inline QString attributeLocation() const { return m_attr_location; } + inline void setAttributeLocation(const QString& a) { m_attr_location = a; m_has_attr_location = true; } + inline void clearAttributeLocation() { m_has_attr_location = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_location; + bool m_has_attr_location; + + // child element data + uint m_children; + + DomHeader(const DomHeader &other); + void operator = (const DomHeader&other); +}; + +class QDESIGNER_UILIB_EXPORT DomCustomWidget { +public: + DomCustomWidget(); + ~DomCustomWidget(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QString elementClass() const { return m_class; } + void setElementClass(const QString& a); + inline bool hasElementClass() const { return m_children & Class; } + void clearElementClass(); + + inline QString elementExtends() const { return m_extends; } + void setElementExtends(const QString& a); + inline bool hasElementExtends() const { return m_children & Extends; } + void clearElementExtends(); + + inline DomHeader* elementHeader() const { return m_header; } + DomHeader* takeElementHeader(); + void setElementHeader(DomHeader* a); + inline bool hasElementHeader() const { return m_children & Header; } + void clearElementHeader(); + + inline DomSize* elementSizeHint() const { return m_sizeHint; } + DomSize* takeElementSizeHint(); + void setElementSizeHint(DomSize* a); + inline bool hasElementSizeHint() const { return m_children & SizeHint; } + void clearElementSizeHint(); + + inline QString elementAddPageMethod() const { return m_addPageMethod; } + void setElementAddPageMethod(const QString& a); + inline bool hasElementAddPageMethod() const { return m_children & AddPageMethod; } + void clearElementAddPageMethod(); + + inline int elementContainer() const { return m_container; } + void setElementContainer(int a); + inline bool hasElementContainer() const { return m_children & Container; } + void clearElementContainer(); + + inline DomSizePolicyData* elementSizePolicy() const { return m_sizePolicy; } + DomSizePolicyData* takeElementSizePolicy(); + void setElementSizePolicy(DomSizePolicyData* a); + inline bool hasElementSizePolicy() const { return m_children & SizePolicy; } + void clearElementSizePolicy(); + + inline QString elementPixmap() const { return m_pixmap; } + void setElementPixmap(const QString& a); + inline bool hasElementPixmap() const { return m_children & Pixmap; } + void clearElementPixmap(); + + inline DomScript* elementScript() const { return m_script; } + DomScript* takeElementScript(); + void setElementScript(DomScript* a); + inline bool hasElementScript() const { return m_children & Script; } + void clearElementScript(); + + inline DomProperties* elementProperties() const { return m_properties; } + DomProperties* takeElementProperties(); + void setElementProperties(DomProperties* a); + inline bool hasElementProperties() const { return m_children & Properties; } + void clearElementProperties(); + + inline DomSlots* elementSlots() const { return m_slots; } + DomSlots* takeElementSlots(); + void setElementSlots(DomSlots* a); + inline bool hasElementSlots() const { return m_children & Slots; } + void clearElementSlots(); + + inline DomPropertySpecifications* elementPropertyspecifications() const { return m_propertyspecifications; } + DomPropertySpecifications* takeElementPropertyspecifications(); + void setElementPropertyspecifications(DomPropertySpecifications* a); + inline bool hasElementPropertyspecifications() const { return m_children & Propertyspecifications; } + void clearElementPropertyspecifications(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QString m_class; + QString m_extends; + DomHeader* m_header; + DomSize* m_sizeHint; + QString m_addPageMethod; + int m_container; + DomSizePolicyData* m_sizePolicy; + QString m_pixmap; + DomScript* m_script; + DomProperties* m_properties; + DomSlots* m_slots; + DomPropertySpecifications* m_propertyspecifications; + enum Child { + Class = 1, + Extends = 2, + Header = 4, + SizeHint = 8, + AddPageMethod = 16, + Container = 32, + SizePolicy = 64, + Pixmap = 128, + Script = 256, + Properties = 512, + Slots = 1024, + Propertyspecifications = 2048 + }; + + DomCustomWidget(const DomCustomWidget &other); + void operator = (const DomCustomWidget&other); +}; + +class QDESIGNER_UILIB_EXPORT DomProperties { +public: + DomProperties(); + ~DomProperties(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomProperties(const DomProperties &other); + void operator = (const DomProperties&other); +}; + +class QDESIGNER_UILIB_EXPORT DomPropertyData { +public: + DomPropertyData(); + ~DomPropertyData(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_type; + bool m_has_attr_type; + + // child element data + uint m_children; + + DomPropertyData(const DomPropertyData &other); + void operator = (const DomPropertyData&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSizePolicyData { +public: + DomSizePolicyData(); + ~DomSizePolicyData(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementHorData() const { return m_horData; } + void setElementHorData(int a); + inline bool hasElementHorData() const { return m_children & HorData; } + void clearElementHorData(); + + inline int elementVerData() const { return m_verData; } + void setElementVerData(int a); + inline bool hasElementVerData() const { return m_children & VerData; } + void clearElementVerData(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_horData; + int m_verData; + enum Child { + HorData = 1, + VerData = 2 + }; + + DomSizePolicyData(const DomSizePolicyData &other); + void operator = (const DomSizePolicyData&other); +}; + +class QDESIGNER_UILIB_EXPORT DomLayoutDefault { +public: + DomLayoutDefault(); + ~DomLayoutDefault(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeSpacing() const { return m_has_attr_spacing; } + inline int attributeSpacing() const { return m_attr_spacing; } + inline void setAttributeSpacing(int a) { m_attr_spacing = a; m_has_attr_spacing = true; } + inline void clearAttributeSpacing() { m_has_attr_spacing = false; } + + inline bool hasAttributeMargin() const { return m_has_attr_margin; } + inline int attributeMargin() const { return m_attr_margin; } + inline void setAttributeMargin(int a) { m_attr_margin = a; m_has_attr_margin = true; } + inline void clearAttributeMargin() { m_has_attr_margin = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + int m_attr_spacing; + bool m_has_attr_spacing; + + int m_attr_margin; + bool m_has_attr_margin; + + // child element data + uint m_children; + + DomLayoutDefault(const DomLayoutDefault &other); + void operator = (const DomLayoutDefault&other); +}; + +class QDESIGNER_UILIB_EXPORT DomLayoutFunction { +public: + DomLayoutFunction(); + ~DomLayoutFunction(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeSpacing() const { return m_has_attr_spacing; } + inline QString attributeSpacing() const { return m_attr_spacing; } + inline void setAttributeSpacing(const QString& a) { m_attr_spacing = a; m_has_attr_spacing = true; } + inline void clearAttributeSpacing() { m_has_attr_spacing = false; } + + inline bool hasAttributeMargin() const { return m_has_attr_margin; } + inline QString attributeMargin() const { return m_attr_margin; } + inline void setAttributeMargin(const QString& a) { m_attr_margin = a; m_has_attr_margin = true; } + inline void clearAttributeMargin() { m_has_attr_margin = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_spacing; + bool m_has_attr_spacing; + + QString m_attr_margin; + bool m_has_attr_margin; + + // child element data + uint m_children; + + DomLayoutFunction(const DomLayoutFunction &other); + void operator = (const DomLayoutFunction&other); +}; + +class QDESIGNER_UILIB_EXPORT DomTabStops { +public: + DomTabStops(); + ~DomTabStops(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QStringList elementTabStop() const { return m_tabStop; } + void setElementTabStop(const QStringList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QStringList m_tabStop; + enum Child { + TabStop = 1 + }; + + DomTabStops(const DomTabStops &other); + void operator = (const DomTabStops&other); +}; + +class QDESIGNER_UILIB_EXPORT DomLayout { +public: + DomLayout(); + ~DomLayout(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeClass() const { return m_has_attr_class; } + inline QString attributeClass() const { return m_attr_class; } + inline void setAttributeClass(const QString& a) { m_attr_class = a; m_has_attr_class = true; } + inline void clearAttributeClass() { m_has_attr_class = false; } + + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeStretch() const { return m_has_attr_stretch; } + inline QString attributeStretch() const { return m_attr_stretch; } + inline void setAttributeStretch(const QString& a) { m_attr_stretch = a; m_has_attr_stretch = true; } + inline void clearAttributeStretch() { m_has_attr_stretch = false; } + + inline bool hasAttributeRowStretch() const { return m_has_attr_rowStretch; } + inline QString attributeRowStretch() const { return m_attr_rowStretch; } + inline void setAttributeRowStretch(const QString& a) { m_attr_rowStretch = a; m_has_attr_rowStretch = true; } + inline void clearAttributeRowStretch() { m_has_attr_rowStretch = false; } + + inline bool hasAttributeColumnStretch() const { return m_has_attr_columnStretch; } + inline QString attributeColumnStretch() const { return m_attr_columnStretch; } + inline void setAttributeColumnStretch(const QString& a) { m_attr_columnStretch = a; m_has_attr_columnStretch = true; } + inline void clearAttributeColumnStretch() { m_has_attr_columnStretch = false; } + + inline bool hasAttributeRowMinimumHeight() const { return m_has_attr_rowMinimumHeight; } + inline QString attributeRowMinimumHeight() const { return m_attr_rowMinimumHeight; } + inline void setAttributeRowMinimumHeight(const QString& a) { m_attr_rowMinimumHeight = a; m_has_attr_rowMinimumHeight = true; } + inline void clearAttributeRowMinimumHeight() { m_has_attr_rowMinimumHeight = false; } + + inline bool hasAttributeColumnMinimumWidth() const { return m_has_attr_columnMinimumWidth; } + inline QString attributeColumnMinimumWidth() const { return m_attr_columnMinimumWidth; } + inline void setAttributeColumnMinimumWidth(const QString& a) { m_attr_columnMinimumWidth = a; m_has_attr_columnMinimumWidth = true; } + inline void clearAttributeColumnMinimumWidth() { m_has_attr_columnMinimumWidth = false; } + + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementAttribute() const { return m_attribute; } + void setElementAttribute(const QList& a); + + inline QList elementItem() const { return m_item; } + void setElementItem(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_class; + bool m_has_attr_class; + + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_stretch; + bool m_has_attr_stretch; + + QString m_attr_rowStretch; + bool m_has_attr_rowStretch; + + QString m_attr_columnStretch; + bool m_has_attr_columnStretch; + + QString m_attr_rowMinimumHeight; + bool m_has_attr_rowMinimumHeight; + + QString m_attr_columnMinimumWidth; + bool m_has_attr_columnMinimumWidth; + + // child element data + uint m_children; + QList m_property; + QList m_attribute; + QList m_item; + enum Child { + Property = 1, + Attribute = 2, + Item = 4 + }; + + DomLayout(const DomLayout &other); + void operator = (const DomLayout&other); +}; + +class QDESIGNER_UILIB_EXPORT DomLayoutItem { +public: + DomLayoutItem(); + ~DomLayoutItem(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeRow() const { return m_has_attr_row; } + inline int attributeRow() const { return m_attr_row; } + inline void setAttributeRow(int a) { m_attr_row = a; m_has_attr_row = true; } + inline void clearAttributeRow() { m_has_attr_row = false; } + + inline bool hasAttributeColumn() const { return m_has_attr_column; } + inline int attributeColumn() const { return m_attr_column; } + inline void setAttributeColumn(int a) { m_attr_column = a; m_has_attr_column = true; } + inline void clearAttributeColumn() { m_has_attr_column = false; } + + inline bool hasAttributeRowSpan() const { return m_has_attr_rowSpan; } + inline int attributeRowSpan() const { return m_attr_rowSpan; } + inline void setAttributeRowSpan(int a) { m_attr_rowSpan = a; m_has_attr_rowSpan = true; } + inline void clearAttributeRowSpan() { m_has_attr_rowSpan = false; } + + inline bool hasAttributeColSpan() const { return m_has_attr_colSpan; } + inline int attributeColSpan() const { return m_attr_colSpan; } + inline void setAttributeColSpan(int a) { m_attr_colSpan = a; m_has_attr_colSpan = true; } + inline void clearAttributeColSpan() { m_has_attr_colSpan = false; } + + // child element accessors + enum Kind { Unknown = 0, Widget, Layout, Spacer }; + inline Kind kind() const { return m_kind; } + + inline DomWidget* elementWidget() const { return m_widget; } + DomWidget* takeElementWidget(); + void setElementWidget(DomWidget* a); + + inline DomLayout* elementLayout() const { return m_layout; } + DomLayout* takeElementLayout(); + void setElementLayout(DomLayout* a); + + inline DomSpacer* elementSpacer() const { return m_spacer; } + DomSpacer* takeElementSpacer(); + void setElementSpacer(DomSpacer* a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + int m_attr_row; + bool m_has_attr_row; + + int m_attr_column; + bool m_has_attr_column; + + int m_attr_rowSpan; + bool m_has_attr_rowSpan; + + int m_attr_colSpan; + bool m_has_attr_colSpan; + + // child element data + Kind m_kind; + DomWidget* m_widget; + DomLayout* m_layout; + DomSpacer* m_spacer; + + DomLayoutItem(const DomLayoutItem &other); + void operator = (const DomLayoutItem&other); +}; + +class QDESIGNER_UILIB_EXPORT DomRow { +public: + DomRow(); + ~DomRow(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomRow(const DomRow &other); + void operator = (const DomRow&other); +}; + +class QDESIGNER_UILIB_EXPORT DomColumn { +public: + DomColumn(); + ~DomColumn(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomColumn(const DomColumn &other); + void operator = (const DomColumn&other); +}; + +class QDESIGNER_UILIB_EXPORT DomItem { +public: + DomItem(); + ~DomItem(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeRow() const { return m_has_attr_row; } + inline int attributeRow() const { return m_attr_row; } + inline void setAttributeRow(int a) { m_attr_row = a; m_has_attr_row = true; } + inline void clearAttributeRow() { m_has_attr_row = false; } + + inline bool hasAttributeColumn() const { return m_has_attr_column; } + inline int attributeColumn() const { return m_attr_column; } + inline void setAttributeColumn(int a) { m_attr_column = a; m_has_attr_column = true; } + inline void clearAttributeColumn() { m_has_attr_column = false; } + + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementItem() const { return m_item; } + void setElementItem(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + int m_attr_row; + bool m_has_attr_row; + + int m_attr_column; + bool m_has_attr_column; + + // child element data + uint m_children; + QList m_property; + QList m_item; + enum Child { + Property = 1, + Item = 2 + }; + + DomItem(const DomItem &other); + void operator = (const DomItem&other); +}; + +class QDESIGNER_UILIB_EXPORT DomWidget { +public: + DomWidget(); + ~DomWidget(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeClass() const { return m_has_attr_class; } + inline QString attributeClass() const { return m_attr_class; } + inline void setAttributeClass(const QString& a) { m_attr_class = a; m_has_attr_class = true; } + inline void clearAttributeClass() { m_has_attr_class = false; } + + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeNative() const { return m_has_attr_native; } + inline bool attributeNative() const { return m_attr_native; } + inline void setAttributeNative(bool a) { m_attr_native = a; m_has_attr_native = true; } + inline void clearAttributeNative() { m_has_attr_native = false; } + + // child element accessors + inline QStringList elementClass() const { return m_class; } + void setElementClass(const QStringList& a); + + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + + inline QList elementScript() const { return m_script; } + void setElementScript(const QList& a); + + inline QList elementWidgetData() const { return m_widgetData; } + void setElementWidgetData(const QList& a); + + inline QList elementAttribute() const { return m_attribute; } + void setElementAttribute(const QList& a); + + inline QList elementRow() const { return m_row; } + void setElementRow(const QList& a); + + inline QList elementColumn() const { return m_column; } + void setElementColumn(const QList& a); + + inline QList elementItem() const { return m_item; } + void setElementItem(const QList& a); + + inline QList elementLayout() const { return m_layout; } + void setElementLayout(const QList& a); + + inline QList elementWidget() const { return m_widget; } + void setElementWidget(const QList& a); + + inline QList elementAction() const { return m_action; } + void setElementAction(const QList& a); + + inline QList elementActionGroup() const { return m_actionGroup; } + void setElementActionGroup(const QList& a); + + inline QList elementAddAction() const { return m_addAction; } + void setElementAddAction(const QList& a); + + inline QStringList elementZOrder() const { return m_zOrder; } + void setElementZOrder(const QStringList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_class; + bool m_has_attr_class; + + QString m_attr_name; + bool m_has_attr_name; + + bool m_attr_native; + bool m_has_attr_native; + + // child element data + uint m_children; + QStringList m_class; + QList m_property; + QList m_script; + QList m_widgetData; + QList m_attribute; + QList m_row; + QList m_column; + QList m_item; + QList m_layout; + QList m_widget; + QList m_action; + QList m_actionGroup; + QList m_addAction; + QStringList m_zOrder; + enum Child { + Class = 1, + Property = 2, + Script = 4, + WidgetData = 8, + Attribute = 16, + Row = 32, + Column = 64, + Item = 128, + Layout = 256, + Widget = 512, + Action = 1024, + ActionGroup = 2048, + AddAction = 4096, + ZOrder = 8192 + }; + + DomWidget(const DomWidget &other); + void operator = (const DomWidget&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSpacer { +public: + DomSpacer(); + ~DomSpacer(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomSpacer(const DomSpacer &other); + void operator = (const DomSpacer&other); +}; + +class QDESIGNER_UILIB_EXPORT DomColor { +public: + DomColor(); + ~DomColor(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeAlpha() const { return m_has_attr_alpha; } + inline int attributeAlpha() const { return m_attr_alpha; } + inline void setAttributeAlpha(int a) { m_attr_alpha = a; m_has_attr_alpha = true; } + inline void clearAttributeAlpha() { m_has_attr_alpha = false; } + + // child element accessors + inline int elementRed() const { return m_red; } + void setElementRed(int a); + inline bool hasElementRed() const { return m_children & Red; } + void clearElementRed(); + + inline int elementGreen() const { return m_green; } + void setElementGreen(int a); + inline bool hasElementGreen() const { return m_children & Green; } + void clearElementGreen(); + + inline int elementBlue() const { return m_blue; } + void setElementBlue(int a); + inline bool hasElementBlue() const { return m_children & Blue; } + void clearElementBlue(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + int m_attr_alpha; + bool m_has_attr_alpha; + + // child element data + uint m_children; + int m_red; + int m_green; + int m_blue; + enum Child { + Red = 1, + Green = 2, + Blue = 4 + }; + + DomColor(const DomColor &other); + void operator = (const DomColor&other); +}; + +class QDESIGNER_UILIB_EXPORT DomGradientStop { +public: + DomGradientStop(); + ~DomGradientStop(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributePosition() const { return m_has_attr_position; } + inline double attributePosition() const { return m_attr_position; } + inline void setAttributePosition(double a) { m_attr_position = a; m_has_attr_position = true; } + inline void clearAttributePosition() { m_has_attr_position = false; } + + // child element accessors + inline DomColor* elementColor() const { return m_color; } + DomColor* takeElementColor(); + void setElementColor(DomColor* a); + inline bool hasElementColor() const { return m_children & Color; } + void clearElementColor(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + double m_attr_position; + bool m_has_attr_position; + + // child element data + uint m_children; + DomColor* m_color; + enum Child { + Color = 1 + }; + + DomGradientStop(const DomGradientStop &other); + void operator = (const DomGradientStop&other); +}; + +class QDESIGNER_UILIB_EXPORT DomGradient { +public: + DomGradient(); + ~DomGradient(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeStartX() const { return m_has_attr_startX; } + inline double attributeStartX() const { return m_attr_startX; } + inline void setAttributeStartX(double a) { m_attr_startX = a; m_has_attr_startX = true; } + inline void clearAttributeStartX() { m_has_attr_startX = false; } + + inline bool hasAttributeStartY() const { return m_has_attr_startY; } + inline double attributeStartY() const { return m_attr_startY; } + inline void setAttributeStartY(double a) { m_attr_startY = a; m_has_attr_startY = true; } + inline void clearAttributeStartY() { m_has_attr_startY = false; } + + inline bool hasAttributeEndX() const { return m_has_attr_endX; } + inline double attributeEndX() const { return m_attr_endX; } + inline void setAttributeEndX(double a) { m_attr_endX = a; m_has_attr_endX = true; } + inline void clearAttributeEndX() { m_has_attr_endX = false; } + + inline bool hasAttributeEndY() const { return m_has_attr_endY; } + inline double attributeEndY() const { return m_attr_endY; } + inline void setAttributeEndY(double a) { m_attr_endY = a; m_has_attr_endY = true; } + inline void clearAttributeEndY() { m_has_attr_endY = false; } + + inline bool hasAttributeCentralX() const { return m_has_attr_centralX; } + inline double attributeCentralX() const { return m_attr_centralX; } + inline void setAttributeCentralX(double a) { m_attr_centralX = a; m_has_attr_centralX = true; } + inline void clearAttributeCentralX() { m_has_attr_centralX = false; } + + inline bool hasAttributeCentralY() const { return m_has_attr_centralY; } + inline double attributeCentralY() const { return m_attr_centralY; } + inline void setAttributeCentralY(double a) { m_attr_centralY = a; m_has_attr_centralY = true; } + inline void clearAttributeCentralY() { m_has_attr_centralY = false; } + + inline bool hasAttributeFocalX() const { return m_has_attr_focalX; } + inline double attributeFocalX() const { return m_attr_focalX; } + inline void setAttributeFocalX(double a) { m_attr_focalX = a; m_has_attr_focalX = true; } + inline void clearAttributeFocalX() { m_has_attr_focalX = false; } + + inline bool hasAttributeFocalY() const { return m_has_attr_focalY; } + inline double attributeFocalY() const { return m_attr_focalY; } + inline void setAttributeFocalY(double a) { m_attr_focalY = a; m_has_attr_focalY = true; } + inline void clearAttributeFocalY() { m_has_attr_focalY = false; } + + inline bool hasAttributeRadius() const { return m_has_attr_radius; } + inline double attributeRadius() const { return m_attr_radius; } + inline void setAttributeRadius(double a) { m_attr_radius = a; m_has_attr_radius = true; } + inline void clearAttributeRadius() { m_has_attr_radius = false; } + + inline bool hasAttributeAngle() const { return m_has_attr_angle; } + inline double attributeAngle() const { return m_attr_angle; } + inline void setAttributeAngle(double a) { m_attr_angle = a; m_has_attr_angle = true; } + inline void clearAttributeAngle() { m_has_attr_angle = false; } + + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + inline bool hasAttributeSpread() const { return m_has_attr_spread; } + inline QString attributeSpread() const { return m_attr_spread; } + inline void setAttributeSpread(const QString& a) { m_attr_spread = a; m_has_attr_spread = true; } + inline void clearAttributeSpread() { m_has_attr_spread = false; } + + inline bool hasAttributeCoordinateMode() const { return m_has_attr_coordinateMode; } + inline QString attributeCoordinateMode() const { return m_attr_coordinateMode; } + inline void setAttributeCoordinateMode(const QString& a) { m_attr_coordinateMode = a; m_has_attr_coordinateMode = true; } + inline void clearAttributeCoordinateMode() { m_has_attr_coordinateMode = false; } + + // child element accessors + inline QList elementGradientStop() const { return m_gradientStop; } + void setElementGradientStop(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + double m_attr_startX; + bool m_has_attr_startX; + + double m_attr_startY; + bool m_has_attr_startY; + + double m_attr_endX; + bool m_has_attr_endX; + + double m_attr_endY; + bool m_has_attr_endY; + + double m_attr_centralX; + bool m_has_attr_centralX; + + double m_attr_centralY; + bool m_has_attr_centralY; + + double m_attr_focalX; + bool m_has_attr_focalX; + + double m_attr_focalY; + bool m_has_attr_focalY; + + double m_attr_radius; + bool m_has_attr_radius; + + double m_attr_angle; + bool m_has_attr_angle; + + QString m_attr_type; + bool m_has_attr_type; + + QString m_attr_spread; + bool m_has_attr_spread; + + QString m_attr_coordinateMode; + bool m_has_attr_coordinateMode; + + // child element data + uint m_children; + QList m_gradientStop; + enum Child { + GradientStop = 1 + }; + + DomGradient(const DomGradient &other); + void operator = (const DomGradient&other); +}; + +class QDESIGNER_UILIB_EXPORT DomBrush { +public: + DomBrush(); + ~DomBrush(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeBrushStyle() const { return m_has_attr_brushStyle; } + inline QString attributeBrushStyle() const { return m_attr_brushStyle; } + inline void setAttributeBrushStyle(const QString& a) { m_attr_brushStyle = a; m_has_attr_brushStyle = true; } + inline void clearAttributeBrushStyle() { m_has_attr_brushStyle = false; } + + // child element accessors + enum Kind { Unknown = 0, Color, Texture, Gradient }; + inline Kind kind() const { return m_kind; } + + inline DomColor* elementColor() const { return m_color; } + DomColor* takeElementColor(); + void setElementColor(DomColor* a); + + inline DomProperty* elementTexture() const { return m_texture; } + DomProperty* takeElementTexture(); + void setElementTexture(DomProperty* a); + + inline DomGradient* elementGradient() const { return m_gradient; } + DomGradient* takeElementGradient(); + void setElementGradient(DomGradient* a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_brushStyle; + bool m_has_attr_brushStyle; + + // child element data + Kind m_kind; + DomColor* m_color; + DomProperty* m_texture; + DomGradient* m_gradient; + + DomBrush(const DomBrush &other); + void operator = (const DomBrush&other); +}; + +class QDESIGNER_UILIB_EXPORT DomColorRole { +public: + DomColorRole(); + ~DomColorRole(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeRole() const { return m_has_attr_role; } + inline QString attributeRole() const { return m_attr_role; } + inline void setAttributeRole(const QString& a) { m_attr_role = a; m_has_attr_role = true; } + inline void clearAttributeRole() { m_has_attr_role = false; } + + // child element accessors + inline DomBrush* elementBrush() const { return m_brush; } + DomBrush* takeElementBrush(); + void setElementBrush(DomBrush* a); + inline bool hasElementBrush() const { return m_children & Brush; } + void clearElementBrush(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_role; + bool m_has_attr_role; + + // child element data + uint m_children; + DomBrush* m_brush; + enum Child { + Brush = 1 + }; + + DomColorRole(const DomColorRole &other); + void operator = (const DomColorRole&other); +}; + +class QDESIGNER_UILIB_EXPORT DomColorGroup { +public: + DomColorGroup(); + ~DomColorGroup(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementColorRole() const { return m_colorRole; } + void setElementColorRole(const QList& a); + + inline QList elementColor() const { return m_color; } + void setElementColor(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_colorRole; + QList m_color; + enum Child { + ColorRole = 1, + Color = 2 + }; + + DomColorGroup(const DomColorGroup &other); + void operator = (const DomColorGroup&other); +}; + +class QDESIGNER_UILIB_EXPORT DomPalette { +public: + DomPalette(); + ~DomPalette(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline DomColorGroup* elementActive() const { return m_active; } + DomColorGroup* takeElementActive(); + void setElementActive(DomColorGroup* a); + inline bool hasElementActive() const { return m_children & Active; } + void clearElementActive(); + + inline DomColorGroup* elementInactive() const { return m_inactive; } + DomColorGroup* takeElementInactive(); + void setElementInactive(DomColorGroup* a); + inline bool hasElementInactive() const { return m_children & Inactive; } + void clearElementInactive(); + + inline DomColorGroup* elementDisabled() const { return m_disabled; } + DomColorGroup* takeElementDisabled(); + void setElementDisabled(DomColorGroup* a); + inline bool hasElementDisabled() const { return m_children & Disabled; } + void clearElementDisabled(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + DomColorGroup* m_active; + DomColorGroup* m_inactive; + DomColorGroup* m_disabled; + enum Child { + Active = 1, + Inactive = 2, + Disabled = 4 + }; + + DomPalette(const DomPalette &other); + void operator = (const DomPalette&other); +}; + +class QDESIGNER_UILIB_EXPORT DomFont { +public: + DomFont(); + ~DomFont(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QString elementFamily() const { return m_family; } + void setElementFamily(const QString& a); + inline bool hasElementFamily() const { return m_children & Family; } + void clearElementFamily(); + + inline int elementPointSize() const { return m_pointSize; } + void setElementPointSize(int a); + inline bool hasElementPointSize() const { return m_children & PointSize; } + void clearElementPointSize(); + + inline int elementWeight() const { return m_weight; } + void setElementWeight(int a); + inline bool hasElementWeight() const { return m_children & Weight; } + void clearElementWeight(); + + inline bool elementItalic() const { return m_italic; } + void setElementItalic(bool a); + inline bool hasElementItalic() const { return m_children & Italic; } + void clearElementItalic(); + + inline bool elementBold() const { return m_bold; } + void setElementBold(bool a); + inline bool hasElementBold() const { return m_children & Bold; } + void clearElementBold(); + + inline bool elementUnderline() const { return m_underline; } + void setElementUnderline(bool a); + inline bool hasElementUnderline() const { return m_children & Underline; } + void clearElementUnderline(); + + inline bool elementStrikeOut() const { return m_strikeOut; } + void setElementStrikeOut(bool a); + inline bool hasElementStrikeOut() const { return m_children & StrikeOut; } + void clearElementStrikeOut(); + + inline bool elementAntialiasing() const { return m_antialiasing; } + void setElementAntialiasing(bool a); + inline bool hasElementAntialiasing() const { return m_children & Antialiasing; } + void clearElementAntialiasing(); + + inline QString elementStyleStrategy() const { return m_styleStrategy; } + void setElementStyleStrategy(const QString& a); + inline bool hasElementStyleStrategy() const { return m_children & StyleStrategy; } + void clearElementStyleStrategy(); + + inline bool elementKerning() const { return m_kerning; } + void setElementKerning(bool a); + inline bool hasElementKerning() const { return m_children & Kerning; } + void clearElementKerning(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QString m_family; + int m_pointSize; + int m_weight; + bool m_italic; + bool m_bold; + bool m_underline; + bool m_strikeOut; + bool m_antialiasing; + QString m_styleStrategy; + bool m_kerning; + enum Child { + Family = 1, + PointSize = 2, + Weight = 4, + Italic = 8, + Bold = 16, + Underline = 32, + StrikeOut = 64, + Antialiasing = 128, + StyleStrategy = 256, + Kerning = 512 + }; + + DomFont(const DomFont &other); + void operator = (const DomFont&other); +}; + +class QDESIGNER_UILIB_EXPORT DomPoint { +public: + DomPoint(); + ~DomPoint(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementX() const { return m_x; } + void setElementX(int a); + inline bool hasElementX() const { return m_children & X; } + void clearElementX(); + + inline int elementY() const { return m_y; } + void setElementY(int a); + inline bool hasElementY() const { return m_children & Y; } + void clearElementY(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_x; + int m_y; + enum Child { + X = 1, + Y = 2 + }; + + DomPoint(const DomPoint &other); + void operator = (const DomPoint&other); +}; + +class QDESIGNER_UILIB_EXPORT DomRect { +public: + DomRect(); + ~DomRect(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementX() const { return m_x; } + void setElementX(int a); + inline bool hasElementX() const { return m_children & X; } + void clearElementX(); + + inline int elementY() const { return m_y; } + void setElementY(int a); + inline bool hasElementY() const { return m_children & Y; } + void clearElementY(); + + inline int elementWidth() const { return m_width; } + void setElementWidth(int a); + inline bool hasElementWidth() const { return m_children & Width; } + void clearElementWidth(); + + inline int elementHeight() const { return m_height; } + void setElementHeight(int a); + inline bool hasElementHeight() const { return m_children & Height; } + void clearElementHeight(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_x; + int m_y; + int m_width; + int m_height; + enum Child { + X = 1, + Y = 2, + Width = 4, + Height = 8 + }; + + DomRect(const DomRect &other); + void operator = (const DomRect&other); +}; + +class QDESIGNER_UILIB_EXPORT DomLocale { +public: + DomLocale(); + ~DomLocale(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeLanguage() const { return m_has_attr_language; } + inline QString attributeLanguage() const { return m_attr_language; } + inline void setAttributeLanguage(const QString& a) { m_attr_language = a; m_has_attr_language = true; } + inline void clearAttributeLanguage() { m_has_attr_language = false; } + + inline bool hasAttributeCountry() const { return m_has_attr_country; } + inline QString attributeCountry() const { return m_attr_country; } + inline void setAttributeCountry(const QString& a) { m_attr_country = a; m_has_attr_country = true; } + inline void clearAttributeCountry() { m_has_attr_country = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_language; + bool m_has_attr_language; + + QString m_attr_country; + bool m_has_attr_country; + + // child element data + uint m_children; + + DomLocale(const DomLocale &other); + void operator = (const DomLocale&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSizePolicy { +public: + DomSizePolicy(); + ~DomSizePolicy(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeHSizeType() const { return m_has_attr_hSizeType; } + inline QString attributeHSizeType() const { return m_attr_hSizeType; } + inline void setAttributeHSizeType(const QString& a) { m_attr_hSizeType = a; m_has_attr_hSizeType = true; } + inline void clearAttributeHSizeType() { m_has_attr_hSizeType = false; } + + inline bool hasAttributeVSizeType() const { return m_has_attr_vSizeType; } + inline QString attributeVSizeType() const { return m_attr_vSizeType; } + inline void setAttributeVSizeType(const QString& a) { m_attr_vSizeType = a; m_has_attr_vSizeType = true; } + inline void clearAttributeVSizeType() { m_has_attr_vSizeType = false; } + + // child element accessors + inline int elementHSizeType() const { return m_hSizeType; } + void setElementHSizeType(int a); + inline bool hasElementHSizeType() const { return m_children & HSizeType; } + void clearElementHSizeType(); + + inline int elementVSizeType() const { return m_vSizeType; } + void setElementVSizeType(int a); + inline bool hasElementVSizeType() const { return m_children & VSizeType; } + void clearElementVSizeType(); + + inline int elementHorStretch() const { return m_horStretch; } + void setElementHorStretch(int a); + inline bool hasElementHorStretch() const { return m_children & HorStretch; } + void clearElementHorStretch(); + + inline int elementVerStretch() const { return m_verStretch; } + void setElementVerStretch(int a); + inline bool hasElementVerStretch() const { return m_children & VerStretch; } + void clearElementVerStretch(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_hSizeType; + bool m_has_attr_hSizeType; + + QString m_attr_vSizeType; + bool m_has_attr_vSizeType; + + // child element data + uint m_children; + int m_hSizeType; + int m_vSizeType; + int m_horStretch; + int m_verStretch; + enum Child { + HSizeType = 1, + VSizeType = 2, + HorStretch = 4, + VerStretch = 8 + }; + + DomSizePolicy(const DomSizePolicy &other); + void operator = (const DomSizePolicy&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSize { +public: + DomSize(); + ~DomSize(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementWidth() const { return m_width; } + void setElementWidth(int a); + inline bool hasElementWidth() const { return m_children & Width; } + void clearElementWidth(); + + inline int elementHeight() const { return m_height; } + void setElementHeight(int a); + inline bool hasElementHeight() const { return m_children & Height; } + void clearElementHeight(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_width; + int m_height; + enum Child { + Width = 1, + Height = 2 + }; + + DomSize(const DomSize &other); + void operator = (const DomSize&other); +}; + +class QDESIGNER_UILIB_EXPORT DomDate { +public: + DomDate(); + ~DomDate(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementYear() const { return m_year; } + void setElementYear(int a); + inline bool hasElementYear() const { return m_children & Year; } + void clearElementYear(); + + inline int elementMonth() const { return m_month; } + void setElementMonth(int a); + inline bool hasElementMonth() const { return m_children & Month; } + void clearElementMonth(); + + inline int elementDay() const { return m_day; } + void setElementDay(int a); + inline bool hasElementDay() const { return m_children & Day; } + void clearElementDay(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_year; + int m_month; + int m_day; + enum Child { + Year = 1, + Month = 2, + Day = 4 + }; + + DomDate(const DomDate &other); + void operator = (const DomDate&other); +}; + +class QDESIGNER_UILIB_EXPORT DomTime { +public: + DomTime(); + ~DomTime(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementHour() const { return m_hour; } + void setElementHour(int a); + inline bool hasElementHour() const { return m_children & Hour; } + void clearElementHour(); + + inline int elementMinute() const { return m_minute; } + void setElementMinute(int a); + inline bool hasElementMinute() const { return m_children & Minute; } + void clearElementMinute(); + + inline int elementSecond() const { return m_second; } + void setElementSecond(int a); + inline bool hasElementSecond() const { return m_children & Second; } + void clearElementSecond(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_hour; + int m_minute; + int m_second; + enum Child { + Hour = 1, + Minute = 2, + Second = 4 + }; + + DomTime(const DomTime &other); + void operator = (const DomTime&other); +}; + +class QDESIGNER_UILIB_EXPORT DomDateTime { +public: + DomDateTime(); + ~DomDateTime(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementHour() const { return m_hour; } + void setElementHour(int a); + inline bool hasElementHour() const { return m_children & Hour; } + void clearElementHour(); + + inline int elementMinute() const { return m_minute; } + void setElementMinute(int a); + inline bool hasElementMinute() const { return m_children & Minute; } + void clearElementMinute(); + + inline int elementSecond() const { return m_second; } + void setElementSecond(int a); + inline bool hasElementSecond() const { return m_children & Second; } + void clearElementSecond(); + + inline int elementYear() const { return m_year; } + void setElementYear(int a); + inline bool hasElementYear() const { return m_children & Year; } + void clearElementYear(); + + inline int elementMonth() const { return m_month; } + void setElementMonth(int a); + inline bool hasElementMonth() const { return m_children & Month; } + void clearElementMonth(); + + inline int elementDay() const { return m_day; } + void setElementDay(int a); + inline bool hasElementDay() const { return m_children & Day; } + void clearElementDay(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_hour; + int m_minute; + int m_second; + int m_year; + int m_month; + int m_day; + enum Child { + Hour = 1, + Minute = 2, + Second = 4, + Year = 8, + Month = 16, + Day = 32 + }; + + DomDateTime(const DomDateTime &other); + void operator = (const DomDateTime&other); +}; + +class QDESIGNER_UILIB_EXPORT DomStringList { +public: + DomStringList(); + ~DomStringList(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QStringList elementString() const { return m_string; } + void setElementString(const QStringList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QStringList m_string; + enum Child { + String = 1 + }; + + DomStringList(const DomStringList &other); + void operator = (const DomStringList&other); +}; + +class QDESIGNER_UILIB_EXPORT DomResourcePixmap { +public: + DomResourcePixmap(); + ~DomResourcePixmap(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeResource() const { return m_has_attr_resource; } + inline QString attributeResource() const { return m_attr_resource; } + inline void setAttributeResource(const QString& a) { m_attr_resource = a; m_has_attr_resource = true; } + inline void clearAttributeResource() { m_has_attr_resource = false; } + + inline bool hasAttributeAlias() const { return m_has_attr_alias; } + inline QString attributeAlias() const { return m_attr_alias; } + inline void setAttributeAlias(const QString& a) { m_attr_alias = a; m_has_attr_alias = true; } + inline void clearAttributeAlias() { m_has_attr_alias = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_resource; + bool m_has_attr_resource; + + QString m_attr_alias; + bool m_has_attr_alias; + + // child element data + uint m_children; + + DomResourcePixmap(const DomResourcePixmap &other); + void operator = (const DomResourcePixmap&other); +}; + +class QDESIGNER_UILIB_EXPORT DomResourceIcon { +public: + DomResourceIcon(); + ~DomResourceIcon(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeResource() const { return m_has_attr_resource; } + inline QString attributeResource() const { return m_attr_resource; } + inline void setAttributeResource(const QString& a) { m_attr_resource = a; m_has_attr_resource = true; } + inline void clearAttributeResource() { m_has_attr_resource = false; } + + // child element accessors + inline DomResourcePixmap* elementNormalOff() const { return m_normalOff; } + DomResourcePixmap* takeElementNormalOff(); + void setElementNormalOff(DomResourcePixmap* a); + inline bool hasElementNormalOff() const { return m_children & NormalOff; } + void clearElementNormalOff(); + + inline DomResourcePixmap* elementNormalOn() const { return m_normalOn; } + DomResourcePixmap* takeElementNormalOn(); + void setElementNormalOn(DomResourcePixmap* a); + inline bool hasElementNormalOn() const { return m_children & NormalOn; } + void clearElementNormalOn(); + + inline DomResourcePixmap* elementDisabledOff() const { return m_disabledOff; } + DomResourcePixmap* takeElementDisabledOff(); + void setElementDisabledOff(DomResourcePixmap* a); + inline bool hasElementDisabledOff() const { return m_children & DisabledOff; } + void clearElementDisabledOff(); + + inline DomResourcePixmap* elementDisabledOn() const { return m_disabledOn; } + DomResourcePixmap* takeElementDisabledOn(); + void setElementDisabledOn(DomResourcePixmap* a); + inline bool hasElementDisabledOn() const { return m_children & DisabledOn; } + void clearElementDisabledOn(); + + inline DomResourcePixmap* elementActiveOff() const { return m_activeOff; } + DomResourcePixmap* takeElementActiveOff(); + void setElementActiveOff(DomResourcePixmap* a); + inline bool hasElementActiveOff() const { return m_children & ActiveOff; } + void clearElementActiveOff(); + + inline DomResourcePixmap* elementActiveOn() const { return m_activeOn; } + DomResourcePixmap* takeElementActiveOn(); + void setElementActiveOn(DomResourcePixmap* a); + inline bool hasElementActiveOn() const { return m_children & ActiveOn; } + void clearElementActiveOn(); + + inline DomResourcePixmap* elementSelectedOff() const { return m_selectedOff; } + DomResourcePixmap* takeElementSelectedOff(); + void setElementSelectedOff(DomResourcePixmap* a); + inline bool hasElementSelectedOff() const { return m_children & SelectedOff; } + void clearElementSelectedOff(); + + inline DomResourcePixmap* elementSelectedOn() const { return m_selectedOn; } + DomResourcePixmap* takeElementSelectedOn(); + void setElementSelectedOn(DomResourcePixmap* a); + inline bool hasElementSelectedOn() const { return m_children & SelectedOn; } + void clearElementSelectedOn(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_resource; + bool m_has_attr_resource; + + // child element data + uint m_children; + DomResourcePixmap* m_normalOff; + DomResourcePixmap* m_normalOn; + DomResourcePixmap* m_disabledOff; + DomResourcePixmap* m_disabledOn; + DomResourcePixmap* m_activeOff; + DomResourcePixmap* m_activeOn; + DomResourcePixmap* m_selectedOff; + DomResourcePixmap* m_selectedOn; + enum Child { + NormalOff = 1, + NormalOn = 2, + DisabledOff = 4, + DisabledOn = 8, + ActiveOff = 16, + ActiveOn = 32, + SelectedOff = 64, + SelectedOn = 128 + }; + + DomResourceIcon(const DomResourceIcon &other); + void operator = (const DomResourceIcon&other); +}; + +class QDESIGNER_UILIB_EXPORT DomString { +public: + DomString(); + ~DomString(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeNotr() const { return m_has_attr_notr; } + inline QString attributeNotr() const { return m_attr_notr; } + inline void setAttributeNotr(const QString& a) { m_attr_notr = a; m_has_attr_notr = true; } + inline void clearAttributeNotr() { m_has_attr_notr = false; } + + inline bool hasAttributeComment() const { return m_has_attr_comment; } + inline QString attributeComment() const { return m_attr_comment; } + inline void setAttributeComment(const QString& a) { m_attr_comment = a; m_has_attr_comment = true; } + inline void clearAttributeComment() { m_has_attr_comment = false; } + + inline bool hasAttributeExtraComment() const { return m_has_attr_extraComment; } + inline QString attributeExtraComment() const { return m_attr_extraComment; } + inline void setAttributeExtraComment(const QString& a) { m_attr_extraComment = a; m_has_attr_extraComment = true; } + inline void clearAttributeExtraComment() { m_has_attr_extraComment = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_notr; + bool m_has_attr_notr; + + QString m_attr_comment; + bool m_has_attr_comment; + + QString m_attr_extraComment; + bool m_has_attr_extraComment; + + // child element data + uint m_children; + + DomString(const DomString &other); + void operator = (const DomString&other); +}; + +class QDESIGNER_UILIB_EXPORT DomPointF { +public: + DomPointF(); + ~DomPointF(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline double elementX() const { return m_x; } + void setElementX(double a); + inline bool hasElementX() const { return m_children & X; } + void clearElementX(); + + inline double elementY() const { return m_y; } + void setElementY(double a); + inline bool hasElementY() const { return m_children & Y; } + void clearElementY(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + double m_x; + double m_y; + enum Child { + X = 1, + Y = 2 + }; + + DomPointF(const DomPointF &other); + void operator = (const DomPointF&other); +}; + +class QDESIGNER_UILIB_EXPORT DomRectF { +public: + DomRectF(); + ~DomRectF(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline double elementX() const { return m_x; } + void setElementX(double a); + inline bool hasElementX() const { return m_children & X; } + void clearElementX(); + + inline double elementY() const { return m_y; } + void setElementY(double a); + inline bool hasElementY() const { return m_children & Y; } + void clearElementY(); + + inline double elementWidth() const { return m_width; } + void setElementWidth(double a); + inline bool hasElementWidth() const { return m_children & Width; } + void clearElementWidth(); + + inline double elementHeight() const { return m_height; } + void setElementHeight(double a); + inline bool hasElementHeight() const { return m_children & Height; } + void clearElementHeight(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + double m_x; + double m_y; + double m_width; + double m_height; + enum Child { + X = 1, + Y = 2, + Width = 4, + Height = 8 + }; + + DomRectF(const DomRectF &other); + void operator = (const DomRectF&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSizeF { +public: + DomSizeF(); + ~DomSizeF(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline double elementWidth() const { return m_width; } + void setElementWidth(double a); + inline bool hasElementWidth() const { return m_children & Width; } + void clearElementWidth(); + + inline double elementHeight() const { return m_height; } + void setElementHeight(double a); + inline bool hasElementHeight() const { return m_children & Height; } + void clearElementHeight(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + double m_width; + double m_height; + enum Child { + Width = 1, + Height = 2 + }; + + DomSizeF(const DomSizeF &other); + void operator = (const DomSizeF&other); +}; + +class QDESIGNER_UILIB_EXPORT DomChar { +public: + DomChar(); + ~DomChar(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline int elementUnicode() const { return m_unicode; } + void setElementUnicode(int a); + inline bool hasElementUnicode() const { return m_children & Unicode; } + void clearElementUnicode(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + int m_unicode; + enum Child { + Unicode = 1 + }; + + DomChar(const DomChar &other); + void operator = (const DomChar&other); +}; + +class QDESIGNER_UILIB_EXPORT DomUrl { +public: + DomUrl(); + ~DomUrl(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline DomString* elementString() const { return m_string; } + DomString* takeElementString(); + void setElementString(DomString* a); + inline bool hasElementString() const { return m_children & String; } + void clearElementString(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + DomString* m_string; + enum Child { + String = 1 + }; + + DomUrl(const DomUrl &other); + void operator = (const DomUrl&other); +}; + +class QDESIGNER_UILIB_EXPORT DomProperty { +public: + DomProperty(); + ~DomProperty(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeStdset() const { return m_has_attr_stdset; } + inline int attributeStdset() const { return m_attr_stdset; } + inline void setAttributeStdset(int a) { m_attr_stdset = a; m_has_attr_stdset = true; } + inline void clearAttributeStdset() { m_has_attr_stdset = false; } + + // child element accessors + enum Kind { Unknown = 0, Bool, Color, Cstring, Cursor, CursorShape, Enum, Font, IconSet, Pixmap, Palette, Point, Rect, Set, Locale, SizePolicy, Size, String, StringList, Number, Float, Double, Date, Time, DateTime, PointF, RectF, SizeF, LongLong, Char, Url, UInt, ULongLong, Brush }; + inline Kind kind() const { return m_kind; } + + inline QString elementBool() const { return m_bool; } + void setElementBool(const QString& a); + + inline DomColor* elementColor() const { return m_color; } + DomColor* takeElementColor(); + void setElementColor(DomColor* a); + + inline QString elementCstring() const { return m_cstring; } + void setElementCstring(const QString& a); + + inline int elementCursor() const { return m_cursor; } + void setElementCursor(int a); + + inline QString elementCursorShape() const { return m_cursorShape; } + void setElementCursorShape(const QString& a); + + inline QString elementEnum() const { return m_enum; } + void setElementEnum(const QString& a); + + inline DomFont* elementFont() const { return m_font; } + DomFont* takeElementFont(); + void setElementFont(DomFont* a); + + inline DomResourceIcon* elementIconSet() const { return m_iconSet; } + DomResourceIcon* takeElementIconSet(); + void setElementIconSet(DomResourceIcon* a); + + inline DomResourcePixmap* elementPixmap() const { return m_pixmap; } + DomResourcePixmap* takeElementPixmap(); + void setElementPixmap(DomResourcePixmap* a); + + inline DomPalette* elementPalette() const { return m_palette; } + DomPalette* takeElementPalette(); + void setElementPalette(DomPalette* a); + + inline DomPoint* elementPoint() const { return m_point; } + DomPoint* takeElementPoint(); + void setElementPoint(DomPoint* a); + + inline DomRect* elementRect() const { return m_rect; } + DomRect* takeElementRect(); + void setElementRect(DomRect* a); + + inline QString elementSet() const { return m_set; } + void setElementSet(const QString& a); + + inline DomLocale* elementLocale() const { return m_locale; } + DomLocale* takeElementLocale(); + void setElementLocale(DomLocale* a); + + inline DomSizePolicy* elementSizePolicy() const { return m_sizePolicy; } + DomSizePolicy* takeElementSizePolicy(); + void setElementSizePolicy(DomSizePolicy* a); + + inline DomSize* elementSize() const { return m_size; } + DomSize* takeElementSize(); + void setElementSize(DomSize* a); + + inline DomString* elementString() const { return m_string; } + DomString* takeElementString(); + void setElementString(DomString* a); + + inline DomStringList* elementStringList() const { return m_stringList; } + DomStringList* takeElementStringList(); + void setElementStringList(DomStringList* a); + + inline int elementNumber() const { return m_number; } + void setElementNumber(int a); + + inline float elementFloat() const { return m_float; } + void setElementFloat(float a); + + inline double elementDouble() const { return m_double; } + void setElementDouble(double a); + + inline DomDate* elementDate() const { return m_date; } + DomDate* takeElementDate(); + void setElementDate(DomDate* a); + + inline DomTime* elementTime() const { return m_time; } + DomTime* takeElementTime(); + void setElementTime(DomTime* a); + + inline DomDateTime* elementDateTime() const { return m_dateTime; } + DomDateTime* takeElementDateTime(); + void setElementDateTime(DomDateTime* a); + + inline DomPointF* elementPointF() const { return m_pointF; } + DomPointF* takeElementPointF(); + void setElementPointF(DomPointF* a); + + inline DomRectF* elementRectF() const { return m_rectF; } + DomRectF* takeElementRectF(); + void setElementRectF(DomRectF* a); + + inline DomSizeF* elementSizeF() const { return m_sizeF; } + DomSizeF* takeElementSizeF(); + void setElementSizeF(DomSizeF* a); + + inline qlonglong elementLongLong() const { return m_longLong; } + void setElementLongLong(qlonglong a); + + inline DomChar* elementChar() const { return m_char; } + DomChar* takeElementChar(); + void setElementChar(DomChar* a); + + inline DomUrl* elementUrl() const { return m_url; } + DomUrl* takeElementUrl(); + void setElementUrl(DomUrl* a); + + inline uint elementUInt() const { return m_UInt; } + void setElementUInt(uint a); + + inline qulonglong elementULongLong() const { return m_uLongLong; } + void setElementULongLong(qulonglong a); + + inline DomBrush* elementBrush() const { return m_brush; } + DomBrush* takeElementBrush(); + void setElementBrush(DomBrush* a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + int m_attr_stdset; + bool m_has_attr_stdset; + + // child element data + Kind m_kind; + QString m_bool; + DomColor* m_color; + QString m_cstring; + int m_cursor; + QString m_cursorShape; + QString m_enum; + DomFont* m_font; + DomResourceIcon* m_iconSet; + DomResourcePixmap* m_pixmap; + DomPalette* m_palette; + DomPoint* m_point; + DomRect* m_rect; + QString m_set; + DomLocale* m_locale; + DomSizePolicy* m_sizePolicy; + DomSize* m_size; + DomString* m_string; + DomStringList* m_stringList; + int m_number; + float m_float; + double m_double; + DomDate* m_date; + DomTime* m_time; + DomDateTime* m_dateTime; + DomPointF* m_pointF; + DomRectF* m_rectF; + DomSizeF* m_sizeF; + qlonglong m_longLong; + DomChar* m_char; + DomUrl* m_url; + uint m_UInt; + qulonglong m_uLongLong; + DomBrush* m_brush; + + DomProperty(const DomProperty &other); + void operator = (const DomProperty&other); +}; + +class QDESIGNER_UILIB_EXPORT DomConnections { +public: + DomConnections(); + ~DomConnections(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementConnection() const { return m_connection; } + void setElementConnection(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_connection; + enum Child { + Connection = 1 + }; + + DomConnections(const DomConnections &other); + void operator = (const DomConnections&other); +}; + +class QDESIGNER_UILIB_EXPORT DomConnection { +public: + DomConnection(); + ~DomConnection(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QString elementSender() const { return m_sender; } + void setElementSender(const QString& a); + inline bool hasElementSender() const { return m_children & Sender; } + void clearElementSender(); + + inline QString elementSignal() const { return m_signal; } + void setElementSignal(const QString& a); + inline bool hasElementSignal() const { return m_children & Signal; } + void clearElementSignal(); + + inline QString elementReceiver() const { return m_receiver; } + void setElementReceiver(const QString& a); + inline bool hasElementReceiver() const { return m_children & Receiver; } + void clearElementReceiver(); + + inline QString elementSlot() const { return m_slot; } + void setElementSlot(const QString& a); + inline bool hasElementSlot() const { return m_children & Slot; } + void clearElementSlot(); + + inline DomConnectionHints* elementHints() const { return m_hints; } + DomConnectionHints* takeElementHints(); + void setElementHints(DomConnectionHints* a); + inline bool hasElementHints() const { return m_children & Hints; } + void clearElementHints(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QString m_sender; + QString m_signal; + QString m_receiver; + QString m_slot; + DomConnectionHints* m_hints; + enum Child { + Sender = 1, + Signal = 2, + Receiver = 4, + Slot = 8, + Hints = 16 + }; + + DomConnection(const DomConnection &other); + void operator = (const DomConnection&other); +}; + +class QDESIGNER_UILIB_EXPORT DomConnectionHints { +public: + DomConnectionHints(); + ~DomConnectionHints(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementHint() const { return m_hint; } + void setElementHint(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_hint; + enum Child { + Hint = 1 + }; + + DomConnectionHints(const DomConnectionHints &other); + void operator = (const DomConnectionHints&other); +}; + +class QDESIGNER_UILIB_EXPORT DomConnectionHint { +public: + DomConnectionHint(); + ~DomConnectionHint(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + // child element accessors + inline int elementX() const { return m_x; } + void setElementX(int a); + inline bool hasElementX() const { return m_children & X; } + void clearElementX(); + + inline int elementY() const { return m_y; } + void setElementY(int a); + inline bool hasElementY() const { return m_children & Y; } + void clearElementY(); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_type; + bool m_has_attr_type; + + // child element data + uint m_children; + int m_x; + int m_y; + enum Child { + X = 1, + Y = 2 + }; + + DomConnectionHint(const DomConnectionHint &other); + void operator = (const DomConnectionHint&other); +}; + +class QDESIGNER_UILIB_EXPORT DomScript { +public: + DomScript(); + ~DomScript(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeSource() const { return m_has_attr_source; } + inline QString attributeSource() const { return m_attr_source; } + inline void setAttributeSource(const QString& a) { m_attr_source = a; m_has_attr_source = true; } + inline void clearAttributeSource() { m_has_attr_source = false; } + + inline bool hasAttributeLanguage() const { return m_has_attr_language; } + inline QString attributeLanguage() const { return m_attr_language; } + inline void setAttributeLanguage(const QString& a) { m_attr_language = a; m_has_attr_language = true; } + inline void clearAttributeLanguage() { m_has_attr_language = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_source; + bool m_has_attr_source; + + QString m_attr_language; + bool m_has_attr_language; + + // child element data + uint m_children; + + DomScript(const DomScript &other); + void operator = (const DomScript&other); +}; + +class QDESIGNER_UILIB_EXPORT DomWidgetData { +public: + DomWidgetData(); + ~DomWidgetData(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomWidgetData(const DomWidgetData &other); + void operator = (const DomWidgetData&other); +}; + +class QDESIGNER_UILIB_EXPORT DomDesignerData { +public: + DomDesignerData(); + ~DomDesignerData(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementProperty() const { return m_property; } + void setElementProperty(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_property; + enum Child { + Property = 1 + }; + + DomDesignerData(const DomDesignerData &other); + void operator = (const DomDesignerData&other); +}; + +class QDESIGNER_UILIB_EXPORT DomSlots { +public: + DomSlots(); + ~DomSlots(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QStringList elementSignal() const { return m_signal; } + void setElementSignal(const QStringList& a); + + inline QStringList elementSlot() const { return m_slot; } + void setElementSlot(const QStringList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QStringList m_signal; + QStringList m_slot; + enum Child { + Signal = 1, + Slot = 2 + }; + + DomSlots(const DomSlots &other); + void operator = (const DomSlots&other); +}; + +class QDESIGNER_UILIB_EXPORT DomPropertySpecifications { +public: + DomPropertySpecifications(); + ~DomPropertySpecifications(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList elementStringpropertyspecification() const { return m_stringpropertyspecification; } + void setElementStringpropertyspecification(const QList& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList m_stringpropertyspecification; + enum Child { + Stringpropertyspecification = 1 + }; + + DomPropertySpecifications(const DomPropertySpecifications &other); + void operator = (const DomPropertySpecifications&other); +}; + +class QDESIGNER_UILIB_EXPORT DomStringPropertySpecification { +public: + DomStringPropertySpecification(); + ~DomStringPropertySpecification(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + inline bool hasAttributeNotr() const { return m_has_attr_notr; } + inline QString attributeNotr() const { return m_attr_notr; } + inline void setAttributeNotr(const QString& a) { m_attr_notr = a; m_has_attr_notr = true; } + inline void clearAttributeNotr() { m_has_attr_notr = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_type; + bool m_has_attr_type; + + QString m_attr_notr; + bool m_has_attr_notr; + + // child element data + uint m_children; + + DomStringPropertySpecification(const DomStringPropertySpecification &other); + void operator = (const DomStringPropertySpecification&other); +}; + + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // UI4_H diff --git a/designer/lib/uilib/uilib.pri b/designer/lib/uilib/uilib.pri new file mode 100644 index 0000000..2072fbc --- /dev/null +++ b/designer/lib/uilib/uilib.pri @@ -0,0 +1,32 @@ + +INCLUDEPATH += $$PWD + +DEFINES += QT_DESIGNER +!contains(QT_CONFIG, script): DEFINES += QT_FORMBUILDER_NO_SCRIPT + +# Input +HEADERS += \ + $$PWD/ui4_p.h \ + $$PWD/abstractformbuilder.h \ + $$PWD/formbuilder.h \ + $$PWD/container.h \ + $$PWD/customwidget.h \ + $$PWD/properties_p.h \ + $$PWD/formbuilderextra_p.h \ + $$PWD/resourcebuilder_p.h \ + $$PWD/textbuilder_p.h + +SOURCES += \ + $$PWD/abstractformbuilder.cpp \ + $$PWD/formbuilder.cpp \ + $$PWD/ui4.cpp \ + $$PWD/properties.cpp \ + $$PWD/formbuilderextra.cpp \ + $$PWD/resourcebuilder.cpp \ + $$PWD/textbuilder.cpp + +!contains(DEFINES, QT_FORMBUILDER_NO_SCRIPT) { + QT += script + HEADERS += $$PWD/formscriptrunner_p.h + SOURCES += $$PWD/formscriptrunner.cpp +} diff --git a/designer/lib/uilib/uilib_global.h b/designer/lib/uilib/uilib_global.h new file mode 100644 index 0000000..a86f5de --- /dev/null +++ b/designer/lib/uilib/uilib_global.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UILIB_GLOBAL_H +#define UILIB_GLOBAL_H + +#include + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +#define QDESIGNER_UILIB_EXTERN Q_DECL_EXPORT +#define QDESIGNER_UILIB_IMPORT Q_DECL_IMPORT + +#ifdef QT_DESIGNER_STATIC +# define QDESIGNER_UILIB_EXPORT +#elif defined(QDESIGNER_UILIB_LIBRARY) +# define QDESIGNER_UILIB_EXPORT QDESIGNER_UILIB_EXTERN +#else +# define QDESIGNER_UILIB_EXPORT QDESIGNER_UILIB_IMPORT +#endif + +QT_END_NAMESPACE +QT_END_HEADER + +#endif // UILIB_GLOBAL_H diff --git a/designer/lib/uilib/widgets.table b/designer/lib/uilib/widgets.table new file mode 100644 index 0000000..9b87278 --- /dev/null +++ b/designer/lib/uilib/widgets.table @@ -0,0 +1,148 @@ + +#ifndef DECLARE_WIDGET_1 +# define DECLARE_WIDGET_1(a,b) DECLARE_WIDGET(a,b) +#endif + +// widgets +#ifndef QT_NO_CHECKBOX +DECLARE_WIDGET(QCheckBox, "text") +#endif +#ifndef QT_NO_COMBOBOX +DECLARE_WIDGET(QComboBox, "") +#endif +#ifndef QT_NO_DATETIMEEDIT +DECLARE_WIDGET(QDateTimeEdit, "") +#endif +#ifndef QT_NO_DATEEDIT +DECLARE_WIDGET(QDateEdit, "") +#endif +#ifndef QT_NO_TIMEEDIT +DECLARE_WIDGET(QTimeEdit, "") +#endif +#ifndef QT_NO_DIAL +DECLARE_WIDGET(QDial, "") +#endif +DECLARE_WIDGET(QDialog, "") +#ifndef QT_NO_DOCKWIDGET +DECLARE_WIDGET(QDockWidget, "") +#endif +DECLARE_WIDGET(QFrame, "") +#ifndef QT_NO_GROUPBOX +DECLARE_WIDGET(QGroupBox, "") +#endif +#ifndef QT_NO_SCROLLAREA +DECLARE_WIDGET(QScrollArea, "") +#endif +#ifndef QT_NO_LCDNUMBER +DECLARE_WIDGET(QLCDNumber, "") +#endif +DECLARE_WIDGET(QLabel, "") +#ifndef QT_NO_LINEEDIT +DECLARE_WIDGET(QLineEdit, "") +#endif +#ifndef QT_NO_LISTVIEW +DECLARE_WIDGET(QListView, "") +#endif +#ifndef QT_NO_LISTWIDGET +DECLARE_WIDGET(QListWidget, "") +#endif +#ifndef QT_NO_MAINWINDOW +DECLARE_WIDGET(QMainWindow, "") +#endif +#ifndef QT_NO_MDIAREA +DECLARE_WIDGET(QMdiArea, "") +#endif +#ifndef QT_NO_MENU +DECLARE_WIDGET(QMenu, "") +#endif +#ifndef QT_NO_MENUBAR +DECLARE_WIDGET(QMenuBar, "") +#endif +#ifndef QT_NO_PROGRESSBAR +DECLARE_WIDGET(QProgressBar, "") +#endif +DECLARE_WIDGET(QPushButton, "text") +DECLARE_WIDGET(QRadioButton, "text") +DECLARE_WIDGET(QCommandLinkButton, "text") +#ifndef QT_NO_SCROLLBAR +DECLARE_WIDGET(QScrollBar, "") +#endif +#ifndef QT_NO_SLIDER +DECLARE_WIDGET(QSlider, "") +#endif +#ifndef QT_NO_SPINBOX +DECLARE_WIDGET(QSpinBox, "") +DECLARE_WIDGET(QDoubleSpinBox, "") +#endif +#ifndef QT_NO_TABWIDGET +DECLARE_WIDGET(QTabWidget, "") +#endif +#ifndef QT_NO_TABLEVIEW +DECLARE_WIDGET(QTableView, "") +#endif +#ifndef QT_NO_TABLEWIDGET +DECLARE_WIDGET(QTableWidget, "") +#endif +#ifndef QT_NO_TEXTBROWSER +DECLARE_WIDGET(QTextBrowser, "") +#endif +#ifndef QT_NO_TEXTEDIT +DECLARE_WIDGET(QTextEdit, "") +DECLARE_WIDGET(QPlainTextEdit, "") +#endif +#ifndef QT_NO_TOOLBAR +DECLARE_WIDGET(QToolBar, "") +#endif +#ifndef QT_NO_TOOLBOX +DECLARE_WIDGET(QToolBox, "") +#endif +#ifndef QT_NO_TOOLBUTTON +DECLARE_WIDGET(QToolButton, "text") +#endif +#ifndef QT_NO_TREEVIEW +DECLARE_WIDGET(QTreeView, "") +#endif +#ifndef QT_NO_TREEWIDGET +DECLARE_WIDGET(QTreeWidget, "") +#endif +DECLARE_WIDGET(QWidget, "") +#ifndef QT_NO_WORKSPACE +DECLARE_WIDGET(QWorkspace, "") +#endif +#ifndef QT_NO_SPLITTER +DECLARE_WIDGET(QSplitter, "") +#endif +#ifndef QT_NO_STACKEDWIDGET +DECLARE_WIDGET(QStackedWidget, "") +#endif +#ifndef QT_NO_STATUSBAR +DECLARE_WIDGET(QStatusBar, "") +#endif +DECLARE_WIDGET(QDialogButtonBox, "") +#ifndef QT_NO_FONTCOMBOBOX +DECLARE_WIDGET(QFontComboBox, "") +#endif +#ifndef QT_NO_CALENDARWIDGET +DECLARE_WIDGET(QCalendarWidget, "") +#endif +#ifndef QT_NO_COLUMNVIEW +DECLARE_WIDGET(QColumnView, "") +#endif + +#ifndef QT_NO_WIZARD +DECLARE_WIDGET(QWizard, "") +DECLARE_WIDGET(QWizardPage, "") +#endif + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW +DECLARE_WIDGET_1(QGraphicsView, "") +#endif + +// layouts +DECLARE_LAYOUT(QGridLayout, "") +DECLARE_LAYOUT(QHBoxLayout, "") +DECLARE_LAYOUT(QStackedLayout, "") +DECLARE_LAYOUT(QVBoxLayout, "") +#ifndef QT_NO_FORMLAYOUT +DECLARE_LAYOUT(QFormLayout, "") +#endif diff --git a/designer/readme.txt b/designer/readme.txt new file mode 100644 index 0000000..78653a9 --- /dev/null +++ b/designer/readme.txt @@ -0,0 +1,3 @@ +双击运行designer目录下的designer.pro文件,编译运行即可。 +PS:只支持Qt5以下的版本。 +qdesigner.cpp最后一行代码改成了启动程序后自动加载qui.ui \ No newline at end of file diff --git a/netserver/api/api.pri b/netserver/api/api.pri new file mode 100644 index 0000000..3cf2854 --- /dev/null +++ b/netserver/api/api.pri @@ -0,0 +1,11 @@ +HEADERS += \ + $$PWD/app.h \ + $$PWD/quiwidget.h \ + $$PWD/tcpserver1.h \ + $$PWD/tcpserver2.h + +SOURCES += \ + $$PWD/app.cpp \ + $$PWD/quiwidget.cpp \ + $$PWD/tcpserver1.cpp \ + $$PWD/tcpserver2.cpp diff --git a/netserver/api/app.cpp b/netserver/api/app.cpp new file mode 100644 index 0000000..456477c --- /dev/null +++ b/netserver/api/app.cpp @@ -0,0 +1,94 @@ +锘#include "app.h" +#include "quiwidget.h" + +QString App::ConfigFile = "config.ini"; + +int App::ListenPort1 = 6907; +int App::CmdStart1 = 76; +int App::CmdLen1 = 12; +bool App::HexData1 = false; +int App::ListenPort2 = 6908; +int App::CmdStart2 = 76; +int App::CmdLen2 = 12; +bool App::HexData2 = false; + +void App::readConfig() +{ + if (!checkConfig()) { + return; + } + + QSettings set(App::ConfigFile, QSettings::IniFormat); + + set.beginGroup("AppConfig"); + App::ListenPort1 = set.value("ListenPort1").toInt(); + App::CmdStart1 = set.value("CmdStart1").toInt(); + App::CmdLen1 = set.value("CmdLen1").toInt(); + App::HexData1 = set.value("HexData1").toBool(); + App::ListenPort2 = set.value("ListenPort2").toInt(); + App::CmdStart2 = set.value("CmdStart2").toInt(); + App::CmdLen2 = set.value("CmdLen2").toInt(); + App::HexData2 = set.value("HexData2").toBool(); + set.endGroup(); +} + +void App::writeConfig() +{ + QSettings set(App::ConfigFile, QSettings::IniFormat); + + set.beginGroup("AppConfig"); + set.setValue("ListenPort1", App::ListenPort1); + set.setValue("CmdStart1", App::CmdStart1); + set.setValue("CmdLen1", App::CmdLen1); + set.setValue("HexData1", App::HexData1); + set.setValue("ListenPort2", App::ListenPort2); + set.setValue("CmdStart2", App::CmdStart2); + set.setValue("CmdLen2", App::CmdLen2); + set.setValue("HexData2", App::HexData2); + set.endGroup(); +} + +void App::newConfig() +{ +#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) +#endif + writeConfig(); +} + +bool App::checkConfig() +{ + //濡傛灉閰嶇疆鏂囦欢澶у皬涓0,鍒欎互鍒濆鍊肩户缁繍琛,骞剁敓鎴愰厤缃枃浠 + QFile file(App::ConfigFile); + if (file.size() == 0) { + newConfig(); + return false; + } + + //濡傛灉閰嶇疆鏂囦欢涓嶅畬鏁,鍒欎互鍒濆鍊肩户缁繍琛,骞剁敓鎴愰厤缃枃浠 + if (file.open(QFile::ReadOnly)) { + bool ok = true; + while (!file.atEnd()) { + QString line = file.readLine(); + line = line.replace("\r", ""); + line = line.replace("\n", ""); + QStringList list = line.split("="); + + if (list.count() == 2) { + if (list.at(1) == "") { + ok = false; + break; + } + } + } + + if (!ok) { + newConfig(); + return false; + } + } else { + newConfig(); + return false; + } + + return true; +} diff --git a/netserver/api/app.h b/netserver/api/app.h new file mode 100644 index 0000000..0ec3b10 --- /dev/null +++ b/netserver/api/app.h @@ -0,0 +1,29 @@ +锘#ifndef APP_H +#define APP_H + +#include "head.h" + +class App +{ +public: + static QString ConfigFile; //閰嶇疆鏂囦欢璺緞 + + static int ListenPort1; //鐩戝惉绔彛1 + static int CmdStart1; //鏍囪瘑绗1寮濮 + static int CmdLen1; //鏍囪瘑绗1闀垮害 + static bool HexData1; //16杩涘埗鏄剧ず + + static int ListenPort2; //鐩戝惉绔彛2 + static int CmdStart2; //鏍囪瘑绗2寮濮 + static int CmdLen2; //鏍囪瘑绗2闀垮害 + static bool HexData2; //16杩涘埗鏄剧ず + + //璇诲啓閰嶇疆鍙傛暟鍙婂叾浠栨搷浣 + static void readConfig(); //璇诲彇閰嶇疆鍙傛暟 + static void writeConfig(); //鍐欏叆閰嶇疆鍙傛暟 + static void newConfig(); //浠ュ垵濮嬪兼柊寤洪厤缃枃浠 + static bool checkConfig(); //鏍¢獙閰嶇疆鏂囦欢 + +}; + +#endif // APP_H diff --git a/netserver/api/quiwidget.cpp b/netserver/api/quiwidget.cpp new file mode 100644 index 0000000..f6dc849 --- /dev/null +++ b/netserver/api/quiwidget.cpp @@ -0,0 +1,2947 @@ +锘#include "quiwidget.h" + +QUIWidget::QUIWidget(QWidget *parent) : QDialog(parent) +{ + this->initControl(); + this->initForm(); +} + +QUIWidget::~QUIWidget() +{ + delete widgetMain; +} + +bool QUIWidget::eventFilter(QObject *obj, QEvent *evt) +{ + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *event = static_cast(evt); + if (event->type() == QEvent::MouseButtonPress) { + if (event->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = event->globalPos() - this->pos(); + return true; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + return true; + } else if (event->type() == QEvent::MouseMove) { + if (mousePressed && (event->buttons() && Qt::LeftButton)) { + this->move(event->globalPos() - mousePoint); + return true; + } + } else if (event->type() == QEvent::MouseButtonDblClick) { + //浠ヤ笅鍐欐硶鍙互灏嗗弻鍑昏瘑鍒檺瀹氬湪鏍囬鏍 + //if (this->btnMenu_Max->isVisible() && obj == this->widgetTitle) { + if (this->btnMenu_Max->isVisible()) { + this->on_btnMenu_Max_clicked(); + return true; + } + } + + return QWidget::eventFilter(obj, evt); +} + +QLabel *QUIWidget::getLabIco() const +{ + return this->labIco; +} + +QLabel *QUIWidget::getLabTitle() const +{ + return this->labTitle; +} + +QToolButton *QUIWidget::getBtnMenu() const +{ + return this->btnMenu; +} + +QPushButton *QUIWidget::getBtnMenuMin() const +{ + return this->btnMenu_Min; +} + +QPushButton *QUIWidget::getBtnMenuMax() const +{ + return this->btnMenu_Max; +} + +QPushButton *QUIWidget::getBtnMenuMClose() const +{ + return this->btnMenu_Close; +} + +QString QUIWidget::getTitle() const +{ + return this->title; +} + +Qt::Alignment QUIWidget::getAlignment() const +{ + return this->alignment; +} + +QSize QUIWidget::sizeHint() const +{ + return QSize(600, 450); +} + +QSize QUIWidget::minimumSizeHint() const +{ + return QSize(200, 150); +} + +void QUIWidget::initControl() +{ + this->setObjectName(QString::fromUtf8("QUIWidget")); + this->resize(900, 750); + verticalLayout1 = new QVBoxLayout(this); + verticalLayout1->setSpacing(0); + verticalLayout1->setContentsMargins(11, 11, 11, 11); + verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1")); + verticalLayout1->setContentsMargins(1, 1, 1, 1); + widgetMain = new QWidget(this); + widgetMain->setObjectName(QString::fromUtf8("widgetMain")); + verticalLayout2 = new QVBoxLayout(widgetMain); + verticalLayout2->setSpacing(0); + verticalLayout2->setContentsMargins(11, 11, 11, 11); + verticalLayout2->setObjectName(QString::fromUtf8("verticalLayout2")); + verticalLayout2->setContentsMargins(0, 0, 0, 0); + widgetTitle = new QWidget(widgetMain); + widgetTitle->setObjectName(QString::fromUtf8("widgetTitle")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(widgetTitle->sizePolicy().hasHeightForWidth()); + widgetTitle->setSizePolicy(sizePolicy); + widgetTitle->setMinimumSize(QSize(0, 30)); + horizontalLayout4 = new QHBoxLayout(widgetTitle); + horizontalLayout4->setSpacing(0); + horizontalLayout4->setContentsMargins(11, 11, 11, 11); + horizontalLayout4->setObjectName(QString::fromUtf8("horizontalLayout4")); + horizontalLayout4->setContentsMargins(0, 0, 0, 0); + labIco = new QLabel(widgetTitle); + labIco->setObjectName(QString::fromUtf8("labIco")); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(labIco->sizePolicy().hasHeightForWidth()); + labIco->setSizePolicy(sizePolicy1); + labIco->setMinimumSize(QSize(30, 0)); + labIco->setAlignment(Qt::AlignCenter); + + horizontalLayout4->addWidget(labIco); + + labTitle = new QLabel(widgetTitle); + labTitle->setObjectName(QString::fromUtf8("labTitle")); + QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(labTitle->sizePolicy().hasHeightForWidth()); + labTitle->setSizePolicy(sizePolicy2); + labTitle->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + + horizontalLayout4->addWidget(labTitle); + + widgetMenu = new QWidget(widgetTitle); + widgetMenu->setObjectName(QString::fromUtf8("widgetMenu")); + sizePolicy1.setHeightForWidth(widgetMenu->sizePolicy().hasHeightForWidth()); + widgetMenu->setSizePolicy(sizePolicy1); + horizontalLayout = new QHBoxLayout(widgetMenu); + horizontalLayout->setSpacing(0); + horizontalLayout->setContentsMargins(11, 11, 11, 11); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + horizontalLayout->setContentsMargins(0, 0, 0, 0); + btnMenu = new QToolButton(widgetMenu); + btnMenu->setObjectName(QString::fromUtf8("btnMenu")); + QSizePolicy sizePolicy3(QSizePolicy::Fixed, QSizePolicy::Expanding); + sizePolicy3.setHorizontalStretch(0); + sizePolicy3.setVerticalStretch(0); + sizePolicy3.setHeightForWidth(btnMenu->sizePolicy().hasHeightForWidth()); + btnMenu->setSizePolicy(sizePolicy3); + btnMenu->setMinimumSize(QSize(30, 0)); + btnMenu->setMaximumSize(QSize(30, 16777215)); + btnMenu->setFocusPolicy(Qt::NoFocus); + btnMenu->setPopupMode(QToolButton::InstantPopup); + + horizontalLayout->addWidget(btnMenu); + + btnMenu_Min = new QPushButton(widgetMenu); + btnMenu_Min->setObjectName(QString::fromUtf8("btnMenu_Min")); + QSizePolicy sizePolicy4(QSizePolicy::Minimum, QSizePolicy::Expanding); + sizePolicy4.setHorizontalStretch(0); + sizePolicy4.setVerticalStretch(0); + sizePolicy4.setHeightForWidth(btnMenu_Min->sizePolicy().hasHeightForWidth()); + btnMenu_Min->setSizePolicy(sizePolicy4); + btnMenu_Min->setMinimumSize(QSize(30, 0)); + btnMenu_Min->setMaximumSize(QSize(30, 16777215)); + btnMenu_Min->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Min->setFocusPolicy(Qt::NoFocus); + + horizontalLayout->addWidget(btnMenu_Min); + + btnMenu_Max = new QPushButton(widgetMenu); + btnMenu_Max->setObjectName(QString::fromUtf8("btnMenu_Max")); + sizePolicy3.setHeightForWidth(btnMenu_Max->sizePolicy().hasHeightForWidth()); + btnMenu_Max->setSizePolicy(sizePolicy3); + btnMenu_Max->setMinimumSize(QSize(30, 0)); + btnMenu_Max->setMaximumSize(QSize(30, 16777215)); + btnMenu_Max->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Max->setFocusPolicy(Qt::NoFocus); + + horizontalLayout->addWidget(btnMenu_Max); + + btnMenu_Close = new QPushButton(widgetMenu); + btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close")); + sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth()); + btnMenu_Close->setSizePolicy(sizePolicy3); + btnMenu_Close->setMinimumSize(QSize(30, 0)); + btnMenu_Close->setMaximumSize(QSize(30, 16777215)); + btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Close->setFocusPolicy(Qt::NoFocus); + + horizontalLayout->addWidget(btnMenu_Close); + horizontalLayout4->addWidget(widgetMenu); + verticalLayout2->addWidget(widgetTitle); + + widget = new QWidget(widgetMain); + widget->setObjectName(QString::fromUtf8("widget")); + verticalLayout3 = new QVBoxLayout(widget); + verticalLayout3->setSpacing(0); + verticalLayout3->setContentsMargins(11, 11, 11, 11); + verticalLayout3->setObjectName(QString::fromUtf8("verticalLayout3")); + verticalLayout3->setContentsMargins(0, 0, 0, 0); + + verticalLayout2->addWidget(widget); + verticalLayout1->addWidget(widgetMain); + + connect(this->btnMenu_Min, SIGNAL(clicked()), this, SLOT(on_btnMenu_Min_clicked())); + connect(this->btnMenu_Max, SIGNAL(clicked()), this, SLOT(on_btnMenu_Max_clicked())); + connect(this->btnMenu_Close, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); +} + +void QUIWidget::initForm() +{ + //璁剧疆鍥惧舰瀛椾綋 + setIcon(QUIWidget::Lab_Ico, QUIConfig::IconMain, 11); + setIcon(QUIWidget::BtnMenu, QUIConfig::IconMenu); + setIcon(QUIWidget::BtnMenu_Min, QUIConfig::IconMin); + setIcon(QUIWidget::BtnMenu_Normal, QUIConfig::IconNormal); + setIcon(QUIWidget::BtnMenu_Close, QUIConfig::IconClose); + + this->setProperty("form", true); + this->widgetTitle->setProperty("form", "title"); + this->setWindowFlags((Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint)); + + //璁剧疆鏍囬鍙婂榻愭柟寮 + title = "QUI Demo"; + alignment = Qt::AlignLeft | Qt::AlignVCenter; + minHide = false; + mainWidget = 0; + + setVisible(QUIWidget::BtnMenu, false); + + //缁戝畾浜嬩欢杩囨护鍣ㄧ洃鍚紶鏍囩Щ鍔 + this->installEventFilter(this); + this->widgetTitle->installEventFilter(this); + + //娣诲姞鎹㈣偆鑿滃崟 + QStringList name; + name << "閾惰壊" << "钃濊壊" << "娴呰摑鑹" << "娣辫摑鑹" << "鐏拌壊" << "娴呯伆鑹" << "娣辩伆鑹" << "榛戣壊" + << "娴呴粦鑹" << "娣遍粦鑹" << "PS榛戣壊" << "榛戣壊鎵佸钩" << "鐧借壊鎵佸钩"; + + foreach (QString str, name) { + QAction *action = new QAction(str, this); + this->btnMenu->addAction(action); + connect(action, SIGNAL(triggered(bool)), this, SLOT(changeStyle())); + } +} + +void QUIWidget::changeStyle() +{ + QAction *act = (QAction *)sender(); + QString name = act->text(); + QString qssFile = ":/qss/blue.css"; + + if (name == "閾惰壊") { + qssFile = ":/qss/silvery.css"; + QUIHelper::setStyle(QUIWidget::Style_Silvery); + } else if (name == "钃濊壊") { + qssFile = ":/qss/blue.css"; + QUIHelper::setStyle(QUIWidget::Style_Blue); + } else if (name == "娴呰摑鑹") { + qssFile = ":/qss/lightblue.css"; + QUIHelper::setStyle(QUIWidget::Style_LightBlue); + } else if (name == "娣辫摑鑹") { + qssFile = ":/qss/darkblue.css"; + QUIHelper::setStyle(QUIWidget::Style_DarkBlue); + } else if (name == "鐏拌壊") { + qssFile = ":/qss/gray.css"; + QUIHelper::setStyle(QUIWidget::Style_Gray); + } else if (name == "娴呯伆鑹") { + qssFile = ":/qss/lightgray.css"; + QUIHelper::setStyle(QUIWidget::Style_LightGray); + } else if (name == "娣辩伆鑹") { + qssFile = ":/qss/darkgray.css"; + QUIHelper::setStyle(QUIWidget::Style_DarkGray); + } else if (name == "榛戣壊") { + qssFile = ":/qss/black.css"; + QUIHelper::setStyle(QUIWidget::Style_Black); + } else if (name == "娴呴粦鑹") { + qssFile = ":/qss/lightblack.css"; + QUIHelper::setStyle(QUIWidget::Style_LightBlack); + } else if (name == "娣遍粦鑹") { + qssFile = ":/qss/darkblack.css"; + QUIHelper::setStyle(QUIWidget::Style_DarkBlack); + } else if (name == "PS榛戣壊") { + qssFile = ":/qss/psblack.css"; + QUIHelper::setStyle(QUIWidget::Style_PSBlack); + } else if (name == "榛戣壊鎵佸钩") { + qssFile = ":/qss/flatblack.css"; + QUIHelper::setStyle(QUIWidget::Style_FlatBlack); + } else if (name == "鐧借壊鎵佸钩") { + qssFile = ":/qss/flatwhite.css"; + QUIHelper::setStyle(QUIWidget::Style_FlatWhite); + } + + emit changeStyle(qssFile); +} + +void QUIWidget::setIcon(QUIWidget::Widget widget, QChar str, quint32 size) +{ + if (widget == QUIWidget::Lab_Ico) { + setIconMain(str, size); + } else if (widget == QUIWidget::BtnMenu) { + QUIConfig::IconMenu = str; + IconHelper::Instance()->setIcon(this->btnMenu, str, size); + } else if (widget == QUIWidget::BtnMenu_Min) { + QUIConfig::IconMin = str; + IconHelper::Instance()->setIcon(this->btnMenu_Min, str, size); + } else if (widget == QUIWidget::BtnMenu_Max) { + QUIConfig::IconMax = str; + IconHelper::Instance()->setIcon(this->btnMenu_Max, str, size); + } else if (widget == QUIWidget::BtnMenu_Normal) { + QUIConfig::IconNormal = str; + IconHelper::Instance()->setIcon(this->btnMenu_Max, str, size); + } else if (widget == QUIWidget::BtnMenu_Close) { + QUIConfig::IconClose = str; + IconHelper::Instance()->setIcon(this->btnMenu_Close, str, size); + } +} + +void QUIWidget::setIconMain(QChar str, quint32 size) +{ + QUIConfig::IconMain = str; + IconHelper::Instance()->setIcon(this->labIco, str, size); + QUIMessageBox::Instance()->setIconMain(str, size); + QUIInputBox::Instance()->setIconMain(str, size); + QUIDateSelect::Instance()->setIconMain(str, size); +} + +void QUIWidget::setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size) +{ + QPixmap pix = QPixmap(file); + //鎸夌収瀹介珮姣旇嚜鍔ㄧ缉鏀 + pix = pix.scaled(size, Qt::KeepAspectRatio); + + if (widget == QUIWidget::Lab_Ico) { + this->labIco->setPixmap(pix); + } else if (widget == QUIWidget::BtnMenu) { + this->btnMenu->setIcon(QIcon(file)); + } else if (widget == QUIWidget::BtnMenu_Min) { + this->btnMenu_Min->setIcon(QIcon(file)); + } else if (widget == QUIWidget::BtnMenu_Max) { + this->btnMenu_Max->setIcon(QIcon(file)); + } else if (widget == QUIWidget::BtnMenu_Close) { + this->btnMenu_Close->setIcon(QIcon(file)); + } +} + +void QUIWidget::setVisible(QUIWidget::Widget widget, bool visible) +{ + if (widget == QUIWidget::Lab_Ico) { + this->labIco->setVisible(visible); + } else if (widget == QUIWidget::BtnMenu) { + this->btnMenu->setVisible(visible); + } else if (widget == QUIWidget::BtnMenu_Min) { + this->btnMenu_Min->setVisible(visible); + } else if (widget == QUIWidget::BtnMenu_Max) { + this->btnMenu_Max->setVisible(visible); + } else if (widget == QUIWidget::BtnMenu_Close) { + this->btnMenu_Close->setVisible(visible); + } +} + +void QUIWidget::setOnlyCloseBtn() +{ + this->btnMenu->setVisible(false); + this->btnMenu_Min->setVisible(false); + this->btnMenu_Max->setVisible(false); +} + +void QUIWidget::setTitleHeight(int height) +{ + this->widgetTitle->setFixedHeight(height); +} + +void QUIWidget::setBtnWidth(int width) +{ + this->labIco->setFixedWidth(width); + this->btnMenu->setFixedWidth(width); + this->btnMenu_Min->setFixedWidth(width); + this->btnMenu_Max->setFixedWidth(width); + this->btnMenu_Close->setFixedWidth(width); +} + +void QUIWidget::setTitle(const QString &title) +{ + if (this->title != title) { + this->title = title; + this->labTitle->setText(title); + this->setWindowTitle(this->labTitle->text()); + } +} + +void QUIWidget::setAlignment(Qt::Alignment alignment) +{ + if (this->alignment != alignment) { + this->alignment = alignment; + this->labTitle->setAlignment(alignment); + } +} + +void QUIWidget::setMinHide(bool minHide) +{ + if (this->minHide != minHide) { + this->minHide = minHide; + } +} + +void QUIWidget::setMainWidget(QWidget *mainWidget) +{ + //涓涓猀UI绐椾綋瀵硅薄鍙兘璁剧疆涓涓富绐椾綋 + if (this->mainWidget == 0) { + //灏嗗瓙绐椾綋娣诲姞鍒板竷灞 + this->widget->layout()->addWidget(mainWidget); + //鑷姩璁剧疆澶у皬 + resize(mainWidget->width(), mainWidget->height() + this->widgetTitle->height()); + + this->mainWidget = mainWidget; + } +} + +void QUIWidget::on_btnMenu_Min_clicked() +{ + if (minHide) { + hide(); + } else { + showMinimized(); + } +} + +void QUIWidget::on_btnMenu_Max_clicked() +{ + static bool max = false; + static QRect location = this->geometry(); + + if (max) { + this->setGeometry(location); + setIcon(QUIWidget::BtnMenu_Normal, QUIConfig::IconNormal); + } else { + location = this->geometry(); + this->setGeometry(qApp->desktop()->availableGeometry()); + setIcon(QUIWidget::BtnMenu_Max, QUIConfig::IconMax); + } + + max = !max; +} + +void QUIWidget::on_btnMenu_Close_clicked() +{ + emit closing(); + exit(0); +} + + +QUIMessageBox *QUIMessageBox::self = NULL; +QUIMessageBox *QUIMessageBox::Instance() +{ + if (!self) { + QMutex mutex; + QMutexLocker locker(&mutex); + if (!self) { + self = new QUIMessageBox; + } + } + + return self; +} + +QUIMessageBox::QUIMessageBox(QWidget *parent) : QDialog(parent) +{ + this->initControl(); + this->initForm(); +} + +QUIMessageBox::~QUIMessageBox() +{ + delete widgetMain; +} + +void QUIMessageBox::initControl() +{ + this->setObjectName(QString::fromUtf8("QUIMessageBox")); + + verticalLayout1 = new QVBoxLayout(this); + verticalLayout1->setSpacing(0); + verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1")); + verticalLayout1->setContentsMargins(1, 1, 1, 1); + widgetTitle = new QWidget(this); + widgetTitle->setObjectName(QString::fromUtf8("widgetTitle")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(widgetTitle->sizePolicy().hasHeightForWidth()); + widgetTitle->setSizePolicy(sizePolicy); + widgetTitle->setMinimumSize(QSize(0, TitleMinSize)); + horizontalLayout3 = new QHBoxLayout(widgetTitle); + horizontalLayout3->setSpacing(0); + horizontalLayout3->setObjectName(QString::fromUtf8("horizontalLayout3")); + horizontalLayout3->setContentsMargins(0, 0, 0, 0); + labIco = new QLabel(widgetTitle); + labIco->setObjectName(QString::fromUtf8("labIco")); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(labIco->sizePolicy().hasHeightForWidth()); + labIco->setSizePolicy(sizePolicy1); + labIco->setMinimumSize(QSize(TitleMinSize, 0)); + labIco->setAlignment(Qt::AlignCenter); + + horizontalLayout3->addWidget(labIco); + + labTitle = new QLabel(widgetTitle); + labTitle->setObjectName(QString::fromUtf8("labTitle")); + labTitle->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + + horizontalLayout3->addWidget(labTitle); + + labTime = new QLabel(widgetTitle); + labTime->setObjectName(QString::fromUtf8("labTime")); + QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(labTime->sizePolicy().hasHeightForWidth()); + labTime->setSizePolicy(sizePolicy2); + labTime->setAlignment(Qt::AlignCenter); + + horizontalLayout3->addWidget(labTime); + + widgetMenu = new QWidget(widgetTitle); + widgetMenu->setObjectName(QString::fromUtf8("widgetMenu")); + sizePolicy1.setHeightForWidth(widgetMenu->sizePolicy().hasHeightForWidth()); + widgetMenu->setSizePolicy(sizePolicy1); + horizontalLayout4 = new QHBoxLayout(widgetMenu); + horizontalLayout4->setSpacing(0); + horizontalLayout4->setObjectName(QString::fromUtf8("horizontalLayout4")); + horizontalLayout4->setContentsMargins(0, 0, 0, 0); + btnMenu_Close = new QPushButton(widgetMenu); + btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close")); + QSizePolicy sizePolicy3(QSizePolicy::Minimum, QSizePolicy::Expanding); + sizePolicy3.setHorizontalStretch(0); + sizePolicy3.setVerticalStretch(0); + sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth()); + btnMenu_Close->setSizePolicy(sizePolicy3); + btnMenu_Close->setMinimumSize(QSize(TitleMinSize, 0)); + btnMenu_Close->setMaximumSize(QSize(TitleMinSize, 16777215)); + btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Close->setFocusPolicy(Qt::NoFocus); + btnMenu_Close->setFlat(true); + + horizontalLayout4->addWidget(btnMenu_Close); + horizontalLayout3->addWidget(widgetMenu); + verticalLayout1->addWidget(widgetTitle); + + widgetMain = new QWidget(this); + widgetMain->setObjectName(QString::fromUtf8("widgetMain")); + verticalLayout2 = new QVBoxLayout(widgetMain); + verticalLayout2->setSpacing(5); + verticalLayout2->setObjectName(QString::fromUtf8("verticalLayout2")); + verticalLayout2->setContentsMargins(5, 5, 5, 5); + frame = new QFrame(widgetMain); + frame->setObjectName(QString::fromUtf8("frame")); + frame->setFrameShape(QFrame::Box); + frame->setFrameShadow(QFrame::Sunken); + verticalLayout4 = new QVBoxLayout(frame); + verticalLayout4->setObjectName(QString::fromUtf8("verticalLayout4")); + verticalLayout4->setContentsMargins(-1, 9, -1, -1); + horizontalLayout1 = new QHBoxLayout(); + horizontalLayout1->setObjectName(QString::fromUtf8("horizontalLayout1")); + labIcoMain = new QLabel(frame); + labIcoMain->setObjectName(QString::fromUtf8("labIcoMain")); + horizontalLayout1->addWidget(labIcoMain); + horizontalSpacer1 = new QSpacerItem(5, 0, QSizePolicy::Minimum, QSizePolicy::Minimum); + horizontalLayout1->addItem(horizontalSpacer1); + + labInfo = new QLabel(frame); + labInfo->setObjectName(QString::fromUtf8("labInfo")); + QSizePolicy sizePolicy4(QSizePolicy::Expanding, QSizePolicy::Expanding); + sizePolicy4.setHorizontalStretch(0); + sizePolicy4.setVerticalStretch(0); + sizePolicy4.setHeightForWidth(labInfo->sizePolicy().hasHeightForWidth()); + labInfo->setSizePolicy(sizePolicy4); + labInfo->setMinimumSize(QSize(0, TitleMinSize)); + labInfo->setScaledContents(false); + labInfo->setWordWrap(true); + horizontalLayout1->addWidget(labInfo); + verticalLayout4->addLayout(horizontalLayout1); + + horizontalLayout2 = new QHBoxLayout(); + horizontalLayout2->setObjectName(QString::fromUtf8("horizontalLayout2")); + horizontalSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayout2->addItem(horizontalSpacer2); + + btnOk = new QPushButton(frame); + btnOk->setObjectName(QString::fromUtf8("btnOk")); + btnOk->setMinimumSize(QSize(85, 0)); + btnOk->setFocusPolicy(Qt::StrongFocus); + btnOk->setIcon(QIcon(":/image/btn_ok.png")); + horizontalLayout2->addWidget(btnOk); + + btnCancel = new QPushButton(frame); + btnCancel->setObjectName(QString::fromUtf8("btnCancel")); + btnCancel->setMinimumSize(QSize(85, 0)); + btnCancel->setFocusPolicy(Qt::StrongFocus); + btnCancel->setIcon(QIcon(":/image/btn_close.png")); + horizontalLayout2->addWidget(btnCancel); + + verticalLayout4->addLayout(horizontalLayout2); + verticalLayout2->addWidget(frame); + verticalLayout1->addWidget(widgetMain); + + widgetTitle->raise(); + widgetMain->raise(); + frame->raise(); + + btnOk->setText("纭畾"); + btnCancel->setText("鍙栨秷"); + + connect(btnOk, SIGNAL(clicked()), this, SLOT(on_btnOk_clicked())); + connect(btnMenu_Close, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); + connect(btnCancel, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); +} + +void QUIMessageBox::initForm() +{ + IconHelper::Instance()->setIcon(labIco, QUIConfig::IconMain, QUIConfig::FontSize + 2); + IconHelper::Instance()->setIcon(btnMenu_Close, QUIConfig::IconClose, QUIConfig::FontSize); + + this->setProperty("form", true); + this->widgetTitle->setProperty("form", "title"); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowStaysOnTopHint); + this->setWindowTitle(this->labTitle->text()); + +#ifdef __arm__ + int width = 90; + int iconWidth = 22; + int iconHeight = 22; + this->setFixedSize(350, 180); + labIcoMain->setFixedSize(40, 40); +#else + int width = 80; + int iconWidth = 18; + int iconHeight = 18; + this->setFixedSize(280, 150); + labIcoMain->setFixedSize(30, 30); +#endif + + QList btns = this->frame->findChildren(); + + foreach (QPushButton *btn, btns) { + btn->setMinimumWidth(width); + btn->setIconSize(QSize(iconWidth, iconHeight)); + } + + closeSec = 0; + currentSec = 0; + + QTimer *timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, SIGNAL(timeout()), this, SLOT(checkSec())); + timer->start(); + + this->installEventFilter(this); +} + +void QUIMessageBox::checkSec() +{ + if (closeSec == 0) { + return; + } + + if (currentSec < closeSec) { + currentSec++; + } else { + this->close(); + } + + QString str = QString("鍏抽棴鍊掕鏃 %1 s").arg(closeSec - currentSec + 1); + this->labTime->setText(str); +} + +void QUIMessageBox::setMessage(const QString &msg, int type, int closeSec) +{ + this->closeSec = closeSec; + this->currentSec = 0; + this->labTime->clear(); + + checkSec(); + + if (type == 0) { + this->labIcoMain->setStyleSheet("border-image: url(:/image/msg_info.png);"); + this->btnCancel->setVisible(false); + this->labTitle->setText("鎻愮ず"); + } else if (type == 1) { + this->labIcoMain->setStyleSheet("border-image: url(:/image/msg_question.png);"); + this->labTitle->setText("璇㈤棶"); + } else if (type == 2) { + this->labIcoMain->setStyleSheet("border-image: url(:/image/msg_error.png);"); + this->btnCancel->setVisible(false); + this->labTitle->setText("閿欒"); + } + + this->labInfo->setText(msg); + this->setWindowTitle(this->labTitle->text()); +} + +void QUIMessageBox::closeEvent(QCloseEvent *) +{ + closeSec = 0; + currentSec = 0; +} + +bool QUIMessageBox::eventFilter(QObject *obj, QEvent *evt) +{ + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *event = static_cast(evt); + if (event->type() == QEvent::MouseButtonPress) { + if (event->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = event->globalPos() - this->pos(); + return true; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + return true; + } else if (event->type() == QEvent::MouseMove) { + if (mousePressed && (event->buttons() && Qt::LeftButton)) { + this->move(event->globalPos() - mousePoint); + return true; + } + } + + return QWidget::eventFilter(obj, evt); +} + +void QUIMessageBox::on_btnOk_clicked() +{ + done(QMessageBox::Yes); + close(); +} + +void QUIMessageBox::on_btnMenu_Close_clicked() +{ + done(QMessageBox::No); + close(); +} + +void QUIMessageBox::setIconMain(QChar str, quint32 size) +{ + IconHelper::Instance()->setIcon(this->labIco, str, size); +} + + +QUIInputBox *QUIInputBox::self = NULL; +QUIInputBox *QUIInputBox::Instance() +{ + if (!self) { + QMutex mutex; + QMutexLocker locker(&mutex); + if (!self) { + self = new QUIInputBox; + } + } + + return self; +} + +QUIInputBox::QUIInputBox(QWidget *parent) : QDialog(parent) +{ + this->initControl(); + this->initForm(); +} + +QUIInputBox::~QUIInputBox() +{ + delete widgetMain; +} + +void QUIInputBox::initControl() +{ + this->setObjectName(QString::fromUtf8("QUIInputBox")); + + verticalLayout1 = new QVBoxLayout(this); + verticalLayout1->setSpacing(0); + verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1")); + verticalLayout1->setContentsMargins(1, 1, 1, 1); + widgetTitle = new QWidget(this); + widgetTitle->setObjectName(QString::fromUtf8("widgetTitle")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(widgetTitle->sizePolicy().hasHeightForWidth()); + widgetTitle->setSizePolicy(sizePolicy); + widgetTitle->setMinimumSize(QSize(0, TitleMinSize)); + horizontalLayout1 = new QHBoxLayout(widgetTitle); + horizontalLayout1->setSpacing(0); + horizontalLayout1->setObjectName(QString::fromUtf8("horizontalLayout1")); + horizontalLayout1->setContentsMargins(0, 0, 0, 0); + labIco = new QLabel(widgetTitle); + labIco->setObjectName(QString::fromUtf8("labIco")); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(labIco->sizePolicy().hasHeightForWidth()); + labIco->setSizePolicy(sizePolicy1); + labIco->setMinimumSize(QSize(TitleMinSize, 0)); + labIco->setAlignment(Qt::AlignCenter); + + horizontalLayout1->addWidget(labIco); + + labTitle = new QLabel(widgetTitle); + labTitle->setObjectName(QString::fromUtf8("labTitle")); + labTitle->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + + horizontalLayout1->addWidget(labTitle); + + labTime = new QLabel(widgetTitle); + labTime->setObjectName(QString::fromUtf8("labTime")); + QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(labTime->sizePolicy().hasHeightForWidth()); + labTime->setSizePolicy(sizePolicy2); + labTime->setAlignment(Qt::AlignCenter); + + horizontalLayout1->addWidget(labTime); + + widgetMenu = new QWidget(widgetTitle); + widgetMenu->setObjectName(QString::fromUtf8("widgetMenu")); + sizePolicy1.setHeightForWidth(widgetMenu->sizePolicy().hasHeightForWidth()); + widgetMenu->setSizePolicy(sizePolicy1); + horizontalLayout2 = new QHBoxLayout(widgetMenu); + horizontalLayout2->setSpacing(0); + horizontalLayout2->setObjectName(QString::fromUtf8("horizontalLayout2")); + horizontalLayout2->setContentsMargins(0, 0, 0, 0); + btnMenu_Close = new QPushButton(widgetMenu); + btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close")); + QSizePolicy sizePolicy3(QSizePolicy::Minimum, QSizePolicy::Expanding); + sizePolicy3.setHorizontalStretch(0); + sizePolicy3.setVerticalStretch(0); + sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth()); + btnMenu_Close->setSizePolicy(sizePolicy3); + btnMenu_Close->setMinimumSize(QSize(TitleMinSize, 0)); + btnMenu_Close->setMaximumSize(QSize(TitleMinSize, 16777215)); + btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Close->setFocusPolicy(Qt::NoFocus); + btnMenu_Close->setFlat(true); + + horizontalLayout2->addWidget(btnMenu_Close); + horizontalLayout1->addWidget(widgetMenu); + verticalLayout1->addWidget(widgetTitle); + + widgetMain = new QWidget(this); + widgetMain->setObjectName(QString::fromUtf8("widgetMain")); + verticalLayout2 = new QVBoxLayout(widgetMain); + verticalLayout2->setSpacing(5); + verticalLayout2->setObjectName(QString::fromUtf8("verticalLayout2")); + verticalLayout2->setContentsMargins(5, 5, 5, 5); + frame = new QFrame(widgetMain); + frame->setObjectName(QString::fromUtf8("frame")); + frame->setFrameShape(QFrame::Box); + frame->setFrameShadow(QFrame::Sunken); + verticalLayout3 = new QVBoxLayout(frame); + verticalLayout3->setObjectName(QString::fromUtf8("verticalLayout3")); + labInfo = new QLabel(frame); + labInfo->setObjectName(QString::fromUtf8("labInfo")); + labInfo->setScaledContents(false); + labInfo->setWordWrap(true); + verticalLayout3->addWidget(labInfo); + + txtValue = new QLineEdit(frame); + txtValue->setObjectName(QString::fromUtf8("txtValue")); + verticalLayout3->addWidget(txtValue); + + cboxValue = new QComboBox(frame); + cboxValue->setObjectName(QString::fromUtf8("cboxValue")); + verticalLayout3->addWidget(cboxValue); + + lay = new QHBoxLayout(); + lay->setObjectName(QString::fromUtf8("lay")); + horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + lay->addItem(horizontalSpacer); + + btnOk = new QPushButton(frame); + btnOk->setObjectName(QString::fromUtf8("btnOk")); + btnOk->setMinimumSize(QSize(85, 0)); + btnOk->setIcon(QIcon(":/image/btn_ok.png")); + lay->addWidget(btnOk); + + btnCancel = new QPushButton(frame); + btnCancel->setObjectName(QString::fromUtf8("btnCancel")); + btnCancel->setMinimumSize(QSize(85, 0)); + btnCancel->setIcon(QIcon(":/image/btn_close.png")); + lay->addWidget(btnCancel); + + verticalLayout3->addLayout(lay); + verticalLayout2->addWidget(frame); + verticalLayout1->addWidget(widgetMain); + + QWidget::setTabOrder(txtValue, btnOk); + QWidget::setTabOrder(btnOk, btnCancel); + + labTitle->setText("杈撳叆妗"); + btnOk->setText("纭畾"); + btnCancel->setText("鍙栨秷"); + + connect(btnOk, SIGNAL(clicked()), this, SLOT(on_btnOk_clicked())); + connect(btnMenu_Close, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); + connect(btnCancel, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); +} + +void QUIInputBox::initForm() +{ + IconHelper::Instance()->setIcon(labIco, QUIConfig::IconMain, QUIConfig::FontSize + 2); + IconHelper::Instance()->setIcon(btnMenu_Close, QUIConfig::IconClose, QUIConfig::FontSize); + + this->setProperty("form", true); + this->widgetTitle->setProperty("form", "title"); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowStaysOnTopHint); + this->setWindowTitle(this->labTitle->text()); + +#ifdef __arm__ + int width = 90; + int iconWidth = 22; + int iconHeight = 22; + this->setFixedSize(350, 180); +#else + int width = 80; + int iconWidth = 18; + int iconHeight = 18; + this->setFixedSize(280, 150); +#endif + + QList btns = this->frame->findChildren(); + + foreach (QPushButton *btn, btns) { + btn->setMinimumWidth(width); + btn->setIconSize(QSize(iconWidth, iconHeight)); + } + + closeSec = 0; + currentSec = 0; + + QTimer *timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, SIGNAL(timeout()), this, SLOT(checkSec())); + timer->start(); + + this->installEventFilter(this); +} + +void QUIInputBox::checkSec() +{ + if (closeSec == 0) { + return; + } + + if (currentSec < closeSec) { + currentSec++; + } else { + this->close(); + } + + QString str = QString("鍏抽棴鍊掕鏃 %1 s").arg(closeSec - currentSec + 1); + this->labTime->setText(str); +} + +void QUIInputBox::setParameter(const QString &title, int type, int closeSec, + QString defaultValue, bool pwd) +{ + this->closeSec = closeSec; + this->currentSec = 0; + this->labTime->clear(); + this->labInfo->setText(title); + + checkSec(); + + if (type == 0) { + this->cboxValue->setVisible(false); + this->txtValue->setPlaceholderText(defaultValue); + + if (pwd) { + this->txtValue->setEchoMode(QLineEdit::Password); + } + } else if (type == 1) { + this->txtValue->setVisible(false); + this->cboxValue->addItems(defaultValue.split("|")); + } +} + +QString QUIInputBox::getValue() const +{ + return this->value; +} + +void QUIInputBox::closeEvent(QCloseEvent *) +{ + closeSec = 0; + currentSec = 0; +} + +bool QUIInputBox::eventFilter(QObject *obj, QEvent *evt) +{ + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *event = static_cast(evt); + if (event->type() == QEvent::MouseButtonPress) { + if (event->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = event->globalPos() - this->pos(); + return true; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + return true; + } else if (event->type() == QEvent::MouseMove) { + if (mousePressed && (event->buttons() && Qt::LeftButton)) { + this->move(event->globalPos() - mousePoint); + return true; + } + } + + return QWidget::eventFilter(obj, evt); +} + +void QUIInputBox::on_btnOk_clicked() +{ + if (this->txtValue->isVisible()) { + value = this->txtValue->text(); + } else if (this->cboxValue->isVisible()) { + value = this->cboxValue->currentText(); + } + + done(QMessageBox::Ok); + close(); +} + +void QUIInputBox::on_btnMenu_Close_clicked() +{ + done(QMessageBox::Cancel); + close(); +} + +void QUIInputBox::setIconMain(QChar str, quint32 size) +{ + IconHelper::Instance()->setIcon(this->labIco, str, size); +} + + +QUIDateSelect *QUIDateSelect::self = NULL; +QUIDateSelect *QUIDateSelect::Instance() +{ + if (!self) { + QMutex mutex; + QMutexLocker locker(&mutex); + if (!self) { + self = new QUIDateSelect; + } + } + + return self; +} + +QUIDateSelect::QUIDateSelect(QWidget *parent) : QDialog(parent) +{ + this->initControl(); + this->initForm(); +} + +QUIDateSelect::~QUIDateSelect() +{ + delete widgetMain; +} + +bool QUIDateSelect::eventFilter(QObject *obj, QEvent *evt) +{ + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *event = static_cast(evt); + if (event->type() == QEvent::MouseButtonPress) { + if (event->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = event->globalPos() - this->pos(); + return true; + } + } else if (event->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + return true; + } else if (event->type() == QEvent::MouseMove) { + if (mousePressed && (event->buttons() && Qt::LeftButton)) { + this->move(event->globalPos() - mousePoint); + return true; + } + } + + return QWidget::eventFilter(obj, evt); +} + +void QUIDateSelect::initControl() +{ + this->setObjectName(QString::fromUtf8("QUIDateSelect")); + + verticalLayout = new QVBoxLayout(this); + verticalLayout->setSpacing(0); + verticalLayout->setObjectName(QString::fromUtf8("verticalLayout")); + verticalLayout->setContentsMargins(1, 1, 1, 1); + widgetTitle = new QWidget(this); + widgetTitle->setObjectName(QString::fromUtf8("widgetTitle")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(widgetTitle->sizePolicy().hasHeightForWidth()); + widgetTitle->setSizePolicy(sizePolicy); + widgetTitle->setMinimumSize(QSize(0, TitleMinSize)); + horizontalLayout1 = new QHBoxLayout(widgetTitle); + horizontalLayout1->setSpacing(0); + horizontalLayout1->setObjectName(QString::fromUtf8("horizontalLayout1")); + horizontalLayout1->setContentsMargins(0, 0, 0, 0); + labIco = new QLabel(widgetTitle); + labIco->setObjectName(QString::fromUtf8("labIco")); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::Preferred); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(labIco->sizePolicy().hasHeightForWidth()); + labIco->setSizePolicy(sizePolicy1); + labIco->setMinimumSize(QSize(TitleMinSize, 0)); + labIco->setAlignment(Qt::AlignCenter); + horizontalLayout1->addWidget(labIco); + + labTitle = new QLabel(widgetTitle); + labTitle->setObjectName(QString::fromUtf8("labTitle")); + QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(labTitle->sizePolicy().hasHeightForWidth()); + labTitle->setSizePolicy(sizePolicy2); + labTitle->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + horizontalLayout1->addWidget(labTitle); + + widgetMenu = new QWidget(widgetTitle); + widgetMenu->setObjectName(QString::fromUtf8("widgetMenu")); + sizePolicy1.setHeightForWidth(widgetMenu->sizePolicy().hasHeightForWidth()); + widgetMenu->setSizePolicy(sizePolicy1); + horizontalLayout = new QHBoxLayout(widgetMenu); + horizontalLayout->setSpacing(0); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + horizontalLayout->setContentsMargins(0, 0, 0, 0); + btnMenu_Close = new QPushButton(widgetMenu); + btnMenu_Close->setObjectName(QString::fromUtf8("btnMenu_Close")); + QSizePolicy sizePolicy3(QSizePolicy::Minimum, QSizePolicy::Expanding); + sizePolicy3.setHorizontalStretch(0); + sizePolicy3.setVerticalStretch(0); + sizePolicy3.setHeightForWidth(btnMenu_Close->sizePolicy().hasHeightForWidth()); + btnMenu_Close->setSizePolicy(sizePolicy3); + btnMenu_Close->setMinimumSize(QSize(TitleMinSize, 0)); + btnMenu_Close->setMaximumSize(QSize(TitleMinSize, 16777215)); + btnMenu_Close->setCursor(QCursor(Qt::ArrowCursor)); + btnMenu_Close->setFocusPolicy(Qt::NoFocus); + btnMenu_Close->setFlat(true); + + horizontalLayout->addWidget(btnMenu_Close); + horizontalLayout1->addWidget(widgetMenu); + verticalLayout->addWidget(widgetTitle); + + widgetMain = new QWidget(this); + widgetMain->setObjectName(QString::fromUtf8("widgetMain")); + verticalLayout1 = new QVBoxLayout(widgetMain); + verticalLayout1->setSpacing(6); + verticalLayout1->setObjectName(QString::fromUtf8("verticalLayout1")); + verticalLayout1->setContentsMargins(6, 6, 6, 6); + frame = new QFrame(widgetMain); + frame->setObjectName(QString::fromUtf8("frame")); + frame->setFrameShape(QFrame::Box); + frame->setFrameShadow(QFrame::Sunken); + gridLayout = new QGridLayout(frame); + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + labStart = new QLabel(frame); + labStart->setObjectName(QString::fromUtf8("labStart")); + labStart->setFocusPolicy(Qt::TabFocus); + gridLayout->addWidget(labStart, 0, 0, 1, 1); + + btnOk = new QPushButton(frame); + btnOk->setObjectName(QString::fromUtf8("btnOk")); + btnOk->setMinimumSize(QSize(85, 0)); + btnOk->setCursor(QCursor(Qt::PointingHandCursor)); + btnOk->setFocusPolicy(Qt::StrongFocus); + btnOk->setIcon(QIcon(":/image/btn_ok.png")); + gridLayout->addWidget(btnOk, 0, 2, 1, 1); + + labEnd = new QLabel(frame); + labEnd->setObjectName(QString::fromUtf8("labEnd")); + labEnd->setFocusPolicy(Qt::TabFocus); + gridLayout->addWidget(labEnd, 1, 0, 1, 1); + + btnClose = new QPushButton(frame); + btnClose->setObjectName(QString::fromUtf8("btnClose")); + btnClose->setMinimumSize(QSize(85, 0)); + btnClose->setCursor(QCursor(Qt::PointingHandCursor)); + btnClose->setFocusPolicy(Qt::StrongFocus); + btnClose->setIcon(QIcon(":/image/btn_close.png")); + gridLayout->addWidget(btnClose, 1, 2, 1, 1); + + dateStart = new QDateTimeEdit(frame); + dateStart->setObjectName(QString::fromUtf8("dateStart")); + QSizePolicy sizePolicy4(QSizePolicy::Expanding, QSizePolicy::Fixed); + sizePolicy4.setHorizontalStretch(0); + sizePolicy4.setVerticalStretch(0); + sizePolicy4.setHeightForWidth(dateStart->sizePolicy().hasHeightForWidth()); + dateStart->setSizePolicy(sizePolicy4); + dateStart->setCalendarPopup(true); + gridLayout->addWidget(dateStart, 0, 1, 1, 1); + + dateEnd = new QDateTimeEdit(frame); + dateEnd->setObjectName(QString::fromUtf8("dateEnd")); + sizePolicy4.setHeightForWidth(dateEnd->sizePolicy().hasHeightForWidth()); + dateEnd->setSizePolicy(sizePolicy4); + dateEnd->setCalendarPopup(true); + + gridLayout->addWidget(dateEnd, 1, 1, 1, 1); + verticalLayout1->addWidget(frame); + verticalLayout->addWidget(widgetMain); + + QWidget::setTabOrder(labStart, labEnd); + QWidget::setTabOrder(labEnd, dateStart); + QWidget::setTabOrder(dateStart, dateEnd); + QWidget::setTabOrder(dateEnd, btnOk); + QWidget::setTabOrder(btnOk, btnClose); + + labTitle->setText("鏃ユ湡鏃堕棿閫夋嫨"); + labStart->setText("寮濮嬫椂闂"); + labEnd->setText("缁撴潫鏃堕棿"); + btnOk->setText("纭畾"); + btnClose->setText("鍏抽棴"); + + dateStart->setDate(QDate::currentDate()); + dateEnd->setDate(QDate::currentDate().addDays(1)); + + dateStart->calendarWidget()->setGridVisible(true); + dateEnd->calendarWidget()->setGridVisible(true); + dateStart->calendarWidget()->setLocale(QLocale::Chinese); + dateEnd->calendarWidget()->setLocale(QLocale::Chinese); + setFormat("yyyy-MM-dd"); + + connect(btnOk, SIGNAL(clicked()), this, SLOT(on_btnOk_clicked())); + connect(btnMenu_Close, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); + connect(btnClose, SIGNAL(clicked()), this, SLOT(on_btnMenu_Close_clicked())); +} + +void QUIDateSelect::initForm() +{ + IconHelper::Instance()->setIcon(labIco, QUIConfig::IconMain, QUIConfig::FontSize + 2); + IconHelper::Instance()->setIcon(btnMenu_Close, QUIConfig::IconClose, QUIConfig::FontSize); + + this->setProperty("form", true); + this->widgetTitle->setProperty("form", "title"); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowStaysOnTopHint); + this->setWindowTitle(this->labTitle->text()); + +#ifdef __arm__ + int width = 90; + int iconWidth = 22; + int iconHeight = 22; + this->setFixedSize(370, 160); +#else + int width = 80; + int iconWidth = 18; + int iconHeight = 18; + this->setFixedSize(320, 130); +#endif + + QList btns = this->frame->findChildren(); + + foreach (QPushButton *btn, btns) { + btn->setMinimumWidth(width); + btn->setIconSize(QSize(iconWidth, iconHeight)); + } + + this->installEventFilter(this); +} + +void QUIDateSelect::on_btnOk_clicked() +{ + if (dateStart->dateTime() > dateEnd->dateTime()) { + QUIHelper::showMessageBoxError("寮濮嬫椂闂翠笉鑳藉ぇ浜庣粨鏉熸椂闂!", 3); + return; + } + + startDateTime = dateStart->dateTime().toString(format); + endDateTime = dateEnd->dateTime().toString(format); + + done(QMessageBox::Ok); + close(); +} + +void QUIDateSelect::on_btnMenu_Close_clicked() +{ + done(QMessageBox::Cancel); + close(); +} + +QString QUIDateSelect::getStartDateTime() const +{ + return this->startDateTime; +} + +QString QUIDateSelect::getEndDateTime() const +{ + return this->endDateTime; +} + +void QUIDateSelect::setIconMain(QChar str, quint32 size) +{ + IconHelper::Instance()->setIcon(this->labIco, str, size); +} + +void QUIDateSelect::setFormat(const QString &format) +{ + this->format = format; + this->dateStart->setDisplayFormat(format); + this->dateEnd->setDisplayFormat(format); +} + + +int QUIHelper::deskWidth() +{ + //娌℃湁蹇呰姣忔閮借幏鍙,鍙湁褰撳彉閲忎负绌烘椂鎵嶅幓鑾峰彇涓娆 + static int width = 0; + if (width == 0) { + width = qApp->desktop()->availableGeometry().width(); + } + + return width; +} + +int QUIHelper::deskHeight() +{ + //娌℃湁蹇呰姣忔閮借幏鍙,鍙湁褰撳彉閲忎负绌烘椂鎵嶅幓鑾峰彇涓娆 + static int height = 0; + if (height == 0) { + height = qApp->desktop()->availableGeometry().height(); + } + + return height; +} + +QString QUIHelper::appName() +{ + //娌℃湁蹇呰姣忔閮借幏鍙,鍙湁褰撳彉閲忎负绌烘椂鎵嶅幓鑾峰彇涓娆 + static QString name; + if (name.isEmpty()) { + name = qApp->applicationFilePath(); + QStringList list = name.split("/"); + name = list.at(list.count() - 1).split(".").at(0); + } + + return name; +} + +QString QUIHelper::appPath() +{ +#ifdef Q_OS_ANDROID + return QString("/sdcard/Android/%1").arg(appName()); +#else + return qApp->applicationDirPath(); +#endif +} + +void QUIHelper::initRand() +{ + //鍒濆鍖栭殢鏈烘暟绉嶅瓙 + QTime t = QTime::currentTime(); + qsrand(t.msec() + t.second() * 1000); +} + +void QUIHelper::newDir(const QString &dirName) +{ + QString strDir = dirName; + + //濡傛灉璺緞涓寘鍚枩鏉犲瓧绗﹀垯璇存槑鏄粷瀵硅矾寰 + //linux绯荤粺璺緞瀛楃甯︽湁 / windows绯荤粺 璺緞瀛楃甯︽湁 :/ + if (!strDir.startsWith("/") && !strDir.contains(":/")) { + strDir = QString("%1/%2").arg(QUIHelper::appPath()).arg(strDir); + } + + QDir dir(strDir); + if (!dir.exists()) { + dir.mkpath(strDir); + } +} + +void QUIHelper::writeInfo(const QString &info, const QString &filePath) +{ + QString fileName = QString("%1/%2/%3_runinfo_%4.txt").arg(QUIHelper::appPath()) + .arg(filePath).arg(QUIHelper::appName()).arg(QDate::currentDate().toString("yyyyMM")); + + QFile file(fileName); + file.open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text); + QTextStream stream(&file); + stream << DATETIME << " " << info << NEWLINE; + file.close(); +} + +void QUIHelper::writeError(const QString &info, const QString &filePath) +{ + //姝e紡杩愯灞忚斀鎺夎緭鍑洪敊璇俊鎭,璋冭瘯闃舵鎵嶉渶瑕 + return; + QString fileName = QString("%1/%2/%3_runerror_%4.txt").arg(QUIHelper::appPath()) + .arg(filePath).arg(QUIHelper::appName()).arg(QDate::currentDate().toString("yyyyMM")); + + QFile file(fileName); + file.open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text); + QTextStream stream(&file); + stream << DATETIME << " " << info << NEWLINE; + file.close(); +} + +void QUIHelper::setStyle(QUIWidget::Style style) +{ + QString qssFile = ":/qss/blue.css"; + + if (style == QUIWidget::Style_Silvery) { + qssFile = ":/qss/silvery.css"; + } else if (style == QUIWidget::Style_Blue) { + qssFile = ":/qss/blue.css"; + } else if (style == QUIWidget::Style_LightBlue) { + qssFile = ":/qss/lightblue.css"; + } else if (style == QUIWidget::Style_DarkBlue) { + qssFile = ":/qss/darkblue.css"; + } else if (style == QUIWidget::Style_Gray) { + qssFile = ":/qss/gray.css"; + } else if (style == QUIWidget::Style_LightGray) { + qssFile = ":/qss/lightgray.css"; + } else if (style == QUIWidget::Style_DarkGray) { + qssFile = ":/qss/darkgray.css"; + } else if (style == QUIWidget::Style_Black) { + qssFile = ":/qss/black.css"; + } else if (style == QUIWidget::Style_LightBlack) { + qssFile = ":/qss/lightblack.css"; + } else if (style == QUIWidget::Style_DarkBlack) { + qssFile = ":/qss/darkblack.css"; + } else if (style == QUIWidget::Style_PSBlack) { + qssFile = ":/qss/psblack.css"; + } else if (style == QUIWidget::Style_FlatBlack) { + qssFile = ":/qss/flatblack.css"; + } else if (style == QUIWidget::Style_FlatWhite) { + qssFile = ":/qss/flatwhite.css"; + } + + QFile file(qssFile); + + if (file.open(QFile::ReadOnly)) { + QString qss = QLatin1String(file.readAll()); + QString paletteColor = qss.mid(20, 7); + getQssColor(qss, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::BorderColor, QUIConfig::NormalColorStart, + QUIConfig::NormalColorEnd, QUIConfig::DarkColorStart, QUIConfig::DarkColorEnd, QUIConfig::HighColor); + + qApp->setPalette(QPalette(QColor(paletteColor))); + qApp->setStyleSheet(qss); + file.close(); + } +} + +void QUIHelper::setStyle(const QString &qssFile, QString &paletteColor, QString &textColor) +{ + QFile file(qssFile); + if (file.open(QFile::ReadOnly)) { + QString qss = QLatin1String(file.readAll()); + paletteColor = qss.mid(20, 7); + textColor = qss.mid(49, 7); + getQssColor(qss, QUIConfig::TextColor, QUIConfig::PanelColor, QUIConfig::BorderColor, QUIConfig::NormalColorStart, + QUIConfig::NormalColorEnd, QUIConfig::DarkColorStart, QUIConfig::DarkColorEnd, QUIConfig::HighColor); + + qApp->setPalette(QPalette(QColor(paletteColor))); + qApp->setStyleSheet(qss); + file.close(); + } +} + +void QUIHelper::setStyle(const QString &qssFile, QString &textColor, QString &panelColor, QString &borderColor, + QString &normalColorStart, QString &normalColorEnd, + QString &darkColorStart, QString &darkColorEnd, QString &highColor) +{ + QFile file(qssFile); + if (file.open(QFile::ReadOnly)) { + QString qss = QLatin1String(file.readAll()); + getQssColor(qss, textColor, panelColor, borderColor, normalColorStart, normalColorEnd, darkColorStart, darkColorEnd, highColor); + qApp->setPalette(QPalette(QColor(panelColor))); + qApp->setStyleSheet(qss); + file.close(); + } +} + +void QUIHelper::getQssColor(const QString &qss, QString &textColor, QString &panelColor, QString &borderColor, + QString &normalColorStart, QString &normalColorEnd, + QString &darkColorStart, QString &darkColorEnd, QString &highColor) +{ + QString str = qss; + + QString flagTextColor = "TextColor:"; + int indexTextColor = str.indexOf(flagTextColor); + if (indexTextColor >= 0) { + textColor = str.mid(indexTextColor + flagTextColor.length(), 7); + } + + QString flagPanelColor = "PanelColor:"; + int indexPanelColor = str.indexOf(flagPanelColor); + if (indexPanelColor >= 0) { + panelColor = str.mid(indexPanelColor + flagPanelColor.length(), 7); + } + + QString flagBorderColor = "BorderColor:"; + int indexBorderColor = str.indexOf(flagBorderColor); + if (indexBorderColor >= 0) { + borderColor = str.mid(indexBorderColor + flagBorderColor.length(), 7); + } + + QString flagNormalColorStart = "NormalColorStart:"; + int indexNormalColorStart = str.indexOf(flagNormalColorStart); + if (indexNormalColorStart >= 0) { + normalColorStart = str.mid(indexNormalColorStart + flagNormalColorStart.length(), 7); + } + + QString flagNormalColorEnd = "NormalColorEnd:"; + int indexNormalColorEnd = str.indexOf(flagNormalColorEnd); + if (indexNormalColorEnd >= 0) { + normalColorEnd = str.mid(indexNormalColorEnd + flagNormalColorEnd.length(), 7); + } + + QString flagDarkColorStart = "DarkColorStart:"; + int indexDarkColorStart = str.indexOf(flagDarkColorStart); + if (indexDarkColorStart >= 0) { + darkColorStart = str.mid(indexDarkColorStart + flagDarkColorStart.length(), 7); + } + + QString flagDarkColorEnd = "DarkColorEnd:"; + int indexDarkColorEnd = str.indexOf(flagDarkColorEnd); + if (indexDarkColorEnd >= 0) { + darkColorEnd = str.mid(indexDarkColorEnd + flagDarkColorEnd.length(), 7); + } + + QString flagHighColor = "HighColor:"; + int indexHighColor = str.indexOf(flagHighColor); + if (indexHighColor >= 0) { + highColor = str.mid(indexHighColor + flagHighColor.length(), 7); + } +} + +QPixmap QUIHelper::ninePatch(const QString &picName, int horzSplit, int vertSplit, int dstWidth, int dstHeight) +{ + QPixmap pix(picName); + return ninePatch(pix, horzSplit, vertSplit, dstWidth, dstHeight); +} + +QPixmap QUIHelper::ninePatch(const QPixmap &pix, int horzSplit, int vertSplit, int dstWidth, int dstHeight) +{ + int pixWidth = pix.width(); + int pixHeight = pix.height(); + + QPixmap pix1 = pix.copy(0, 0, horzSplit, vertSplit); + QPixmap pix2 = pix.copy(horzSplit, 0, pixWidth - horzSplit * 2, vertSplit); + QPixmap pix3 = pix.copy(pixWidth - horzSplit, 0, horzSplit, vertSplit); + + QPixmap pix4 = pix.copy(0, vertSplit, horzSplit, pixHeight - vertSplit * 2); + QPixmap pix5 = pix.copy(horzSplit, vertSplit, pixWidth - horzSplit * 2, pixHeight - vertSplit * 2); + QPixmap pix6 = pix.copy(pixWidth - horzSplit, vertSplit, horzSplit, pixHeight - vertSplit * 2); + + QPixmap pix7 = pix.copy(0, pixHeight - vertSplit, horzSplit, vertSplit); + QPixmap pix8 = pix.copy(horzSplit, pixHeight - vertSplit, pixWidth - horzSplit * 2, pixWidth - horzSplit * 2); + QPixmap pix9 = pix.copy(pixWidth - horzSplit, pixHeight - vertSplit, horzSplit, vertSplit); + + //淇濇寔楂樺害鎷夊 + pix2 = pix2.scaled(dstWidth - horzSplit * 2, vertSplit, Qt::IgnoreAspectRatio); + //淇濇寔瀹藉害鎷夐珮 + pix4 = pix4.scaled(horzSplit, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); + //瀹介珮閮界缉鏀 + pix5 = pix5.scaled(dstWidth - horzSplit * 2, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); + //淇濇寔瀹藉害鎷夐珮 + pix6 = pix6.scaled(horzSplit, dstHeight - vertSplit * 2, Qt::IgnoreAspectRatio); + //淇濇寔楂樺害鎷夊 + pix8 = pix8.scaled(dstWidth - horzSplit * 2, vertSplit); + + //鐢熸垚瀹介珮鍥剧墖骞跺~鍏呴忔槑鑳屾櫙棰滆壊 + QPixmap resultImg(dstWidth, dstHeight); + resultImg.fill(Qt::transparent); + + QPainter painter; + painter.begin(&resultImg); + + if (!resultImg.isNull()) { + painter.drawPixmap(0, 0, pix1); + painter.drawPixmap(horzSplit, 0, pix2); + painter.drawPixmap(dstWidth - horzSplit, 0, pix3); + + painter.drawPixmap(0, vertSplit, pix4); + painter.drawPixmap(horzSplit, vertSplit, pix5); + painter.drawPixmap(dstWidth - horzSplit, vertSplit, pix6); + + painter.drawPixmap(0, dstHeight - vertSplit, pix7); + painter.drawPixmap(horzSplit, dstHeight - vertSplit, pix8); + painter.drawPixmap(dstWidth - horzSplit, dstHeight - vertSplit, pix9); + } + + painter.end(); + + return resultImg; +} + +void QUIHelper::setLabStyle(QLabel *lab, quint8 type) +{ + QString qssRed = "QLabel{border:none;background-color:rgb(214,64,48);color:rgb(255,255,255);}"; + QString qssGreen = "QLabel{border:none;background-color:rgb(46,138,87);color:rgb(255,255,255);}"; + QString qssBlue = "QLabel{border:none;background-color:rgb(67,122,203);color:rgb(255,255,255);}"; + QString qssDark = "QLabel{border:none;background-color:rgb(75,75,75);color:rgb(255,255,255);}"; + + if (type == 0) { + lab->setStyleSheet(qssRed); + } else if (type == 1) { + lab->setStyleSheet(qssGreen); + } else if (type == 2) { + lab->setStyleSheet(qssBlue); + } else if (type == 3) { + lab->setStyleSheet(qssDark); + } +} + +void QUIHelper::setFormInCenter(QWidget *frm) +{ + int frmX = frm->width(); + int frmY = frm->height(); + QDesktopWidget w; + int deskWidth = w.availableGeometry().width(); + int deskHeight = w.availableGeometry().height(); + QPoint movePoint(deskWidth / 2 - frmX / 2, deskHeight / 2 - frmY / 2); + frm->move(movePoint); +} + +void QUIHelper::setTranslator(const QString &qmFile) +{ + QTranslator *translator = new QTranslator(qApp); + translator->load(qmFile); + qApp->installTranslator(translator); +} + +void QUIHelper::setCode() +{ +#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif +} + +void QUIHelper::sleep(int sec) +{ + QTime dieTime = QTime::currentTime().addMSecs(sec); + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } +} + +void QUIHelper::setSystemDateTime(const QString &year, const QString &month, const QString &day, const QString &hour, const QString &min, const QString &sec) +{ +#ifdef Q_OS_WIN + QProcess p(0); + p.start("cmd"); + p.waitForStarted(); + p.write(QString("date %1-%2-%3\n").arg(year).arg(month).arg(day).toLatin1()); + p.closeWriteChannel(); + p.waitForFinished(1000); + p.close(); + p.start("cmd"); + p.waitForStarted(); + p.write(QString("time %1:%2:%3.00\n").arg(hour).arg(min).arg(sec).toLatin1()); + p.closeWriteChannel(); + p.waitForFinished(1000); + p.close(); +#else + QString cmd = QString("date %1%2%3%4%5.%6").arg(month).arg(day).arg(hour).arg(min).arg(year).arg(sec); + system(cmd.toLatin1()); + system("hwclock -w"); +#endif +} + +void QUIHelper::runWithSystem(const QString &strName, const QString &strPath, bool autoRun) +{ +#ifdef Q_OS_WIN + QSettings reg("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); + reg.setValue(strName, autoRun ? strPath : ""); +#endif +} + +bool QUIHelper::isIP(const QString &ip) +{ + QRegExp RegExp("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)"); + return RegExp.exactMatch(ip); +} + +bool QUIHelper::isMac(const QString &mac) +{ + QRegExp RegExp("^[A-F0-9]{2}(-[A-F0-9]{2}){5}$"); + return RegExp.exactMatch(mac); +} + +bool QUIHelper::isTel(const QString &tel) +{ + if (tel.length() != 11) { + return false; + } + + if (!tel.startsWith("13") && !tel.startsWith("14") && !tel.startsWith("15") && !tel.startsWith("18")) { + return false; + } + + return true; +} + +bool QUIHelper::isEmail(const QString &email) +{ + if (!email.contains("@") || !email.contains(".com")) { + return false; + } + + return true; +} + +int QUIHelper::strHexToDecimal(const QString &strHex) +{ + bool ok; + return strHex.toInt(&ok, 16); +} + +int QUIHelper::strDecimalToDecimal(const QString &strDecimal) +{ + bool ok; + return strDecimal.toInt(&ok, 10); +} + +int QUIHelper::strBinToDecimal(const QString &strBin) +{ + bool ok; + return strBin.toInt(&ok, 2); +} + +QString QUIHelper::strHexToStrBin(const QString &strHex) +{ + uchar decimal = strHexToDecimal(strHex); + QString bin = QString::number(decimal, 2); + uchar len = bin.length(); + + if (len < 8) { + for (int i = 0; i < 8 - len; i++) { + bin = "0" + bin; + } + } + + return bin; +} + +QString QUIHelper::decimalToStrBin1(int decimal) +{ + QString bin = QString::number(decimal, 2); + uchar len = bin.length(); + + if (len <= 8) { + for (int i = 0; i < 8 - len; i++) { + bin = "0" + bin; + } + } + + return bin; +} + +QString QUIHelper::decimalToStrBin2(int decimal) +{ + QString bin = QString::number(decimal, 2); + uchar len = bin.length(); + + if (len <= 16) { + for (int i = 0; i < 16 - len; i++) { + bin = "0" + bin; + } + } + + return bin; +} + +QString QUIHelper::decimalToStrHex(int decimal) +{ + QString temp = QString::number(decimal, 16); + if (temp.length() == 1) { + temp = "0" + temp; + } + + return temp; +} + +QByteArray QUIHelper::intToByte(int i) +{ + QByteArray result; + result.resize(4); + result[3] = (uchar)(0x000000ff & i); + result[2] = (uchar)((0x0000ff00 & i) >> 8); + result[1] = (uchar)((0x00ff0000 & i) >> 16); + result[0] = (uchar)((0xff000000 & i) >> 24); + return result; +} + +QByteArray QUIHelper::intToByteRec(int i) +{ + QByteArray result; + result.resize(4); + result[0] = (uchar)(0x000000ff & i); + result[1] = (uchar)((0x0000ff00 & i) >> 8); + result[2] = (uchar)((0x00ff0000 & i) >> 16); + result[3] = (uchar)((0xff000000 & i) >> 24); + return result; +} + +int QUIHelper::byteToInt(const QByteArray &data) +{ + int i = data.at(3) & 0x000000ff; + i |= ((data.at(2) << 8) & 0x0000ff00); + i |= ((data.at(1) << 16) & 0x00ff0000); + i |= ((data.at(0) << 24) & 0xff000000); + return i; +} + +int QUIHelper::byteToIntRec(const QByteArray &data) +{ + int i = data.at(0) & 0x000000ff; + i |= ((data.at(1) << 8) & 0x0000ff00); + i |= ((data.at(2) << 16) & 0x00ff0000); + i |= ((data.at(3) << 24) & 0xff000000); + return i; +} + +QByteArray QUIHelper::ushortToByte(ushort i) +{ + QByteArray result; + result.resize(2); + result[1] = (uchar)(0x000000ff & i); + result[0] = (uchar)((0x0000ff00 & i) >> 8); + return result; +} + +QByteArray QUIHelper::ushortToByteRec(ushort i) +{ + QByteArray result; + result.resize(2); + result[0] = (uchar) (0x000000ff & i); + result[1] = (uchar) ((0x0000ff00 & i) >> 8); + return result; +} + +int QUIHelper::byteToUShort(const QByteArray &data) +{ + int i = data.at(1) & 0x000000FF; + i |= ((data.at(0) << 8) & 0x0000FF00); + + if (i >= 32768) { + i = i - 65536; + } + + return i; +} + +int QUIHelper::byteToUShortRec(const QByteArray &data) +{ + int i = data.at(0) & 0x000000FF; + i |= ((data.at(1) << 8) & 0x0000FF00); + + if (i >= 32768) { + i = i - 65536; + } + + return i; +} + +QString QUIHelper::getXorEncryptDecrypt(const QString &str, char key) +{ + QByteArray data = str.toLatin1(); + int size = data.size(); + + for (int i = 0; i < size; i++) { + data[i] = data.at(i) ^ key; + } + + return QString(data); +} + +uchar QUIHelper::getOrCode(const QByteArray &data) +{ + int len = data.length(); + uchar result = 0; + + for (int i = 0; i < len; i++) { + result ^= data.at(i); + } + + return result; +} + +uchar QUIHelper::getCheckCode(const QByteArray &data) +{ + int len = data.length(); + uchar temp = 0; + + for (uchar i = 0; i < len; i++) { + temp += data.at(i); + } + + return temp % 256; +} + +QString QUIHelper::getValue(quint8 value) +{ + QString result = QString::number(value); + if (result.length() <= 1) { + result = QString("0%1").arg(result); + } + return result; +} + +//鍑芥暟鍔熻兘锛氳绠桟RC16 +//鍙傛暟1锛*data 16浣岰RC鏍¢獙鏁版嵁锛 +//鍙傛暟2锛歭en 鏁版嵁娴侀暱搴 +//鍙傛暟3锛歩nit 鍒濆鍖栧 +//鍙傛暟4锛歵able 16浣岰RC鏌ユ壘琛 + +//閫嗗簭CRC璁$畻 +quint16 QUIHelper::getRevCrc_16(quint8 *data, int len, quint16 init, const quint16 *table) +{ + quint16 cRc_16 = init; + quint8 temp; + + while(len-- > 0) { + temp = cRc_16 >> 8; + cRc_16 = (cRc_16 << 8) ^ table[(temp ^ *data++) & 0xff]; + } + + return cRc_16; +} + +//姝e簭CRC璁$畻 +quint16 QUIHelper::getCrc_16(quint8 *data, int len, quint16 init, const quint16 *table) +{ + quint16 cRc_16 = init; + quint8 temp; + + while(len-- > 0) { + temp = cRc_16 & 0xff; + cRc_16 = (cRc_16 >> 8) ^ table[(temp ^ *data++) & 0xff]; + } + + return cRc_16; +} + +//Modbus CRC16鏍¢獙 +quint16 QUIHelper::getModbus16(quint8 *data, int len) +{ + //MODBUS CRC-16琛 8005 閫嗗簭 + const quint16 table_16[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 + }; + + return getCrc_16(data, len, 0xFFFF, table_16); +} + +//CRC16鏍¢獙 +QByteArray QUIHelper::getCRCCode(const QByteArray &data) +{ + quint16 result = getModbus16((quint8 *)data.data(), data.length()); + return QUIHelper::ushortToByteRec(result); +} + +QString QUIHelper::byteArrayToAsciiStr(const QByteArray &data) +{ + QString temp; + int len = data.size(); + + for (int i = 0; i < len; i++) { + //0x20涓虹┖鏍,绌烘牸浠ヤ笅閮芥槸涓嶅彲瑙佸瓧绗 + char b = data.at(i); + + if (0x00 == b) { + temp += QString("\\NUL"); + } else if (0x01 == b) { + temp += QString("\\SOH"); + } else if (0x02 == b) { + temp += QString("\\STX"); + } else if (0x03 == b) { + temp += QString("\\ETX"); + } else if (0x04 == b) { + temp += QString("\\EOT"); + } else if (0x05 == b) { + temp += QString("\\ENQ"); + } else if (0x06 == b) { + temp += QString("\\ACK"); + } else if (0x07 == b) { + temp += QString("\\BEL"); + } else if (0x08 == b) { + temp += QString("\\BS"); + } else if (0x09 == b) { + temp += QString("\\HT"); + } else if (0x0A == b) { + temp += QString("\\LF"); + } else if (0x0B == b) { + temp += QString("\\VT"); + } else if (0x0C == b) { + temp += QString("\\FF"); + } else if (0x0D == b) { + temp += QString("\\CR"); + } else if (0x0E == b) { + temp += QString("\\SO"); + } else if (0x0F == b) { + temp += QString("\\SI"); + } else if (0x10 == b) { + temp += QString("\\DLE"); + } else if (0x11 == b) { + temp += QString("\\DC1"); + } else if (0x12 == b) { + temp += QString("\\DC2"); + } else if (0x13 == b) { + temp += QString("\\DC3"); + } else if (0x14 == b) { + temp += QString("\\DC4"); + } else if (0x15 == b) { + temp += QString("\\NAK"); + } else if (0x16 == b) { + temp += QString("\\SYN"); + } else if (0x17 == b) { + temp += QString("\\ETB"); + } else if (0x18 == b) { + temp += QString("\\CAN"); + } else if (0x19 == b) { + temp += QString("\\EM"); + } else if (0x1A == b) { + temp += QString("\\SUB"); + } else if (0x1B == b) { + temp += QString("\\ESC"); + } else if (0x1C == b) { + temp += QString("\\FS"); + } else if (0x1D == b) { + temp += QString("\\GS"); + } else if (0x1E == b) { + temp += QString("\\RS"); + } else if (0x1F == b) { + temp += QString("\\US"); + } else if (0x7F == b) { + temp += QString("\\x7F"); + } else if (0x5C == b) { + temp += QString("\\x5C"); + } else if (0x20 >= b) { + temp += QString("\\x%1").arg(decimalToStrHex((quint8)b)); + } else { + temp += QString("%1").arg(b); + } + } + + return temp.trimmed(); +} + +QByteArray QUIHelper::hexStrToByteArray(const QString &str) +{ + QByteArray senddata; + int hexdata, lowhexdata; + int hexdatalen = 0; + int len = str.length(); + senddata.resize(len / 2); + char lstr, hstr; + + for (int i = 0; i < len;) { + hstr = str.at(i).toLatin1(); + if (hstr == ' ') { + i++; + continue; + } + + i++; + if (i >= len) { + break; + } + + lstr = str.at(i).toLatin1(); + hexdata = convertHexChar(hstr); + lowhexdata = convertHexChar(lstr); + + if ((hexdata == 16) || (lowhexdata == 16)) { + break; + } else { + hexdata = hexdata * 16 + lowhexdata; + } + + i++; + senddata[hexdatalen] = (char)hexdata; + hexdatalen++; + } + + senddata.resize(hexdatalen); + return senddata; +} + +char QUIHelper::convertHexChar(char ch) +{ + if ((ch >= '0') && (ch <= '9')) { + return ch - 0x30; + } else if ((ch >= 'A') && (ch <= 'F')) { + return ch - 'A' + 10; + } else if ((ch >= 'a') && (ch <= 'f')) { + return ch - 'a' + 10; + } else { + return (-1); + } +} + +QByteArray QUIHelper::asciiStrToByteArray(const QString &str) +{ + QByteArray buffer; + int len = str.length(); + QString letter; + QString hex; + + for (int i = 0; i < len; i++) { + letter = str.at(i); + + if (letter == "\\") { + i++; + letter = str.mid(i, 1); + + if (letter == "x") { + i++; + hex = str.mid(i, 2); + buffer.append(strHexToDecimal(hex)); + i++; + continue; + } else if (letter == "N") { + i++; + hex = str.mid(i, 1); + + if (hex == "U") { + i++; + hex = str.mid(i, 1); + + if (hex == "L") { //NUL=0x00 + buffer.append((char)0x00); + continue; + } + } else if (hex == "A") { + i++; + hex = str.mid(i, 1); + + if (hex == "K") { //NAK=0x15 + buffer.append(0x15); + continue; + } + } + } else if (letter == "S") { + i++; + hex = str.mid(i, 1); + + if (hex == "O") { + i++; + hex = str.mid(i, 1); + + if (hex == "H") { //SOH=0x01 + buffer.append(0x01); + continue; + } else { //SO=0x0E + buffer.append(0x0E); + i--; + continue; + } + } else if (hex == "T") { + i++; + hex = str.mid(i, 1); + + if (hex == "X") { //STX=0x02 + buffer.append(0x02); + continue; + } + } else if (hex == "I") { //SI=0x0F + buffer.append(0x0F); + continue; + } else if (hex == "Y") { + i++; + hex = str.mid(i, 1); + + if (hex == "N") { //SYN=0x16 + buffer.append(0x16); + continue; + } + } else if (hex == "U") { + i++; + hex = str.mid(i, 1); + + if (hex == "B") { //SUB=0x1A + buffer.append(0x1A); + continue; + } + } + } else if (letter == "E") { + i++; + hex = str.mid(i, 1); + + if (hex == "T") { + i++; + hex = str.mid(i, 1); + + if (hex == "X") { //ETX=0x03 + buffer.append(0x03); + continue; + } else if (hex == "B") { //ETB=0x17 + buffer.append(0x17); + continue; + } + } else if (hex == "O") { + i++; + hex = str.mid(i, 1); + + if (hex == "T") { //EOT=0x04 + buffer.append(0x04); + continue; + } + } else if (hex == "N") { + i++; + hex = str.mid(i, 1); + + if (hex == "Q") { //ENQ=0x05 + buffer.append(0x05); + continue; + } + } else if (hex == "M") { //EM=0x19 + buffer.append(0x19); + continue; + } else if (hex == "S") { + i++; + hex = str.mid(i, 1); + + if (hex == "C") { //ESC=0x1B + buffer.append(0x1B); + continue; + } + } + } else if (letter == "A") { + i++; + hex = str.mid(i, 1); + + if (hex == "C") { + i++; + hex = str.mid(i, 1); + + if (hex == "K") { //ACK=0x06 + buffer.append(0x06); + continue; + } + } + } else if (letter == "B") { + i++; + hex = str.mid(i, 1); + + if (hex == "E") { + i++; + hex = str.mid(i, 1); + + if (hex == "L") { //BEL=0x07 + buffer.append(0x07); + continue; + } + } else if (hex == "S") { //BS=0x08 + buffer.append(0x08); + continue; + } + } else if (letter == "C") { + i++; + hex = str.mid(i, 1); + + if (hex == "R") { //CR=0x0D + buffer.append(0x0D); + continue; + } else if (hex == "A") { + i++; + hex = str.mid(i, 1); + + if (hex == "N") { //CAN=0x18 + buffer.append(0x18); + continue; + } + } + } else if (letter == "D") { + i++; + hex = str.mid(i, 1); + + if (hex == "L") { + i++; + hex = str.mid(i, 1); + + if (hex == "E") { //DLE=0x10 + buffer.append(0x10); + continue; + } + } else if (hex == "C") { + i++; + hex = str.mid(i, 1); + + if (hex == "1") { //DC1=0x11 + buffer.append(0x11); + continue; + } else if (hex == "2") { //DC2=0x12 + buffer.append(0x12); + continue; + } else if (hex == "3") { //DC3=0x13 + buffer.append(0x13); + continue; + } else if (hex == "4") { //DC2=0x14 + buffer.append(0x14); + continue; + } + } + } else if (letter == "F") { + i++; + hex = str.mid(i, 1); + + if (hex == "F") { //FF=0x0C + buffer.append(0x0C); + continue; + } else if (hex == "S") { //FS=0x1C + buffer.append(0x1C); + continue; + } + } else if (letter == "H") { + i++; + hex = str.mid(i, 1); + + if (hex == "T") { //HT=0x09 + buffer.append(0x09); + continue; + } + } else if (letter == "L") { + i++; + hex = str.mid(i, 1); + + if (hex == "F") { //LF=0x0A + buffer.append(0x0A); + continue; + } + } else if (letter == "G") { + i++; + hex = str.mid(i, 1); + + if (hex == "S") { //GS=0x1D + buffer.append(0x1D); + continue; + } + } else if (letter == "R") { + i++; + hex = str.mid(i, 1); + + if (hex == "S") { //RS=0x1E + buffer.append(0x1E); + continue; + } + } else if (letter == "U") { + i++; + hex = str.mid(i, 1); + + if (hex == "S") { //US=0x1F + buffer.append(0x1F); + continue; + } + } else if (letter == "V") { + i++; + hex = str.mid(i, 1); + + if (hex == "T") { //VT=0x0B + buffer.append(0x0B); + continue; + } + } else if (letter == "\\") { + //濡傛灉杩炵潃鐨勬槸澶氫釜\\鍒欏搴旀坊鍔燶瀵瑰簲鐨16杩涘埗0x5C + buffer.append(0x5C); + continue; + } else { + //灏嗗搴旂殑\[鍓嶉潰鐨刓\涔熻鍔犲叆 + buffer.append(0x5C); + buffer.append(letter.toLatin1()); + continue; + } + } + + buffer.append(str.mid(i, 1).toLatin1()); + + } + + return buffer; +} + +QString QUIHelper::byteArrayToHexStr(const QByteArray &data) +{ + QString temp = ""; + QString hex = data.toHex(); + + for (int i = 0; i < hex.length(); i = i + 2) { + temp += hex.mid(i, 2) + " "; + } + + return temp.trimmed().toUpper(); +} + +QString QUIHelper::getSaveName(const QString &filter, QString defaultDir) +{ + return QFileDialog::getSaveFileName(0, "閫夋嫨鏂囦欢", defaultDir , filter); +} + +QString QUIHelper::getFileName(const QString &filter, QString defaultDir) +{ + return QFileDialog::getOpenFileName(0, "閫夋嫨鏂囦欢", defaultDir , filter); +} + +QStringList QUIHelper::getFileNames(const QString &filter, QString defaultDir) +{ + return QFileDialog::getOpenFileNames(0, "閫夋嫨鏂囦欢", defaultDir, filter); +} + +QString QUIHelper::getFolderName() +{ + return QFileDialog::getExistingDirectory(); +} + +QString QUIHelper::getFileNameWithExtension(const QString &strFilePath) +{ + QFileInfo fileInfo(strFilePath); + return fileInfo.fileName(); +} + +QStringList QUIHelper::getFolderFileNames(const QStringList &filter) +{ + QStringList fileList; + QString strFolder = QFileDialog::getExistingDirectory(); + + if (!strFolder.length() == 0) { + QDir myFolder(strFolder); + + if (myFolder.exists()) { + fileList = myFolder.entryList(filter); + } + } + + return fileList; +} + +bool QUIHelper::folderIsExist(const QString &strFolder) +{ + QDir tempFolder(strFolder); + return tempFolder.exists(); +} + +bool QUIHelper::fileIsExist(const QString &strFile) +{ + QFile tempFile(strFile); + return tempFile.exists(); +} + +bool QUIHelper::copyFile(const QString &sourceFile, const QString &targetFile) +{ + bool ok; + ok = QFile::copy(sourceFile, targetFile); + //灏嗗鍒惰繃鍘荤殑鏂囦欢鍙灞炴у彇娑 + ok = QFile::setPermissions(targetFile, QFile::WriteOwner); + return ok; +} + +void QUIHelper::deleteDirectory(const QString &path) +{ + QDir dir(path); + if (!dir.exists()) { + return; + } + + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + QFileInfoList fileList = dir.entryInfoList(); + + foreach (QFileInfo fi, fileList) { + if (fi.isFile()) { + fi.dir().remove(fi.fileName()); + } else { + deleteDirectory(fi.absoluteFilePath()); + dir.rmdir(fi.absoluteFilePath()); + } + } +} + +bool QUIHelper::ipLive(const QString &ip, int port, int timeout) +{ + QTcpSocket tcpClient; + tcpClient.abort(); + tcpClient.connectToHost(ip, port); + //瓒呮椂娌℃湁杩炴帴涓婂垯鍒ゆ柇涓嶅湪绾 + return tcpClient.waitForConnected(timeout); +} + +QString QUIHelper::getHtml(const QString &url) +{ + QNetworkAccessManager *manager = new QNetworkAccessManager(); + QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url))); + QByteArray responseData; + QEventLoop eventLoop; + QObject::connect(manager, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit())); + eventLoop.exec(); + responseData = reply->readAll(); + return QString(responseData); +} + +QString QUIHelper::getNetIP(const QString &webCode) +{ + QString web = webCode; + web = web.replace(' ', ""); + web = web.replace("\r", ""); + web = web.replace("\n", ""); + QStringList list = web.split("
"); + QString tar = list.at(3); + QStringList ip = tar.split("="); + return ip.at(1); +} + +QString QUIHelper::getLocalIP() +{ + QStringList ips; + QList addrs = QNetworkInterface::allAddresses(); + foreach (QHostAddress addr, addrs) { + QString ip = addr.toString(); + if (QUIHelper::isIP(ip)) { + ips << ip; + } + } + + //浼樺厛鍙192寮澶寸殑IP,濡傛灉鑾峰彇涓嶅埌IP鍒欏彇127.0.0.1 + QString ip = "127.0.0.1"; + foreach (QString str, ips) { + if (str.startsWith("192.168.1") || str.startsWith("192")) { + ip = str; + break; + } + } + + return ip; +} + +QString QUIHelper::urlToIP(const QString &url) +{ + QHostInfo host = QHostInfo::fromName(url); + return host.addresses().at(0).toString(); +} + +bool QUIHelper::isWebOk() +{ + //鑳芥帴閫氱櫨搴P璇存槑鍙互閫氬缃 + return ipLive("115.239.211.112", 80); +} + +void QUIHelper::showMessageBoxInfo(const QString &info, int closeSec, bool exec) +{ +#ifdef Q_OS_ANDROID + QAndroid::Instance()->makeToast(info); +#else + if (exec) { + QUIMessageBox msg; + msg.setMessage(info, 0, closeSec); + msg.exec(); + } else { + QUIMessageBox::Instance()->setMessage(info, 0, closeSec); + QUIMessageBox::Instance()->show(); + } +#endif +} + +void QUIHelper::showMessageBoxError(const QString &info, int closeSec, bool exec) +{ +#ifdef Q_OS_ANDROID + QAndroid::Instance()->makeToast(info); +#else + if (exec) { + QUIMessageBox msg; + msg.setMessage(info, 2, closeSec); + msg.exec(); + } else { + QUIMessageBox::Instance()->setMessage(info, 2, closeSec); + QUIMessageBox::Instance()->show(); + } +#endif +} + +int QUIHelper::showMessageBoxQuestion(const QString &info) +{ + QUIMessageBox msg; + msg.setMessage(info, 1); + return msg.exec(); +} + +QString QUIHelper::showInputBox(const QString &title, int type, int closeSec, + QString defaultValue, bool pwd) +{ + QUIInputBox input; + input.setParameter(title, type, closeSec, defaultValue, pwd); + input.exec(); + return input.getValue(); +} + +void QUIHelper::showDateSelect(QString &dateStart, QString &dateEnd, const QString &format) +{ + QUIDateSelect select; + select.setFormat(format); + select.exec(); + dateStart = select.getStartDateTime(); + dateEnd = select.getEndDateTime(); +} + + +IconHelper *IconHelper::self = NULL; +IconHelper *IconHelper::Instance() +{ + if (!self) { + QMutex mutex; + QMutexLocker locker(&mutex); + if (!self) { + self = new IconHelper; + } + } + + return self; +} + +IconHelper::IconHelper(QObject *) : QObject(qApp) +{ + int fontId = QFontDatabase::addApplicationFont(":/image/fontawesome-webfont.ttf"); + QStringList fontName = QFontDatabase::applicationFontFamilies(fontId); + + if (fontName.count() > 0) { + iconFont = QFont(fontName.at(0)); + } else { + qDebug() << "load fontawesome-webfont.ttf error"; + } +} + +void IconHelper::setIcon(QLabel *lab, QChar c, quint32 size) +{ + iconFont.setPixelSize(size); + lab->setFont(iconFont); + lab->setText(c); +} + +void IconHelper::setIcon(QAbstractButton *btn, QChar c, quint32 size) +{ + iconFont.setPixelSize(size); + btn->setFont(iconFont); + btn->setText(c); +} + +QPixmap IconHelper::getPixmap(const QString &color, QChar c, quint32 size, + quint32 pixWidth, quint32 pixHeight) +{ + QPixmap pix(pixWidth, pixHeight); + pix.fill(Qt::transparent); + + QPainter painter; + painter.begin(&pix); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + painter.setPen(QColor(color)); + painter.setBrush(QColor(color)); + + iconFont.setPixelSize(size); + painter.setFont(iconFont); + painter.drawText(pix.rect(), Qt::AlignCenter, c); + painter.end(); + + return pix; +} + +QPixmap IconHelper::getPixmap(QToolButton *btn, bool normal) +{ + QPixmap pix; + int index = btns.indexOf(btn); + if (index >= 0) { + if (normal) { + pix = pixNormal.at(index); + } else { + pix = pixDark.at(index); + } + } + + return pix; +} + +void IconHelper::setStyle(QWidget *widget, const QString &type, int borderWidth, const QString &borderColor, + const QString &normalBgColor, const QString &darkBgColor, + const QString &normalTextColor, const QString &darkTextColor) +{ + QString strBorder; + if (type == "top") { + strBorder = QString("border-width:%1px 0px 0px 0px;padding-top:%1px;padding-bottom:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "right") { + strBorder = QString("border-width:0px %1px 0px 0px;padding-right:%1px;padding-left:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "bottom") { + strBorder = QString("border-width:0px 0px %1px 0px;padding-bottom:%1px;padding-top:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "left") { + strBorder = QString("border-width:0px 0px 0px %1px;padding-left:%1px;padding-right:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } + + QStringList qss; + qss.append(QString("QWidget[flag=\"%1\"] QAbstractButton{border-style:none;border-radius:0px;padding:5px;color:%2;background:%3;}") + .arg(type).arg(normalTextColor).arg(normalBgColor)); + + qss.append(QString("QWidget[flag=\"%1\"] QAbstractButton:hover," + "QWidget[flag=\"%1\"] QAbstractButton:pressed," + "QWidget[flag=\"%1\"] QAbstractButton:checked{" + "border-style:solid;%2border-color:%3;color:%4;background:%5;}") + .arg(type).arg(strBorder).arg(borderColor).arg(darkTextColor).arg(darkBgColor)); + + widget->setStyleSheet(qss.join("")); +} + +void IconHelper::removeStyle(QList btns) +{ + for (int i = 0; i < btns.count(); i++) { + for (int j = 0; j < this->btns.count(); j++) { + if (this->btns.at(j) == btns.at(i)) { + this->btns.at(j)->removeEventFilter(this); + this->btns.removeAt(j); + this->pixNormal.removeAt(j); + this->pixDark.removeAt(j); + break; + } + } + } +} + +void IconHelper::setStyle(QWidget *widget, QList btns, QList pixChar, + quint32 iconSize, quint32 iconWidth, quint32 iconHeight, + const QString &type, int borderWidth, const QString &borderColor, + const QString &normalBgColor, const QString &darkBgColor, + const QString &normalTextColor, const QString &darkTextColor) +{ + int btnCount = btns.count(); + int charCount = pixChar.count(); + if (btnCount <= 0 || charCount <= 0 || btnCount != charCount) { + return; + } + + QString strBorder; + if (type == "top") { + strBorder = QString("border-width:%1px 0px 0px 0px;padding-top:%1px;padding-bottom:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "right") { + strBorder = QString("border-width:0px %1px 0px 0px;padding-right:%1px;padding-left:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "bottom") { + strBorder = QString("border-width:0px 0px %1px 0px;padding-bottom:%1px;padding-top:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } else if (type == "left") { + strBorder = QString("border-width:0px 0px 0px %1px;padding-left:%1px;padding-right:%2px;") + .arg(borderWidth).arg(borderWidth * 2); + } + + //濡傛灉鍥炬爣鏄乏渚ф樉绀哄垯闇瑕佽娌℃湁閫変腑鐨勬寜閽乏渚т篃鏈夊姞娣辩殑杈规,棰滆壊涓鸿儗鏅鑹 + QStringList qss; + if (btns.at(0)->toolButtonStyle() == Qt::ToolButtonTextBesideIcon) { + qss.append(QString("QWidget[flag=\"%1\"] QAbstractButton{border-style:solid;border-radius:0px;%2border-color:%3;color:%4;background:%5;}") + .arg(type).arg(strBorder).arg(normalBgColor).arg(normalTextColor).arg(normalBgColor)); + } else { + qss.append(QString("QWidget[flag=\"%1\"] QAbstractButton{border-style:none;border-radius:0px;padding:5px;color:%2;background:%3;}") + .arg(type).arg(normalTextColor).arg(normalBgColor)); + } + + qss.append(QString("QWidget[flag=\"%1\"] QAbstractButton:hover," + "QWidget[flag=\"%1\"] QAbstractButton:pressed," + "QWidget[flag=\"%1\"] QAbstractButton:checked{" + "border-style:solid;%2border-color:%3;color:%4;background:%5;}") + .arg(type).arg(strBorder).arg(borderColor).arg(darkTextColor).arg(darkBgColor)); + + qss.append(QString("QWidget#%1{background:%2;}").arg(widget->objectName()).arg(normalBgColor)); + + qss.append(QString("QWidget>QToolButton{border-width:0px;}")); + qss.append(QString("QWidget>QToolButton{background-color:%1;color:%2;}") + .arg(normalBgColor).arg(normalTextColor)); + qss.append(QString("QWidget>QToolButton:hover,QWidget>QToolButton:pressed,QWidget>QToolButton:checked{background-color:%1;color:%2;}") + .arg(darkBgColor).arg(darkTextColor)); + + widget->setStyleSheet(qss.join("")); + + for (int i = 0; i < btnCount; i++) { + //瀛樺偍瀵瑰簲鎸夐挳瀵硅薄,鏂逛究榧犳爣绉讳笂鍘荤殑鏃跺欏垏鎹㈠浘鐗 + QPixmap pixNormal = getPixmap(normalTextColor, QChar(pixChar.at(i)), iconSize, iconWidth, iconHeight); + QPixmap pixDark = getPixmap(darkTextColor, QChar(pixChar.at(i)), iconSize, iconWidth, iconHeight); + + btns.at(i)->setIcon(QIcon(pixNormal)); + btns.at(i)->setIconSize(QSize(iconWidth, iconHeight)); + btns.at(i)->installEventFilter(this); + + this->btns.append(btns.at(i)); + this->pixNormal.append(pixNormal); + this->pixDark.append(pixDark); + } +} + +void IconHelper::setStyle(QFrame *frame, QList btns, QList pixChar, + quint32 iconSize, quint32 iconWidth, quint32 iconHeight, + const QString &normalBgColor, const QString &darkBgColor, + const QString &normalTextColor, const QString &darkTextColor) +{ + int btnCount = btns.count(); + int charCount = pixChar.count(); + if (btnCount <= 0 || charCount <= 0 || btnCount != charCount) { + return; + } + + QStringList qss; + qss.append(QString("QFrame>QToolButton{border-style:none;border-width:0px;}")); + qss.append(QString("QFrame>QToolButton{background-color:%1;color:%2;}") + .arg(normalBgColor).arg(normalTextColor)); + qss.append(QString("QFrame>QToolButton:hover,QFrame>QToolButton:pressed,QFrame>QToolButton:checked{background-color:%1;color:%2;}") + .arg(darkBgColor).arg(darkTextColor)); + + frame->setStyleSheet(qss.join("")); + + for (int i = 0; i < btnCount; i++) { + //瀛樺偍瀵瑰簲鎸夐挳瀵硅薄,鏂逛究榧犳爣绉讳笂鍘荤殑鏃跺欏垏鎹㈠浘鐗 + QPixmap pixNormal = getPixmap(normalTextColor, QChar(pixChar.at(i)), iconSize, iconWidth, iconHeight); + QPixmap pixDark = getPixmap(darkTextColor, QChar(pixChar.at(i)), iconSize, iconWidth, iconHeight); + + btns.at(i)->setIcon(QIcon(pixNormal)); + btns.at(i)->setIconSize(QSize(iconWidth, iconHeight)); + btns.at(i)->installEventFilter(this); + + this->btns.append(btns.at(i)); + this->pixNormal.append(pixNormal); + this->pixDark.append(pixDark); + } +} + +bool IconHelper::eventFilter(QObject *watched, QEvent *event) +{ + if (watched->inherits("QToolButton")) { + QToolButton *btn = (QToolButton *)watched; + int index = btns.indexOf(btn); + if (index >= 0) { + if (event->type() == QEvent::Enter) { + btn->setIcon(QIcon(pixDark.at(index))); + } else if (event->type() == QEvent::Leave) { + if (btn->isChecked()) { + btn->setIcon(QIcon(pixDark.at(index))); + } else { + btn->setIcon(QIcon(pixNormal.at(index))); + } + } + } + } + + return QObject::eventFilter(watched, event); +} + + +QChar QUIConfig::IconMain = QChar(0xf072); +QChar QUIConfig::IconMenu = QChar(0xf0d7); +QChar QUIConfig::IconMin = QChar(0xf068); +QChar QUIConfig::IconMax = QChar(0xf2d2); +QChar QUIConfig::IconNormal = QChar(0xf2d0); +QChar QUIConfig::IconClose = QChar(0xf00d); + +#ifdef __arm__ +#ifdef Q_OS_ANDROID +QString QUIConfig::FontName = "Droid Sans Fallback"; +int QUIConfig::FontSize = 50; +#else +QString QUIConfig::FontName = "WenQuanYi Micro Hei"; +int QUIConfig::FontSize = 18; +#endif +#else +QString QUIConfig::FontName = "Microsoft Yahei"; +int QUIConfig::FontSize = 12; +#endif + +QString QUIConfig::TextColor = "#F0F0F0"; +QString QUIConfig::PanelColor = "#F0F0F0"; +QString QUIConfig::BorderColor = "#F0F0F0"; +QString QUIConfig::NormalColorStart = "#F0F0F0"; +QString QUIConfig::NormalColorEnd = "#F0F0F0"; +QString QUIConfig::DarkColorStart = "#F0F0F0"; +QString QUIConfig::DarkColorEnd = "#F0F0F0"; +QString QUIConfig::HighColor = "#F0F0F0"; diff --git a/netserver/api/quiwidget.h b/netserver/api/quiwidget.h new file mode 100644 index 0000000..1056174 --- /dev/null +++ b/netserver/api/quiwidget.h @@ -0,0 +1,646 @@ +锘#ifndef QUIWIDGET_H +#define QUIWIDGET_H + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) +#define TIME qPrintable(QTime::currentTime().toString("HH:mm:ss")) +#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd")) +#define QTIME qPrintable(QTime::currentTime().toString("HH-mm-ss")) +#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss")) +#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) +#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz")) + +#ifdef Q_OS_WIN +#define NEWLINE "\r\n" +#else +#define NEWLINE "\n" +#endif + +#ifdef __arm__ +#define TitleMinSize 40 +#else +#define TitleMinSize 30 +#endif + +/** + * QUI鏃犺竟妗嗙獥浣撴帶浠 浣滆:feiyangqingyun(QQ:517216493) + * 1:鍐呯疆 N >= 12 濂楃簿缇庢牱寮,鍙洿鎺ュ垏鎹,涔熷彲鑷畾涔夋牱寮忚矾寰 + * 2:鍙缃儴浠(宸︿笂瑙掑浘鏍/鏈灏忓寲鎸夐挳/鏈澶у寲鎸夐挳/鍏抽棴鎸夐挳)鐨勫浘鏍囨垨鑰呭浘鐗囧強鏄惁鍙 + * 3:鍙泦鎴愯璁″笀鎻掍欢,鐩存帴鎷栨洺浣跨敤,鎵瑙佸嵆鎵寰 + * 4:濡傛灉闇瑕佺獥浣撳彲鎷栧姩澶у皬,璁剧疆 setSizeGripEnabled(true); + * 5:鍙缃叏灞鏍峰紡 setStyle + * 6:鍙脊鍑烘秷鎭,鍙夐樆濉炴ā寮忓拰涓嶉樆濉,榛樿涓嶉樆濉 showMessageBoxInfo + * 7:鍙脊鍑洪敊璇,鍙夐樆濉炴ā寮忓拰涓嶉樆濉,榛樿涓嶉樆濉 showMessageBoxError + * 8:鍙脊鍑鸿闂 showMessageBoxError + * 9:鍙脊鍑鸿緭鍏ユ showInputBox + * 10:娑堟伅妗嗘敮鎸佽缃掕鏃跺叧闂 + * 11:闆嗘垚鍥惧舰瀛椾綋璁剧疆鏂规硶鍙婃牴鎹寚瀹氭枃瀛楄幏鍙栧浘鐗 + * 12:闆嗘垚璁剧疆绐椾綋灞呬腑鏄剧ず/璁剧疆缈昏瘧鏂囦欢/璁剧疆缂栫爜/璁剧疆寤舵椂/璁剧疆绯荤粺鏃堕棿绛夐潤鎬佹柟娉 + * 13:闆嗘垚鑾峰彇搴旂敤绋嬪簭鏂囦欢鍚/鏂囦欢璺緞 绛夋柟娉 + */ + +#include "head.h" +#include "app.h" + +#ifdef quc +#if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) +#include +#else +#include +#endif + +class QDESIGNER_WIDGET_EXPORT QUIWidget : public QDialog +#else +class QUIWidget : public QDialog +#endif + +{ + Q_OBJECT + Q_ENUMS(Style) + Q_PROPERTY(QString title READ getTitle WRITE setTitle) + Q_PROPERTY(Qt::Alignment alignment READ getAlignment WRITE setAlignment) + +public: + //灏嗛儴鍒嗗璞′綔涓烘灇涓惧兼毚闇茬粰澶栭儴 + enum Widget { + Lab_Ico = 0, //宸︿笂瑙掑浘鏍 + BtnMenu = 1, //涓嬫媺鑿滃崟鎸夐挳 + BtnMenu_Min = 2, //鏈灏忓寲鎸夐挳 + BtnMenu_Max = 3, //鏈澶у寲鎸夐挳 + BtnMenu_Normal = 4, //杩樺師鎸夐挳 + BtnMenu_Close = 5 //鍏抽棴鎸夐挳 + }; + + //鏍峰紡鏋氫妇 + enum Style { + Style_Silvery = 0, //閾惰壊鏍峰紡 + Style_Blue = 1, //钃濊壊鏍峰紡 + Style_LightBlue = 2, //娣¤摑鑹叉牱寮 + Style_DarkBlue = 3, //娣辫摑鑹叉牱寮 + Style_Gray = 4, //鐏拌壊鏍峰紡 + Style_LightGray = 5, //娴呯伆鑹叉牱寮 + Style_DarkGray = 6, //娣辩伆鑹叉牱寮 + Style_Black = 7, //榛戣壊鏍峰紡 + Style_LightBlack = 8, //娴呴粦鑹叉牱寮 + Style_DarkBlack = 12, //娣遍粦鑹叉牱寮 + Style_PSBlack = 10, //PS榛戣壊鏍峰紡 + Style_FlatBlack = 11, //榛戣壊鎵佸钩鏍峰紡 + Style_FlatWhite = 12 //鐧借壊鎵佸钩鏍峰紡 + }; + +public: + explicit QUIWidget(QWidget *parent = 0); + ~QUIWidget(); + +protected: + bool eventFilter(QObject *obj, QEvent *evt); + +private: + QVBoxLayout *verticalLayout1; + QWidget *widgetMain; + QVBoxLayout *verticalLayout2; + QWidget *widgetTitle; + QHBoxLayout *horizontalLayout4; + QLabel *labIco; + QLabel *labTitle; + QWidget *widgetMenu; + QHBoxLayout *horizontalLayout; + QToolButton *btnMenu; + QPushButton *btnMenu_Min; + QPushButton *btnMenu_Max; + QPushButton *btnMenu_Close; + QWidget *widget; + QVBoxLayout *verticalLayout3; + +private: + QString title; //鏍囬 + Qt::Alignment alignment; //鏍囬鏂囨湰瀵归綈 + bool minHide; //鏈灏忓寲闅愯棌 + QWidget *mainWidget; //涓荤獥浣撳璞 + +public: + QLabel *getLabIco() const; + QLabel *getLabTitle() const; + QToolButton *getBtnMenu() const; + QPushButton *getBtnMenuMin() const; + QPushButton *getBtnMenuMax() const; + QPushButton *getBtnMenuMClose() const; + + Style getStyle() const; + QString getTitle() const; + Qt::Alignment getAlignment() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +private slots: + void initControl(); //鍒濆鍖栨帶浠 + void initForm(); //鍒濆鍖栫獥浣 + void changeStyle(); //鏇存崲鏍峰紡 + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); + +public Q_SLOTS: + //璁剧疆閮ㄤ欢鍥炬爣 + void setIcon(QUIWidget::Widget widget, QChar str, quint32 size = 12); + void setIconMain(QChar str, quint32 size = 12); + //璁剧疆閮ㄤ欢鍥剧墖 + void setPixmap(QUIWidget::Widget widget, const QString &file, const QSize &size = QSize(16, 16)); + //璁剧疆閮ㄤ欢鏄惁鍙 + void setVisible(QUIWidget::Widget widget, bool visible = true); + //璁剧疆鍙湁鍏抽棴鎸夐挳 + void setOnlyCloseBtn(); + + //璁剧疆鏍囬鏍忛珮搴 + void setTitleHeight(int height); + //璁剧疆鎸夐挳缁熶竴瀹藉害 + void setBtnWidth(int width); + + //璁剧疆鏍囬鍙婃枃鏈牱寮 + void setTitle(const QString &title); + void setAlignment(Qt::Alignment alignment); + + //璁剧疆鏈灏忓寲闅愯棌 + void setMinHide(bool minHide); + + //璁剧疆涓荤獥浣 + void setMainWidget(QWidget *mainWidget); + +Q_SIGNALS: + void changeStyle(const QString &qssFile); + void closing(); +}; + +//寮瑰嚭淇℃伅妗嗙被 +class QUIMessageBox : public QDialog +{ + Q_OBJECT + +public: + static QUIMessageBox *Instance(); + explicit QUIMessageBox(QWidget *parent = 0); + ~QUIMessageBox(); + +protected: + void closeEvent(QCloseEvent *); + bool eventFilter(QObject *obj, QEvent *evt); + +private: + static QUIMessageBox *self; + + QVBoxLayout *verticalLayout1; + QWidget *widgetTitle; + QHBoxLayout *horizontalLayout3; + QLabel *labIco; + QLabel *labTitle; + QLabel *labTime; + QWidget *widgetMenu; + QHBoxLayout *horizontalLayout4; + QPushButton *btnMenu_Close; + QWidget *widgetMain; + QVBoxLayout *verticalLayout2; + QFrame *frame; + QVBoxLayout *verticalLayout4; + QHBoxLayout *horizontalLayout1; + QLabel *labIcoMain; + QSpacerItem *horizontalSpacer1; + QLabel *labInfo; + QHBoxLayout *horizontalLayout2; + QSpacerItem *horizontalSpacer2; + QPushButton *btnOk; + QPushButton *btnCancel; + +private: + int closeSec; //鎬绘樉绀烘椂闂 + int currentSec; //褰撳墠宸叉樉绀烘椂闂 + +private slots: + void initControl(); //鍒濆鍖栨帶浠 + void initForm(); //鍒濆鍖栫獥浣 + void checkSec(); //鏍¢獙鍊掕鏃 + +private slots: + void on_btnOk_clicked(); + void on_btnMenu_Close_clicked(); + +public Q_SLOTS: + void setIconMain(QChar str, quint32 size = 12); + void setMessage(const QString &msg, int type, int closeSec = 0); +}; + +//寮瑰嚭杈撳叆妗嗙被 +class QUIInputBox : public QDialog +{ + Q_OBJECT + +public: + static QUIInputBox *Instance(); + explicit QUIInputBox(QWidget *parent = 0); + ~QUIInputBox(); + +protected: + void closeEvent(QCloseEvent *); + bool eventFilter(QObject *obj, QEvent *evt); + +private: + static QUIInputBox *self; + + QVBoxLayout *verticalLayout1; + QWidget *widgetTitle; + QHBoxLayout *horizontalLayout1; + QLabel *labIco; + QLabel *labTitle; + QLabel *labTime; + QWidget *widgetMenu; + QHBoxLayout *horizontalLayout2; + QPushButton *btnMenu_Close; + QWidget *widgetMain; + QVBoxLayout *verticalLayout2; + QFrame *frame; + QVBoxLayout *verticalLayout3; + QLabel *labInfo; + QLineEdit *txtValue; + QComboBox *cboxValue; + QHBoxLayout *lay; + QSpacerItem *horizontalSpacer; + QPushButton *btnOk; + QPushButton *btnCancel; + +private: + int closeSec; //鎬绘樉绀烘椂闂 + int currentSec; //褰撳墠宸叉樉绀烘椂闂 + QString value; //褰撳墠鍊 + +private slots: + void initControl(); //鍒濆鍖栨帶浠 + void initForm(); //鍒濆鍖栫獥浣 + void checkSec(); //鏍¢獙鍊掕鏃 + +private slots: + void on_btnOk_clicked(); + void on_btnMenu_Close_clicked(); + +public: + QString getValue()const; + +public Q_SLOTS: + void setIconMain(QChar str, quint32 size = 12); + void setParameter(const QString &title, int type = 0, int closeSec = 0, + QString defaultValue = QString(), bool pwd = false); + +}; + +//寮瑰嚭鏃ユ湡閫夋嫨瀵硅瘽妗 +class QUIDateSelect : public QDialog +{ + Q_OBJECT + +public: + static QUIDateSelect *Instance(); + explicit QUIDateSelect(QWidget *parent = 0); + ~QUIDateSelect(); + +protected: + bool eventFilter(QObject *obj, QEvent *evt); + +private: + static QUIDateSelect *self; + + QVBoxLayout *verticalLayout; + QWidget *widgetTitle; + QHBoxLayout *horizontalLayout1; + QLabel *labIco; + QLabel *labTitle; + QWidget *widgetMenu; + QHBoxLayout *horizontalLayout; + QPushButton *btnMenu_Close; + QWidget *widgetMain; + QVBoxLayout *verticalLayout1; + QFrame *frame; + QGridLayout *gridLayout; + QLabel *labStart; + QPushButton *btnOk; + QLabel *labEnd; + QPushButton *btnClose; + QDateTimeEdit *dateStart; + QDateTimeEdit *dateEnd; + +private: + QString startDateTime; //寮濮嬫椂闂 + QString endDateTime; //缁撴潫鏃堕棿 + QString format; //鏃ユ湡鏃堕棿鏍煎紡 + +private slots: + void initControl(); //鍒濆鍖栨帶浠 + void initForm(); //鍒濆鍖栫獥浣 + +private slots: + void on_btnOk_clicked(); + void on_btnMenu_Close_clicked(); + +public: + //鑾峰彇褰撳墠閫夋嫨鐨勫紑濮嬫椂闂村拰缁撴潫鏃堕棿 + QString getStartDateTime() const; + QString getEndDateTime() const; + +public Q_SLOTS: + void setIconMain(QChar str, quint32 size = 12); + void setFormat(const QString &format); + +}; + +//鍏ㄥ眬闈欐佹柟娉曠被 +class QUIHelper : public QObject +{ + Q_OBJECT +public: + //妗岄潰瀹藉害楂樺害 + static int deskWidth(); + static int deskHeight(); + + //绋嬪簭鏈韩鏂囦欢鍚嶇О + static QString appName(); + //绋嬪簭褰撳墠鎵鍦ㄨ矾寰 + static QString appPath(); + + //鍒濆鍖栭殢鏈烘暟绉嶅瓙 + static void initRand(); + + //鏂板缓鐩綍 + static void newDir(const QString &dirName); + + //鍐欏叆娑堟伅鍒伴澶栫殑鐨勬秷鎭棩蹇楁枃浠 + static void writeInfo(const QString &info, const QString &filePath = "log"); + static void writeError(const QString &info, const QString &filePath = "log"); + + //璁剧疆鍏ㄥ眬鏍峰紡 + static void setStyle(QUIWidget::Style style); + static void setStyle(const QString &qssFile, QString &paletteColor, QString &textColor); + static void setStyle(const QString &qssFile, QString &textColor, + QString &panelColor, QString &borderColor, + QString &normalColorStart, QString &normalColorEnd, + QString &darkColorStart, QString &darkColorEnd, + QString &highColor); + + //鏍规嵁QSS鏍峰紡鑾峰彇瀵瑰簲棰滆壊鍊 + static void getQssColor(const QString &qss, QString &textColor, + QString &panelColor, QString &borderColor, + QString &normalColorStart, QString &normalColorEnd, + QString &darkColorStart, QString &darkColorEnd, + QString &highColor); + + //涔濆鏍煎浘鐗 horzSplit-瀹牸1/3/7/9瀹藉害 vertSplit-瀹牸1/3/7/9楂樺害 dstWidth-鐩爣鍥剧墖瀹藉害 dstHeight-鐩爣鍥剧墖楂樺害 + static QPixmap ninePatch(const QString &picName, int horzSplit, int vertSplit, int dstWidth, int dstHeight); + static QPixmap ninePatch(const QPixmap &pix, int horzSplit, int vertSplit, int dstWidth, int dstHeight); + + //璁剧疆鏍囩棰滆壊 + static void setLabStyle(QLabel *lab, quint8 type); + + //璁剧疆绐椾綋灞呬腑鏄剧ず + static void setFormInCenter(QWidget *frm); + //璁剧疆缈昏瘧鏂囦欢 + static void setTranslator(const QString &qmFile = ":/image/qt_zh_CN.qm"); + //璁剧疆缂栫爜 + static void setCode(); + //璁剧疆寤舵椂 + static void sleep(int sec); + //璁剧疆绯荤粺鏃堕棿 + static void setSystemDateTime(const QString &year, const QString &month, const QString &day, + const QString &hour, const QString &min, const QString &sec); + //璁剧疆寮鏈鸿嚜鍚姩 + static void runWithSystem(const QString &strName, const QString &strPath, bool autoRun = true); + + //鍒ゆ柇鏄惁鏄疘P鍦板潃 + static bool isIP(const QString &ip); + + //鍒ゆ柇鏄惁鏄疢AC鍦板潃 + static bool isMac(const QString &mac); + + //鍒ゆ柇鏄惁鏄悎娉曠殑鐢佃瘽鍙风爜 + static bool isTel(const QString &tel); + + //鍒ゆ柇鏄惁鏄悎娉曠殑閭鍦板潃 + static bool isEmail(const QString &email); + + + //16杩涘埗瀛楃涓茶浆10杩涘埗 + static int strHexToDecimal(const QString &strHex); + + //10杩涘埗瀛楃涓茶浆10杩涘埗 + static int strDecimalToDecimal(const QString &strDecimal); + + //2杩涘埗瀛楃涓茶浆10杩涘埗 + static int strBinToDecimal(const QString &strBin); + + //16杩涘埗瀛楃涓茶浆2杩涘埗瀛楃涓 + static QString strHexToStrBin(const QString &strHex); + + //10杩涘埗杞2杩涘埗瀛楃涓蹭竴涓瓧鑺 + static QString decimalToStrBin1(int decimal); + + //10杩涘埗杞2杩涘埗瀛楃涓蹭袱涓瓧鑺 + static QString decimalToStrBin2(int decimal); + + //10杩涘埗杞16杩涘埗瀛楃涓,琛ラ浂. + static QString decimalToStrHex(int decimal); + + + //int杞瓧鑺傛暟缁 + static QByteArray intToByte(int i); + static QByteArray intToByteRec(int i); + + //瀛楄妭鏁扮粍杞琲nt + static int byteToInt(const QByteArray &data); + static int byteToIntRec(const QByteArray &data); + + //ushort杞瓧鑺傛暟缁 + static QByteArray ushortToByte(ushort i); + static QByteArray ushortToByteRec(ushort i); + + //瀛楄妭鏁扮粍杞瑄short + static int byteToUShort(const QByteArray &data); + static int byteToUShortRec(const QByteArray &data); + + //寮傛垨鍔犲瘑绠楁硶 + static QString getXorEncryptDecrypt(const QString &str, char key); + + //寮傛垨鏍¢獙 + static uchar getOrCode(const QByteArray &data); + + //璁$畻鏍¢獙鐮 + static uchar getCheckCode(const QByteArray &data); + + //瀛楃涓茶ˉ鍏 + static QString getValue(quint8 value); + + //CRC鏍¢獙 + static quint16 getRevCrc_16(quint8 *data, int len, quint16 init, const quint16 *table); + static quint16 getCrc_16(quint8 *data, int len, quint16 init, const quint16 *table); + static quint16 getModbus16(quint8 *data, int len); + static QByteArray getCRCCode(const QByteArray &data); + + + //瀛楄妭鏁扮粍杞珹scii瀛楃涓 + static QString byteArrayToAsciiStr(const QByteArray &data); + + //16杩涘埗瀛楃涓茶浆瀛楄妭鏁扮粍 + static QByteArray hexStrToByteArray(const QString &str); + static char convertHexChar(char ch); + + //Ascii瀛楃涓茶浆瀛楄妭鏁扮粍 + static QByteArray asciiStrToByteArray(const QString &str); + + //瀛楄妭鏁扮粍杞16杩涘埗瀛楃涓 + static QString byteArrayToHexStr(const QByteArray &data); + + //鑾峰彇淇濆瓨鐨勬枃浠 + static QString getSaveName(const QString &filter, QString defaultDir = QCoreApplication::applicationDirPath()); + + //鑾峰彇閫夋嫨鐨勬枃浠 + static QString getFileName(const QString &filter, QString defaultDir = QCoreApplication::applicationDirPath()); + + //鑾峰彇閫夋嫨鐨勬枃浠堕泦鍚 + static QStringList getFileNames(const QString &filter, QString defaultDir = QCoreApplication::applicationDirPath()); + + //鑾峰彇閫夋嫨鐨勭洰褰 + static QString getFolderName(); + + //鑾峰彇鏂囦欢鍚,鍚嫇灞曞悕 + static QString getFileNameWithExtension(const QString &strFilePath); + + //鑾峰彇閫夋嫨鏂囦欢澶逛腑鐨勬枃浠 + static QStringList getFolderFileNames(const QStringList &filter); + + //鏂囦欢澶规槸鍚﹀瓨鍦 + static bool folderIsExist(const QString &strFolder); + + //鏂囦欢鏄惁瀛樺湪 + static bool fileIsExist(const QString &strFile); + + //澶嶅埗鏂囦欢 + static bool copyFile(const QString &sourceFile, const QString &targetFile); + + //鍒犻櫎鏂囦欢澶逛笅鎵鏈夋枃浠 + static void deleteDirectory(const QString &path); + + //鍒ゆ柇IP鍦板潃鍙婄鍙f槸鍚﹀湪绾 + static bool ipLive(const QString &ip, int port, int timeout = 1000); + + //鑾峰彇缃戦〉鎵鏈夋簮浠g爜 + static QString getHtml(const QString &url); + + //鑾峰彇鏈満鍏綉IP鍦板潃 + static QString getNetIP(const QString &webCode); + + //鑾峰彇鏈満IP + static QString getLocalIP(); + + //Url鍦板潃杞负IP鍦板潃 + static QString urlToIP(const QString &url); + + //鍒ゆ柇鏄惁閫氬缃 + static bool isWebOk(); + + + //寮瑰嚭娑堟伅妗 + static void showMessageBoxInfo(const QString &info, int closeSec = 0, bool exec = false); + //寮瑰嚭閿欒妗 + static void showMessageBoxError(const QString &info, int closeSec = 0, bool exec = false); + //寮瑰嚭璇㈤棶妗 + static int showMessageBoxQuestion(const QString &info); + + //寮瑰嚭杈撳叆妗 + static QString showInputBox(const QString &title, int type = 0, int closeSec = 0, + QString defaultValue = QString(), bool pwd = false); + + //寮瑰嚭鏃ユ湡閫夋嫨妗 + static void showDateSelect(QString &dateStart, QString &dateEnd, const QString &format = "yyyy-MM-dd"); + +}; + +//鍥惧舰瀛椾綋澶勭悊绫 +class IconHelper : public QObject +{ + Q_OBJECT + +public: + static IconHelper *Instance(); + explicit IconHelper(QObject *parent = 0); + + void setIcon(QLabel *lab, QChar c, quint32 size = 12); + void setIcon(QAbstractButton *btn, QChar c, quint32 size = 12); + QPixmap getPixmap(const QString &color, QChar c, quint32 size = 12, + quint32 pixWidth = 10, quint32 pixHeight = 10); + + //鏍规嵁鎸夐挳鑾峰彇璇ユ寜閽搴旂殑鍥炬爣 + QPixmap getPixmap(QToolButton *btn, bool normal); + + //鎸囧畾瀵艰埅闈㈡澘鏍峰紡,涓嶅甫鍥炬爣 + static void setStyle(QWidget *widget, const QString &type = "left", int borderWidth = 3, + const QString &borderColor = "#029FEA", + const QString &normalBgColor = "#292F38", + const QString &darkBgColor = "#1D2025", + const QString &normalTextColor = "#54626F", + const QString &darkTextColor = "#FDFDFD"); + + //绉婚櫎瀵艰埅闈㈡澘鏍峰紡,闃叉閲嶅 + void removeStyle(QList btns); + + //鎸囧畾QWidget瀵艰埅闈㈡澘鏍峰紡,甯﹀浘鏍囧拰鏁堟灉鍒囨崲 + void setStyle(QWidget *widget, QList btns, QList pixChar, + quint32 iconSize = 12, quint32 iconWidth = 10, quint32 iconHeight = 10, + const QString &type = "left", int borderWidth = 3, + const QString &borderColor = "#029FEA", + const QString &normalBgColor = "#292F38", + const QString &darkBgColor = "#1D2025", + const QString &normalTextColor = "#54626F", + const QString &darkTextColor = "#FDFDFD"); + + //鎸囧畾QFrame瀵艰埅鎸夐挳鏍峰紡,甯﹀浘鏍 + void setStyle(QFrame *frame, QList btns, QList pixChar, + quint32 iconSize = 12, quint32 iconWidth = 10, quint32 iconHeight = 10, + const QString &normalBgColor = "#2FC5A2", + const QString &darkBgColor = "#3EA7E9", + const QString &normalTextColor = "#EEEEEE", + const QString &darkTextColor = "#FFFFFF"); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + static IconHelper *self; //瀵硅薄鑷韩 + QFont iconFont; //鍥惧舰瀛椾綋 + QList btns; //鎸夐挳闃熷垪 + QList pixNormal; //姝e父鍥剧墖闃熷垪 + QList pixDark; //鍔犳繁鍥剧墖闃熷垪 +}; + +//鍏ㄥ眬鍙橀噺鎺у埗 +class QUIConfig +{ +public: + //鍏ㄥ眬鍥炬爣 + static QChar IconMain; //鏍囬鏍忓乏涓婅鍥炬爣 + static QChar IconMenu; //涓嬫媺鑿滃崟鍥炬爣 + static QChar IconMin; //鏈灏忓寲鍥炬爣 + static QChar IconMax; //鏈澶у寲鍥炬爣 + static QChar IconNormal; //杩樺師鍥炬爣 + static QChar IconClose; //鍏抽棴鍥炬爣 + + static QString FontName; //鍏ㄥ眬瀛椾綋鍚嶇О + static int FontSize; //鍏ㄥ眬瀛椾綋澶у皬 + + //鏍峰紡琛ㄩ鑹插 + static QString TextColor; //鏂囧瓧棰滆壊 + static QString PanelColor; //闈㈡澘棰滆壊 + static QString BorderColor; //杈规棰滆壊 + static QString NormalColorStart;//姝e父鐘舵佸紑濮嬮鑹 + static QString NormalColorEnd; //姝e父鐘舵佺粨鏉熼鑹 + static QString DarkColorStart; //鍔犳繁鐘舵佸紑濮嬮鑹 + static QString DarkColorEnd; //鍔犳繁鐘舵佺粨鏉熼鑹 + static QString HighColor; //楂樹寒棰滆壊 +}; + +#endif // QUIWIDGET_H diff --git a/netserver/api/tcpserver1.cpp b/netserver/api/tcpserver1.cpp new file mode 100644 index 0000000..a21aed3 --- /dev/null +++ b/netserver/api/tcpserver1.cpp @@ -0,0 +1,151 @@ +锘#include "tcpserver1.h" +#include "quiwidget.h" + +TcpClient1::TcpClient1(QObject *parent) : QTcpSocket(parent) +{ + ip = "127.0.0.1"; + port = 6907; + deviceID = "SSJC00000001"; + + connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(deleteLater())); + connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater())); + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); +} + +void TcpClient1::setIP(const QString &ip) +{ + this->ip = ip; +} + +QString TcpClient1::getIP() const +{ + return this->ip; +} + +void TcpClient1::setPort(int port) +{ + this->port = port; +} + +int TcpClient1::getPort() const +{ + return this->port; +} + +QString TcpClient1::getDeviceID() +{ + return this->deviceID; +} + +void TcpClient1::readData() +{ + QByteArray data = this->readAll(); + if (data.length() <= 0) { + return; + } + + //鍙栧嚭鍞竴鏍囪瘑绗,骞惰繃婊,鍙嚜琛屾洿鏀硅繃婊ゆ潯浠 + QByteArray cmd = data.mid(App::CmdStart1, App::CmdLen1); + QString id = QString(cmd); + if (id.startsWith("S") && deviceID != id) { + deviceID = id; + //鍙戦佷俊鍙锋洿鏂版爣璇嗙 + emit receiveDeviceID(ip, port, deviceID); + } + + QString buffer; + if (App::HexData1) { + buffer = QUIHelper::byteArrayToHexStr(data); + } else { + buffer = QString(data); + } + + emit receiveData(ip, port, deviceID, buffer); +} + +void TcpClient1::sendData(const QString &data) +{ + QByteArray buffer; + if (App::HexData1) { + buffer = QUIHelper::hexStrToByteArray(data); + } else { + buffer = data.toLatin1(); + } + + this->write(buffer); + emit sendData(ip, port, deviceID, data); +} + +TcpServer1::TcpServer1(QObject *parent) : QTcpServer(parent) +{ +} + +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) +void TcpServer1::incomingConnection(qintptr handle) +#else +void TcpServer1::incomingConnection(int handle) +#endif +{ + TcpClient1 *client = new TcpClient1(this); + client->setSocketDescriptor(handle); + connect(client, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(client, SIGNAL(sendData(QString, int, QString, QString)), this, SIGNAL(sendData(QString, int, QString, QString))); + connect(client, SIGNAL(receiveData(QString, int, QString, QString)), this, SIGNAL(receiveData(QString, int, QString, QString))); + connect(client, SIGNAL(receiveDeviceID(QString, int, QString)), this, SIGNAL(receiveDeviceID(QString, int, QString))); + + QString ip = client->peerAddress().toString(); + int port = client->peerPort(); + QString deviceID = client->getDeviceID(); + client->setIP(ip); + client->setPort(port); + emit clientConnected(ip, port, deviceID); + emit sendData(ip, port, deviceID, "瀹㈡埛绔笂绾"); + + //杩藉姞鍒伴摼琛ㄤ腑 + clients.append(client); +} + +void TcpServer1::disconnected() +{ + TcpClient1 *client = (TcpClient1 *)sender(); + QString ip = client->getIP(); + int port = client->getPort(); + QString deviceID = client->getDeviceID(); + emit clientDisconnected(ip, port, deviceID); + emit sendData(ip, port, deviceID, "瀹㈡埛绔笅绾"); + + //鏂紑杩炴帴鍚庝粠閾捐〃涓Щ闄 + clients.removeOne(client); +} + +bool TcpServer1::start() +{ +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) + bool ok = listen(QHostAddress::AnyIPv4, App::ListenPort1); +#else + bool ok = listen(QHostAddress::Any, App::ListenPort1); +#endif + return ok; +} + +void TcpServer1::stop() +{ + foreach (TcpClient1 *client, clients) { + client->disconnectFromHost(); + } + + this->close(); +} + +bool TcpServer1::writeData(const QString &deviceID, const QString &data) +{ + bool ok = false; + foreach (TcpClient1 *client, clients) { + if (client->getDeviceID() == deviceID) { + client->sendData(data); + ok = true; + } + } + + return ok; +} diff --git a/netserver/api/tcpserver1.h b/netserver/api/tcpserver1.h new file mode 100644 index 0000000..0d002cc --- /dev/null +++ b/netserver/api/tcpserver1.h @@ -0,0 +1,74 @@ +锘#ifndef TCPSERVER1_H +#define TCPSERVER1_H + +#include + +class TcpClient1 : public QTcpSocket +{ + Q_OBJECT +public: + explicit TcpClient1(QObject *parent = 0); + +private: + QString ip; + int port; + QString deviceID; + +public: + void setIP(const QString &ip); + QString getIP()const; + void setPort(int port); + int getPort()const; + + QString getDeviceID(); + +private slots: + void readData(); + +signals: + void sendData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID(const QString &ip, int port, const QString &deviceID); + +public slots: + void sendData(const QString &data); + +}; + +class TcpServer1 : public QTcpServer +{ + Q_OBJECT +public: + explicit TcpServer1(QObject *parent = 0); + +private: + QList clients; + +protected: +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) + void incomingConnection(qintptr handle); +#else + void incomingConnection(int handle); +#endif + +private slots: + void disconnected(); + +signals: + void clientConnected(const QString &ip, int port, const QString &deviceID); + void clientDisconnected(const QString &ip, int port, const QString &deviceID); + void sendData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID(const QString &ip, int port, const QString &deviceID); + +public slots: + //鍚姩鏈嶅姟 + bool start(); + //鍋滄鏈嶅姟 + void stop(); + + //瀵规寚瀹氳繛鎺ュ彂閫佹暟鎹 + bool writeData(const QString &deviceID, const QString &data); +}; + +#endif // TCPSERVER1_H diff --git a/netserver/api/tcpserver2.cpp b/netserver/api/tcpserver2.cpp new file mode 100644 index 0000000..8c425fb --- /dev/null +++ b/netserver/api/tcpserver2.cpp @@ -0,0 +1,151 @@ +锘#include "tcpserver2.h" +#include "quiwidget.h" + +TcpClient2::TcpClient2(QObject *parent) : QTcpSocket(parent) +{ + ip = "127.0.0.1"; + port = 6908; + deviceID = "SSJC00000001"; + + connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(deleteLater())); + connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater())); + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); +} + +void TcpClient2::setIP(const QString &ip) +{ + this->ip = ip; +} + +QString TcpClient2::getIP() const +{ + return this->ip; +} + +void TcpClient2::setPort(int port) +{ + this->port = port; +} + +int TcpClient2::getPort() const +{ + return this->port; +} + +QString TcpClient2::getDeviceID() +{ + return this->deviceID; +} + +void TcpClient2::readData() +{ + QByteArray data = this->readAll(); + if (data.length() <= 0) { + return; + } + + //鍙栧嚭鍞竴鏍囪瘑绗,骞惰繃婊,鍙嚜琛屾洿鏀硅繃婊ゆ潯浠 + QByteArray cmd = data.mid(App::CmdStart2, App::CmdLen2); + QString id = QString(cmd); + if (id.startsWith("S") && deviceID != id) { + deviceID = id; + //鍙戦佷俊鍙锋洿鏂版爣璇嗙 + emit receiveDeviceID(ip, port, deviceID); + } + + QString buffer; + if (App::HexData2) { + buffer = QUIHelper::byteArrayToHexStr(data); + } else { + buffer = QString(data); + } + + emit receiveData(ip, port, deviceID, buffer); +} + +void TcpClient2::sendData(const QString &data) +{ + QByteArray buffer; + if (App::HexData2) { + buffer = QUIHelper::hexStrToByteArray(data); + } else { + buffer = data.toLatin1(); + } + + this->write(buffer); + emit sendData(ip, port, deviceID, data); +} + +TcpServer2::TcpServer2(QObject *parent) : QTcpServer(parent) +{ +} + +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) +void TcpServer2::incomingConnection(qintptr handle) +#else +void TcpServer2::incomingConnection(int handle) +#endif +{ + TcpClient2 *client = new TcpClient2(this); + client->setSocketDescriptor(handle); + connect(client, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(client, SIGNAL(sendData(QString, int, QString, QString)), this, SIGNAL(sendData(QString, int, QString, QString))); + connect(client, SIGNAL(receiveData(QString, int, QString, QString)), this, SIGNAL(receiveData(QString, int, QString, QString))); + connect(client, SIGNAL(receiveDeviceID(QString, int, QString)), this, SIGNAL(receiveDeviceID(QString, int, QString))); + + QString ip = client->peerAddress().toString(); + int port = client->peerPort(); + QString deviceID = client->getDeviceID(); + client->setIP(ip); + client->setPort(port); + emit clientConnected(ip, port, deviceID); + emit sendData(ip, port, deviceID, "瀹㈡埛绔笂绾"); + + //杩藉姞鍒伴摼琛ㄤ腑 + clients.append(client); +} + +void TcpServer2::disconnected() +{ + TcpClient2 *client = (TcpClient2 *)sender(); + QString ip = client->getIP(); + int port = client->getPort(); + QString deviceID = client->getDeviceID(); + emit clientDisconnected(ip, port, deviceID); + emit sendData(ip, port, deviceID, "瀹㈡埛绔笅绾"); + + //鏂紑杩炴帴鍚庝粠閾捐〃涓Щ闄 + clients.removeOne(client); +} + +bool TcpServer2::start() +{ +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) + bool ok = listen(QHostAddress::AnyIPv4, App::ListenPort2); +#else + bool ok = listen(QHostAddress::Any, App::ListenPort2); +#endif + return ok; +} + +void TcpServer2::stop() +{ + foreach (TcpClient2 *client, clients) { + client->disconnectFromHost(); + } + + this->close(); +} + +bool TcpServer2::writeData(const QString &deviceID, const QString &data) +{ + bool ok = false; + foreach (TcpClient2 *client, clients) { + if (client->getDeviceID() == deviceID) { + client->sendData(data); + ok = true; + } + } + + return ok; +} diff --git a/netserver/api/tcpserver2.h b/netserver/api/tcpserver2.h new file mode 100644 index 0000000..9f73427 --- /dev/null +++ b/netserver/api/tcpserver2.h @@ -0,0 +1,74 @@ +锘#ifndef TCPSERVER2_H +#define TCPSERVER2_H + +#include + +class TcpClient2 : public QTcpSocket +{ + Q_OBJECT +public: + explicit TcpClient2(QObject *parent = 0); + +private: + QString ip; + int port; + QString deviceID; + +public: + void setIP(const QString &ip); + QString getIP()const; + void setPort(int port); + int getPort()const; + + QString getDeviceID(); + +private slots: + void readData(); + +signals: + void sendData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID(const QString &ip, int port, const QString &deviceID); + +public slots: + void sendData(const QString &data); + +}; + +class TcpServer2 : public QTcpServer +{ + Q_OBJECT +public: + explicit TcpServer2(QObject *parent = 0); + +private: + QList clients; + +protected: +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) + void incomingConnection(qintptr handle); +#else + void incomingConnection(int handle); +#endif + +private slots: + void disconnected(); + +signals: + void clientConnected(const QString &ip, int port, const QString &deviceID); + void clientDisconnected(const QString &ip, int port, const QString &deviceID); + void sendData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID(const QString &ip, int port, const QString &deviceID); + +public slots: + //鍚姩鏈嶅姟 + bool start(); + //鍋滄鏈嶅姟 + void stop(); + + //瀵规寚瀹氳繛鎺ュ彂閫佹暟鎹 + bool writeData(const QString &deviceID, const QString &data); +}; + +#endif // TCPSERVER2_H diff --git a/netserver/form/form.pri b/netserver/form/form.pri new file mode 100644 index 0000000..ab07823 --- /dev/null +++ b/netserver/form/form.pri @@ -0,0 +1,8 @@ +FORMS += \ + $$PWD/frmmain.ui + +HEADERS += \ + $$PWD/frmmain.h + +SOURCES += \ + $$PWD/frmmain.cpp diff --git a/netserver/form/frmmain.cpp b/netserver/form/frmmain.cpp new file mode 100644 index 0000000..21ebe98 --- /dev/null +++ b/netserver/form/frmmain.cpp @@ -0,0 +1,269 @@ +锘#include "frmmain.h" +#include "ui_frmmain.h" +#include "quiwidget.h" + +frmMain::frmMain(QWidget *parent) : + QWidget(parent), + ui(new Ui::frmMain) +{ + ui->setupUi(this); + this->initForm(); + this->initConfig(); + on_btnListen1_clicked(); + on_btnListen2_clicked(); +} + +frmMain::~frmMain() +{ + delete ui; +} + +void frmMain::initForm() +{ + tcpServer1 = new TcpServer1(this); + connect(tcpServer1, SIGNAL(clientConnected(QString, int, QString)), this, SLOT(clientConnected1(QString, int, QString))); + connect(tcpServer1, SIGNAL(clientDisconnected(QString, int, QString)), this, SLOT(clientDisconnected1(QString, int, QString))); + connect(tcpServer1, SIGNAL(sendData(QString, int, QString, QString)), this, SLOT(sendData1(QString, int, QString, QString))); + connect(tcpServer1, SIGNAL(receiveData(QString, int, QString, QString)), this, SLOT(receiveData1(QString, int, QString, QString))); + connect(tcpServer1, SIGNAL(receiveDeviceID(QString, int, QString)), this, SLOT(receiveDeviceID1(QString, int, QString))); + + tcpServer2 = new TcpServer2(this); + connect(tcpServer2, SIGNAL(clientConnected(QString, int, QString)), this, SLOT(clientConnected2(QString, int, QString))); + connect(tcpServer2, SIGNAL(clientDisconnected(QString, int, QString)), this, SLOT(clientDisconnected2(QString, int, QString))); + connect(tcpServer2, SIGNAL(sendData(QString, int, QString, QString)), this, SLOT(sendData2(QString, int, QString, QString))); + connect(tcpServer2, SIGNAL(receiveData(QString, int, QString, QString)), this, SLOT(receiveData2(QString, int, QString, QString))); + connect(tcpServer2, SIGNAL(receiveDeviceID(QString, int, QString)), this, SLOT(receiveDeviceID2(QString, int, QString))); + + QUIHelper::setLabStyle(ui->labCount1, 3); + QUIHelper::setLabStyle(ui->labCount2, 3); +} + +void frmMain::initConfig() +{ + ui->txtListenPort1->setText(QString::number(App::ListenPort1)); + connect(ui->txtListenPort1, SIGNAL(textChanged(QString)), this, SLOT(saveConfig())); + + ui->txtListenPort2->setText(QString::number(App::ListenPort2)); + connect(ui->txtListenPort2, SIGNAL(textChanged(QString)), this, SLOT(saveConfig())); +} + +void frmMain::saveConfig() +{ + App::ListenPort1 = ui->txtListenPort1->text().trimmed().toInt(); + App::ListenPort2 = ui->txtListenPort1->text().trimmed().toInt(); + App::writeConfig(); +} + +void frmMain::append1(int type, const QString &data, bool clear) +{ + static int currentCount = 0; + static int maxCount = 100; + + if (clear) { + ui->txtMain1->clear(); + currentCount = 0; + return; + } + + if (currentCount >= maxCount) { + ui->txtMain1->clear(); + currentCount = 0; + } + + //杩囨护鍥炶溅鎹㈣绗 + QString strData = data.left(500); + strData = strData.replace("\r", ""); + strData = strData.replace("\n", ""); + + //涓嶅悓绫诲瀷涓嶅悓棰滆壊鏄剧ず + QString strType; + if (type == 0) { + strType = "鍙戦"; + ui->txtMain1->setTextColor(QColor("darkgreen")); + } else { + strType = "鎺ユ敹"; + ui->txtMain1->setTextColor(QColor("red")); + } + + strData = QString("鏃堕棿[%1] %2: %3").arg(TIMEMS).arg(strType).arg(strData); + ui->txtMain1->append(strData); + currentCount++; +} + +void frmMain::append2(int type, const QString &data, bool clear) +{ + static int currentCount = 0; + static int maxCount = 100; + + if (clear) { + ui->txtMain2->clear(); + currentCount = 0; + return; + } + + if (currentCount >= maxCount) { + ui->txtMain2->clear(); + currentCount = 0; + } + + //杩囨护鍥炶溅鎹㈣绗 + QString strData = data.left(500); + strData = strData.replace("\r", ""); + strData = strData.replace("\n", ""); + + //涓嶅悓绫诲瀷涓嶅悓棰滆壊鏄剧ず + QString strType; + if (type == 0) { + strType = "鍙戦"; + ui->txtMain2->setTextColor(QColor("darkgreen")); + } else { + strType = "鎺ユ敹"; + ui->txtMain2->setTextColor(QColor("red")); + } + + strData = QString("鏃堕棿[%1] %2: %3").arg(TIMEMS).arg(strType).arg(strData); + ui->txtMain2->append(strData); + currentCount++; +} + +void frmMain::clientConnected1(const QString &ip, int port, const QString &deviceID) +{ + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + ui->listWidget1->addItem(str); + ui->labCount1->setText(QString("鍏 %1 涓繛鎺").arg(ui->listWidget1->count())); +} + +void frmMain::clientDisconnected1(const QString &ip, int port, const QString &deviceID) +{ + int row = -1; + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + for (int i = 0; i < ui->listWidget1->count(); i++) { + if (ui->listWidget1->item(i)->text() == str) { + row = i; + break; + } + } + + ui->listWidget1->takeItem(row); + ui->labCount1->setText(QString("鍏 %1 涓繛鎺").arg(ui->listWidget1->count())); +} + +void frmMain::sendData1(const QString &ip, int port, const QString &deviceID, const QString &data) +{ + QString str = QString("%1 [%2:%3] %4").arg(deviceID).arg(ip).arg(port).arg(data); + bool error = (data.contains("涓嬬嚎") || data.contains("绂荤嚎")); + append1(error ? 1 : 0, str); +} + +void frmMain::receiveData1(const QString &ip, int port, const QString &deviceID, const QString &data) +{ + QString str = QString("%1 [%2:%3] %4").arg(deviceID).arg(ip).arg(port).arg(data); + append1(1, str); + + //灏嗘敹鍒扮殑鏁版嵁杞彂鍒板彟涓璺綉缁 + bool ok = tcpServer2->writeData(deviceID, data); + sendData2(ip, port, deviceID, ok ? "杞彂鎴愬姛" : "瀵规柟绂荤嚎"); + if (!ok) { + tcpServer1->writeData(deviceID, "deviceError"); + } +} + +void frmMain::receiveDeviceID1(const QString &ip, int port, const QString &deviceID) +{ + QString temp = QString("%1:%2").arg(ip).arg(port); + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + for (int i = 0; i < ui->listWidget1->count(); i++) { + if (ui->listWidget1->item(i)->text().endsWith(temp)) { + ui->listWidget1->item(i)->setText(str); + break; + } + } +} + +void frmMain::clientConnected2(const QString &ip, int port, const QString &deviceID) +{ + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + ui->listWidget2->addItem(str); + ui->labCount2->setText(QString("鍏 %1 涓繛鎺").arg(ui->listWidget2->count())); +} + +void frmMain::clientDisconnected2(const QString &ip, int port, const QString &deviceID) +{ + int row = -1; + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + for (int i = 0; i < ui->listWidget2->count(); i++) { + if (ui->listWidget2->item(i)->text() == str) { + row = i; + break; + } + } + + ui->listWidget2->takeItem(row); + ui->labCount2->setText(QString("鍏 %1 涓繛鎺").arg(ui->listWidget2->count())); +} + +void frmMain::sendData2(const QString &ip, int port, const QString &deviceID, const QString &data) +{ + QString str = QString("%1 [%2:%3] %4").arg(deviceID).arg(ip).arg(port).arg(data); + bool error = (data.contains("涓嬬嚎") || data.contains("绂荤嚎")); + append2(error ? 1 : 0, str); +} + +void frmMain::receiveData2(const QString &ip, int port, const QString &deviceID, const QString &data) +{ + QString str = QString("%1 [%2:%3] %4").arg(deviceID).arg(ip).arg(port).arg(data); + append2(1, str); + + //灏嗘敹鍒扮殑鏁版嵁杞彂鍒板彟涓璺綉缁 + bool ok = tcpServer1->writeData(deviceID, data); + sendData1(ip, port, deviceID, ok ? "杞彂鎴愬姛" : "瀵规柟绂荤嚎"); + if (!ok) { + tcpServer2->writeData(deviceID, "deviceError"); + } +} + +void frmMain::receiveDeviceID2(const QString &ip, int port, const QString &deviceID) +{ + QString temp = QString("%1:%2").arg(ip).arg(port); + QString str = QString("%1 %2:%3").arg(deviceID).arg(ip).arg(port); + for (int i = 0; i < ui->listWidget2->count(); i++) { + if (ui->listWidget2->item(i)->text().endsWith(temp)) { + ui->listWidget2->item(i)->setText(str); + break; + } + } +} + +void frmMain::on_btnListen1_clicked() +{ + if (ui->btnListen1->text() == "鐩戝惉") { + if (tcpServer1->start()) { + ui->btnListen1->setText("鍏抽棴"); + } + } else { + tcpServer1->stop(); + ui->btnListen1->setText("鐩戝惉"); + } +} + +void frmMain::on_btnClear1_clicked() +{ + append1(0, "", true); +} + +void frmMain::on_btnListen2_clicked() +{ + if (ui->btnListen2->text() == "鐩戝惉") { + if (tcpServer2->start()) { + ui->btnListen2->setText("鍏抽棴"); + } + } else { + tcpServer2->stop(); + ui->btnListen2->setText("鐩戝惉"); + } +} + +void frmMain::on_btnClear2_clicked() +{ + append2(0, "", true); +} diff --git a/netserver/form/frmmain.h b/netserver/form/frmmain.h new file mode 100644 index 0000000..be3e64e --- /dev/null +++ b/netserver/form/frmmain.h @@ -0,0 +1,53 @@ +锘#ifndef FRMMAIN_H +#define FRMMAIN_H + +#include +#include "tcpserver1.h" +#include "tcpserver2.h" + +namespace Ui +{ + class frmMain; +} + +class frmMain : public QWidget +{ + Q_OBJECT + +public: + explicit frmMain(QWidget *parent = 0); + ~frmMain(); + +private: + Ui::frmMain *ui; + TcpServer1 *tcpServer1; + TcpServer2 *tcpServer2; + +private slots: + void initForm(); + void initConfig(); + void saveConfig(); + void append1(int type, const QString &data, bool clear = false); + void append2(int type, const QString &data, bool clear = false); + +private slots: + void clientConnected1(const QString &ip, int port, const QString &deviceID); + void clientDisconnected1(const QString &ip, int port, const QString &deviceID); + void sendData1(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData1(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID1(const QString &ip, int port, const QString &deviceID); + + void clientConnected2(const QString &ip, int port, const QString &deviceID); + void clientDisconnected2(const QString &ip, int port, const QString &deviceID); + void sendData2(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveData2(const QString &ip, int port, const QString &deviceID, const QString &data); + void receiveDeviceID2(const QString &ip, int port, const QString &deviceID); + +private slots: + void on_btnListen1_clicked(); + void on_btnClear1_clicked(); + void on_btnListen2_clicked(); + void on_btnClear2_clicked(); +}; + +#endif // FRMMAIN_H diff --git a/netserver/form/frmmain.ui b/netserver/form/frmmain.ui new file mode 100644 index 0000000..461f9fb --- /dev/null +++ b/netserver/form/frmmain.ui @@ -0,0 +1,155 @@ + + + frmMain + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + + true + + + + + + + + 250 + 16777215 + + + + QFrame::Box + + + QFrame::Sunken + + + + + + + + + + + + 娓呯┖ + + + + + + + 鐩戝惉 + + + + + + + + 0 + 25 + + + + QFrame::Box + + + QFrame::Sunken + + + 鍏 0 涓繛鎺 + + + Qt::AlignCenter + + + + + + + + + + true + + + + + + + + 250 + 16777215 + + + + QFrame::Box + + + QFrame::Sunken + + + + + + 鐩戝惉 + + + + + + + + + + 娓呯┖ + + + + + + + + + + + 0 + 25 + + + + QFrame::Box + + + QFrame::Sunken + + + 鍏 0 涓繛鎺 + + + Qt::AlignCenter + + + + + + + + + + + diff --git a/netserver/head.h b/netserver/head.h new file mode 100644 index 0000000..7083402 --- /dev/null +++ b/netserver/head.h @@ -0,0 +1,9 @@ +锘#include +#include +#include + +#if (QT_VERSION > QT_VERSION_CHECK(5,0,0)) +#include +#endif + +#pragma execution_character_set("utf-8") diff --git a/netserver/main.cpp b/netserver/main.cpp new file mode 100644 index 0000000..5f25f15 --- /dev/null +++ b/netserver/main.cpp @@ -0,0 +1,28 @@ +锘#include "frmmain.h" +#include "quiwidget.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + a.setWindowIcon(QIcon(":/main.ico")); + + QFont font; + font.setFamily(QUIConfig::FontName); + font.setPixelSize(QUIConfig::FontSize); + a.setFont(font); + + //璁剧疆缂栫爜浠ュ強鍔犺浇涓枃缈昏瘧鏂囦欢 + QUIHelper::setCode(); + QUIHelper::setTranslator(":/qt_zh_CN.qm"); + QUIHelper::setTranslator(":/widgets.qm"); + QUIHelper::initRand(); + + App::ConfigFile = QString("%1/%2.ini").arg(QUIHelper::appPath()).arg(QUIHelper::appName()); + App::readConfig(); + + frmMain w; + w.setWindowTitle(QString("缃戠粶涓浆鏈嶅姟鍣╒2018 鏈満IP: %1 QQ: 517216493").arg(QUIHelper::getLocalIP())); + w.show(); + + return a.exec(); +} diff --git a/netserver/netserver.pro b/netserver/netserver.pro new file mode 100644 index 0000000..c1feade --- /dev/null +++ b/netserver/netserver.pro @@ -0,0 +1,30 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-09-19T13:33:20 +# +#------------------------------------------------- + +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = netserver +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = bin +RC_FILE = other/main.rc + +SOURCES += main.cpp +HEADERS += head.h +RESOURCES += other/main.qrc +CONFIG += warn_off + +include ($$PWD/api/api.pri) +include ($$PWD/form/form.pri) + +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/api +INCLUDEPATH += $$PWD/form diff --git a/netserver/other/main.ico b/netserver/other/main.ico new file mode 100644 index 0000000000000000000000000000000000000000..920d3932c864d5c5df2d878e8156b6000a9091c7 GIT binary patch literal 67646 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 VIMIZ4khVj26lnWvM*(vA{{eWaedPcE literal 0 HcmV?d00001 diff --git a/netserver/other/main.qrc b/netserver/other/main.qrc new file mode 100644 index 0000000..4e13808 --- /dev/null +++ b/netserver/other/main.qrc @@ -0,0 +1,7 @@ + + + main.ico + qt_zh_CN.qm + widgets.qm + + diff --git a/netserver/other/main.rc b/netserver/other/main.rc new file mode 100644 index 0000000..fc0d770 --- /dev/null +++ b/netserver/other/main.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "main.ico" \ No newline at end of file diff --git a/netserver/other/qt_zh_CN.qm b/netserver/other/qt_zh_CN.qm new file mode 100644 index 0000000000000000000000000000000000000000..d6f3648ff65c8ff7392494ac1069c138e9e1518e GIT binary patch literal 117340 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 -%Bg?v!hW$Si;g>q(CKsKv?{cF@qVQ}dKKp9 zO{HTC<}LpahebCkXJlhN^xUg-9W@gnE?-$c40>bc5aldo6!iEV$~iT#XTDJ>=TN)m zy%8LKSI?lJ{ZcvSsU7eqE0m4uR|$FiBjs%$bisVbEAN2)B6Mu4T=p^go3T&1(!Cpg zL%ecLx8+1wZc%QC8IAovQh6t0w8EUW%DZZ@k6*1;-nGyJzibYN&+k*-yE75}Iyn69 zKIPV3n75HK<+iNv;V&d8A9O&@&*Uo~v!)Vq=Pu>m)JE6=Gn9J^_8|V$sC@bh&`TJ= zVc~ub7p5wo*$qCD`YWINZYAtznexTHkk^VwlrK%wWBqSYzSD;C+G;+38U_FIwmB-( zBFukTA61V@Mk36ftLpvm1BekXRrOh#if#V8s^6tkL>Tb1YQWDO;Gb<(4H|%Xv3{r; zvKs4e@=n!tPov$cIjW)UmcTyvLp5sHG3?_@s+7E`h)dk9O6`0Ed~H>wePt%X4UecM zBw@d9U9Fnf@nf84yHpb&!?^VtRo?85SkKE;ldc1Ovr{$Ykr+arpP;h3Unk^knab07 z3EIz7)gA3Zgo&F~vlEJdcfD%%eE3>W=kcYm3_A#a`P-t>_~n7mN6wM|Q$U$3aP zA4j`I*Q*}>{1)i*uT^^{4#)ZUq-t+zZz7P}Rj)j~5Pr!C4o`Pgz53!7$Sq0rx-1s! z@mtj!pFW9wvrl#Cf(HK3*Q$^E;rCOcRbSnM@$~JYI(}g<;zfG?4@ zAM#bdj$clMvbR)cseL;0Hr1cCfNRIA1;mO;3zJ$FHyC>5b#>HTxmfS()KL#%e^#cd zHTDJ~WL>L{c0f->->GiZ9_PRlFR0u84LvN^s5{;eM}+8k>W+s`0KTT~bg2&OV!XPu z4dYETsIQ&-1R-iRZyrk74S;Gr;FtJ=7x> zeuFr}(;WUWTs?~ZT>U49b=}k{o;29stJPU=pMpK#MLj7F`tHuI>dARpLQ>u8o0j7D z<>S=TPo9K-6Q!Ph$_)S9qqZqAA18CwwsB7*9hiR8us?oKJATBxuU?{_*&llO(>K(&_8Ne5eusK?b`BAYrRoJcfVXz1df^1Tf`~>(NxLTu_19{IHqfy^t#~svEjpnll;Ai#++y=Y&SB>TjPgV^?dIYrseMzckB#!G3vY zmuBM~#}K#5(A?ScA`vpInoVOz!cS__+_MdOa%8>cz9(P9IppSW;bP5$jg9cTKh*ju&5qINcf(N4Lmw=G9rCT_(UE^3?)IJL(FJ=kFRy8yIEBC8v{UoUx0p9a6^Hek zHP1O^gwzhuyzsRddU=`Vz}v4vet&2V7W5|MqZc%vPsI6R5;R{BtfRkTG`}pxyshl2 zIaSd>gc0eQ-{X1^^5=NXIX%u1!*b2}=dlit{i3_59Uw_XGN-&31=u?YKFr_H;05cJR!+NrD1{~a~j>5nU5-=F00 z?eSWh9OJKPskQ9~{^EJs;so%w&#f(fdJOJ=|IwDb|1Qqkp<4T9*gvf`LKUP~dI1hHuXWFt;;J0(JcE&w@5SRT#>rMsVopoBz@LcSZ z&pEWN(AH4?J{-&8>C@V}>1HC_nV_Ay=`}(Yt<=^JhMb;i(l-3D7Iw8xyXXh3x44_N zw;#Fz_i(>xS9eGIVU^mAX`6BHbv=iD`fxb-8SSP6!w_G5OuPA!SlBC%bNKF4+Ivc| zer_~scTB*#Iq|Xf;kn%rN4$^2!~3+4#8n~wS+0Gg&m!D+$vM0+S-We~a_G4|+Q&w` zLWJ9kw7cJd{Ca(^ePJp5AM!u#{^@c;rjOIU@&?AAL9_=v3dALT(!RYGa?d%VeLw3N z@LA5F(CV1>qpvW|NlQ7LGFE$R2=3J1+^#(~68pqir~Uqgp|~f@(f(A5d5!7FVe$d( z`9aHIC*808^C0wc_BrjJ7rmV)wo}p%3;bvoje)m!{oC%#Rkah-ejF-Bl^4U zv`%v#KhOG1r|Z-jc5sqTUx|5YzfgB=Uk~m*mg$miGb8?O)eY`^2=WszD z*KF5SUVt9!_L8pdP3V_4U+89jjdQJEj;?;I8}Zs6x`k)ZE~j4CxF8mB`y;xhZ;eFo z4A3ndkN%8rad_i5x@Dikp6!*OTWN3-A@z6Ns_qw|PY>!=C*Fwj_Ka@L#nm{cTI)9G z9f+I0uemK_b^kU0p9Hu;>+tXn=?C5&k zeh=np-BR5vGd@DRca-kc4{ryZ1G={|Fdr{|s(b6rR@fiMbO+5NanAp)d*?amIZGei zd-2C$UmntZ)HfAB|El}s2f859hH z?)yB*vuL;O#~-l&m0fhdy*(V~-XYx|_Z-K*BOIQ7NOv~rZQMVd)16!U8tlMl^s?ymgiu{emza! z`k_6zzo^!?nK>Tzq($FmAs_k`osg^qwk0M>&EVZzDU%M zd=KpkhI-*I9Nu&vhtogfaK;i2Z`sS?9YZ+0Z=QaXXEq`K^wOu!%SAkDm_F?%jH~Zt zeb$ZWPb=5wOj?e6^dts_R&4=y!EXFmZ%ModdQQ+=aK}!Twbxr-!v1>cqQ2lH?DIBz z^abCY#r@3;ec`&h2>Cl*Up##u5$u2K%b|Bk`YZaIuX7PHYOB6#E6$n37W&%xXK;SD z(ciXd3lRoBreFGUBK+z&{nEpuaZdHpFaI8T;Tn~G<%{66|9%eZ{^4-VuN-cENWbcL zBkbi|{p!2!1KywdHQ!^s42?!@|gPQUpd3*>s2{_dV7*vF^z z_iP9KnlJSaEm7f|o2`Fj7}m?J9rTY-e)^y1&|b~q%mxlu6?3?5G>7kW)jt{ozd<;y z-+kLhuxF0x_a5#7f8dDz8LJ%jfJ*G#lT7 zLoLy7K@0r{3r@j4um+2|DEh1!rZm`OaC~Du;eiV*$;hmQEia5@W37~HAKm* zh%dimi0W)4!pJg%VyF%K;JiVR2mhsRwL$UKJlqGqYfxn-BA)xBL3{4gnW0EYDupuR|CV+Kw^g zJ`efi95dwWE+X#qilJomH}HE#8A`vmBOV>kpdg=OD0{R$;(Heij$-{Aw$hSFGD`F4Yelx z-c@C&jo*gzVV9wH)^CKU#u@5pyF*WK`0aTH1?^3Sx;YQv_g``NT{i{=?QMqHg}V?x zpJAA{3-fblp<&@$n76!24h!!v+_nvR;-6E7dKGH;WAS&G~?ksz(hE zpUJ^}PNm`TEm=4((+p23o`9U14Etu9anEtT;mr^75Vv^Sa9}s&@$^Q+yT6V_d}NB@ z=wFa~k1q_LPlmtTdbr^P)t9}y7=BD$f%8FY_~`|#v$6dRKYvW^z5#~cJcDsxbw7hb z+i1gYJGwysw>O+tFNa@mGn{*DBoXHKG5pyX^VaYVgF@RKhCh2^-1>UM-}Lw0mKl{Y zoIBA2jmkRw{tdNJf1?%mux}Y-?#?Bo=PG0D?MLDFe92+!8OAm)$T|5dW5+W3_s@;j ze1@L~{cG%X`%avf|1c=TZZYulrXoEqq(?Z(N6ji5K&Sg6H#HjOhD-lM@i>~}_M;_L9!yBVzu5^+wxZ7ier zTHpU@bk>hU-bbCW_P`?8M+=Pg>hAFOS{vszJPZ3}k#V6Bau|1=arv=ButPpJu6cM6 z@@_^OAB^7xKk9wsjvlA5zXuz4eu&@y^Q3XldFY)P6O7OA!o1$!!}#Kx8*px%HSYU< zIrho7#{FNyj@goLd~-D975|m-;J2?}zFQd&$rZ>4*lYaUjr~yF$N0tii-?z{8&A{w z%14Ot{IMb;%zEDVw;StK_DHnu7p#{S@D~p~`5({~?jGnmlH1uNE z=!qw<#XWpNbb9wh_?`bo=bs0Ew$;(agE7wc)-ovQK94RN*c#_|t7r%HZ=>Fit}Nez z`>_MjRTnW|cXy7iUx@wPy;bzA2I#RbyF||&(j9S_cF}VS1{0#27(G88{Vjcl!@Clq zn`U7?ZeA9>Zu$?%yQq%dyx$3bEh&0yAnKc+xDp{2lK zN>=opmO1B1r|BHU@bL3Fk zTSp&oe}whipTje*==bS;|F+Mfze-2{4X;Lj-?kO}@Vlaa7=0f8!R+WCvcTW2F42E_ z2Ee{N7JXq~7eeZ0aJaIV!viZhe0zEH#kD1{{~nCKl!x{4PniSBORJ;6KouEI%87?m zk_s|1M^$XgN!swo##NJ!*+(|*D^L*={%#{9NgU~qfAypPzNBZrbK|Fxg$r{!t3~F|%Fv_=fFCGKMGif#n1P3jfUWL*E@{cIzL+uZ#|)|ESo%S|`e zRC`P|d$F|=ShZG1h1FJpe>qD`<<@FbxudMy?lx7q?WK0N-C1l;9Nv%hhy$GHl4&oQ zHJ+*(k3i*60WL)zFDC(Fs@PhlnR+sF3LBk`Fj-7G=KL%tNg}ZUK8Zz?K@pch?&XJV zDskDXO-`4`ROfQfNMuqNYptlDf@5-m^r(ymP#HBl(w5V01%{j?I=iM$bY4xJ#X0Ga zW3RQlAyg`PQ1lAFM_m6NVWpomoS*v2ezO;sl*CSHcXN|OC|(0EnZ zHIB;nIm5JeUT=#Q zTPwz4+p@s~(cJKdIm_QFqo&$X9ER4)4s|ERRtB@n<0$oZ9FFFaH#V*kTPr4E(YQis zmNu2wQGj%unWOQ--k_@;AQwYP(zLq}up+nptBf@>p*1cNOj)j`5Y zB8fO9l1N{iDix##EgZy&R(>?v42uuucZ2xxm+dT#bt5f;NVg!wOc(kZfIC0_W(Ju{e3w>jhs7lN(&69*9Nj= zer@BD<1>Xx&XJLu1J4HYaf>-cZW>?M!dN3QsvxmWh0!e=jafI>?W*TnQ>liPkv zjl*p(F%`Q??8PQWwW-p9J?Jb;Wb4Ff$Dgo3ybFM>ky2Mhg{zJp1q>&lx+iTxk5K#> z1d3fwkKO4RYKlvuv)(1ltY_Fu9M(+R&Guq`9?mlx=KVX36ueC>0XApart{@5m7Scv z!@CuNXWvAJNiBex&ZY;Z?;u(_qx5%JD-~D?wlFIubfws^wW^6-vQ6qztWJ9FVZCD+ z`|N^5lUAEjZpSfI@2YW|#P#cw3)4Z&7O1q&uwxgv?X;yy%O<$OVG|dNxKP+m^Y5!G zl~fFs6rI|eR%cqADAz+a=O--QnK`#6K{BqP{&Bggpi#KR#%+Z}6E$YMb_154 zwE~K(q#n~^s;R!pF;kX8#@s2W^Zz7Lqx~!U_}WJ2%GtB>n@>-D;y=>DKdHL ztFD~qq^9#!P*ilz!}bK^hbe~_+ml4Y!?BD%KoVe@r^PuC| zPOV1g(DK;5))HpkQj56ORZ&xE=aQ|kmwGB)uzRaq)ed^jQU#F|*N5o=H%wmsSNt-r ze;i$5vA)ediBYkz4w!P+Q|_4lk@BMB?Vu|zhaW>tF*pNpWI$Q3!cFEhaXc;K`u8!# z4e+k~QiqeDHmdA_)S#$fy|*zNN{us}P$r;4Puj&h3*;W9NTXeHrOi=R|-dae%NOkVs!wq0zf zOmkMlCJs-q?i#V3Jl98j%Wk_^Ss=EgG&vjih5-ptIZ(AN5y&caQseP%%W%!yPC zVcOHJAJSeCp9ysn9>cim-P}3328NR(=|dHt!-gInOP~2g;bewmk>DueEwH}AW5ks8 zDqfw_7R#2(D^G%@iO+LZ!_wkwJ#J~Sk+o<9F z&^W?SsV&PEqzR1olCW){h9+G^)ux1AJ;P(i24N=#P>7aNNQ^{7KBZ`S4{==DWRJ(~ zu+?}XiA{a%#wi=S^<;q;j#?PzeJAS}AM!K%pavclh=|%M z#Z^^ra=A?zE|}|9CX?`V^f?`i{7`NLMiIsqPlhef;o@7hf zFxOE38{4c#{5c1qwr(OzzY82w1}_!Rpw_z@%S0KEj)kExenM%p^L zEzDXoY1l4y7nA9P@#|gY^`@Gt@I#c-<4r`8M?Td88!Bi+Q+Sg4jg3p*qEe$yrAWFp zB2~56#!NApQn(w)0wkdajz}T%>*+3$LW=+jY0lCJCw$DRckR1Z%*nR1r7=cwSg3O1 z?G-L7i#LS!D!2UDbV2M*lLKd=ugh7%f=yvsH(kiSQ*5net?S&5NZL@_biwiow{S%y zHASMD(kS*)Qa z4sFiZlG=p^ie|Fao8luML%^roUBs7KiGKXZIso`|Od`J2I`iWca?3QQ?W) zt0@Lu>){)#L#9bb5vuuAg~w@0!yDB6q7#ist@M28?2jfCb5l&@R?Ag{CVOByVk;G6 ztC_E4^uT|QNBxEMB~*IUJ`F=MPD-+p zTBua8)LgyHAUm-^-ob}RwA{5q)Pl-xfH+m{rR1qi1WvUcmo{zyoriF8qw6V1ZY}*p zm?959TK)VVX@CLoW&BTMK%asOKvyNyo%a?72S$d~;mn84!S6{zu4=?F#mPvxMt z#*%k4r!3xClVH0pYsw6p*iB~~yKWv&M0ZtHh`&Pn(Pf8-7Be?nWDhjkJ@jHO%kA>G zid{U!5z)u~$WMRTV!55yn6WL)tKs`x4r6%aNuBM3U-M zd#Sai!o!Y{(6&X1IS+|#<>T!Y%z6oJN8M4eos7HiA+7Y(Z(?UhisVRB#8ZcfE)OjR zWuNI8Q{v$9X8r7q%LcLbS(5h3_$+HRKixwR=rtxokh=Q{xceo{LFLO18;92KJrYhP?HPpOG#XKQeCHMJP%9i7-z>$Mi( z5r-3uoZcFVjll;lSAFdqo(lVDtDCKKHEnn62Fr1=Z41`6nypk^4q!`!!;7^ja(mO2 zPjPD-bLW^(NCKk0sLhBiMeh(QD-nPLIhP6la8%V)P|uZagUG~9)WD&HsWm|F254su zXKW7?F05@V-Y(JmV^bZ~G`7##rP{>gDwR-YaV&a|;bq3@al0ylnQ@3Bmr6!U8OSc5 zl4)rp%BE8WZUv;GF_qRJrcsSUfJd0(3nC1i}a4%Clsr=i=JB7ec`P!w$d3cLc2!o9Cx1Ua=JMEXigJ@iXuxJlHOo+;=MbmFKo zz>D{=!2y@+a#i^DpOz$)^{P9Gz@T3XXcB6iGo0X@#mEfV1M}P!{4jCxP20)i|I_Hq+Dy|`j}942mmOWVP)P>cisC45yn<1>?N`5y zx`*<>4au;p`pUS#xp0+>IW~}s#XIR96UW{ybnLh}W4FybZGs2$!>k`YwZYi2FSTz} z@%~t{I(uNg<5sJiXHw`3m)c@j`>_%Qp^8U*4}p2QNQ2r_YmBv*h6EJxKC>*abxX6( zOlI)d3w&+dxQ`UasERkk&@!n|xMow>OLzY$8;l&i>7jG-f=QpBMR3e*gxe$G% zU<^@_HzR2yCqUfLCUgh=3A|PhJH>Cwblm?-+3@_ei6@*8XyI!gsCW=p;jP+S7-kebP%9}C9 z7>uQj>D~RUx}p`ejpkw0wh@u9^&xYE499Po+gjNJxjC>$B?hoQJD6^gt!#0+?}(?_ zKqMQtdNluWz$LxqM|#O?zD@kyK#A?$XUt4{agB#1wbBesUuICXDI3Xy1N!jGUiQO4 z@6B#-=g3XTo?ZAXj1qJja97ef%?XPhH^dH_GYf+04x8KRt~W{UP=cvlUgv4DlV|eP zz-3BONG8cB{A;O|%^)s+CATU1l*|lMJkL2|mj*%Da(Zbj&Vbj^3~JMcE=!V({pdl8 zQ7!i5w#{TJB$8wq@EjCl;y2{G9 zve0A)v{TdbA%LVR&sj_pmckHiS9u}l-kc;JG5Wa_jcbL0b|1@?*PUHOt&h2 z9Cfa|*F(Wf8yoKPV!@r4EE49>N5F}>^OoGDMGyM4N&uF{>WP45H1G1_ObY60lC>fN zPCG2I3S>2w6YI(jD=oh1k@Ogy9A8mYZZ+ZbfLe}3$JqFVca#W1NU)WoE8=T>w}K&} zS>Rq8#k$0`iUdh9z9v2r4hu8TH6Siky>}-t>U0f;E}TfA1}tq?rOoBr0^zf0f*J+h zT!~j)L9Jxr@@InD#L|E*qR^$-;w~J!rh@*<{HasXas7vyNIjdnnc{8fD*{P!kq`INYyip%Y zCcsK)Atk{q1jB0)N_F-8=S=^vGqaicAADY}B^}GD=O;Flr@EZXxll1Cy%~7g75^*kXMV{%{*ph-N1tDA^}xc+MF#gM z%1!{r08T8MJ2)YpavC8 zbisrtA-CNn8M_K`_hJMoBDYuaE=6f+v4VTBZrD1G&`J2qqzH|t{afSThRU=uM2MTA zZKT&H-t<#xI;qwij~9e4-&3aueT8!>A~X%3r4ym?s(0r)MHwk@q}Z8u58ql%Bh~?T zb1}ZCwe*fIJhFV%yGVqXeO8b{fgob&y1Ma42xU zFtlBzGiQf5dy270q+-h|v|;I)DJ{f?@^oA+Q;8VSaSc3tizE~yrxzRkru0f9Qq$&@ zxFMSb3o_6bo?r1(HhbBOuJqE6Mp7ylPj~s8GqJyR@hq*0y>`F@K#FOymLZBlc?d&M zyELllV@gTl`505}wwf}Nw`1nBg~ObGCU*`EdPp$!WAPxMf(JPw;VJo}H<4qt?-5!A z4rA`C;dn|kwCOAf9#Tw*Cu&1#S zBJI3H5Uq2<)MF$Z&QJEq32ViW#wIB7nR{se@!skldTIC~pW2e@Qy#=;Y$@2#-+Sx$$^hSy0f?jgZTWC<%UZ;6)1Iwg-C zt}onzr(^#s+%%&Y??Z6sL?1kegPMpW6X*h#NL=p^`6gKP)-&!)3G^_gijKwEU4YxJY%#~0AtBv-eE-Mi3#*d*kQ|oJ*C6s z(1#!NCBTyTrI(6rg$n}=#=ck_zUNkasuFQlvsZ6*RwrCZ7b1wbW5;WHhlg2|A!FCG za2kEtAhiy0t5NRqrX7T$T=wxu2ue#y!Y%DfnjNUaZG|)!J_K2RnWrHk2v_nlBn0Vl z4?{u_61*YaPNn^_@67zd_CReTpR5%}O8fDr8$r``1B>Y*=)m^~z0qai{E$iWzSux8 z!0N28v(|^7x|pE%7XDl4(AOFv++Y1;jSv#;0^h0l#36BLy3#i({`V-}CFva<6-M}h zMR?T_!T@vK>0+cRL!vMwiFMw-e0R0=;T6EuJkR+X-oUx%Utu2+FdPbvnr?)a(g__XR{qYptzCZDZ5c zSsTiRiu~v@>JiGX#9_|E2BAoPRVg9`J%$Lui4b}XX{U4#3CQS($wD*;=Y)8=hFv4t zGP2@uZDW3t-T%zMkYg{Vm|662@n#p&8I_!>a+-f24ndhi6AGBw8Ol~O8Tg@%sjVq7 zmK4mm49oa`O12N}kSF%kT-?CCfF2K{t+!77-LUrN@n?&10D5!4r`Dt_{Pn zq!i{aSP+0S@p3rmCnKnzj4Qy&4Z_K721iZJq5#g+7>%vo`ywy=fTknM09AviFXlTG zXsFirs+>Jwq!osSBfvJ2KcTjKThN9EPieLw^=k_AXT{STXR{%}f0{1vHZvpjTXwsAu)f8%+#hw~w4bxj+_l#OUTfYoclngz=1md| zb+Q{*4GycXdr71J$L9BVfoa6Qs0L?Uj)jR$3+m@PYTJl@3!g%*f04 zN*K|m+H0*&1PI)bI?$F6-#kB86Dam^c9**>ve=bH`uP(iy(s9_bR;=yEhlm{wgc{Z z34(fD&CO5{o=8lo?Juu%mO2A5+?6#m$|GVF=`;J4*(Dgt@phY=Kfw(%yJ~B6L64lC zl37ruA+!;B#_T60wAB-=RaPg0lo94vo&RdXlhdA-V5lbIV%u6`jfAlDIxK$a5>iYo zN5P^9&E*?@kkD36L<}n89#PH{iaSeMN2Oa?hJP5LC~XJKf~2V;9a9u(@5xupl1NXH zPK#$mU{}AX0+ct|4ivm0p{~iWB4Zu@c`v6zcDHh_qUL%Df-0ll>crU*p%*#L(-!x( z(k$j+hZ(8dzFES&D$`csxP_OW2-8?Oqeo3U2_K5gvhs*bs|)O_-dX>8Ky$aFKH|h% zPFUK`=pk*MLyLe#7^8aDyiAL^ucUcPi=z^$STxdWNyhe8_)d-BO|rte>pt^ln8ReaorqoAQivOt`1!AxyVjs*N)-HSNvwN zO~SioKRda0)bgr{z`Ax*#gtg{@q*p~80A*z!U!13ir*&vD`_pyEw@HeHgfvpROCdJ z>#*U;f24h)Zb%NAvR1inD<1A3Cjqh4=I2bg><)``#H=+oz!1a>61 zk1yNom)e-pQUq*#?oMFS7&?fJhsuYuPNXG2bd-DCxyRS@ys;4O04Ul+;K2efihI~r5BYuY6n|K?-RE)Va`%e0Df{h0+|HUq& z^WE`S3b3X9PbossW9>Isj!gsfZ#sn^IhG>R z4kIw{O??LY-o7I;&r+x96_JDiAL}%Ax|lxhVn&+3_jZ%MG7q&^6+0;x^YlFNn*LiH zJl@o?!Ap{_*(E^8mLzGuD>rposoMT`JOoT^x} zZp9qxQiY||F%^$`Xi)>YXjv$qXP|W^5wTkM zumV!u{WHOG3OnOe=Z% z)@<-jh@~N<$V!BTl&*|nMk(Utwi78eJF}?JMW1^GWK|oy(IAuESBb_yH?h9gK4Hb( zKCkME){~N?lU$M`u83CIxzo~<3o`R^r=_Olj7gytbLnb|&K_6e2u^{#GL|*jbj8q8 zA57z3;d=|Ch?QK1SRg|+auMYx<=+_Kp(7WL15bIT!$laWlV>|RMT|( zrU#l{7NylTxd!1Lr^lQ4@5`uXDp!#`-t!+083~&Alwl`K;Q18sl#yUx`QZ?ZbY+im zBv@YMPZd5%pn)~2v?XVHSBbLqKIbtdAPgp|@Sb$CBn1Df=9Z99(=#q$GZ|?#2Fk$g zuBq}qbCI;~l-q(9s5JLoaddHd_MMZC6&UR2EQf2Z382Am`q>p5RSj5dRW6kCqel?0 zQ5iIl7&0E)E0<(7ov#>8H>$XFT8+=m&0-4%*WzA{7KEJSLq@WoiK;dB+{1*QEF}Gs zK67m0LaOi<@0{DF>F|o9W&WI}p_I7Ar}+3m4tEYuQ}Jw;=5$%o#X`PR^&^qC3Sm83 z*EWcUD?e`}vb@>@M}m3KeCFm+)F+K?+L1c2@Em~VJNSy0V0c6BQQ;xbEzVuE}SG1v1 zN)w=$%%@Xc+eoW+ORn2Gno)xprMnSRrfJYvc*WeZ!asw?rZ*q=C_{8sXOxPVSsx^o zh?>hsB0#`i5?3Z+Jze^E(uNF4iJ2OmgR;bQ z($byP<+Kp5KTBVN-G<9-nyl#h{3LGp1y}`fwDvg-$}{`eCqTMQYLG>Z;yz%A*UFF( zy|Qydf`Ci#VpZX$+D7_hw6tH)MNbFcnI%5GuBKImdC(*pdtG~RApJObmF#kUb6t%TLaK{x?Gy1yY#&5jXsXxWv>57s*7x4ha*H~96(4SXUdT%;%11k2@8EN2CYh?WH`Aju;rW)Ap=7GBnT8D8~6yU<~=*=K3Kqv3;RO424IkHfr~zN6DK zSsFzs6~+#Irbee;TtAYBZoX$~Z0f@giE+ZxAcHw+<$oc@(uE`Q!D!G@bfg&?2$z+Z zE<=OWbPJ{-dF;ls8=E1*yf~HveCw_2&PYtow$yUQw>|7Il}b_h2%j!t--4n-(PXD( z;$|4%!(oAjSQbb^$~g_{_^RQF(rr%()v!lIYL@%Y_@^$4Gn zOs49)!v8p=b%wRrlxZKJV=u951i6!}XA4gxsxkw$U4k8uH ztLo7&&rs#xEF~qGOwuIJ0i!0TdEWzubgJh~gF1@=!j*KSr+73|C~LSR56?ITgL0$1 z6~5&lzN%(DOKy$e0U~Sv-W79-*3f_rD|6|uZP>w^I!|MACVVY~l}4a%e0gz0b+)%$ z0$Y9EC@E^Fg3thS-`Ylc?Z|SSXo+_EftP{eYUu7I?6z0CDqwh{awYC0QHvefN{E!k z4L}zI>FNsXf^ln^8G+kQUZOQDCVg3c@#%$m|CXAj$W5K;&`q6!29H>~??^<=w<;6O z%?&?t_7s}FcVpD}*TjOUNshD{Tz)r4u8Wu;1|iZfD^V|&9<(TMeZwmMGt{10>?=q- z*WVY*W7i$nrLf4c)gepDcBmv-!6mUD1N=M<{S*i-kioy@Bu-TinoGt)z`jgvYO}Ic z1@8$ZGx!&V5<~mAky7&Z(V2b|#C)5y5J%A(q=qNZ#e(u;SRCyC4`O9|3i0z`GF9YZ z1m%JE!^T5PPSPz%13*Y^@Ye3|F3?0%LfnAFxPggrgL;|@;|5O+6hoe?3!td5QAwf`kOxV_in;Ym-N)yf@kl+@93f*XzC=&IP>cGAL9); zATZ~^*L`T^0Qx0`KBh9Pj&5-SyM;=+8zSia^<2t{|rdD$VJC&wbf?oWbqsi$-=i9KIZ9aP!Ml;|y-ze38wd)^d8u zEl#iGgD^quE?bK;sJ*um1-;Jje-}yY2Oqr|YC@G3Xm+|o=}x69L7&aa2lEIPJ1$ri z34&`^n!Xy~j~XcnGkv2Z-XBFkztka&nbMk~hc#6~sd&digItsab_eV~{$eR5U%-p; z;yw6633%v4G9rRfW*g12pM`_*tWqK z>mg1JBSUW!XwfFbU0tRMtGg^`SPP$i<4!Z`Dmb9S}xp zL4`{17V);#e|a5B!8Xg z1sUWwF{gleU|xPCAqSLKnD;LwA|WC1WxYjc;8gIX!g!FNv-z%43Kyt8ssYVg;z0vc@>6i;8|*HJSf1BFI!nA91i`e7PA^NBo2ilo|e9* zq7H`=*3eX=?mH85B&^Hih=%J(6hnWB7?e~7{UgaBQU$dz+_X}(^Br6FJGTr@&FFz|p7m;4c$#!MfUpHN0}GT6YS_5Sr1 z4?ebw@L+tdi+wgB%vg9yNNHzyJP6M(Vt0{NlOmf=4cl~bdOV2Ct9Zpw#(qGfu%DZf zw7|y$@KJVzKtxjBPqJrwQ?9+GHhfj2A}n4V7-JPZeC_n~<3k9XKAX z1u9AP=)#&KTO015KSpY*#&tI;f3jB}?LtZNSQeYjUNqujzrCyvtv$Q)d-FLIXV%$o zr`xi&F+!&q&3B4VC|Er_YUDwnp_s>e5x#ZS_`W4T1HibFr?qL=QPMn*Cd;=p;K~hl zT9<;l!nKEU`{e4)JQ4u@h(5|8?c0?Mn)OxiX zUMZ}2v!DlVA($g93ZRSV0Mqc2=mb-FRO5T^&5n@eZBgn0b+cnw-d0f2?1lMeRQT0!6~6q?EL&Pm}87zbxYE{eT6(V2eo7NUKZHFdY5x zrR1AQooJZAms9MG4p?hZjmTFtG$O?qA4NP%NAVVkyxIlPtn$@qX;$)WmdwxXSCc^N zB;?;nwIqM;E>7pQl&Xz>tXayhPd)mhhY=a>Quvp7Ic1uaphvO|m2hc4zQ-it%FwG65(8;aHso9x|b;Ggsx!QF<-8Dn|%m9C~4_3k-o&W^kv@R zBRt$@YL;;j^>dFJ0tfK|i3 zc0d=g$O$4Pq8boSXrK0LmaQ0;6agPLBo90$Fw={_VCs5zo9e?RD%6I#~{>CB` zK-yg!e^grg`uvKh5}{tDI$k(xLGC?#uLG zqBbDqenU}NA5?}x`U4A-B9?%XH^|u(=QhrlM$F35dGqe*{-SAW75ui9PtIk!Y{T+S zA`hx_KK;0*cT{vi(^0|Hx#BT(pp);H+@%g-CcaDX#D~s#yP!GuR7t129eriy6P5j3 zxXIy@URRFA!ajoKdnM!_$mP1wUZ#sq(k^PmXGmAtL5JE|{;GxiZBs~we-hq-u?LfVf~JQEdOIPZf4BGCMWtV@DBHa%xt%%3LhQ6pY2j z|6eXS!V(I}_LYRbS+;LrLL{Ssu$eE>m*xJ@8xC82f!w2{;iK9{$w#4kNT!9R!{SRH zHI>L(gRN*|i8bEK3sF20+E;aZ63m!*cfuJ9i0(^5;J&*P&Wsoe562FZI~`yEEr|){ zqtz@GcGb1wDALRV>-kGkEs^-)!YA&0<@~X3B|>3bT=l(2uyh!etN&btE;}=Ibc$pb zUFnry2%?Ev&eYyF#nO6$^dm_;3`5IK=G-&yUvW#M_*anE?1%E=EU*t1(&FL1?-XLe zLU8sHB`dWBf0jr*Wf+K_lW&_vHpJ5C6)P&@2t;A8yEcn3BO@yaKEtU{k()Gce zfyC-gxP`U-C6ix4uBx^cf{!-b=%;~rl^7iGMPuf!A zIo7ka!&maqp>BGsMsq@Z{t>-o>w$~uX(@R*mdu=KmfYmrw3Hqu#FoVUWU_itRss(g z?TM%j$jbAwPax5nv~Ck!nx+!=EfeY>mO9F6_{Bcm%v?CZ`{=5^5E7omRV#!LGHegg zt59*tOHKiH4H|X;;xmRT{!Kctd zxDW?IKOHFHLE?#W9&n>u>u}Yu2vZ1^|E3p1$jAzx5``ci@8V_dRN4HiV%!|^z8E#J zJ(9+(t^OiTv@fG4FDDH)Revh80Cas>FZwEw20n5PB3etNl(QfQO$6fAq(d+}3L=l6RNi@)XXY+4p#e1g32V>C<=U}jHjQ(WQS|ZwpFOH?a$~AYi#SXv#uz|$WMko=nVZl zRd_m~wy&Q~r0-0HP`Yx|M3_7(&Kq**LozY9axTMJgpZ1TiVJ8U`M7&_bsP z#g!~61S<<)Ww6*iHC572R@@p;PI4@+%Bax7H&$*sQuLtYNrq~?otD>QpIiu!VO;Qg z;h&5{`Ub_HU9Q82D$9}cp5WkL(GHI-zqwbg|MM4`bo$=B*jmMI!Xsd;cy^ip1GcIR zB!uEK_mM|robi)af|dWTxGxW`^SbVPB0&%UK@g%Snx-h~6HS631%Lnuu3{JgTtr&n z!}1;pQ6LFF01u0a&Dgj|q9hA;DU&j()S?upP17Y!I-IngcG8Le$l{Lcq-nh*tvYej zG)_~e9=C2fmPT&uxa{wD?(*I5d*6KkrFJ?TTO{z^d(S=h+;h)u?zIJN17)f3Gbq673o$&uo=Tw6@515&+ zXCI6l^@H7cR0e=QcWm(^FXQOQlIXglM$`3NeCg8O-;7XnDXzxsND;)8!tUde*y{et z*;(Y(qy8f(%};2h=l>!yZk1%eEjlXmZe&#DH1FHui=`_f!zyMcrxqPl%`^;^%y8?) zuDqi&Y!q*kO3pD!3^Zcp2PKPPb(acuXy=bq1H+|x5)uY(=Hp(_7vr<@|321cVHb>z z#;@k-ih*FWX_cm^`(jjfbxwnPooulht~RubW`DMIxbY(fk5KwWN$N_K@E2(YvJb09 zjls4-$$<3I5Z?d_2uj6uNiw1&DOf68eeLb?x35Ud5&B)>wjD}^5w5Fhw7d`;Iz*yc zeDq`d5bXIj?UnEcsRekL4hf2u{I8!zKuP584vbs!Z8R}cBt+SbOP zc7ID#a5-pd+J7L>{Jan?Ts_nxmbHrFQ25yvekQB$!D=n|8&}tm#MXYWwJE6GbD#xl z6M~{khp8INTAPzM&ZV1W=@$H#DB#i>jDn?`gW8tnMl4MLiY^`IGA?V~?O%GgEZvM< z@6$_bFbbC59n>}-*oUPFK+&bc3O&nOTauT43cDtnYq94hyK4;wm%djC^f%zQdo@@j zg$4pC0!T;-+ge)ySQBwV@W#jMg8dE6d-2bvriOjY!Gl;Lk6YoflREY|w7Dl4SRT|*-vl)R8c(bb(Y@iUz!-G6)X`5bu9#pf~EIj>Bc?WIshoT z^gd_leaTA)*fnXQ7XNQ<1dj?p#1^h@-51pE3|-TayO^f%;M(7eV21 zwIuAl8gt;h_)fGevT=u1w|QB0n@}VvGsL?_LTuX)wt-de5u|hT-d&eqFAl42^RjAu zrZ>Dt{-!8Yz$jQ6ta|sJ-6T=~P;_aBRkwLq^%g*AST*;pu)P~N06+yxlNs+JsS$vp zOFN9W&BJ(~!qSHE?z1mJv!Gv=#w)QGufhR~kpuV_pY4KIqK#~MAD;z+$5&!+!vUh` zK*Ro)#a@ZFl*6jqysR3Z$q_YyNNc?I{d}FcdeLF`KZ zT!;JI1Y{bP&TCZopzW|L;$dl!JJhIea9z-JfXu#mPZ4e})n}+EGAy<0=a-}?{tTnQ z7!U((ksbi*p>(l*7GL@M_z&$t_tDU36f<)_F?@0 zv_R5=)s1M^3`!`xJ`bx2ae;S}Z8YyIa)Zjo-s!s$p~)5PdGzK%oIQ7Hy(K zSvuJ#fQ-wcuZ|0HGFofYLKbGwv*>|`KUsG-4QqABBeHYT^Lm8+2maY){RjCghVnfL67 zRcgdEnjv_2o{XXC$&Oj0#>^lFOTs6T*IF0u4ji%*%--#-@sOGWK`gSz+RmfLJ$mhy z*gTVnQJBU525|pI(Z9_#0dI`YikNWvTC_#D@UY6xt?9YZ|H}^fuFQfJPL7I;vhe#l zggE5TT5b$%XcTOK%wJQdmfM9H#4gu*JtvqE?monK@B!~5z9ra{{zmX9b7?vcG3w&% z_Vnk2P2G*=XO8W2A8nu8kE7J+c2#(a-RH>CQ7~~BakVT-g=8$1@3cKTslW2j)7qCl zf99v`2Lx`>nE6r1pPhTap85LI8Fq~vhqv-$cv+84%;(1tdz^gqjfE2lrucFj_SB5W z5Vt*sscp&2;5iplKyxu0Rka(Q%S&Z}U#$Xj83KxzFB#@4G6dRo8E`beV8=cvQNm+a zOg@1MJ1nG((pc@@l}?Cb!q?&^-ey->zO88+;d)3}fjb$+kiX;@gME3K-T{@pf8F(C zxasBM*>zTzE4t!FJAt}!nefKh>DD>Z;i_fisui8Jl#wXTV_R{2bQ*aS zQ}8Yo9fy$H3txD9?yY{icG+?Gf|oXqZWKGNoa2yssOlhl@mI{d=N(k0f@GZi!7aFy z?ZI&qK}&g>36hKNju>GKmEE24(}^FoI6lc$6GkG$YEF`gy?ZQ)c(Wo7H1C*FhV3|5 zSuF&PFrCG>=zC7RYsq`{_QGqwXK{kn;8_Xur7DV+3g-6qWx7u}z&J8*22iRf$Kw!-ZkC#tae_kwvZ2-1|m9 zJ#ELVz!;So>K{?Y5ubd#FGO60j0zZ5L$dP^E$RQ!>yO&SN{>uV0ZGM&l%f&EPaWqu zcEv&B9riC&k=}ayd)=kH{dT!EN0EV%zl;llYXU43U$-PVCd0pE$<&m~exF9IW5h|Q zceu!?H(%-qsj0l3W$b507sKPS{CDiOO45_6salam=;X8mR%)+i@%@(}5C6vz&bdZW zO<;(q`1sPd=kinOZ`f6ZW6wzTQ}Ka|iTnO-E-v=_$CfaF!2*kF7!tft9MY1Ax}Xaj z=#Y~lD_#15bK(_XaS&9NaOB;gnSGh+Z{c5~isjWHv)80hLd3Z|r8O*pCwE5+YYvAh zg;tF}775Fv8kzH@=WZlS*>UQn31@xKH%g`hMII2=lISSY3hYp%oUyAl`PERk^^C7% zAhwKkOC_%AteG|(nI~EZXG!ECUFkfbh2IKOaT?9zYw(kZCBQ+IBo8~gSdm4lum}20 zCMgnCO+U{_QABT|2=5zT@BN}fn{{D?A2v0CCyoU%P~&nGVW-5cWR&P^LvFVWynm8e zn8NQPi%^x2MSdJxq&~I?9>t_pboO82D(lnVxb?>fO|9oP5_(r`RGCJ~ladF~{~o zi;RBb>t|O-MnBBaND0iNrWbFOXYp!P>i29DOC6(;^$EM@hjqdX5^!Lm0RCAVMaP6Y z&{wM<`mT$&{QJIfFDF@ z7m0}vCI<%Qrood623DZA_WJpSYrl6fRB@}qRzra6MRQg+Z1B4o;T&;Q=3juhO}+8WSOxeEojH{9W!O4w zfpah@n8NS8R|)e7Vwv{J`7A4NQfH;KC)!Aol<$@Cs{od`d5%U0@7#bqV(luIulZ;sK~kD|H}bWz;7KTT z;^#3_+De%`8SD~j85V&s)y>F@oj5IHxgBg*bW1FEQFKrIhwfjsv#(O8=S4~dzwU0l z_3bzQO~DuxZ1WxJ`M*e9#fMCgLFV1ah)NBY7onsa0nUO|u7`M#`u4m-z{_>*#W=<2 z3eYB4+|Df(+oQ4JJPuqGDaU^bwTz}!Nd;Y{TDnUwFdG9|cb|tFCDeAMl(x?1kpw&V z^U>%9h1Sq-HjsF;gn26|7vDn{jfl5i;2NCF&oEUwblNA3*X6w-ZO=m9zL*Lk(*GKk z@EDq%RI;^_a}p-e=%Iwity%O)!E+qW zhO1W{SC1^t!N4QS_DLr~Kk$_F?i}s@CA+wcr}qsh$gJxlLSDjnDaMb=K81zSq<}qC z%7S8P{#krA`Jp@&whu~}M(XdP7&=aYuVmyfrMSOPmb{lvBh7?~)+@St;V0Mr^z8*S zSi*!6$$Kl0X66ctFpk%cj!&`cyh;m&tb~khk>AW47{3daT*lsc^MT$kT8sp3}=HoMaU6q zQW4!X#jZLce+VL4p^(;TG_o}k zmU8in6{XicPp#{yI!+0svMlw4MgId&5Yy%0e=zqAXxn=!!lqG$z<#v6Rh7YxeO8jK zy6vdktbmg+*;)i%vSHOl1Px5d-|}|ia*mOPUkL-N?7&$%{q3NY90ZjrVMtj=`mJ}L z{l&h6_Tvilg`5f6CgC1U`u^tfb89%peh-N7y{R1>+i=;<5^hj~VoL3}}Z zS;sO{c0|>Ol>qG+aOUeUp4ogYQO%yO!w_Wi_3k9pYe>SVVe_@b4#(%lZf9X#8nehL zIWjvf)}Yvz3~S6g-z`6?j7G7~D%EEa4HeR3q;X8-N|fPy_DBomxe^bNl%|fcqnZVD z`7u>#H3mS1d?tZd_R|&8#Vi3?oD(58_QU1JW(E>It5lx}K{=bk$2u=I216-gf?O-z z^5ZI{KK5DJaO(8mnr?EQ>hj~c`AJq=lNTfQdC5yZx#gVG@v+gw>(PDc)*ssqtwe6| zrQEbU+41QHmc4$-epq?}p;AgRjD1&@I>Ccag}PqJiTqg7()@A~8NDHjY#E8zoF(Ze z9bacTi%p=?pHhKhpO&Kb%0z0$0=oR9N-d6kR;fM%q|*+AEyc(1%qATvjl2@Q)9B>l zRkY_&J7*-QZIr03_!@RhQ_`5G;$xbV#xxfnvpZ?b?&4$6{mdfE?x&^rm_12j_7oqp zH)+h?;$!wDjoDXx%>JY?`-_h`kTm8%@iDDQV_J)kX-gW@R(#Bnq%lW|k2#t&=4kOT z$CAbzD?Y{zWwf-TA{b+Lp^SIo_CI!_(N^iZa|%U*6E0i@2b|sb+|PAnEO}O%A#i5p z62_ECSS>U%JEDZvl7ufSRSBNh{1vA-aloLXgfSKB>%m{kAGY)B5uIbIadB+Zl{8YI zV0&~IRcaLz`{Q5f#+RIOca>^XcYa_pkv(hZI?p+Va}8YvndJ$ql%a!Z9=tLP`L=y0 zV*5~ELi)4&#+RzYa_q}x*|4(sva|%T$G)hB%Xtn=zfjC7OV5q_q^v4EDlTKK#2Al= zwO$YD7am`@+$s$1hY`~=yh2LCvbI;)XcVL!`KWZ-TU%ivR@+vV+bnqNWxK6;?Njf zKZgaw=}Ws2(>E>7$k@ac_Hl$gn$f z!ki_~pX~dLg>fZ%vrG*3ixnY;^pXgtD)I2BN`QC;bj}0)l%2LTb7cnAj1!h;T|RzK zd7eB*#(@x6(Jy=zsxTh*7jy)KK_^imEmZ;TcE#lgT~AmJis-pDU;HCSZ(Wt0&a*YF z?{ZOdjSMK)0}^jo32Ydl9K9^@8-d}ici;G?1$NaLRu1*TQ1aGU7hwJIDU=FK-r?Ep zk=e`jfJ9)H4ehdEEI*s5mK4PJMw|#MyE0=4Z?nFSh)*EuL5O}@+vwfi#5<@_NYo|$0ksnMs-yW7=F*fb)e@C z=-kiXzw@a*s&a8x>y0p5tQK|y)96!-u47}9mta%uzk-T&N1n;d@6qLpi>_DAGR|C+ zh2kQuGi#jr;Ypv8eg?X~z!Z88NaVtp+;H!1Dmi=qA71+tu3&NVNYczso6+LaajHnb z%%e%u#Ttlsuw8Xw|7%}WxQ!ToH$R=9K=93243j2arYJ@~Xpa*Qh=Rf!|KP?~A^E}uR`Q-<2WR0+GyJjPF>5y{bmRx}iP=|`R89?xL(0ExHmb}ZB483)$tQkwe&MC{x8ArH zB5AD(F;k^#p^?qb{4B3pI1nj_H~xVI01@A)V5>^vIp6p?QHP&Y@snHM9?ntGa@Kzc zrb@&&s17X%nuNj?2Vme2o&Ft;)hL`iK@$ppS<&$)z0byWy(*KNN7sa;?GC-?2)?DK zrx3{`Z*@3f(z_e}7Nq|2PY1r$@e2}_)qutjUr7%kAJ!$FIv}0z2WVy<4={>k4KPPx zh)gAK8*{-`Qm5;E!qq!7rXDU8I7$~SC^nO7S0-gr>b){9a$}r%}*c+_jAD z$z+r6~= z;k;MAf;hc~7%e;+oplO@qDcfr4!>H(@g{8gBWft9|0fAoup z`25=C2f9CUeWZYh9ygVPK}q7F4hBZ2*zS(a?E)VeZB%>ZUHIb78(--fOW64?=%8k= z;tmv!?;tdon(H4M9VmtiDW@;N$OuV{#os!GnpN351||B?q20Yp4W6j(Qff^GLfI7D zh)R-NB+fzhC;m>d*yJfbfTOk_>K*z(vU6F@#Nl|lZV*3&A_uI}C|X%()q zdIeO6DNzh6yRtB;;+dXBWScw|l`p*gdW(#Rl{Q40s61^6LlV0WuR!$s6G{O^HZX2h6`Y$gNU_Hev7bYkhr=ASg(F zX&Vu5fDN)69_K)6kADO4`i+@)&$l}Q>elw|V*y}8BH84SgzeYQPS#5(Al}dZ{2&{T z@jMn^Y@IB23Z>okLL)10ghtu*?cJvWa;tEDRL_;LiN&W@`{)Sw!63%hP{Y(B>|^cS z9lm`qpLkj)MlQbAI#{Dwi6iJcfP^yCh8{WON=V2WZorvj~+`mDm2++k>eoV7}R27z9CAyFMJ(lNEJ*khUf;;cCSOMQ-r^)lf&-HvfcrHIZsp=L4pG2YSrC$=x zoDS!H>q2byjnDO@Bh#}t-u*1GF2yA>pezl3#9|KMGo3Ec~PQLn!Zx z%vF&PNB59auCXlM%96368_#?veen4+bNQRg^RxY#{I8`yJJOH-SHE=YMGHoaK#dT= z+@MNMnx30drpzgn#2P^H5hzS1>LUVJC0iX{zstU&P6OFBrsbiu5LFhJ7+88BEfLPZ z2hG3%Np{oLwAfJH<3hBPW--N}cw*-_Ukn^|_dyg}somCmdE4$_YV>k`OjWHdBUWkl zRA=4`=J1lH#ARyR3kkitJ{G26WMGh65Fh6BsxXO3z1#~ZIfeS$claB%WjB%R>giu zOsdP??EmYopSN3iM6v+`q{zeSHXZbgqNitODvx6S>Y0kpQpQ;#krV2#`Wbrd;~3; zrh)VUm~mmRr(;h!Pom>XD4$w1l^FRt`cWA_I`hA$pSB;O{sIKoljAyKD&cd=y!P{Q z`J7^I;SNeD+1f9cADI(@HacU;FaD8ho;#W!${`D^aIQ)Uy%1JT-}w5uFr?y9V||-s z06!D{D75gemvrPqbTwy|pTA=vdn`K3j1hkP^OQtdM3f&j>C%O8m-O~HK`AKLhjt4I zUqzyFS3W%%n84g9{3Ve9yb65h#Nb6gt-5pjGv6eoXKzMQQ9pWu7CTF`G*s)mlK@uV zPv`O$O{FJ?C#?C_WlAOHvp{`eCbY)7@%+SGfXKt~Qc$^n8eTBF+DZt#Q`#3D-B}sZ zKDT%8aL`(M5;?7-5?vYnbPL|_94z%YJhis?*^Qwrq=r;I{^uGsAO1Lcki(~r%4b3A z80?woho>yyOPG^J=7c#v;>-z|oR;VNnRVb>@{GqJUGK~aN69z%1BzuI+jWMy|_Pmgnr zx1WOOC)XC4zB4e?!Fd?F&+;l4!oD_~k*sz231`-wi_bbZIRNMPL;}BYK)v4XOj@(} zqmhUCOk{Xsx6diLTw5P2VO638doMj`)^&!h5_eD0H< zID1rnlF!={<)^Bi+WeQUzLoj&bbZ7q$G*<2)q>#DOAvTr?ve!LC>Ba?OCi zsz@LbsDX>f)n#vdJ^v-T!|d#c7Ni07&E|PMRb6@im(%6g*IM zj!~{g%3O-PVKx+%H6HOyRD4r~;)QRXhhrc(B)>dtemi3WX7E)V3U|$pHVRmUpze3lC!FxF z%AS$j?DiSF5~C{cth$Ronc=@%BG*zGfiE<4|0DVQgyV5uQDagkBOh0h317_1=P2N4(Ym6>q%K81UmN`# zxC*UpD`iL^9?|F^^bAD*X1k7?y0-FAP-Bc)kwGQCpugx>(HqEm>ZbgdgOQSJ|Ml7r z?A9bHd2~YcUyja3MBDF>4XiZv=r_0i-I@Pnzj1AG(a(4Y_A{Hd8u>5CBhqb>x=-t+Qp5#!`)q~=C;^t9D%(!YfGdD`2wB`uPT#0d^U;qsPGT>C?kg2*Q zE|3@@QHBT!be}P$WnwU~K7}lii7b)PHAf0SI(ND+CEvRt6=eviKp2~IE7>0Z zu4*`Sc6(;MO#Z9`O!=u?&1Dp*Q$n)c;Z*l28RQ0xRAy##SJ+{_#+KKK z9$#}$t#pik7GAyfXaDC=71|}d-}%vdl`pNo`~d&WV7lr5e)V7T@7M1RKj!4-oXt-l z>VT1mgL#xsG=V?h`OMkLjo}_eeuJoGa8kJABB6X@(|h(*yZb3=v!v_UaTV$l^wdfc z;1)!3_G?C*AbpK{*5XTXis4`Jow<$P=aYFl(#hk8VZF^Ab46AEyN={s%q1O zYi;PMeeKVOZ;yTNjo(+8VEIA%yzClV)f%S$pxz9sk4dFDhIwn#VZo$bpXvSb2b>g7 zsj^3glL9M*H2*h72AD9yc2Sqfw#2rq@^v=17;uA z=^u*JU}(|+ODq%Wh)ALh$n|LUXu;~NG>pb2OJDuz54t*nQl>}yk-imH01dI3hzFJf zErN#@TNv~MBVes6T6z{$Z3kAS{|n1aTQqMsg{|0jS)^ygoU_y|dC}+usvyl-K{wn; z1?|i+7Pb~)`jk>xs7iCE=sB!EyD|NFavu<=bJHv+H7GMiCk6P5usiKKD1Mp5{zkId=tvh+@?rD`#`V zk&2HYxVm&}#2>KEDc#CKy*8{Z99E?&zS3M0srtI!8LFMYG!jP-IG{;ckupz2B_5>f z5j!5g+&kwc^sO$q&A7prrde1Ytc0&lbcrqAQ$S8qj3Ap4geI9th8*wrv0TetpiZ!U zvga3^a=$eSNKql>+9V%$6#6>)LzQ-@t|Y_WA!u|WkceH#P9nK*2hRCx&(}B&l|H7r z;^A7DyV`lWt^MSY0DfkW>dYt^- z9kZR-jYC*PUeEf_e8nA@*0oyBjJJwUbAB)pZEJ}t9+U~mb|%?)%!p}fhjV!Crr5kl zxk=)%tY^9B^y-TPmnWE4b@>5`EzWecoDNl6<@H3PxJguD9+*X6Cpiebd2$CPW)tDu zz*`kON=SoEym{B}Qjl|||L8H1%7X3NnzqX;sRTIBzRM_(G91aG8l5M#l$nI+YV|!i z466n^>u#{)ESHg3`^0Um<6A(tGMdx;?X&A+7<&BJWt7u3ct8ii1x-j<6 z{6hA{?CPsm-+V8Imdz)}#@N14s82LPS6)t>P`{ul5$(BSXSeH3{tE?fyWz#BiQUEr z-D$k{VuQNP3J~UD5}0(e1I{DvJca|ZnBl~5B1{`0n^}z|U9aZn9dhQ}X66i~8?fn^ zqLDbeu9r?I`yQUFb>`e;=CmjaMY_bPDivrX7iSuawxR@4rN;8cbRKd>KWu=|&ycY5 z#MRfS89WSctC{D1`P}B7T2^8vV5CxneUu|E_9#jXW>c*Wpf%~Et=U#;V|X`p!DkJ# zqm?B>9++z|zhJmU_o{Sb;V7I2OvMzE2g?vzflH&KARFO*Xp z_yc-WDN-^V3IYWG>IyH3kXQ4pnFEg!^qAP04m}lhvip+wLG{S*j6Z|o`C906CxSQ9 z75lD$70ileoe0s^o?4?y%r8)Z5gsg$B0C|*NPJJX)luNDvKoaxH^D6qG`v%O>f1QH zlX8`_+NOflwnSEo3s_t&aZ8)YagAm)TpnynoyVWU!KMqhxw0_U`&9mhcF_=$b$RF(*j9|0BR4RRpPHS44uFU73IZ5lg_xL3 zguR-Sr=B5FodfMdn2;?6As-v*FL8z~2DFZyJlMW}`S$Z&Kc4wgPc2{T^fOuaIsI9B zDjhLzY9>E7IO&|u;9oym8aqR)6Z_AfCEK@ zVk5;w8gIxl)FfoG=7u|{*yT(Z@>)ZEI)ic)y*Cg)i1G@FU?TJN`dyu%FOLPE9%l_? zQd&Vs-Ua>eBu*BD?+uv{VlZ&cLC!c!W_mf(?ub1xO4Ub9t_dJ5%JjEVG)e{8n=la5 zG#q3Oym2c1S$a%yd0VnK&mDHws)IyF=CloMP;;{=BgnH;3u&Gu&GCSyiM(uUb=NH9 z?M_@@^zBgXdhpDQy~AquvSQfi1?7C5CaBYSyvAr*CGiXivxt_ES&@Ar{aI)`2&LBo zX|uVd^9$@h(s3&ClpW(5P?>U2+R@QtHjgU=;~+MYgI3M%CZUOhThlS+5HdyBBtzcT z?AzFL3YAKAIlFqejb^e&G&UQ1r~enae%##{{_MCQks4|a41d!5jaIaV;0Pj6m#>*t8WdpVwyrIRVdblU@ZAt`k>5q!w3mD%2Axo+>%vboWUg5^Ia^nmq|6 zA3Jms?kd=A;U*#f$c00iws;T|b|+EC>D6j?Su`)7U-;zX!!L2cn9Pmx-=rc(vqlIt z6D9M(I4g8mF+5=vs|oWMRiEAbmq$*hi(y47l-|}Wm@?EeNG2H12|@ZWw^6YVs@Wmm zB@=MS4|VqK#3Ac7SJ^kSuoid{HF8LEl7J#S9ji7WiV0ooV;YV|lN2hPF!9J61{GJ~u1ci|!L*cF&C;M2rGk z64s>o8XUuY4ionERJcO8E5Yag7+I-y*83{OiJ@Jc@ZI9;W|%Y#Kiut{H8Hw-rf+jje~vn7O`D@xR+5$ z**CXlE<9lKq%SzPiXK>fdJa|y1dH39d0-t0W(g4u7jB=BK-4I_QXGTrfe2hWPzM0iEDGibMC0ft@L>Ux;wGu!`rVt$ z(N^=b&i<>g)FqJG0K`~rz)ZmLS~{Cq{Db1NY$g=KA8DohCC)d-lcj=d|29H4j}=Tx+PK6VNigIlAL8r8Fbq`$VPc{aixI9%YGY2PuB7X!gn~vF zZU+ty4skKSx5&p=tLKV2{r=ZIRGJo{O zH?ud{lUNK?&vc)1AiB^Bdx<0zCL2@Ll@k9x-Y6l-A=GH6PrMF1V|tW9Zgrw}O-@PU z#t4(6wJG6P_KOqoy@6k*3!OMRGeSkO@A}yndm8)t`>Pw5DZV9*R)Tk-X1M(MYuVL?ZlJRP zou+z_14?6SU{HcnV@)QQ|1YC-k`7xG?mjv^p_)?qNvcIw{hObByr+j3;QHNpAn3+d zI~p68DM2Lt4Stb+Qog9?gXBPQrWFED|1|QHDf!OfDb{xcl0}{mK4juSjkB>U5y3;f zIr!}mGUY(^x28 zxDTLr4XT1{@^Bb5iPV5DLKDMEpb=?OWF{XvHjV7-TXTJcNFe~jO!HIj#HXg4lvkFZ)D#Y9?daTLWAmuf0QydUR(s60kiM8XPI|3Sr45kEoqBYfdC zW%i>QwDy+xx$w)5R0#tN{if=WiQ>j(vOA4zymUY@zvr9H-zc5r!t!(LAUD^dJi-wE z+c}SWypsx6<1(Y}D9K-$eH^$t6W|gWHm@=82rJHn zqYDg<4x{X3gJg=xMWUQDsLL4npk2#{%O!IM1`xV=d{~Zw&fqczxp^h9fLmBBER zLqlxaWh7LsB%sMGT0Y`UXrSOXF)%jA02Zo#e5_2*Hh$#8QdRbe7k>Y>uXcZw61lVP z!>!}}qr-EPb2Gs-UjvcLeUls|QR__x#*EGfFuc$5TbN)`=1oDUOvlwfNuS6MCRNd zheRHJOM&;z7x&XH;9ej;e_UJfVXl4gn|OHeQ+`R}IgL~T2REkykrDWpM!CT;SVU2j zSZfK2)(sh<;Tn88K9tET7zf#)OL<9Hd5l6BEYq&A{xbyAhFf@Um~xchu|i1gEGv}h zxJDm1B3Ajpxqg>rNgYfzEd>O5%~Z2W6$_FeA9Rz5x$94Vdg{x3#uT5X&w@Wea8oD$ zs}#6TTq}?NlB;w9r9Z z@T}4Bkj?^qG{m(MQ}xXk&D4iZn#h_-VpK*mzsFtncJeIAs~kk`ZZ*m#`4HyU$E0|X z@peHmGr;-<<1Ji@3z=nJdqt5EK}kn7i~88I^^s*G8pQ`*ko?yQKTBB3XH#GtmOT$w=cky9p%X(4)-XI&?(^2U|(FVDEKn>h}B=8q54yJ!D|H}?So@lk4 z3Dm4ze)N%z&&|$3Z>IWXUQ&+~*Wli8CEBK&;oe?-n_?1uuJr11X%=nMA;bO*yClz^v=JRb<%EsLYuPzX(f zq$h`v&wK!wJ*ef2r)hoBJ8d+{pqU@Q9kjRgJ{voBwd=$5sr>Tx0dr_y6Y2 z996b^l^5pI>u9It_t)@h0+HH50j+=&K6P{y_4gTlkSjDK zK(2+_Rf{vO&($l>)ed7j)K}AYR&9$y7tkIeXxg2sy*TjtV|U*7z}UVxjN-uvYNwzd z(wjlw+Dvd2wL6yz@76nmWA8xCIi? zEtX!C^-zw+3BOJdxtpN{i6Arl5_KVXi=hqO{wYO^pmc(kbDyGk3hkb`jjmw$>F@~ana z#oQ(hQ9#I(wY}8E5?C=ZohH44<@)G4N>_4-KJcs}dJ!_~mIOa4VM2e5MZrDc%#4Ra zgfT60<>>vz&)8~$?i1f47I@yF;Z(LN_MUHamXPZKQ9K`v!NusCvCg2wsF1_uTB2wc z&IL)cYaV6+khz1TpNX%d4#L$ib$Q905z-}EOBk3B6su0k<`7TFMador5P?aciDQX? zSZnXLX2l`ZLS=m#!P;Rs5qNCj*cB{5IC?HgTl9Q~o@a3e3FX%u836}Tp@ z6uhx;VQgzgsFU;xPvDeeXFomsx6)uCori9N_!l!r-mC0=HT~a-rhYhk=l@r>5-u1q zVOo!w4VE;&5yJ((b561UU%^ZnQNKvn zA|v8E-%%grehuZ(=Vxf{Ughn-pFV-z^(i)>f4*DOVv7U)F+iUWSre^z6K)jmw&psj zUz)jU0yRmU5WGo?Ej|E0K>(kx6Yh?uYiQS1@T2aJ5R5zTzxmRIf2hI_QnmT-7^iD$ zhXVeba4>4|bp13gD+1mHO4ER&LdXV`ne-jesBaC|H z6T>(;6{B z{-3XXEnTmILYRq6vF6t7y-LOp>3=uhn$@JU`_`;OJVyy1QeQId^;r{&2#DaXB{1QG z3>|(DHD;)*sbA_a5`N;Nody?1VvI1-DM2M-U)TuoX@5-&DNQ!XEml4(lQvm-JKB%X#A0AF&U>4hN z2~`Q{8PdY3FxSmYbb@2 zlrd=zg`Fu5vLPflEDeI6iU5;LdHP2c5fN&&9j~3BmSLFprO!2f!IKdJ1qYxIQA)YK)J#*7k>F5NZXcNsdS_A_XIU<;A zGTM>65G61b$pT`*f_~HY2C`)8pY$9i?qz545==j4jTdJpKAyJrTq#!xQ%W36ZO81F zM#qrGiZcf_yXZ10CP0#J>MfiYFZ543PIt}szDiRG{aD#5b~E4$?JU)Lk|nsfj=M+> zEe`Bk41<=~>2njPk%Lp34leS@AkB-%$|EAwsiSi2G_X3+%4Y;0=kUTSzAq6RIXTjY zHup6-;iz(wJ|TJ?->#uuEWXtB^XNm`k$Y@@!3yY`PB9yZA&xjOtRcp@JRCuc{P1my zk`5!Rh+}Pv$2-1AFx{zBD)LGNRLv;SCgSNb&&!91ovR&d`{d*Icl!k_7>qvT?bilC z+7$7C2@7A;0EnvHVpSAtqiY>liV0A{5**UfF)Qh3tqHKe$d>=k)k_{T+u2iSn=qR_rdQr64v+E(|>gK0p-#Mar)u>xBzW znAhMAA7^PafD-^deoMXqr$YeUIMDw$H=*LCu5161O{*P7SDnt!GBXTCBFFQfH{TYw z2;Z_n!RdqZ$oe3u-TLF}?c`a`oXu$@u4%m{X(Tl9XaNtPvPvIIn-8F4v^(l zu9=@wDg{)0kgHZ1Tw#zm2FW&fRU{SynkTDnxgsx>2dk6%g+eLB5ZZ5ZT3`wBySgIfY9Y|gD&ts*(3u!Vw8;DQyT&zolU{SmdWsudjc^!~tFzi;&T2L| zi#8h-w&SCptC{cf0Rieyg&)?@$&?eKm#uj$+&_ z#`9-m_>+^!a3KKWp%iO2Gzr?SP2B*;4;rcRCQvsAXn0%Ubt^Ju%08M)K^ zC@OHUxyK8 zi1*M{v&4qy^~rB72ARO?^5mN0FeuT*n|uoMrInNtcbk6Z&G+tAMj;}-C^qWg;+P8u z&Yx)A1@3R#JTBW?|6KFXPJeU+QmuHX+hsbWDI7RZY=RbSJ|a-D(H28HLU z$;0A~h5~{z^nEDJroGeHgcP%*-5n+o_wsybwPuS8aZjyczn;kPx)2_|ygGTtXikO= z7iYjb`#)#yaOUZ2zY=C?cWmta>sQ`;;kPdQ!}*1QzO!zy?ygScU`Cw6TOErltQWDv|RG*GJxbuWROopJE0u%BA@wrW;Q{X+QojI z$gV^AIuukNh2+inBj90M)00xS&;7Aql&V!A@D|(Ur6?e~9kD22A;+)_3v-YaRkc8H zZn~_7sOEY%?V8mwH^Y+}ncIJ6Bwf$yMe-m^g@pM9<7H(6nNe`I-XWGpaJ*L{ z&F6w<{8ZO6@W&c#U|}BRa6wTV*6gD8Qt!&GWD5LHdqI4AtcE0pVlaeixP1EQi@$C~md%6%)nCYOIg$1) z4pBr#1iR<(w;0#25iWabQE45{9Fs=k?DPOvu=Zsh5v9vXaj8aPk(CD=S6qAbeVb`C zi*q|lj+Uk2Z&Fd)U@7nt^uIE-&*OlZ4fNo?ODq8v z)3o<-IYR;R$}k!XZXw3okg@4RNu_!Ve`fT85A8#wk^GnY));OZh5)NP^33;n4E_Hq z{Q!^RUL}baVl#er_3HeK&(~yrq&T){;WxvQS27<&@mQweYXZKd6*sOpOfxq})(&om zb2PQ9A!3MQ$o}U2u8k;1v{16i3g|xv=pC zem+qdvH1#20mhigl(xkJZNX5ogaLo_JqOym=i-~dXH}pjdaP; zZmNhZYP3}3CcY^hcI`lTJcMwG7EJx_8-J9Q%;zk6Uz7rM=y9#VRJy712{W_9+ptoPc+EucNzXazd+efWj632D4a24 z-oE~X3uGF4Y(UH+iz}jb4(d5#LSR#&|MdK!bea>JVFVu=ip#HU{YKKAkQz_J6&<|7 zOXXwYZC5)}A^>B3SYU-eM-p_UG1KGch+K|4HTYH6KfU^jv%?#9 zhu{TH1f4SP2xVgHj(%g!?1Q7I(ShK59n!-c!v88gc_1Mfwqlhe{vPybe*t9*m_lVw zyxfrEko)T+)ty91|LD2X3Kik@RSc~0h)2ZwCnMWZ&6E&V0>&0)V@F>HKPKopPFxDtDgD(rfXUXlGaNRp2R@2BNQe@68lIrSU8pCgfP)<^ zgc9UV@Rr2Zu$3b46+%I-q}1FC`{_JQuQSwj?twRdK%*xs^g38PSqPup2zk@+5=w5S zSd{c*M*@NR%T|IYuj~})^*lw#4Y8Bdd3Aex*tddUr4L@cdavxpJ0R+VCdB9EN~(bK z3+f68IY6wqS8gB2D^Y;eyO2oK=4rrI6e-ls`iUCvgcOu^Xj-Tl0nQgmPNOqBJRAHZ zH>gC`Llp>9+kZ}$y&ZF*n5@P!-3kKYst1P-eS--#kY zo{nT32ycy*KQjkRQ#Onid_r&|NRwLV2zfF3n(B~8gbocdY6>zFB2rMhLr4>{6GW(- z1Veh`78aDOb8+CcuSx^O33*v*5*>p!cGa;T?zldZIc@Hc@ow^G)*{Xff2-PPzsep4 zfMHm=o(UN*fXUdi2&0iX>;1WG(yqH7_q67Ge}g_9TC^4 zYO0v?@gHT&OhGcE9FcUxK5Gt~*9hkl3xqqUCC7sb4@DLFA!IyjTLEu@uuFFstGG;y ze5$2;FmtkMIBr2ns>y+Gue#v6-lOijW#ktJ`uZ63N)ng1IYJ5YEch1NtHY7tB{3cp z2EM5)2gKQ|Pb(R~u;&0F@|=o`6Vb@cC#3del0(r6gc+>b#=*B-XMgJKAg?t~ngjt5 z81M~Pa#e7qQu+=c#@Lcjp8|==h6<6BP@vNar za4+y5hg^(s`AD~xb+WX=dk_FL!$=Qmkh+j9iT8aIP2kw`K_r5s+{v~Ug0skNJfl}> zPPNGVP4fQ?Xy951i&M?a8ke10lYd{tMtrG67*^_UeBjC-W^Z2nZ&&^hB~eBs@T3t1 zH^Rn%eF9T+%RvDPRajh@(1wMPhBHAkjNX)LbSeKj&0QfN6M5rET^d#VHWI=RG(H)4 z-lvwg=db;Wio75PQ1`v3_N@z7uNp0nY*rJrI5VA7O`~nMs-WeDW$4SUvu$w4MHzsQ zii$4GtumG}OUfj^BBWd=HZ7;|ATJ*2EB#rOZ_fung-kiH9m>M$WY=m6HBG% z=M1p&ky-=u5O~QYNUL)AvEfH-)C{Df{9aEKO?{SrDb?E<$YAOMI!c(9RD1&wK9ooD z6Fc7c7DvOfOK9&C@*u;golQS-dg9{5d)+3nRhmaz1>HHk~_Oj;WEqd$)q zm$zflwGS%gkbFF@_~GA?m>qs2H(COf9Bj9q(TrQMPYN+sZIkyt{HG4!kuX#3xJEFO z@2L0oWsD|7YZN)^?Txp7*Bm${LQ{Za0J;(Nl1PyHbPuzi>0B_44S}0bFM{l^%`87C)`>9siD-dE=r#t+Xu_QLZD9^O*LowJqc6#s>eVDeq47AadL zf7iH-|1X3sA}?dM3o3ElO4tbwnBF*KCZd2M`H^gG6zr;CYeq)l<>$7+J9X7HOagn2 z`901_*+NbGpFp^wqe`hDJKtqE*q0H_5Tb<&y5@7mhzBBu#sY-^$~!DNAV`UKaGjT} zx+)&%iS!XEgMf!h3ZjWujtNe>6s#%q8}7&oPU5d(IGCzL5ZkHe(cQ_Y4j$I^yH|dK z@@b#FS9xdftqXk_=6I=V!r#8|e7fGsYaKl{{=yB`pxlJMos&2!z9?+-sBF$S64c31 zL0x=uOngUxOhcGxe7#?$!{>$a0N?)bC%nl-b{?fau3p89FoDPihVSeMWd)-jxDht) zZz&-%#Ff(I$iCiA=hlxZY1ncbaY(D5C5aRyy0}0=7L48US88A=0B9=&Wep#t>2%r) z08dGCDvFJU(U4iNr=kON{aLoAR1biu-RD##?9UH_s1|E)Q9^>SN4-KcH4p8SU7qM( z*@HUL_VZQw%P4q==xkU4J%cLJDP9W}L<@&W77mLHPJ4&ldB0#-rReCm;YG%ksc|lh zW|SYs((^xj>+DhD{h2iEU{q+b~1PX#rf%)D#`pZw&+lFa-(ysik!3StMF5`tt3i*u=32|jHs qK?>7S;GxUrlA4o>Ka@Boa3;MB%$V>3u8Aucs42Nb!7(QXmrVfZnS|T` literal 0 HcmV?d00001 diff --git a/netserver/readme.txt b/netserver/readme.txt new file mode 100644 index 0000000..c11134d --- /dev/null +++ b/netserver/readme.txt @@ -0,0 +1,9 @@ +需求 +1:手机端或者其他端可以对设备进行回控,并查看设备各种运行状态,接收报警推送等。 +2:同时支持在局域网、广域网、互联网访问,尤其是互联网访问。 +3:权限控制,给定账号控制授权的设备,并自动拉取设备信息。 +4:设备不在线要给出反馈信息提示以便分析。 +5:每个连接都有自己的唯一编号作为标识符。 + + +查看端口号连接 netstat -aon|findstr "6907" \ No newline at end of file

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 literal 0 HcmV?d00001 diff --git a/snap/qwtdemo.jpg b/0snap/qwtdemo.jpg similarity index 100% rename from snap/qwtdemo.jpg rename to 0snap/qwtdemo.jpg diff --git a/snap/screenwidget.gif b/0snap/screenwidget.gif similarity index 100% rename from snap/screenwidget.gif rename to 0snap/screenwidget.gif diff --git a/snap/styledemo_flatwhite.png b/0snap/styledemo_flatwhite.png similarity index 100% rename from snap/styledemo_flatwhite.png rename to 0snap/styledemo_flatwhite.png diff --git a/snap/styledemo_lightblue.png b/0snap/styledemo_lightblue.png similarity index 100% rename from snap/styledemo_lightblue.png rename to 0snap/styledemo_lightblue.png diff --git a/snap/styledemo_psblack.png b/0snap/styledemo_psblack.png similarity index 100% rename from snap/styledemo_psblack.png rename to 0snap/styledemo_psblack.png diff --git a/snap/videopanel.gif b/0snap/videopanel.gif similarity index 100% rename from snap/videopanel.gif rename to 0snap/videopanel.gif diff --git a/snap/videowidget.gif b/0snap/videowidget.gif similarity index 100% rename from snap/videowidget.gif rename to 0snap/videowidget.gif diff --git a/snap/vlcdemo.png b/0snap/vlcdemo.png similarity index 100% rename from snap/vlcdemo.png rename to 0snap/vlcdemo.png diff --git a/snap/zhtopy.gif b/0snap/zhtopy.gif similarity index 100% rename from snap/zhtopy.gif rename to 0snap/zhtopy.gif diff --git a/README.md b/README.md index c7a6bbf..50b0bb4 100644 --- a/README.md +++ b/README.md @@ -36,44 +36,48 @@ | 33 | imageswitch | 鍥剧墖寮鍏虫帶浠 | | 34 | ffmpegdemo | 瑙嗛娴佹挱鏀緁fmpeg鍐呮牳 | | 35 | vlcdemo | 瑙嗛娴佹挱鏀緑lc鍐呮牳 | +| 36 | netserver | 缃戠粶涓浆鏈嶅姟鍣 | +| 37 | designer | QtDesigner4婧愮爜 | ### 浜屻佸涔犵兢 1. **Qt浜ゆ祦澶т細缇 853086607(闆ㄧ敯鍝)** 2. **Qt鎶鏈氦娴佺兢 46679801(3000浜虹兢)** ### 涓夈佹晥鏋滃浘 -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/lightbutton.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/movewidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/flatui.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/countcode.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/gifwidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/comtool.jpg) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/nettool.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/devicesizetable.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/styledemo_psblack.png) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/styledemo_lightblue.png) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/styledemo_flatwhite.png) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/navbutton.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/videopanel.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/framelesswidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/ipaddress.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/bgdemo.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/dbpage.png) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/pngtool.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/colorwidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/maskwidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/battery.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/lineeditnext.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/zhtopy.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/qwtdemo.jpg) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/buttondefence.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/mouseline.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/emailtool.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/ntpclient.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/lunarcalendarwidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/videowidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/screenwidget.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/echartgauge.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/imageswitch.gif) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/ffmpegdemo.png) -![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/snap/vlcdemo.png) \ No newline at end of file +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/lightbutton.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/movewidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/flatui.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/countcode.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/gifwidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/comtool.jpg) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/nettool.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/devicesizetable.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/styledemo_psblack.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/styledemo_lightblue.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/styledemo_flatwhite.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/navbutton.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/videopanel.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/framelesswidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/ipaddress.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/bgdemo.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/dbpage.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/pngtool.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/colorwidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/maskwidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/battery.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/lineeditnext.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/zhtopy.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/qwtdemo.jpg) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/buttondefence.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/mouseline.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/emailtool.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/ntpclient.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/lunarcalendarwidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/videowidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/screenwidget.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/echartgauge.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/imageswitch.gif) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/ffmpegdemo.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/vlcdemo.png) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/netserver.jpg) +![avatar](https://github.com/feiyangqingyun/QWidgetDemo/raw/master/0snap/designer.png) \ No newline at end of file diff --git a/designer/designer/appfontdialog.cpp b/designer/designer/appfontdialog.cpp new file mode 100644 index 0000000..bbfe241 --- /dev/null +++ b/designer/designer/appfontdialog.cpp @@ -0,0 +1,429 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "appfontdialog.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum {FileNameRole = Qt::UserRole + 1, IdRole = Qt::UserRole + 2 }; +enum { debugAppFontWidget = 0 }; + +static const char fontFileKeyC[] = "fontFiles"; + +// AppFontManager: Singleton that maintains the mapping of loaded application font +// ids to the file names (which are not stored in QFontDatabase) +// and provides API for loading/unloading fonts as well for saving/restoring settings. + +class AppFontManager +{ + Q_DISABLE_COPY(AppFontManager) + AppFontManager(); +public: + static AppFontManager &instance(); + + void save(QDesignerSettingsInterface *s, const QString &prefix) const; + void restore(const QDesignerSettingsInterface *s, const QString &prefix); + + // Return id or -1 + int add(const QString &fontFile, QString *errorMessage); + + bool remove(int id, QString *errorMessage); + bool remove(const QString &fontFile, QString *errorMessage); + bool removeAt(int index, QString *errorMessage); + + // Store loaded fonts as pair of file name and Id + typedef QPair FileNameFontIdPair; + typedef QList FileNameFontIdPairs; + const FileNameFontIdPairs &fonts() const; + +private: + FileNameFontIdPairs m_fonts; +}; + +AppFontManager::AppFontManager() +{ +} + +AppFontManager &AppFontManager::instance() +{ + static AppFontManager rc; + return rc; +} + +void AppFontManager::save(QDesignerSettingsInterface *s, const QString &prefix) const +{ + // Store as list of file names + QStringList fontFiles; + const FileNameFontIdPairs::const_iterator cend = m_fonts.constEnd(); + for (FileNameFontIdPairs::const_iterator it = m_fonts.constBegin(); it != cend; ++it) + fontFiles.push_back(it->first); + + s->beginGroup(prefix); + s->setValue(QLatin1String(fontFileKeyC), fontFiles); + s->endGroup(); + + if (debugAppFontWidget) + qDebug() << "AppFontManager::saved" << fontFiles.size() << "fonts under " << prefix; +} + +void AppFontManager::restore(const QDesignerSettingsInterface *s, const QString &prefix) +{ + QString key = prefix; + key += QLatin1Char('/'); + key += QLatin1String(fontFileKeyC); + const QStringList fontFiles = s->value(key, QStringList()).toStringList(); + + if (debugAppFontWidget) + qDebug() << "AppFontManager::restoring" << fontFiles.size() << "fonts from " << prefix; + if (!fontFiles.empty()) { + QString errorMessage; + const QStringList::const_iterator cend = fontFiles.constEnd(); + for (QStringList::const_iterator it = fontFiles.constBegin(); it != cend; ++it) + if (add(*it, &errorMessage) == -1) + qWarning("%s", qPrintable(errorMessage)); + } +} + +int AppFontManager::add(const QString &fontFile, QString *errorMessage) +{ + const QFileInfo inf(fontFile); + if (!inf.isFile()) { + *errorMessage = QCoreApplication::translate("AppFontManager", "'%1' is not a file.").arg(fontFile); + return -1; + } + if (!inf.isReadable()) { + *errorMessage = QCoreApplication::translate("AppFontManager", "The font file '%1' does not have read permissions.").arg(fontFile); + return -1; + } + const QString fullPath = inf.absoluteFilePath(); + // Check if already loaded + const FileNameFontIdPairs::const_iterator cend = m_fonts.constEnd(); + for (FileNameFontIdPairs::const_iterator it = m_fonts.constBegin(); it != cend; ++it) { + if (it->first == fullPath) { + *errorMessage = QCoreApplication::translate("AppFontManager", "The font file '%1' is already loaded.").arg(fontFile); + return -1; + } + } + + const int id = QFontDatabase::addApplicationFont(fullPath); + if (id == -1) { + *errorMessage = QCoreApplication::translate("AppFontManager", "The font file '%1' could not be loaded.").arg(fontFile); + return -1; + } + + if (debugAppFontWidget) + qDebug() << "AppFontManager::add" << fontFile << id; + m_fonts.push_back(FileNameFontIdPair(fullPath, id)); + return id; +} + +bool AppFontManager::remove(int id, QString *errorMessage) +{ + const int count = m_fonts.size(); + for (int i = 0; i < count; i++) + if (m_fonts[i].second == id) + return removeAt(i, errorMessage); + + *errorMessage = QCoreApplication::translate("AppFontManager", "'%1' is not a valid font id.").arg(id); + return false; +} + +bool AppFontManager::remove(const QString &fontFile, QString *errorMessage) +{ + const int count = m_fonts.size(); + for (int i = 0; i < count; i++) + if (m_fonts[i].first == fontFile) + return removeAt(i, errorMessage); + + *errorMessage = QCoreApplication::translate("AppFontManager", "There is no loaded font matching the id '%1'.").arg(fontFile); + return false; +} + +bool AppFontManager::removeAt(int index, QString *errorMessage) +{ + Q_ASSERT(index >= 0 && index < m_fonts.size()); + + const QString fontFile = m_fonts[index].first; + const int id = m_fonts[index].second; + + if (debugAppFontWidget) + qDebug() << "AppFontManager::removeAt" << index << '(' << fontFile << id << ')'; + + if (!QFontDatabase::removeApplicationFont(id)) { + *errorMessage = QCoreApplication::translate("AppFontManager", "The font '%1' (%2) could not be unloaded.").arg(fontFile).arg(id); + return false; + } + m_fonts.removeAt(index); + return true; +} + +const AppFontManager::FileNameFontIdPairs &AppFontManager::fonts() const +{ + return m_fonts; +} + +// ------------- AppFontModel +class AppFontModel : public QStandardItemModel { + Q_DISABLE_COPY(AppFontModel) +public: + AppFontModel(QObject *parent = 0); + + void init(const AppFontManager &mgr); + void add(const QString &fontFile, int id); + int idAt(const QModelIndex &idx) const; +}; + +AppFontModel::AppFontModel(QObject * parent) : + QStandardItemModel(parent) +{ + setHorizontalHeaderLabels(QStringList(AppFontWidget::tr("Fonts"))); +} + +void AppFontModel::init(const AppFontManager &mgr) +{ + typedef AppFontManager::FileNameFontIdPairs FileNameFontIdPairs; + + const FileNameFontIdPairs &fonts = mgr.fonts(); + const FileNameFontIdPairs::const_iterator cend = fonts.constEnd(); + for (FileNameFontIdPairs::const_iterator it = fonts.constBegin(); it != cend; ++it) + add(it->first, it->second); +} + +void AppFontModel::add(const QString &fontFile, int id) +{ + const QFileInfo inf(fontFile); + // Root item with base name + QStandardItem *fileItem = new QStandardItem(inf.completeBaseName()); + const QString fullPath = inf.absoluteFilePath(); + fileItem->setData(fullPath, FileNameRole); + fileItem->setToolTip(fullPath); + fileItem->setData(id, IdRole); + fileItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + appendRow(fileItem); + const QStringList families = QFontDatabase::applicationFontFamilies(id); + const QStringList::const_iterator cend = families.constEnd(); + for (QStringList::const_iterator it = families.constBegin(); it != cend; ++it) { + QStandardItem *familyItem = new QStandardItem(*it); + familyItem->setToolTip(fullPath); + familyItem->setFont(QFont(*it)); + familyItem->setFlags(Qt::ItemIsEnabled); + fileItem->appendRow(familyItem); + } +} + +int AppFontModel::idAt(const QModelIndex &idx) const +{ + if (const QStandardItem *item = itemFromIndex(idx)) + return item->data(IdRole).toInt(); + return -1; +} + +// ------------- AppFontWidget +AppFontWidget::AppFontWidget(QWidget *parent) : + QGroupBox(parent), + m_view(new QTreeView), + m_addButton(new QToolButton), + m_removeButton(new QToolButton), + m_removeAllButton(new QToolButton), + m_model(new AppFontModel(this)) +{ + m_model->init(AppFontManager::instance()); + m_view->setModel(m_model); + m_view->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_view->expandAll(); + connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection))); + + m_addButton->setToolTip(tr("Add font files")); + m_addButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("plus.png"))); + connect(m_addButton, SIGNAL(clicked()), this, SLOT(addFiles())); + + m_removeButton->setEnabled(false); + m_removeButton->setToolTip(tr("Remove current font file")); + m_removeButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("minus.png"))); + connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemoveFiles())); + + m_removeAllButton->setToolTip(tr("Remove all font files")); + m_removeAllButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("editdelete.png"))); + connect(m_removeAllButton, SIGNAL(clicked()), this, SLOT(slotRemoveAll())); + + QHBoxLayout *hLayout = new QHBoxLayout; + hLayout->addWidget(m_addButton); + hLayout->addWidget(m_removeButton); + hLayout->addWidget(m_removeAllButton); + hLayout->addItem(new QSpacerItem(0, 0,QSizePolicy::MinimumExpanding)); + + QVBoxLayout *vLayout = new QVBoxLayout; + vLayout->addWidget(m_view); + vLayout->addLayout(hLayout); + setLayout(vLayout); +} + +void AppFontWidget::addFiles() +{ + const QStringList files = + QFileDialog::getOpenFileNames(this, tr("Add Font Files"), QString(), + tr("Font files (*.ttf)")); + if (files.empty()) + return; + + QString errorMessage; + + AppFontManager &fmgr = AppFontManager::instance(); + const QStringList::const_iterator cend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != cend; ++it) { + const int id = fmgr.add(*it, &errorMessage); + if (id != -1) { + m_model->add(*it, id); + } else { + QMessageBox::critical(this, tr("Error Adding Fonts"), errorMessage); + } + } + m_view->expandAll(); +} + +static void removeFonts(const QModelIndexList &selectedIndexes, AppFontModel *model, QWidget *dialogParent) +{ + if (selectedIndexes.empty()) + return; + + // Reverse sort top level rows and remove + AppFontManager &fmgr = AppFontManager::instance(); + QVector rows; + rows.reserve(selectedIndexes.size()); + + QString errorMessage; + const QModelIndexList::const_iterator cend = selectedIndexes.constEnd(); + for (QModelIndexList::const_iterator it = selectedIndexes.constBegin(); it != cend; ++it) { + const int id = model->idAt(*it); + if (id != -1) { + if (fmgr.remove(id, &errorMessage)) { + rows.push_back(it->row()); + } else { + QMessageBox::critical(dialogParent, AppFontWidget::tr("Error Removing Fonts"), errorMessage); + } + } + } + + qStableSort(rows.begin(), rows.end()); + for (int i = rows.size() - 1; i >= 0; i--) + model->removeRow(rows[i]); +} + +void AppFontWidget::slotRemoveFiles() +{ + removeFonts(m_view->selectionModel()->selectedIndexes(), m_model, this); +} + +void AppFontWidget::slotRemoveAll() +{ + const int count = m_model->rowCount(); + if (!count) + return; + + const QMessageBox::StandardButton answer = + QMessageBox::question(this, tr("Remove Fonts"), tr("Would you like to remove all fonts?"), + QMessageBox::Yes|QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) + return; + + QModelIndexList topLevels; + for (int i = 0; i < count; i++) + topLevels.push_back(m_model->index(i, 0)); + removeFonts(topLevels, m_model, this); +} + +void AppFontWidget::selectionChanged(const QItemSelection &selected, const QItemSelection & /*deselected*/) +{ + m_removeButton->setEnabled(!selected.indexes().empty()); +} + +void AppFontWidget::save(QDesignerSettingsInterface *s, const QString &prefix) +{ + AppFontManager::instance().save(s, prefix); +} + +void AppFontWidget::restore(const QDesignerSettingsInterface *s, const QString &prefix) +{ + AppFontManager::instance().restore(s, prefix); +} + +// ------------ AppFontDialog +AppFontDialog::AppFontDialog(QWidget *parent) : + QDialog(parent), + m_appFontWidget(new AppFontWidget) +{ + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Additional Fonts")); + setModal(false); + QVBoxLayout *vl = new QVBoxLayout; + vl->addWidget(m_appFontWidget); + + QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close); + QDialog::connect(bb, SIGNAL(rejected()), this, SLOT(reject())); + vl->addWidget(bb); + setLayout(vl); +} + +QT_END_NAMESPACE diff --git a/designer/designer/appfontdialog.h b/designer/designer/appfontdialog.h new file mode 100644 index 0000000..750509e --- /dev/null +++ b/designer/designer/appfontdialog.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_APPFONTWIDGET_H +#define QDESIGNER_APPFONTWIDGET_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class AppFontModel; + +class QTreeView; +class QToolButton; +class QItemSelection; +class QDesignerSettingsInterface; + +// AppFontWidget: Manages application fonts which the user can load and +// provides API for saving/restoring them. + +class AppFontWidget : public QGroupBox +{ + Q_DISABLE_COPY(AppFontWidget) + Q_OBJECT +public: + explicit AppFontWidget(QWidget *parent = 0); + + QStringList fontFiles() const; + + static void save(QDesignerSettingsInterface *s, const QString &prefix); + static void restore(const QDesignerSettingsInterface *s, const QString &prefix); + +private slots: + void addFiles(); + void slotRemoveFiles(); + void slotRemoveAll(); + void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected); + +private: + QTreeView *m_view; + QToolButton *m_addButton; + QToolButton *m_removeButton; + QToolButton *m_removeAllButton; + AppFontModel *m_model; +}; + +// AppFontDialog: Non modal dialog for AppFontWidget which has Qt::WA_DeleteOnClose set. + +class AppFontDialog : public QDialog +{ + Q_DISABLE_COPY(AppFontDialog) + Q_OBJECT +public: + explicit AppFontDialog(QWidget *parent = 0); + +private: + AppFontWidget *m_appFontWidget; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_APPFONTWIDGET_H diff --git a/designer/designer/assistantclient.cpp b/designer/designer/assistantclient.cpp new file mode 100644 index 0000000..6f1c705 --- /dev/null +++ b/designer/designer/assistantclient.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "assistantclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { debugAssistantClient = 0 }; + +AssistantClient::AssistantClient() : + m_process(0) +{ +} + +AssistantClient::~AssistantClient() +{ + if (isRunning()) { + m_process->terminate(); + m_process->waitForFinished(); + } + delete m_process; +} + +bool AssistantClient::showPage(const QString &path, QString *errorMessage) +{ + QString cmd = QLatin1String("SetSource "); + cmd += path; + return sendCommand(cmd, errorMessage); +} + +bool AssistantClient::activateIdentifier(const QString &identifier, QString *errorMessage) +{ + QString cmd = QLatin1String("ActivateIdentifier "); + cmd += identifier; + return sendCommand(cmd, errorMessage); +} + +bool AssistantClient::activateKeyword(const QString &keyword, QString *errorMessage) +{ + QString cmd = QLatin1String("ActivateKeyword "); + cmd += keyword; + return sendCommand(cmd, errorMessage); +} + +bool AssistantClient::sendCommand(const QString &cmd, QString *errorMessage) +{ + if (debugAssistantClient) + qDebug() << "sendCommand " << cmd; + if (!ensureRunning(errorMessage)) + return false; + if (!m_process->isWritable() || m_process->bytesToWrite() > 0) { + *errorMessage = QCoreApplication::translate("AssistantClient", "Unable to send request: Assistant is not responding."); + return false; + } + QTextStream str(m_process); + str << cmd << QLatin1Char('\n') << endl; + return true; +} + +bool AssistantClient::isRunning() const +{ + return m_process && m_process->state() != QProcess::NotRunning; +} + +QString AssistantClient::binary() +{ + QString app = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator(); +#if !defined(Q_OS_MAC) + app += QLatin1String("assistant"); +#else + app += QLatin1String("Assistant.app/Contents/MacOS/Assistant"); +#endif + +#if defined(Q_OS_WIN) + app += QLatin1String(".exe"); +#endif + + return app; +} + +bool AssistantClient::ensureRunning(QString *errorMessage) +{ + if (isRunning()) + return true; + + if (!m_process) + m_process = new QProcess; + + const QString app = binary(); + if (!QFileInfo(app).isFile()) { + *errorMessage = QCoreApplication::translate("AssistantClient", "The binary '%1' does not exist.").arg(app); + return false; + } + if (debugAssistantClient) + qDebug() << "Running " << app; + // run + QStringList args(QLatin1String("-enableRemoteControl")); + m_process->start(app, args); + if (!m_process->waitForStarted()) { + *errorMessage = QCoreApplication::translate("AssistantClient", "Unable to launch assistant (%1).").arg(app); + return false; + } + return true; +} + +QString AssistantClient::documentUrl(const QString &prefix, int qtVersion) +{ + if (qtVersion == 0) + qtVersion = QT_VERSION; + QString rc; + QTextStream(&rc) << QLatin1String("qthelp://com.trolltech.") << prefix << QLatin1Char('.') + << (qtVersion >> 16) << ((qtVersion >> 8) & 0xFF) << (qtVersion & 0xFF) + << QLatin1String("/qdoc/"); + return rc; +} + +QString AssistantClient::designerManualUrl(int qtVersion) +{ + return documentUrl(QLatin1String("designer"), qtVersion); +} + +QString AssistantClient::qtReferenceManualUrl(int qtVersion) +{ + return documentUrl(QLatin1String("qt"), qtVersion); +} + +QT_END_NAMESPACE diff --git a/designer/designer/assistantclient.h b/designer/designer/assistantclient.h new file mode 100644 index 0000000..acfd210 --- /dev/null +++ b/designer/designer/assistantclient.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ASSISTANTCLIENT_H +#define ASSISTANTCLIENT_H + +#include + +QT_BEGIN_NAMESPACE + +class QProcess; +class QString; + +class AssistantClient +{ + AssistantClient(const AssistantClient &); + AssistantClient &operator=(const AssistantClient &); + +public: + AssistantClient(); + ~AssistantClient(); + + bool showPage(const QString &path, QString *errorMessage); + bool activateIdentifier(const QString &identifier, QString *errorMessage); + bool activateKeyword(const QString &keyword, QString *errorMessage); + + bool isRunning() const; + + static QString documentUrl(const QString &prefix, int qtVersion = 0); + // Root of the Qt Designer documentation + static QString designerManualUrl(int qtVersion = 0); + // Root of the Qt Reference documentation + static QString qtReferenceManualUrl(int qtVersion = 0); + +private: + static QString binary(); + bool sendCommand(const QString &cmd, QString *errorMessage); + bool ensureRunning(QString *errorMessage); + + QProcess *m_process; +}; + +QT_END_NAMESPACE + +#endif // ASSISTANTCLIENT_H diff --git a/designer/designer/designer.pro b/designer/designer/designer.pro new file mode 100644 index 0000000..0352fe0 --- /dev/null +++ b/designer/designer/designer.pro @@ -0,0 +1,69 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2017-04-07T09:15:51 +# +#------------------------------------------------- + +QT += xml network +CONFIG += warn_off release +RESOURCES += designer.qrc +LIBS += -lQtDesignerComponents -lQtDesigner + +INCLUDEPATH += $$PWD +INCLUDEPATH += ../lib/sdk ../lib/extension ../lib/shared ../lib/uilib + +TARGET = designer +TEMPLATE = app +MOC_DIR = temp/moc +RCC_DIR = temp/rcc +UI_DIR = temp/ui +OBJECTS_DIR = temp/obj +DESTDIR = bin +TRANSLATIONS = designer_zh_CN.ts +PRECOMPILED_HEADER = qdesigner_pch.h +win32:RC_FILE = main.rc + +HEADERS += \ + qdesigner.h \ + qdesigner_toolwindow.h \ + qdesigner_formwindow.h \ + qdesigner_workbench.h \ + qdesigner_settings.h \ + qdesigner_actions.h \ + qdesigner_server.h \ + qdesigner_appearanceoptions.h \ + saveformastemplate.h \ + newform.h \ + versiondialog.h \ + designer_enums.h \ + appfontdialog.h \ + preferencesdialog.h \ + assistantclient.h \ + mainwindow.h \ + qttoolbardialog.h \ + fontpanel.h + +SOURCES += main.cpp \ + qdesigner.cpp \ + qdesigner_toolwindow.cpp \ + qdesigner_formwindow.cpp \ + qdesigner_workbench.cpp \ + qdesigner_settings.cpp \ + qdesigner_server.cpp \ + qdesigner_actions.cpp \ + qdesigner_appearanceoptions.cpp \ + saveformastemplate.cpp \ + newform.cpp \ + versiondialog.cpp \ + appfontdialog.cpp \ + preferencesdialog.cpp \ + assistantclient.cpp \ + mainwindow.cpp \ + qttoolbardialog.cpp \ + fontpanel.cpp + +FORMS += saveformastemplate.ui \ + preferencesdialog.ui \ + qdesigner_appearanceoptions.ui \ + qttoolbardialog.ui + diff --git a/designer/designer/designer.qrc b/designer/designer/designer.qrc new file mode 100644 index 0000000..567dec2 --- /dev/null +++ b/designer/designer/designer.qrc @@ -0,0 +1,12 @@ + + + images/designer.png + images/back.png + images/down.png + images/forward.png + images/minus.png + images/plus.png + images/up.png + images/designer_zh_CN.qm + + diff --git a/designer/designer/designer_enums.h b/designer/designer/designer_enums.h new file mode 100644 index 0000000..b31a6a9 --- /dev/null +++ b/designer/designer/designer_enums.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERENUMS_H +#define DESIGNERENUMS_H + +enum UIMode +{ + NeutralMode, + TopLevelMode, + DockedMode +}; + +#endif // DESIGNERENUMS_H diff --git a/designer/designer/fontpanel.cpp b/designer/designer/fontpanel.cpp new file mode 100644 index 0000000..b564451 --- /dev/null +++ b/designer/designer/fontpanel.cpp @@ -0,0 +1,308 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fontpanel.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +FontPanel::FontPanel(QWidget *parentWidget) : + QGroupBox(parentWidget), + m_previewLineEdit(new QLineEdit), + m_writingSystemComboBox(new QComboBox), + m_familyComboBox(new QFontComboBox), + m_styleComboBox(new QComboBox), + m_pointSizeComboBox(new QComboBox), + m_previewFontUpdateTimer(0) +{ + setTitle(tr("Font")); + + QFormLayout *formLayout = new QFormLayout(this); + // writing systems + m_writingSystemComboBox->setEditable(false); + + QList writingSystems = m_fontDatabase.writingSystems(); + writingSystems.push_front(QFontDatabase::Any); + foreach (QFontDatabase::WritingSystem ws, writingSystems) + m_writingSystemComboBox->addItem(QFontDatabase::writingSystemName(ws), QVariant(ws)); + connect(m_writingSystemComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotWritingSystemChanged(int))); + formLayout->addRow(tr("&Writing system"), m_writingSystemComboBox); + + connect(m_familyComboBox, SIGNAL(currentFontChanged(QFont)), this, SLOT(slotFamilyChanged(QFont))); + formLayout->addRow(tr("&Family"), m_familyComboBox); + + m_styleComboBox->setEditable(false); + connect(m_styleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotStyleChanged(int))); + formLayout->addRow(tr("&Style"), m_styleComboBox); + + m_pointSizeComboBox->setEditable(false); + connect(m_pointSizeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPointSizeChanged(int))); + formLayout->addRow(tr("&Point size"), m_pointSizeComboBox); + + m_previewLineEdit->setReadOnly(true); + formLayout->addRow(m_previewLineEdit); + + setWritingSystem(QFontDatabase::Any); +} + +QFont FontPanel::selectedFont() const +{ + QFont rc = m_familyComboBox->currentFont(); + const QString family = rc.family(); + rc.setPointSize(pointSize()); + const QString styleDescription = styleString(); + if (styleDescription.contains(QLatin1String("Italic"))) + rc.setStyle(QFont::StyleItalic); + else if (styleDescription.contains(QLatin1String("Oblique"))) + rc.setStyle(QFont::StyleOblique); + else + rc.setStyle(QFont::StyleNormal); + rc.setBold(m_fontDatabase.bold(family, styleDescription)); + + // Weight < 0 asserts... + const int weight = m_fontDatabase.weight(family, styleDescription); + if (weight >= 0) + rc.setWeight(weight); + return rc; +} + +void FontPanel::setSelectedFont(const QFont &f) +{ + m_familyComboBox->setCurrentFont(f); + if (m_familyComboBox->currentIndex() < 0) { + // family not in writing system - find the corresponding one? + QList familyWritingSystems = m_fontDatabase.writingSystems(f.family()); + if (familyWritingSystems.empty()) + return; + + setWritingSystem(familyWritingSystems.front()); + m_familyComboBox->setCurrentFont(f); + } + + updateFamily(family()); + + const int pointSizeIndex = closestPointSizeIndex(f.pointSize()); + m_pointSizeComboBox->setCurrentIndex( pointSizeIndex); + + const QString styleString = m_fontDatabase.styleString(f); + const int styleIndex = m_styleComboBox->findText(styleString); + m_styleComboBox->setCurrentIndex(styleIndex); + slotUpdatePreviewFont(); +} + + +QFontDatabase::WritingSystem FontPanel::writingSystem() const +{ + const int currentIndex = m_writingSystemComboBox->currentIndex(); + if ( currentIndex == -1) + return QFontDatabase::Latin; + return static_cast(m_writingSystemComboBox->itemData(currentIndex).toInt()); +} + +QString FontPanel::family() const +{ + const int currentIndex = m_familyComboBox->currentIndex(); + return currentIndex != -1 ? m_familyComboBox->currentFont().family() : QString(); +} + +int FontPanel::pointSize() const +{ + const int currentIndex = m_pointSizeComboBox->currentIndex(); + return currentIndex != -1 ? m_pointSizeComboBox->itemData(currentIndex).toInt() : 9; +} + +QString FontPanel::styleString() const +{ + const int currentIndex = m_styleComboBox->currentIndex(); + return currentIndex != -1 ? m_styleComboBox->itemText(currentIndex) : QString(); +} + +void FontPanel::setWritingSystem(QFontDatabase::WritingSystem ws) +{ + m_writingSystemComboBox->setCurrentIndex(m_writingSystemComboBox->findData(QVariant(ws))); + updateWritingSystem(ws); +} + + +void FontPanel::slotWritingSystemChanged(int) +{ + updateWritingSystem(writingSystem()); + delayedPreviewFontUpdate(); +} + +void FontPanel::slotFamilyChanged(const QFont &) +{ + updateFamily(family()); + delayedPreviewFontUpdate(); +} + +void FontPanel::slotStyleChanged(int) +{ + updatePointSizes(family(), styleString()); + delayedPreviewFontUpdate(); +} + +void FontPanel::slotPointSizeChanged(int) +{ + delayedPreviewFontUpdate(); +} + +void FontPanel::updateWritingSystem(QFontDatabase::WritingSystem ws) +{ + + m_previewLineEdit->setText(QFontDatabase::writingSystemSample(ws)); + m_familyComboBox->setWritingSystem (ws); + // Current font not in WS ... set index 0. + if (m_familyComboBox->currentIndex() < 0) { + m_familyComboBox->setCurrentIndex(0); + updateFamily(family()); + } +} + +void FontPanel::updateFamily(const QString &family) +{ + // Update styles and trigger update of point sizes. + // Try to maintain selection or select normal + const QString oldStyleString = styleString(); + + const QStringList styles = m_fontDatabase.styles(family); + const bool hasStyles = !styles.empty(); + + m_styleComboBox->setCurrentIndex(-1); + m_styleComboBox->clear(); + m_styleComboBox->setEnabled(hasStyles); + + int normalIndex = -1; + const QString normalStyle = QLatin1String("Normal"); + + if (hasStyles) { + foreach (const QString &style, styles) { + // try to maintain selection or select 'normal' preferably + const int newIndex = m_styleComboBox->count(); + m_styleComboBox->addItem(style); + if (oldStyleString == style) { + m_styleComboBox->setCurrentIndex(newIndex); + } else { + if (oldStyleString == normalStyle) + normalIndex = newIndex; + } + } + if (m_styleComboBox->currentIndex() == -1 && normalIndex != -1) + m_styleComboBox->setCurrentIndex(normalIndex); + } + updatePointSizes(family, styleString()); +} + +int FontPanel::closestPointSizeIndex(int desiredPointSize) const +{ + // try to maintain selection or select closest. + int closestIndex = -1; + int closestAbsError = 0xFFFF; + + const int pointSizeCount = m_pointSizeComboBox->count(); + for (int i = 0; i < pointSizeCount; i++) { + const int itemPointSize = m_pointSizeComboBox->itemData(i).toInt(); + const int absError = qAbs(desiredPointSize - itemPointSize); + if (absError < closestAbsError) { + closestIndex = i; + closestAbsError = absError; + if (closestAbsError == 0) + break; + } else { // past optimum + if (absError > closestAbsError) { + break; + } + } + } + return closestIndex; +} + + +void FontPanel::updatePointSizes(const QString &family, const QString &styleString) +{ + const int oldPointSize = pointSize(); + + QList pointSizes = m_fontDatabase.pointSizes(family, styleString); + if (pointSizes.empty()) + pointSizes = QFontDatabase::standardSizes(); + + const bool hasSizes = !pointSizes.empty(); + m_pointSizeComboBox->clear(); + m_pointSizeComboBox->setEnabled(hasSizes); + m_pointSizeComboBox->setCurrentIndex(-1); + + // try to maintain selection or select closest. + if (hasSizes) { + QString n; + foreach (int pointSize, pointSizes) + m_pointSizeComboBox->addItem(n.setNum(pointSize), QVariant(pointSize)); + const int closestIndex = closestPointSizeIndex(oldPointSize); + if (closestIndex != -1) + m_pointSizeComboBox->setCurrentIndex(closestIndex); + } +} + +void FontPanel::slotUpdatePreviewFont() +{ + m_previewLineEdit->setFont(selectedFont()); +} + +void FontPanel::delayedPreviewFontUpdate() +{ + if (!m_previewFontUpdateTimer) { + m_previewFontUpdateTimer = new QTimer(this); + connect(m_previewFontUpdateTimer, SIGNAL(timeout()), this, SLOT(slotUpdatePreviewFont())); + m_previewFontUpdateTimer->setInterval(0); + m_previewFontUpdateTimer->setSingleShot(true); + } + if (m_previewFontUpdateTimer->isActive()) + return; + m_previewFontUpdateTimer->start(); +} + +QT_END_NAMESPACE diff --git a/designer/designer/fontpanel.h b/designer/designer/fontpanel.h new file mode 100644 index 0000000..3d94ae6 --- /dev/null +++ b/designer/designer/fontpanel.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** 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 the Qt tools. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef FONTPANEL_H +#define FONTPANEL_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QComboBox; +class QFontComboBox; +class QTimer; +class QLineEdit; + +class FontPanel: public QGroupBox +{ + Q_OBJECT +public: + FontPanel(QWidget *parentWidget = 0); + + QFont selectedFont() const; + void setSelectedFont(const QFont &); + + QFontDatabase::WritingSystem writingSystem() const; + void setWritingSystem(QFontDatabase::WritingSystem ws); + +private slots: + void slotWritingSystemChanged(int); + void slotFamilyChanged(const QFont &); + void slotStyleChanged(int); + void slotPointSizeChanged(int); + void slotUpdatePreviewFont(); + +private: + QString family() const; + QString styleString() const; + int pointSize() const; + int closestPointSizeIndex(int ps) const; + + void updateWritingSystem(QFontDatabase::WritingSystem ws); + void updateFamily(const QString &family); + void updatePointSizes(const QString &family, const QString &style); + void delayedPreviewFontUpdate(); + + QFontDatabase m_fontDatabase; + QLineEdit *m_previewLineEdit; + QComboBox *m_writingSystemComboBox; + QFontComboBox* m_familyComboBox; + QComboBox *m_styleComboBox; + QComboBox *m_pointSizeComboBox; + QTimer *m_previewFontUpdateTimer; +}; + +QT_END_NAMESPACE + +#endif // FONTPANEL_H diff --git a/designer/designer/images/back.png b/designer/designer/images/back.png new file mode 100644 index 0000000000000000000000000000000000000000..e58177f43cbd7e3f7d3886fe04278acb404c3f60 GIT binary patch literal 678 zcmV;X0$KfuP)J~2iFzHobR$W9+ed&iz#gEmW3Y&v zZ*1~MdipzQPYUyy_~Z-G`o@qna0>{{%>1pCHZ|9c3=DO#|4^B1;VbnCc=)W9ww8m4 z5Dx_AeopgXzy^VPz%mf{RiNioWwhsV2m3k#e&uEe!zFx$pqOEPVH#0TK(sQx&BWn* zqx`Q2i^!>#*2L)b+g-FLjck4v377E{R4Ncv5S+PyuQ8$#$gRtDp0Op+s|WHmRvNvJ7zd!z7-kcBSVc z_~L*!C{+>EtN7Fen^`C#i@?y-J4A)lg+B1=;Kz4Z%7e*Tj#t0Sg%}Y4<*J=$W`T(0 zfIE{D;0dX|{tEQ*qfOrK&&#Me!Yy0cg-^T%RZ`vE@$xZX5m<3Tf(V+A=3BoNF8s|n z{9Yjiya48^fXk86pr+z#@Tn<20mDERSTVEWKfT8e{7KYRtIBHHAITZSk@xgqT>t<8 M07*qoM6N<$f+VOsGXMYp literal 0 HcmV?d00001 diff --git a/designer/designer/images/designer.png b/designer/designer/images/designer.png new file mode 100644 index 0000000000000000000000000000000000000000..0988fcee3f28eacfbd0cb0722b34093e8eace9a8 GIT binary patch literal 4205 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