From 1acc2778b0c35a67edcbd71d3cc8218ebdd58e0b Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Sat, 5 Mar 2016 22:04:34 +0000 Subject: [PATCH] add Designer back Signed-off-by: Ivailo Monev --- README | 4 +- mkspecs/mkspecs.cmake | 2 +- src/designer/CMakeLists.txt | 3 + src/designer/components/CMakeLists.txt | 120 + .../components/buddyeditor/buddyeditor.cmake | 16 + .../components/buddyeditor/buddyeditor.cpp | 447 +++ src/designer/components/buddyeditor/buddyeditor.h | 92 + .../components/buddyeditor/buddyeditor_global.h | 57 + .../buddyeditor/buddyeditor_instance.cpp | 50 + .../components/buddyeditor/buddyeditor_plugin.cpp | 134 + .../components/buddyeditor/buddyeditor_plugin.h | 93 + .../components/buddyeditor/buddyeditor_tool.cpp | 112 + .../components/buddyeditor/buddyeditor_tool.h | 89 + .../components/formeditor/brushmanagerproxy.cpp | 304 ++ .../components/formeditor/brushmanagerproxy.h | 77 + .../formeditor/default_actionprovider.cpp | 208 ++ .../components/formeditor/default_actionprovider.h | 131 + .../components/formeditor/default_container.cpp | 174 ++ .../components/formeditor/default_container.h | 213 ++ .../formeditor/default_layoutdecoration.cpp | 80 + .../formeditor/default_layoutdecoration.h | 69 + .../components/formeditor/defaultbrushes.xml | 542 ++++ .../components/formeditor/deviceprofiledialog.cpp | 204 ++ .../components/formeditor/deviceprofiledialog.h | 104 + .../components/formeditor/deviceprofiledialog.ui | 108 + src/designer/components/formeditor/dpi_chooser.cpp | 208 ++ src/designer/components/formeditor/dpi_chooser.h | 94 + .../components/formeditor/embeddedoptionspage.cpp | 454 +++ .../components/formeditor/embeddedoptionspage.h | 103 + .../components/formeditor/formeditor.cmake | 77 + src/designer/components/formeditor/formeditor.cpp | 206 ++ src/designer/components/formeditor/formeditor.h | 69 + src/designer/components/formeditor/formeditor.qrc | 175 ++ .../components/formeditor/formeditor_global.h | 57 + .../formeditor/formeditor_optionspage.cpp | 191 ++ .../components/formeditor/formeditor_optionspage.h | 79 + src/designer/components/formeditor/formwindow.cpp | 2983 ++++++++++++++++++++ src/designer/components/formeditor/formwindow.h | 374 +++ .../components/formeditor/formwindow_dnditem.cpp | 116 + .../components/formeditor/formwindow_dnditem.h | 65 + .../formeditor/formwindow_widgetstack.cpp | 218 ++ .../components/formeditor/formwindow_widgetstack.h | 102 + .../components/formeditor/formwindowcursor.cpp | 212 ++ .../components/formeditor/formwindowcursor.h | 93 + .../components/formeditor/formwindowmanager.cpp | 1037 +++++++ .../components/formeditor/formwindowmanager.h | 200 ++ .../components/formeditor/formwindowsettings.cpp | 283 ++ .../components/formeditor/formwindowsettings.h | 85 + .../components/formeditor/formwindowsettings.ui | 328 +++ src/designer/components/formeditor/iconcache.cpp | 122 + src/designer/components/formeditor/iconcache.h | 78 + .../components/formeditor/images/cleartext.png | Bin 0 -> 760 bytes .../components/formeditor/images/color.png | Bin 0 -> 117 bytes .../components/formeditor/images/configure.png | Bin 0 -> 1016 bytes .../components/formeditor/images/cursors/arrow.png | Bin 0 -> 171 bytes .../components/formeditor/images/cursors/busy.png | Bin 0 -> 201 bytes .../formeditor/images/cursors/closedhand.png | Bin 0 -> 147 bytes .../components/formeditor/images/cursors/cross.png | Bin 0 -> 130 bytes .../components/formeditor/images/cursors/hand.png | Bin 0 -> 159 bytes .../formeditor/images/cursors/hsplit.png | Bin 0 -> 155 bytes .../components/formeditor/images/cursors/ibeam.png | Bin 0 -> 124 bytes .../components/formeditor/images/cursors/no.png | Bin 0 -> 199 bytes .../formeditor/images/cursors/openhand.png | Bin 0 -> 160 bytes .../formeditor/images/cursors/sizeall.png | Bin 0 -> 174 bytes .../components/formeditor/images/cursors/sizeb.png | Bin 0 -> 161 bytes .../components/formeditor/images/cursors/sizef.png | Bin 0 -> 161 bytes .../components/formeditor/images/cursors/sizeh.png | Bin 0 -> 145 bytes .../components/formeditor/images/cursors/sizev.png | Bin 0 -> 141 bytes .../formeditor/images/cursors/uparrow.png | Bin 0 -> 132 bytes .../formeditor/images/cursors/vsplit.png | Bin 0 -> 161 bytes .../components/formeditor/images/cursors/wait.png | Bin 0 -> 172 bytes .../formeditor/images/cursors/whatsthis.png | Bin 0 -> 191 bytes .../components/formeditor/images/downplus.png | Bin 0 -> 562 bytes .../formeditor/images/dropdownbutton.png | Bin 0 -> 527 bytes src/designer/components/formeditor/images/edit.png | Bin 0 -> 929 bytes .../components/formeditor/images/editdelete-16.png | Bin 0 -> 553 bytes .../components/formeditor/images/emptyicon.png | Bin 0 -> 108 bytes .../components/formeditor/images/filenew-16.png | Bin 0 -> 454 bytes .../components/formeditor/images/fileopen-16.png | Bin 0 -> 549 bytes .../components/formeditor/images/leveldown.png | Bin 0 -> 557 bytes .../components/formeditor/images/levelup.png | Bin 0 -> 564 bytes .../formeditor/images/mac/adjustsize.png | Bin 0 -> 1929 bytes .../components/formeditor/images/mac/back.png | Bin 0 -> 678 bytes .../components/formeditor/images/mac/buddytool.png | Bin 0 -> 2046 bytes .../components/formeditor/images/mac/down.png | Bin 0 -> 594 bytes .../formeditor/images/mac/editbreaklayout.png | Bin 0 -> 2067 bytes .../components/formeditor/images/mac/editcopy.png | Bin 0 -> 1468 bytes .../components/formeditor/images/mac/editcut.png | Bin 0 -> 1512 bytes .../formeditor/images/mac/editdelete.png | Bin 0 -> 1097 bytes .../components/formeditor/images/mac/editform.png | Bin 0 -> 621 bytes .../components/formeditor/images/mac/editgrid.png | Bin 0 -> 751 bytes .../formeditor/images/mac/edithlayout.png | Bin 0 -> 1395 bytes .../formeditor/images/mac/edithlayoutsplit.png | Bin 0 -> 1188 bytes .../components/formeditor/images/mac/editlower.png | Bin 0 -> 595 bytes .../components/formeditor/images/mac/editpaste.png | Bin 0 -> 1906 bytes .../components/formeditor/images/mac/editraise.png | Bin 0 -> 1213 bytes .../formeditor/images/mac/editvlayout.png | Bin 0 -> 586 bytes .../formeditor/images/mac/editvlayoutsplit.png | Bin 0 -> 872 bytes .../components/formeditor/images/mac/filenew.png | Bin 0 -> 772 bytes .../components/formeditor/images/mac/fileopen.png | Bin 0 -> 904 bytes .../components/formeditor/images/mac/filesave.png | Bin 0 -> 1206 bytes .../components/formeditor/images/mac/forward.png | Bin 0 -> 655 bytes .../formeditor/images/mac/insertimage.png | Bin 0 -> 1280 bytes .../components/formeditor/images/mac/minus.png | Bin 0 -> 488 bytes .../components/formeditor/images/mac/plus.png | Bin 0 -> 810 bytes .../components/formeditor/images/mac/redo.png | Bin 0 -> 1752 bytes .../formeditor/images/mac/resetproperty.png | Bin 0 -> 169 bytes .../formeditor/images/mac/resourceeditortool.png | Bin 0 -> 2171 bytes .../formeditor/images/mac/signalslottool.png | Bin 0 -> 1989 bytes .../formeditor/images/mac/simplifyrichtext.png | Bin 0 -> 1988 bytes .../formeditor/images/mac/tabordertool.png | Bin 0 -> 1963 bytes .../formeditor/images/mac/textanchor.png | Bin 0 -> 2543 bytes .../components/formeditor/images/mac/textbold.png | Bin 0 -> 1611 bytes .../formeditor/images/mac/textcenter.png | Bin 0 -> 1404 bytes .../formeditor/images/mac/textitalic.png | Bin 0 -> 1164 bytes .../formeditor/images/mac/textjustify.png | Bin 0 -> 1257 bytes .../components/formeditor/images/mac/textleft.png | Bin 0 -> 1235 bytes .../components/formeditor/images/mac/textright.png | Bin 0 -> 1406 bytes .../formeditor/images/mac/textsubscript.png | Bin 0 -> 1054 bytes .../formeditor/images/mac/textsuperscript.png | Bin 0 -> 1109 bytes .../components/formeditor/images/mac/textunder.png | Bin 0 -> 1183 bytes .../components/formeditor/images/mac/undo.png | Bin 0 -> 1746 bytes .../components/formeditor/images/mac/up.png | Bin 0 -> 692 bytes .../formeditor/images/mac/widgettool.png | Bin 0 -> 1874 bytes .../components/formeditor/images/minus-16.png | Bin 0 -> 296 bytes .../components/formeditor/images/plus-16.png | Bin 0 -> 383 bytes .../components/formeditor/images/prefix-add.png | Bin 0 -> 411 bytes .../components/formeditor/images/qt3logo.png | Bin 0 -> 1101 bytes .../components/formeditor/images/qtlogo.png | Bin 0 -> 825 bytes .../components/formeditor/images/reload.png | Bin 0 -> 1363 bytes .../components/formeditor/images/resetproperty.png | Bin 0 -> 169 bytes src/designer/components/formeditor/images/sort.png | Bin 0 -> 563 bytes .../components/formeditor/images/submenu.png | Bin 0 -> 179 bytes .../formeditor/images/widgets/calendarwidget.png | Bin 0 -> 968 bytes .../formeditor/images/widgets/checkbox.png | Bin 0 -> 817 bytes .../formeditor/images/widgets/columnview.png | Bin 0 -> 518 bytes .../formeditor/images/widgets/combobox.png | Bin 0 -> 853 bytes .../images/widgets/commandlinkbutton.png | Bin 0 -> 1208 bytes .../formeditor/images/widgets/dateedit.png | Bin 0 -> 672 bytes .../formeditor/images/widgets/datetimeedit.png | Bin 0 -> 1132 bytes .../components/formeditor/images/widgets/dial.png | Bin 0 -> 978 bytes .../formeditor/images/widgets/dialogbuttonbox.png | Bin 0 -> 1003 bytes .../formeditor/images/widgets/dockwidget.png | Bin 0 -> 638 bytes .../formeditor/images/widgets/doublespinbox.png | Bin 0 -> 749 bytes .../formeditor/images/widgets/fontcombobox.png | Bin 0 -> 966 bytes .../components/formeditor/images/widgets/frame.png | Bin 0 -> 721 bytes .../formeditor/images/widgets/graphicsview.png | Bin 0 -> 1182 bytes .../formeditor/images/widgets/groupbox.png | Bin 0 -> 439 bytes .../images/widgets/groupboxcollapsible.png | Bin 0 -> 702 bytes .../formeditor/images/widgets/hscrollbar.png | Bin 0 -> 408 bytes .../formeditor/images/widgets/hslider.png | Bin 0 -> 729 bytes .../formeditor/images/widgets/hsplit.png | Bin 0 -> 164 bytes .../components/formeditor/images/widgets/label.png | Bin 0 -> 953 bytes .../formeditor/images/widgets/lcdnumber.png | Bin 0 -> 555 bytes .../components/formeditor/images/widgets/line.png | Bin 0 -> 287 bytes .../formeditor/images/widgets/lineedit.png | Bin 0 -> 405 bytes .../formeditor/images/widgets/listbox.png | Bin 0 -> 797 bytes .../formeditor/images/widgets/listview.png | Bin 0 -> 756 bytes .../formeditor/images/widgets/mdiarea.png | Bin 0 -> 643 bytes .../formeditor/images/widgets/plaintextedit.png | Bin 0 -> 807 bytes .../formeditor/images/widgets/progress.png | Bin 0 -> 559 bytes .../formeditor/images/widgets/pushbutton.png | Bin 0 -> 408 bytes .../formeditor/images/widgets/radiobutton.png | Bin 0 -> 586 bytes .../formeditor/images/widgets/scrollarea.png | Bin 0 -> 548 bytes .../formeditor/images/widgets/spacer.png | Bin 0 -> 686 bytes .../formeditor/images/widgets/spinbox.png | Bin 0 -> 680 bytes .../formeditor/images/widgets/tabbar.png | Bin 0 -> 623 bytes .../components/formeditor/images/widgets/table.png | Bin 0 -> 483 bytes .../formeditor/images/widgets/tabwidget.png | Bin 0 -> 572 bytes .../formeditor/images/widgets/textedit.png | Bin 0 -> 823 bytes .../formeditor/images/widgets/timeedit.png | Bin 0 -> 1353 bytes .../formeditor/images/widgets/toolbox.png | Bin 0 -> 783 bytes .../formeditor/images/widgets/toolbutton.png | Bin 0 -> 1167 bytes .../components/formeditor/images/widgets/vline.png | Bin 0 -> 314 bytes .../formeditor/images/widgets/vscrollbar.png | Bin 0 -> 415 bytes .../formeditor/images/widgets/vslider.png | Bin 0 -> 726 bytes .../formeditor/images/widgets/vspacer.png | Bin 0 -> 677 bytes .../formeditor/images/widgets/widget.png | Bin 0 -> 716 bytes .../formeditor/images/widgets/widgetstack.png | Bin 0 -> 828 bytes .../formeditor/images/widgets/wizard.png | Bin 0 -> 898 bytes .../formeditor/images/win/adjustsize.png | Bin 0 -> 1262 bytes .../components/formeditor/images/win/back.png | Bin 0 -> 678 bytes .../components/formeditor/images/win/buddytool.png | Bin 0 -> 997 bytes .../components/formeditor/images/win/down.png | Bin 0 -> 594 bytes .../formeditor/images/win/editbreaklayout.png | Bin 0 -> 1321 bytes .../components/formeditor/images/win/editcopy.png | Bin 0 -> 1325 bytes .../components/formeditor/images/win/editcut.png | Bin 0 -> 1384 bytes .../formeditor/images/win/editdelete.png | Bin 0 -> 850 bytes .../components/formeditor/images/win/editform.png | Bin 0 -> 349 bytes .../components/formeditor/images/win/editgrid.png | Bin 0 -> 349 bytes .../formeditor/images/win/edithlayout.png | Bin 0 -> 455 bytes .../formeditor/images/win/edithlayoutsplit.png | Bin 0 -> 860 bytes .../components/formeditor/images/win/editlower.png | Bin 0 -> 1038 bytes .../components/formeditor/images/win/editpaste.png | Bin 0 -> 1482 bytes .../components/formeditor/images/win/editraise.png | Bin 0 -> 1045 bytes .../formeditor/images/win/editvlayout.png | Bin 0 -> 340 bytes .../formeditor/images/win/editvlayoutsplit.png | Bin 0 -> 740 bytes .../components/formeditor/images/win/filenew.png | Bin 0 -> 768 bytes .../components/formeditor/images/win/fileopen.png | Bin 0 -> 1662 bytes .../components/formeditor/images/win/filesave.png | Bin 0 -> 1205 bytes .../components/formeditor/images/win/forward.png | Bin 0 -> 655 bytes .../formeditor/images/win/insertimage.png | Bin 0 -> 885 bytes .../components/formeditor/images/win/minus.png | Bin 0 -> 429 bytes .../components/formeditor/images/win/plus.png | Bin 0 -> 709 bytes .../components/formeditor/images/win/redo.png | Bin 0 -> 1212 bytes .../formeditor/images/win/resourceeditortool.png | Bin 0 -> 1429 bytes .../formeditor/images/win/signalslottool.png | Bin 0 -> 1128 bytes .../formeditor/images/win/simplifyrichtext.png | Bin 0 -> 1933 bytes .../formeditor/images/win/tabordertool.png | Bin 0 -> 1205 bytes .../formeditor/images/win/textanchor.png | Bin 0 -> 1581 bytes .../components/formeditor/images/win/textbold.png | Bin 0 -> 1134 bytes .../formeditor/images/win/textcenter.png | Bin 0 -> 627 bytes .../formeditor/images/win/textitalic.png | Bin 0 -> 829 bytes .../formeditor/images/win/textjustify.png | Bin 0 -> 695 bytes .../components/formeditor/images/win/textleft.png | Bin 0 -> 673 bytes .../components/formeditor/images/win/textright.png | Bin 0 -> 677 bytes .../formeditor/images/win/textsubscript.png | Bin 0 -> 897 bytes .../formeditor/images/win/textsuperscript.png | Bin 0 -> 864 bytes .../components/formeditor/images/win/textunder.png | Bin 0 -> 971 bytes .../components/formeditor/images/win/undo.png | Bin 0 -> 1181 bytes .../components/formeditor/images/win/up.png | Bin 0 -> 692 bytes .../formeditor/images/win/widgettool.png | Bin 0 -> 1039 bytes .../formeditor/itemview_propertysheet.cpp | 271 ++ .../components/formeditor/itemview_propertysheet.h | 92 + .../components/formeditor/layout_propertysheet.cpp | 547 ++++ .../components/formeditor/layout_propertysheet.h | 82 + .../components/formeditor/line_propertysheet.cpp | 87 + .../components/formeditor/line_propertysheet.h | 71 + .../components/formeditor/previewactiongroup.cpp | 150 + .../components/formeditor/previewactiongroup.h | 90 + .../components/formeditor/qdesigner_resource.cpp | 2475 ++++++++++++++++ .../components/formeditor/qdesigner_resource.h | 178 ++ .../components/formeditor/qdesignerundostack.cpp | 113 + .../components/formeditor/qdesignerundostack.h | 90 + .../formeditor/qlayoutwidget_propertysheet.cpp | 84 + .../formeditor/qlayoutwidget_propertysheet.h | 72 + .../formeditor/qmainwindow_container.cpp | 200 ++ .../components/formeditor/qmainwindow_container.h | 81 + .../components/formeditor/qmdiarea_container.cpp | 282 ++ .../components/formeditor/qmdiarea_container.h | 119 + .../components/formeditor/qtbrushmanager.cpp | 141 + .../components/formeditor/qtbrushmanager.h | 85 + .../components/formeditor/qwizard_container.cpp | 227 ++ .../components/formeditor/qwizard_container.h | 123 + .../components/formeditor/qworkspace_container.cpp | 101 + .../components/formeditor/qworkspace_container.h | 79 + .../components/formeditor/spacer_propertysheet.cpp | 83 + .../components/formeditor/spacer_propertysheet.h | 72 + .../components/formeditor/templateoptionspage.cpp | 185 ++ .../components/formeditor/templateoptionspage.h | 110 + .../components/formeditor/templateoptionspage.ui | 59 + .../components/formeditor/tool_widgeteditor.cpp | 364 +++ .../components/formeditor/tool_widgeteditor.h | 107 + .../components/formeditor/widgetselection.cpp | 745 +++++ .../components/formeditor/widgetselection.h | 145 + .../objectinspector/objectinspector.cmake | 13 + .../components/objectinspector/objectinspector.cpp | 836 ++++++ .../components/objectinspector/objectinspector.h | 95 + .../objectinspector/objectinspector_global.h | 61 + .../objectinspector/objectinspectormodel.cpp | 516 ++++ .../objectinspector/objectinspectormodel_p.h | 168 ++ .../propertyeditor/brushpropertymanager.cpp | 298 ++ .../propertyeditor/brushpropertymanager.h | 105 + .../propertyeditor/designerpropertymanager.cpp | 2837 +++++++++++++++++++ .../propertyeditor/designerpropertymanager.h | 315 +++ .../components/propertyeditor/fontmapping.xml | 73 + .../propertyeditor/fontpropertymanager.cpp | 377 +++ .../propertyeditor/fontpropertymanager.h | 124 + .../propertyeditor/newdynamicpropertydialog.cpp | 171 ++ .../propertyeditor/newdynamicpropertydialog.h | 104 + .../propertyeditor/newdynamicpropertydialog.ui | 106 + .../components/propertyeditor/paletteeditor.cpp | 618 ++++ .../components/propertyeditor/paletteeditor.h | 204 ++ .../components/propertyeditor/paletteeditor.ui | 264 ++ .../propertyeditor/paletteeditorbutton.cpp | 89 + .../propertyeditor/paletteeditorbutton.h | 86 + .../components/propertyeditor/previewframe.cpp | 120 + .../components/propertyeditor/previewframe.h | 76 + .../components/propertyeditor/previewwidget.cpp | 60 + .../components/propertyeditor/previewwidget.h | 66 + .../components/propertyeditor/previewwidget.ui | 238 ++ .../components/propertyeditor/propertyeditor.cmake | 38 + .../components/propertyeditor/propertyeditor.cpp | 1297 +++++++++ .../components/propertyeditor/propertyeditor.h | 207 ++ .../components/propertyeditor/propertyeditor.qrc | 5 + .../propertyeditor/propertyeditor_global.h | 61 + .../propertyeditor/qlonglongvalidator.cpp | 154 + .../components/propertyeditor/qlonglongvalidator.h | 110 + .../components/propertyeditor/stringlisteditor.cpp | 213 ++ .../components/propertyeditor/stringlisteditor.h | 92 + .../components/propertyeditor/stringlisteditor.ui | 265 ++ .../propertyeditor/stringlisteditorbutton.cpp | 82 + .../propertyeditor/stringlisteditorbutton.h | 81 + src/designer/components/qdesigner_components.cpp | 277 ++ .../components/signalsloteditor/connectdialog.cpp | 336 +++ .../components/signalsloteditor/connectdialog.ui | 150 + .../components/signalsloteditor/connectdialog_p.h | 109 + .../signalsloteditor/signalslot_utils.cpp | 334 +++ .../signalsloteditor/signalslot_utils_p.h | 104 + .../signalsloteditor/signalsloteditor.cmake | 24 + .../signalsloteditor/signalsloteditor.cpp | 530 ++++ .../components/signalsloteditor/signalsloteditor.h | 98 + .../signalsloteditor/signalsloteditor_global.h | 57 + .../signalsloteditor/signalsloteditor_instance.cpp | 50 + .../signalsloteditor/signalsloteditor_p.h | 138 + .../signalsloteditor/signalsloteditor_plugin.cpp | 134 + .../signalsloteditor/signalsloteditor_plugin.h | 92 + .../signalsloteditor/signalsloteditor_tool.cpp | 124 + .../signalsloteditor/signalsloteditor_tool.h | 93 + .../signalsloteditor/signalsloteditorwindow.cpp | 865 ++++++ .../signalsloteditor/signalsloteditorwindow.h | 96 + .../components/tabordereditor/tabordereditor.cmake | 15 + .../components/tabordereditor/tabordereditor.cpp | 434 +++ .../components/tabordereditor/tabordereditor.h | 109 + .../tabordereditor/tabordereditor_global.h | 57 + .../tabordereditor/tabordereditor_instance.cpp | 49 + .../tabordereditor/tabordereditor_plugin.cpp | 134 + .../tabordereditor/tabordereditor_plugin.h | 93 + .../tabordereditor/tabordereditor_tool.cpp | 115 + .../tabordereditor/tabordereditor_tool.h | 89 + .../components/taskmenu/button_taskmenu.cpp | 710 +++++ src/designer/components/taskmenu/button_taskmenu.h | 170 ++ .../components/taskmenu/combobox_taskmenu.cpp | 134 + .../components/taskmenu/combobox_taskmenu.h | 94 + .../taskmenu/containerwidget_taskmenu.cpp | 349 +++ .../components/taskmenu/containerwidget_taskmenu.h | 157 ++ .../components/taskmenu/groupbox_taskmenu.cpp | 106 + .../components/taskmenu/groupbox_taskmenu.h | 77 + .../components/taskmenu/inplace_editor.cpp | 137 + src/designer/components/taskmenu/inplace_editor.h | 110 + .../components/taskmenu/inplace_widget_helper.cpp | 122 + .../components/taskmenu/inplace_widget_helper.h | 89 + .../components/taskmenu/itemlisteditor.cpp | 479 ++++ src/designer/components/taskmenu/itemlisteditor.h | 165 ++ src/designer/components/taskmenu/itemlisteditor.ui | 156 + .../components/taskmenu/label_taskmenu.cpp | 118 + src/designer/components/taskmenu/label_taskmenu.h | 81 + .../components/taskmenu/layouttaskmenu.cpp | 94 + src/designer/components/taskmenu/layouttaskmenu.h | 93 + .../components/taskmenu/lineedit_taskmenu.cpp | 104 + .../components/taskmenu/lineedit_taskmenu.h | 74 + .../components/taskmenu/listwidget_taskmenu.cpp | 118 + .../components/taskmenu/listwidget_taskmenu.h | 85 + .../components/taskmenu/listwidgeteditor.cpp | 139 + .../components/taskmenu/listwidgeteditor.h | 78 + src/designer/components/taskmenu/menutaskmenu.cpp | 108 + src/designer/components/taskmenu/menutaskmenu.h | 106 + .../components/taskmenu/tablewidget_taskmenu.cpp | 116 + .../components/taskmenu/tablewidget_taskmenu.h | 85 + .../components/taskmenu/tablewidgeteditor.cpp | 451 +++ .../components/taskmenu/tablewidgeteditor.h | 130 + .../components/taskmenu/tablewidgeteditor.ui | 157 ++ src/designer/components/taskmenu/taskmenu.cmake | 51 + .../components/taskmenu/taskmenu_component.cpp | 107 + .../components/taskmenu/taskmenu_component.h | 73 + src/designer/components/taskmenu/taskmenu_global.h | 57 + .../components/taskmenu/textedit_taskmenu.cpp | 106 + .../components/taskmenu/textedit_taskmenu.h | 89 + .../components/taskmenu/toolbar_taskmenu.cpp | 112 + .../components/taskmenu/toolbar_taskmenu.h | 99 + .../components/taskmenu/treewidget_taskmenu.cpp | 115 + .../components/taskmenu/treewidget_taskmenu.h | 85 + .../components/taskmenu/treewidgeteditor.cpp | 644 +++++ .../components/taskmenu/treewidgeteditor.h | 129 + .../components/taskmenu/treewidgeteditor.ui | 257 ++ src/designer/components/widgetbox/widgetbox.cmake | 18 + src/designer/components/widgetbox/widgetbox.cpp | 238 ++ src/designer/components/widgetbox/widgetbox.h | 103 + src/designer/components/widgetbox/widgetbox.qrc | 5 + src/designer/components/widgetbox/widgetbox.xml | 932 ++++++ .../components/widgetbox/widgetbox_dnditem.cpp | 225 ++ .../components/widgetbox/widgetbox_dnditem.h | 67 + .../components/widgetbox/widgetbox_global.h | 57 + .../widgetbox/widgetboxcategorylistview.cpp | 511 ++++ .../widgetbox/widgetboxcategorylistview.h | 118 + .../components/widgetbox/widgetboxtreewidget.cpp | 1002 +++++++ .../components/widgetbox/widgetboxtreewidget.h | 150 + src/designer/data/generate_header.xsl | 465 +++ src/designer/data/generate_impl.xsl | 1161 ++++++++ src/designer/data/generate_shared.xsl | 331 +++ src/designer/data/ui3.xsd | 353 +++ src/designer/data/ui4.xsd | 589 ++++ src/designer/designer/CMakeLists.txt | 104 + src/designer/designer/Info_mac.plist | 35 + src/designer/designer/appfontdialog.cpp | 430 +++ src/designer/designer/appfontdialog.h | 101 + src/designer/designer/assistantclient.cpp | 175 ++ src/designer/designer/assistantclient.h | 83 + src/designer/designer/designer.icns | Bin 0 -> 154893 bytes src/designer/designer/designer.ico | Bin 0 -> 355574 bytes src/designer/designer/designer.pro | 13 + src/designer/designer/designer.qrc | 5 + src/designer/designer/designer.rc | 32 + src/designer/designer/designer_enums.h | 52 + src/designer/designer/fontpanel/fontpanel.cmake | 9 + src/designer/designer/fontpanel/fontpanel.cpp | 309 ++ src/designer/designer/fontpanel/fontpanel.h | 108 + src/designer/designer/images/designer.png | Bin 0 -> 4205 bytes src/designer/designer/images/mdi.png | Bin 0 -> 59505 bytes src/designer/designer/images/sdi.png | Bin 0 -> 61037 bytes src/designer/designer/images/workbench.png | Bin 0 -> 2085 bytes src/designer/designer/main.cpp | 60 + src/designer/designer/mainwindow.cpp | 420 +++ src/designer/designer/mainwindow.h | 187 ++ src/designer/designer/newform.cpp | 228 ++ src/designer/designer/newform.h | 104 + src/designer/designer/preferencesdialog.cpp | 119 + src/designer/designer/preferencesdialog.h | 82 + src/designer/designer/preferencesdialog.ui | 91 + src/designer/designer/qdesigner.cpp | 321 +++ src/designer/designer/qdesigner.h | 102 + src/designer/designer/qdesigner_actions.cpp | 1438 ++++++++++ src/designer/designer/qdesigner_actions.h | 231 ++ .../designer/qdesigner_appearanceoptions.cpp | 168 ++ .../designer/qdesigner_appearanceoptions.h | 136 + .../designer/qdesigner_appearanceoptions.ui | 57 + src/designer/designer/qdesigner_formwindow.cpp | 291 ++ src/designer/designer/qdesigner_formwindow.h | 97 + src/designer/designer/qdesigner_pch.h | 59 + src/designer/designer/qdesigner_server.cpp | 157 ++ src/designer/designer/qdesigner_server.h | 89 + src/designer/designer/qdesigner_settings.cpp | 250 ++ src/designer/designer/qdesigner_settings.h | 94 + src/designer/designer/qdesigner_toolwindow.cpp | 439 +++ src/designer/designer/qdesigner_toolwindow.h | 123 + src/designer/designer/qdesigner_workbench.cpp | 1111 ++++++++ src/designer/designer/qdesigner_workbench.h | 215 ++ .../designer/qttoolbardialog/images/back.png | Bin 0 -> 678 bytes .../designer/qttoolbardialog/images/down.png | Bin 0 -> 594 bytes .../designer/qttoolbardialog/images/forward.png | Bin 0 -> 655 bytes .../designer/qttoolbardialog/images/minus.png | Bin 0 -> 250 bytes .../designer/qttoolbardialog/images/plus.png | Bin 0 -> 462 bytes .../designer/qttoolbardialog/images/up.png | Bin 0 -> 692 bytes .../designer/qttoolbardialog/qttoolbardialog.cmake | 11 + .../designer/qttoolbardialog/qttoolbardialog.cpp | 1871 ++++++++++++ .../designer/qttoolbardialog/qttoolbardialog.h | 138 + .../designer/qttoolbardialog/qttoolbardialog.qrc | 10 + .../designer/qttoolbardialog/qttoolbardialog.ui | 207 ++ src/designer/designer/saveformastemplate.cpp | 174 ++ src/designer/designer/saveformastemplate.h | 77 + src/designer/designer/saveformastemplate.ui | 166 ++ src/designer/designer/uifile.icns | Bin 0 -> 123696 bytes src/designer/designer/versiondialog.cpp | 192 ++ src/designer/designer/versiondialog.h | 58 + 444 files changed, 58950 insertions(+), 3 deletions(-) create mode 100644 src/designer/components/CMakeLists.txt create mode 100644 src/designer/components/buddyeditor/buddyeditor.cmake create mode 100644 src/designer/components/buddyeditor/buddyeditor.cpp create mode 100644 src/designer/components/buddyeditor/buddyeditor.h create mode 100644 src/designer/components/buddyeditor/buddyeditor_global.h create mode 100644 src/designer/components/buddyeditor/buddyeditor_instance.cpp create mode 100644 src/designer/components/buddyeditor/buddyeditor_plugin.cpp create mode 100644 src/designer/components/buddyeditor/buddyeditor_plugin.h create mode 100644 src/designer/components/buddyeditor/buddyeditor_tool.cpp create mode 100644 src/designer/components/buddyeditor/buddyeditor_tool.h create mode 100644 src/designer/components/formeditor/brushmanagerproxy.cpp create mode 100644 src/designer/components/formeditor/brushmanagerproxy.h create mode 100644 src/designer/components/formeditor/default_actionprovider.cpp create mode 100644 src/designer/components/formeditor/default_actionprovider.h create mode 100644 src/designer/components/formeditor/default_container.cpp create mode 100644 src/designer/components/formeditor/default_container.h create mode 100644 src/designer/components/formeditor/default_layoutdecoration.cpp create mode 100644 src/designer/components/formeditor/default_layoutdecoration.h create mode 100644 src/designer/components/formeditor/defaultbrushes.xml create mode 100644 src/designer/components/formeditor/deviceprofiledialog.cpp create mode 100644 src/designer/components/formeditor/deviceprofiledialog.h create mode 100644 src/designer/components/formeditor/deviceprofiledialog.ui create mode 100644 src/designer/components/formeditor/dpi_chooser.cpp create mode 100644 src/designer/components/formeditor/dpi_chooser.h create mode 100644 src/designer/components/formeditor/embeddedoptionspage.cpp create mode 100644 src/designer/components/formeditor/embeddedoptionspage.h create mode 100644 src/designer/components/formeditor/formeditor.cmake create mode 100644 src/designer/components/formeditor/formeditor.cpp create mode 100644 src/designer/components/formeditor/formeditor.h create mode 100644 src/designer/components/formeditor/formeditor.qrc create mode 100644 src/designer/components/formeditor/formeditor_global.h create mode 100644 src/designer/components/formeditor/formeditor_optionspage.cpp create mode 100644 src/designer/components/formeditor/formeditor_optionspage.h create mode 100644 src/designer/components/formeditor/formwindow.cpp create mode 100644 src/designer/components/formeditor/formwindow.h create mode 100644 src/designer/components/formeditor/formwindow_dnditem.cpp create mode 100644 src/designer/components/formeditor/formwindow_dnditem.h create mode 100644 src/designer/components/formeditor/formwindow_widgetstack.cpp create mode 100644 src/designer/components/formeditor/formwindow_widgetstack.h create mode 100644 src/designer/components/formeditor/formwindowcursor.cpp create mode 100644 src/designer/components/formeditor/formwindowcursor.h create mode 100644 src/designer/components/formeditor/formwindowmanager.cpp create mode 100644 src/designer/components/formeditor/formwindowmanager.h create mode 100644 src/designer/components/formeditor/formwindowsettings.cpp create mode 100644 src/designer/components/formeditor/formwindowsettings.h create mode 100644 src/designer/components/formeditor/formwindowsettings.ui create mode 100644 src/designer/components/formeditor/iconcache.cpp create mode 100644 src/designer/components/formeditor/iconcache.h create mode 100644 src/designer/components/formeditor/images/cleartext.png create mode 100644 src/designer/components/formeditor/images/color.png create mode 100644 src/designer/components/formeditor/images/configure.png create mode 100644 src/designer/components/formeditor/images/cursors/arrow.png create mode 100644 src/designer/components/formeditor/images/cursors/busy.png create mode 100644 src/designer/components/formeditor/images/cursors/closedhand.png create mode 100644 src/designer/components/formeditor/images/cursors/cross.png create mode 100644 src/designer/components/formeditor/images/cursors/hand.png create mode 100644 src/designer/components/formeditor/images/cursors/hsplit.png create mode 100644 src/designer/components/formeditor/images/cursors/ibeam.png create mode 100644 src/designer/components/formeditor/images/cursors/no.png create mode 100644 src/designer/components/formeditor/images/cursors/openhand.png create mode 100644 src/designer/components/formeditor/images/cursors/sizeall.png create mode 100644 src/designer/components/formeditor/images/cursors/sizeb.png create mode 100644 src/designer/components/formeditor/images/cursors/sizef.png create mode 100644 src/designer/components/formeditor/images/cursors/sizeh.png create mode 100644 src/designer/components/formeditor/images/cursors/sizev.png create mode 100644 src/designer/components/formeditor/images/cursors/uparrow.png create mode 100644 src/designer/components/formeditor/images/cursors/vsplit.png create mode 100644 src/designer/components/formeditor/images/cursors/wait.png create mode 100644 src/designer/components/formeditor/images/cursors/whatsthis.png create mode 100644 src/designer/components/formeditor/images/downplus.png create mode 100644 src/designer/components/formeditor/images/dropdownbutton.png create mode 100644 src/designer/components/formeditor/images/edit.png create mode 100644 src/designer/components/formeditor/images/editdelete-16.png create mode 100644 src/designer/components/formeditor/images/emptyicon.png create mode 100644 src/designer/components/formeditor/images/filenew-16.png create mode 100644 src/designer/components/formeditor/images/fileopen-16.png create mode 100644 src/designer/components/formeditor/images/leveldown.png create mode 100644 src/designer/components/formeditor/images/levelup.png create mode 100644 src/designer/components/formeditor/images/mac/adjustsize.png create mode 100644 src/designer/components/formeditor/images/mac/back.png create mode 100644 src/designer/components/formeditor/images/mac/buddytool.png create mode 100644 src/designer/components/formeditor/images/mac/down.png create mode 100644 src/designer/components/formeditor/images/mac/editbreaklayout.png create mode 100644 src/designer/components/formeditor/images/mac/editcopy.png create mode 100644 src/designer/components/formeditor/images/mac/editcut.png create mode 100644 src/designer/components/formeditor/images/mac/editdelete.png create mode 100644 src/designer/components/formeditor/images/mac/editform.png create mode 100644 src/designer/components/formeditor/images/mac/editgrid.png create mode 100644 src/designer/components/formeditor/images/mac/edithlayout.png create mode 100644 src/designer/components/formeditor/images/mac/edithlayoutsplit.png create mode 100644 src/designer/components/formeditor/images/mac/editlower.png create mode 100644 src/designer/components/formeditor/images/mac/editpaste.png create mode 100644 src/designer/components/formeditor/images/mac/editraise.png create mode 100644 src/designer/components/formeditor/images/mac/editvlayout.png create mode 100644 src/designer/components/formeditor/images/mac/editvlayoutsplit.png create mode 100644 src/designer/components/formeditor/images/mac/filenew.png create mode 100644 src/designer/components/formeditor/images/mac/fileopen.png create mode 100644 src/designer/components/formeditor/images/mac/filesave.png create mode 100644 src/designer/components/formeditor/images/mac/forward.png create mode 100644 src/designer/components/formeditor/images/mac/insertimage.png create mode 100644 src/designer/components/formeditor/images/mac/minus.png create mode 100644 src/designer/components/formeditor/images/mac/plus.png create mode 100644 src/designer/components/formeditor/images/mac/redo.png create mode 100644 src/designer/components/formeditor/images/mac/resetproperty.png create mode 100644 src/designer/components/formeditor/images/mac/resourceeditortool.png create mode 100644 src/designer/components/formeditor/images/mac/signalslottool.png create mode 100644 src/designer/components/formeditor/images/mac/simplifyrichtext.png create mode 100644 src/designer/components/formeditor/images/mac/tabordertool.png create mode 100644 src/designer/components/formeditor/images/mac/textanchor.png create mode 100644 src/designer/components/formeditor/images/mac/textbold.png create mode 100644 src/designer/components/formeditor/images/mac/textcenter.png create mode 100644 src/designer/components/formeditor/images/mac/textitalic.png create mode 100644 src/designer/components/formeditor/images/mac/textjustify.png create mode 100644 src/designer/components/formeditor/images/mac/textleft.png create mode 100644 src/designer/components/formeditor/images/mac/textright.png create mode 100644 src/designer/components/formeditor/images/mac/textsubscript.png create mode 100644 src/designer/components/formeditor/images/mac/textsuperscript.png create mode 100644 src/designer/components/formeditor/images/mac/textunder.png create mode 100644 src/designer/components/formeditor/images/mac/undo.png create mode 100644 src/designer/components/formeditor/images/mac/up.png create mode 100644 src/designer/components/formeditor/images/mac/widgettool.png create mode 100644 src/designer/components/formeditor/images/minus-16.png create mode 100644 src/designer/components/formeditor/images/plus-16.png create mode 100644 src/designer/components/formeditor/images/prefix-add.png create mode 100644 src/designer/components/formeditor/images/qt3logo.png create mode 100644 src/designer/components/formeditor/images/qtlogo.png create mode 100644 src/designer/components/formeditor/images/reload.png create mode 100644 src/designer/components/formeditor/images/resetproperty.png create mode 100644 src/designer/components/formeditor/images/sort.png create mode 100644 src/designer/components/formeditor/images/submenu.png create mode 100644 src/designer/components/formeditor/images/widgets/calendarwidget.png create mode 100644 src/designer/components/formeditor/images/widgets/checkbox.png create mode 100644 src/designer/components/formeditor/images/widgets/columnview.png create mode 100644 src/designer/components/formeditor/images/widgets/combobox.png create mode 100644 src/designer/components/formeditor/images/widgets/commandlinkbutton.png create mode 100644 src/designer/components/formeditor/images/widgets/dateedit.png create mode 100644 src/designer/components/formeditor/images/widgets/datetimeedit.png create mode 100644 src/designer/components/formeditor/images/widgets/dial.png create mode 100644 src/designer/components/formeditor/images/widgets/dialogbuttonbox.png create mode 100644 src/designer/components/formeditor/images/widgets/dockwidget.png create mode 100644 src/designer/components/formeditor/images/widgets/doublespinbox.png create mode 100644 src/designer/components/formeditor/images/widgets/fontcombobox.png create mode 100644 src/designer/components/formeditor/images/widgets/frame.png create mode 100644 src/designer/components/formeditor/images/widgets/graphicsview.png create mode 100644 src/designer/components/formeditor/images/widgets/groupbox.png create mode 100644 src/designer/components/formeditor/images/widgets/groupboxcollapsible.png create mode 100644 src/designer/components/formeditor/images/widgets/hscrollbar.png create mode 100644 src/designer/components/formeditor/images/widgets/hslider.png create mode 100644 src/designer/components/formeditor/images/widgets/hsplit.png create mode 100644 src/designer/components/formeditor/images/widgets/label.png create mode 100644 src/designer/components/formeditor/images/widgets/lcdnumber.png create mode 100644 src/designer/components/formeditor/images/widgets/line.png create mode 100644 src/designer/components/formeditor/images/widgets/lineedit.png create mode 100644 src/designer/components/formeditor/images/widgets/listbox.png create mode 100644 src/designer/components/formeditor/images/widgets/listview.png create mode 100644 src/designer/components/formeditor/images/widgets/mdiarea.png create mode 100644 src/designer/components/formeditor/images/widgets/plaintextedit.png create mode 100644 src/designer/components/formeditor/images/widgets/progress.png create mode 100644 src/designer/components/formeditor/images/widgets/pushbutton.png create mode 100644 src/designer/components/formeditor/images/widgets/radiobutton.png create mode 100644 src/designer/components/formeditor/images/widgets/scrollarea.png create mode 100644 src/designer/components/formeditor/images/widgets/spacer.png create mode 100644 src/designer/components/formeditor/images/widgets/spinbox.png create mode 100644 src/designer/components/formeditor/images/widgets/tabbar.png create mode 100644 src/designer/components/formeditor/images/widgets/table.png create mode 100644 src/designer/components/formeditor/images/widgets/tabwidget.png create mode 100644 src/designer/components/formeditor/images/widgets/textedit.png create mode 100644 src/designer/components/formeditor/images/widgets/timeedit.png create mode 100644 src/designer/components/formeditor/images/widgets/toolbox.png create mode 100644 src/designer/components/formeditor/images/widgets/toolbutton.png create mode 100644 src/designer/components/formeditor/images/widgets/vline.png create mode 100644 src/designer/components/formeditor/images/widgets/vscrollbar.png create mode 100644 src/designer/components/formeditor/images/widgets/vslider.png create mode 100644 src/designer/components/formeditor/images/widgets/vspacer.png create mode 100644 src/designer/components/formeditor/images/widgets/widget.png create mode 100644 src/designer/components/formeditor/images/widgets/widgetstack.png create mode 100644 src/designer/components/formeditor/images/widgets/wizard.png create mode 100644 src/designer/components/formeditor/images/win/adjustsize.png create mode 100644 src/designer/components/formeditor/images/win/back.png create mode 100644 src/designer/components/formeditor/images/win/buddytool.png create mode 100644 src/designer/components/formeditor/images/win/down.png create mode 100644 src/designer/components/formeditor/images/win/editbreaklayout.png create mode 100644 src/designer/components/formeditor/images/win/editcopy.png create mode 100644 src/designer/components/formeditor/images/win/editcut.png create mode 100644 src/designer/components/formeditor/images/win/editdelete.png create mode 100644 src/designer/components/formeditor/images/win/editform.png create mode 100644 src/designer/components/formeditor/images/win/editgrid.png create mode 100644 src/designer/components/formeditor/images/win/edithlayout.png create mode 100644 src/designer/components/formeditor/images/win/edithlayoutsplit.png create mode 100644 src/designer/components/formeditor/images/win/editlower.png create mode 100644 src/designer/components/formeditor/images/win/editpaste.png create mode 100644 src/designer/components/formeditor/images/win/editraise.png create mode 100644 src/designer/components/formeditor/images/win/editvlayout.png create mode 100644 src/designer/components/formeditor/images/win/editvlayoutsplit.png create mode 100644 src/designer/components/formeditor/images/win/filenew.png create mode 100644 src/designer/components/formeditor/images/win/fileopen.png create mode 100644 src/designer/components/formeditor/images/win/filesave.png create mode 100644 src/designer/components/formeditor/images/win/forward.png create mode 100644 src/designer/components/formeditor/images/win/insertimage.png create mode 100644 src/designer/components/formeditor/images/win/minus.png create mode 100644 src/designer/components/formeditor/images/win/plus.png create mode 100644 src/designer/components/formeditor/images/win/redo.png create mode 100644 src/designer/components/formeditor/images/win/resourceeditortool.png create mode 100644 src/designer/components/formeditor/images/win/signalslottool.png create mode 100644 src/designer/components/formeditor/images/win/simplifyrichtext.png create mode 100644 src/designer/components/formeditor/images/win/tabordertool.png create mode 100644 src/designer/components/formeditor/images/win/textanchor.png create mode 100644 src/designer/components/formeditor/images/win/textbold.png create mode 100644 src/designer/components/formeditor/images/win/textcenter.png create mode 100644 src/designer/components/formeditor/images/win/textitalic.png create mode 100644 src/designer/components/formeditor/images/win/textjustify.png create mode 100644 src/designer/components/formeditor/images/win/textleft.png create mode 100644 src/designer/components/formeditor/images/win/textright.png create mode 100644 src/designer/components/formeditor/images/win/textsubscript.png create mode 100644 src/designer/components/formeditor/images/win/textsuperscript.png create mode 100644 src/designer/components/formeditor/images/win/textunder.png create mode 100644 src/designer/components/formeditor/images/win/undo.png create mode 100644 src/designer/components/formeditor/images/win/up.png create mode 100644 src/designer/components/formeditor/images/win/widgettool.png create mode 100644 src/designer/components/formeditor/itemview_propertysheet.cpp create mode 100644 src/designer/components/formeditor/itemview_propertysheet.h create mode 100644 src/designer/components/formeditor/layout_propertysheet.cpp create mode 100644 src/designer/components/formeditor/layout_propertysheet.h create mode 100644 src/designer/components/formeditor/line_propertysheet.cpp create mode 100644 src/designer/components/formeditor/line_propertysheet.h create mode 100644 src/designer/components/formeditor/previewactiongroup.cpp create mode 100644 src/designer/components/formeditor/previewactiongroup.h create mode 100644 src/designer/components/formeditor/qdesigner_resource.cpp create mode 100644 src/designer/components/formeditor/qdesigner_resource.h create mode 100644 src/designer/components/formeditor/qdesignerundostack.cpp create mode 100644 src/designer/components/formeditor/qdesignerundostack.h create mode 100644 src/designer/components/formeditor/qlayoutwidget_propertysheet.cpp create mode 100644 src/designer/components/formeditor/qlayoutwidget_propertysheet.h create mode 100644 src/designer/components/formeditor/qmainwindow_container.cpp create mode 100644 src/designer/components/formeditor/qmainwindow_container.h create mode 100644 src/designer/components/formeditor/qmdiarea_container.cpp create mode 100644 src/designer/components/formeditor/qmdiarea_container.h create mode 100644 src/designer/components/formeditor/qtbrushmanager.cpp create mode 100644 src/designer/components/formeditor/qtbrushmanager.h create mode 100644 src/designer/components/formeditor/qwizard_container.cpp create mode 100644 src/designer/components/formeditor/qwizard_container.h create mode 100644 src/designer/components/formeditor/qworkspace_container.cpp create mode 100644 src/designer/components/formeditor/qworkspace_container.h create mode 100644 src/designer/components/formeditor/spacer_propertysheet.cpp create mode 100644 src/designer/components/formeditor/spacer_propertysheet.h create mode 100644 src/designer/components/formeditor/templateoptionspage.cpp create mode 100644 src/designer/components/formeditor/templateoptionspage.h create mode 100644 src/designer/components/formeditor/templateoptionspage.ui create mode 100644 src/designer/components/formeditor/tool_widgeteditor.cpp create mode 100644 src/designer/components/formeditor/tool_widgeteditor.h create mode 100644 src/designer/components/formeditor/widgetselection.cpp create mode 100644 src/designer/components/formeditor/widgetselection.h create mode 100644 src/designer/components/objectinspector/objectinspector.cmake create mode 100644 src/designer/components/objectinspector/objectinspector.cpp create mode 100644 src/designer/components/objectinspector/objectinspector.h create mode 100644 src/designer/components/objectinspector/objectinspector_global.h create mode 100644 src/designer/components/objectinspector/objectinspectormodel.cpp create mode 100644 src/designer/components/objectinspector/objectinspectormodel_p.h create mode 100644 src/designer/components/propertyeditor/brushpropertymanager.cpp create mode 100644 src/designer/components/propertyeditor/brushpropertymanager.h create mode 100644 src/designer/components/propertyeditor/designerpropertymanager.cpp create mode 100644 src/designer/components/propertyeditor/designerpropertymanager.h create mode 100644 src/designer/components/propertyeditor/fontmapping.xml create mode 100644 src/designer/components/propertyeditor/fontpropertymanager.cpp create mode 100644 src/designer/components/propertyeditor/fontpropertymanager.h create mode 100644 src/designer/components/propertyeditor/newdynamicpropertydialog.cpp create mode 100644 src/designer/components/propertyeditor/newdynamicpropertydialog.h create mode 100644 src/designer/components/propertyeditor/newdynamicpropertydialog.ui create mode 100644 src/designer/components/propertyeditor/paletteeditor.cpp create mode 100644 src/designer/components/propertyeditor/paletteeditor.h create mode 100644 src/designer/components/propertyeditor/paletteeditor.ui create mode 100644 src/designer/components/propertyeditor/paletteeditorbutton.cpp create mode 100644 src/designer/components/propertyeditor/paletteeditorbutton.h create mode 100644 src/designer/components/propertyeditor/previewframe.cpp create mode 100644 src/designer/components/propertyeditor/previewframe.h create mode 100644 src/designer/components/propertyeditor/previewwidget.cpp create mode 100644 src/designer/components/propertyeditor/previewwidget.h create mode 100644 src/designer/components/propertyeditor/previewwidget.ui create mode 100644 src/designer/components/propertyeditor/propertyeditor.cmake create mode 100644 src/designer/components/propertyeditor/propertyeditor.cpp create mode 100644 src/designer/components/propertyeditor/propertyeditor.h create mode 100644 src/designer/components/propertyeditor/propertyeditor.qrc create mode 100644 src/designer/components/propertyeditor/propertyeditor_global.h create mode 100644 src/designer/components/propertyeditor/qlonglongvalidator.cpp create mode 100644 src/designer/components/propertyeditor/qlonglongvalidator.h create mode 100644 src/designer/components/propertyeditor/stringlisteditor.cpp create mode 100644 src/designer/components/propertyeditor/stringlisteditor.h create mode 100644 src/designer/components/propertyeditor/stringlisteditor.ui create mode 100644 src/designer/components/propertyeditor/stringlisteditorbutton.cpp create mode 100644 src/designer/components/propertyeditor/stringlisteditorbutton.h create mode 100644 src/designer/components/qdesigner_components.cpp create mode 100644 src/designer/components/signalsloteditor/connectdialog.cpp create mode 100644 src/designer/components/signalsloteditor/connectdialog.ui create mode 100644 src/designer/components/signalsloteditor/connectdialog_p.h create mode 100644 src/designer/components/signalsloteditor/signalslot_utils.cpp create mode 100644 src/designer/components/signalsloteditor/signalslot_utils_p.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditor.cmake create mode 100644 src/designer/components/signalsloteditor/signalsloteditor.cpp create mode 100644 src/designer/components/signalsloteditor/signalsloteditor.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_global.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_instance.cpp create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_p.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_plugin.cpp create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_plugin.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_tool.cpp create mode 100644 src/designer/components/signalsloteditor/signalsloteditor_tool.h create mode 100644 src/designer/components/signalsloteditor/signalsloteditorwindow.cpp create mode 100644 src/designer/components/signalsloteditor/signalsloteditorwindow.h create mode 100644 src/designer/components/tabordereditor/tabordereditor.cmake create mode 100644 src/designer/components/tabordereditor/tabordereditor.cpp create mode 100644 src/designer/components/tabordereditor/tabordereditor.h create mode 100644 src/designer/components/tabordereditor/tabordereditor_global.h create mode 100644 src/designer/components/tabordereditor/tabordereditor_instance.cpp create mode 100644 src/designer/components/tabordereditor/tabordereditor_plugin.cpp create mode 100644 src/designer/components/tabordereditor/tabordereditor_plugin.h create mode 100644 src/designer/components/tabordereditor/tabordereditor_tool.cpp create mode 100644 src/designer/components/tabordereditor/tabordereditor_tool.h create mode 100644 src/designer/components/taskmenu/button_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/button_taskmenu.h create mode 100644 src/designer/components/taskmenu/combobox_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/combobox_taskmenu.h create mode 100644 src/designer/components/taskmenu/containerwidget_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/containerwidget_taskmenu.h create mode 100644 src/designer/components/taskmenu/groupbox_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/groupbox_taskmenu.h create mode 100644 src/designer/components/taskmenu/inplace_editor.cpp create mode 100644 src/designer/components/taskmenu/inplace_editor.h create mode 100644 src/designer/components/taskmenu/inplace_widget_helper.cpp create mode 100644 src/designer/components/taskmenu/inplace_widget_helper.h create mode 100644 src/designer/components/taskmenu/itemlisteditor.cpp create mode 100644 src/designer/components/taskmenu/itemlisteditor.h create mode 100644 src/designer/components/taskmenu/itemlisteditor.ui create mode 100644 src/designer/components/taskmenu/label_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/label_taskmenu.h create mode 100644 src/designer/components/taskmenu/layouttaskmenu.cpp create mode 100644 src/designer/components/taskmenu/layouttaskmenu.h create mode 100644 src/designer/components/taskmenu/lineedit_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/lineedit_taskmenu.h create mode 100644 src/designer/components/taskmenu/listwidget_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/listwidget_taskmenu.h create mode 100644 src/designer/components/taskmenu/listwidgeteditor.cpp create mode 100644 src/designer/components/taskmenu/listwidgeteditor.h create mode 100644 src/designer/components/taskmenu/menutaskmenu.cpp create mode 100644 src/designer/components/taskmenu/menutaskmenu.h create mode 100644 src/designer/components/taskmenu/tablewidget_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/tablewidget_taskmenu.h create mode 100644 src/designer/components/taskmenu/tablewidgeteditor.cpp create mode 100644 src/designer/components/taskmenu/tablewidgeteditor.h create mode 100644 src/designer/components/taskmenu/tablewidgeteditor.ui create mode 100644 src/designer/components/taskmenu/taskmenu.cmake create mode 100644 src/designer/components/taskmenu/taskmenu_component.cpp create mode 100644 src/designer/components/taskmenu/taskmenu_component.h create mode 100644 src/designer/components/taskmenu/taskmenu_global.h create mode 100644 src/designer/components/taskmenu/textedit_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/textedit_taskmenu.h create mode 100644 src/designer/components/taskmenu/toolbar_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/toolbar_taskmenu.h create mode 100644 src/designer/components/taskmenu/treewidget_taskmenu.cpp create mode 100644 src/designer/components/taskmenu/treewidget_taskmenu.h create mode 100644 src/designer/components/taskmenu/treewidgeteditor.cpp create mode 100644 src/designer/components/taskmenu/treewidgeteditor.h create mode 100644 src/designer/components/taskmenu/treewidgeteditor.ui create mode 100644 src/designer/components/widgetbox/widgetbox.cmake create mode 100644 src/designer/components/widgetbox/widgetbox.cpp create mode 100644 src/designer/components/widgetbox/widgetbox.h create mode 100644 src/designer/components/widgetbox/widgetbox.qrc create mode 100644 src/designer/components/widgetbox/widgetbox.xml create mode 100644 src/designer/components/widgetbox/widgetbox_dnditem.cpp create mode 100644 src/designer/components/widgetbox/widgetbox_dnditem.h create mode 100644 src/designer/components/widgetbox/widgetbox_global.h create mode 100644 src/designer/components/widgetbox/widgetboxcategorylistview.cpp create mode 100644 src/designer/components/widgetbox/widgetboxcategorylistview.h create mode 100644 src/designer/components/widgetbox/widgetboxtreewidget.cpp create mode 100644 src/designer/components/widgetbox/widgetboxtreewidget.h create mode 100644 src/designer/data/generate_header.xsl create mode 100644 src/designer/data/generate_impl.xsl create mode 100644 src/designer/data/generate_shared.xsl create mode 100644 src/designer/data/ui3.xsd create mode 100644 src/designer/data/ui4.xsd create mode 100644 src/designer/designer/CMakeLists.txt create mode 100644 src/designer/designer/Info_mac.plist create mode 100644 src/designer/designer/appfontdialog.cpp create mode 100644 src/designer/designer/appfontdialog.h create mode 100644 src/designer/designer/assistantclient.cpp create mode 100644 src/designer/designer/assistantclient.h create mode 100644 src/designer/designer/designer.icns create mode 100644 src/designer/designer/designer.ico create mode 100644 src/designer/designer/designer.pro create mode 100644 src/designer/designer/designer.qrc create mode 100644 src/designer/designer/designer.rc create mode 100644 src/designer/designer/designer_enums.h create mode 100644 src/designer/designer/fontpanel/fontpanel.cmake create mode 100644 src/designer/designer/fontpanel/fontpanel.cpp create mode 100644 src/designer/designer/fontpanel/fontpanel.h create mode 100644 src/designer/designer/images/designer.png create mode 100644 src/designer/designer/images/mdi.png create mode 100644 src/designer/designer/images/sdi.png create mode 100644 src/designer/designer/images/workbench.png create mode 100644 src/designer/designer/main.cpp create mode 100644 src/designer/designer/mainwindow.cpp create mode 100644 src/designer/designer/mainwindow.h create mode 100644 src/designer/designer/newform.cpp create mode 100644 src/designer/designer/newform.h create mode 100644 src/designer/designer/preferencesdialog.cpp create mode 100644 src/designer/designer/preferencesdialog.h create mode 100644 src/designer/designer/preferencesdialog.ui create mode 100644 src/designer/designer/qdesigner.cpp create mode 100644 src/designer/designer/qdesigner.h create mode 100644 src/designer/designer/qdesigner_actions.cpp create mode 100644 src/designer/designer/qdesigner_actions.h create mode 100644 src/designer/designer/qdesigner_appearanceoptions.cpp create mode 100644 src/designer/designer/qdesigner_appearanceoptions.h create mode 100644 src/designer/designer/qdesigner_appearanceoptions.ui create mode 100644 src/designer/designer/qdesigner_formwindow.cpp create mode 100644 src/designer/designer/qdesigner_formwindow.h create mode 100644 src/designer/designer/qdesigner_pch.h create mode 100644 src/designer/designer/qdesigner_server.cpp create mode 100644 src/designer/designer/qdesigner_server.h create mode 100644 src/designer/designer/qdesigner_settings.cpp create mode 100644 src/designer/designer/qdesigner_settings.h create mode 100644 src/designer/designer/qdesigner_toolwindow.cpp create mode 100644 src/designer/designer/qdesigner_toolwindow.h create mode 100644 src/designer/designer/qdesigner_workbench.cpp create mode 100644 src/designer/designer/qdesigner_workbench.h create mode 100644 src/designer/designer/qttoolbardialog/images/back.png create mode 100644 src/designer/designer/qttoolbardialog/images/down.png create mode 100644 src/designer/designer/qttoolbardialog/images/forward.png create mode 100644 src/designer/designer/qttoolbardialog/images/minus.png create mode 100644 src/designer/designer/qttoolbardialog/images/plus.png create mode 100644 src/designer/designer/qttoolbardialog/images/up.png create mode 100644 src/designer/designer/qttoolbardialog/qttoolbardialog.cmake create mode 100644 src/designer/designer/qttoolbardialog/qttoolbardialog.cpp create mode 100644 src/designer/designer/qttoolbardialog/qttoolbardialog.h create mode 100644 src/designer/designer/qttoolbardialog/qttoolbardialog.qrc create mode 100644 src/designer/designer/qttoolbardialog/qttoolbardialog.ui create mode 100644 src/designer/designer/saveformastemplate.cpp create mode 100644 src/designer/designer/saveformastemplate.h create mode 100644 src/designer/designer/saveformastemplate.ui create mode 100644 src/designer/designer/uifile.icns create mode 100644 src/designer/designer/versiondialog.cpp create mode 100644 src/designer/designer/versiondialog.h diff --git a/README b/README index 8cc9a7d89..dbce91708 100644 --- a/README +++ b/README @@ -5,8 +5,8 @@ on the latest Git revision of Qt v4.8. There are several things you should be aware before considering Katie: - some components and tools have been removed: - - QMake, Designer, D-Bus viewer, Linguist, Assistant, checksdk, macdeployqt, - pixeltool, qconfig, qdoc, qev, qvfb, runonphone and other non-essential + - QMake, D-Bus viewer, Linguist, Assistant, checksdk, macdeployqt, pixeltool, + qconfig, qdoc, qev, qvfb, runonphone and other non-essential - Qt3Support, QtWebKit and ActiveQt - some things have changed: diff --git a/mkspecs/mkspecs.cmake b/mkspecs/mkspecs.cmake index 2acef473f..dc20b7eb0 100644 --- a/mkspecs/mkspecs.cmake +++ b/mkspecs/mkspecs.cmake @@ -8,7 +8,7 @@ set(KATIE_VERSION "${KATIE_MAJOR}.${KATIE_MINOR}.${KATIE_MICRO}") set(KATIE_STRING "katie ${KATIE_MAJOR}.${KATIE_MINOR}.${KATIE_MICRO}") set(KATIE_BUGREPORT "xakepa10@gmail.com") set(KATIE_URL "http://github.com/fluxer/katie") -set(KATIE_COMPONENTS "Core Gui DBus Declarative Designer Help Multimedia Network OpenGL Phonon Sql Svg Xml XmlPatterns Script ScriptTools Test UiTools") +set(KATIE_COMPONENTS "Core Gui DBus Declarative Designer DesignerComponents Help Multimedia Network OpenGL Phonon Sql Svg Xml XmlPatterns Script ScriptTools Test UiTools") # TODO: make dbus tools optional set(KATIE_TOOLS "moc uic rcc qdbusxml2cpp qdbuscpp2xml qhelpgenerator qcollectiongenerator lupdate lrelease lconvert") set(QT_LICENSE "Open Source") diff --git a/src/designer/CMakeLists.txt b/src/designer/CMakeLists.txt index 6e9f7f6b2..a65101c4d 100644 --- a/src/designer/CMakeLists.txt +++ b/src/designer/CMakeLists.txt @@ -8,6 +8,9 @@ include(extension/extension.cmake) include(sdk/sdk.cmake) include(shared/shared.cmake) +add_subdirectory(components) +add_subdirectory(designer) + set(DESIGNER_PUBLIC_HEADERS ${DESIGNER_PUBLIC_HEADERS} QAbstractExtensionFactory diff --git a/src/designer/components/CMakeLists.txt b/src/designer/components/CMakeLists.txt new file mode 100644 index 000000000..cf73fcde9 --- /dev/null +++ b/src/designer/components/CMakeLists.txt @@ -0,0 +1,120 @@ +add_definitions(-DQT_STATICPLUGIN) +set(EXTRA_DESIGNERCOMPONENTS_LIBS KtCore KtGui KtXml KtDesigner KtScript) + +include(buddyeditor/buddyeditor.cmake) +include(formeditor/formeditor.cmake) +include(objectinspector/objectinspector.cmake) +include(propertyeditor/propertyeditor.cmake) +include(signalsloteditor/signalsloteditor.cmake) +include(tabordereditor/tabordereditor.cmake) +include(taskmenu/taskmenu.cmake) +include(widgetbox/widgetbox.cmake) + +set(DESIGNERCOMPONENTS_PUBLIC_HEADERS + ${DESIGNERCOMPONENTS_PUBLIC_HEADERS} +) + +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_components.cpp +) + +include_directories( + ${CMAKE_BINARY_DIR}/include + ${CMAKE_BINARY_DIR}/privateinclude + ${CMAKE_BINARY_DIR}/include/QtCore + ${CMAKE_BINARY_DIR}/privateinclude/QtCore + ${CMAKE_BINARY_DIR}/include/QtGui + ${CMAKE_BINARY_DIR}/privateinclude/QtGui + ${CMAKE_BINARY_DIR}/include/QtXml + ${CMAKE_BINARY_DIR}/privateinclude/QtXml + ${CMAKE_BINARY_DIR}/include/QtScript + ${CMAKE_BINARY_DIR}/privateinclude/QtScript + ${CMAKE_BINARY_DIR}/include/QtUiTools + ${CMAKE_BINARY_DIR}/privateinclude/QtUiTools + ${CMAKE_BINARY_DIR}/include/QtDesigner + ${CMAKE_BINARY_DIR}/privateinclude/QtDesigner + ${CMAKE_BINARY_DIR}/include/QtDesignerComponents + ${CMAKE_BINARY_DIR}/privateinclude/QtDesignerComponents + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox + ${CMAKE_CURRENT_BINARY_DIR}/buddyeditor + ${CMAKE_CURRENT_BINARY_DIR}/formeditor + ${CMAKE_CURRENT_BINARY_DIR}/objectinspector + ${CMAKE_CURRENT_BINARY_DIR}/propertyeditor + ${CMAKE_CURRENT_BINARY_DIR}/signalsloteditor + ${CMAKE_CURRENT_BINARY_DIR}/tabordereditor + ${CMAKE_CURRENT_BINARY_DIR}/taskmenu + ${CMAKE_CURRENT_BINARY_DIR}/widgetbox + ${CMAKE_SOURCE_DIR}/src/designer/components + ${CMAKE_SOURCE_DIR}/src/designer/sdk + ${CMAKE_SOURCE_DIR}/src/designer/extension + ${CMAKE_SOURCE_DIR}/src/designer/shared + ${CMAKE_SOURCE_DIR}/src/designer +) + +if(KATIE_TYPE STREQUAL SHARED) + add_definitions( + -DQDESIGNER_SDK_LIBRARY + -DQDESIGNER_EXTENSION_LIBRARY + -DQDESIGNER_UILIB_LIBRARY + -DQDESIGNER_SHARED_LIBRARY + -DQDESIGNER_COMPONENTS_LIBRARY + ) +else() + add_definitions(-DQT_DESIGNER_STATIC) +endif() + +# headers go in one place! +katie_generate_misc("${DESIGNERCOMPONENTS_HEADERS}" QtDesignerComponents) +katie_generate_public("${DESIGNERCOMPONENTS_PUBLIC_HEADERS}" QtDesignerComponents) +katie_generate_map( + QtDesignerComponents + "QT_FORMEDITOR_EXPORT|QT_PROPERTYEDITOR_EXPORT|QT_SIGNALSLOTEDITOR_EXPORT|QT_OBJECTINSPECTOR_EXPORT|QT_WIDGETBOX_EXPORT|QT_BUDDYEDITOR_EXPORT|QT_TABORDEREDITOR_EXPORT|QT_TASKMENU_EXPORT" +) +katie_resources(${DESIGNERCOMPONENTS_SOURCES}) +katie_resources(${DESIGNERCOMPONENTS_HEADERS}) +katie_setup_flags() + +add_library(KtDesignerComponents ${KATIE_TYPE} + $ + $ + $ + ${DESIGNERCOMPONENTS_SOURCES} ${DESIGNERCOMPONENTS_HEADERS} +) +target_link_libraries(KtDesignerComponents ${EXTRA_DESIGNERCOMPONENTS_LIBS}) +set_target_properties(KtDesignerComponents PROPERTIES + VERSION ${KATIE_MAJOR}.${KATIE_MINOR} + SOVERSION ${KATIE_VERSION} + EXPORT_NAME DesignerComponents +) + +katie_generate_package( + KtDesignerComponents + "" + "${EXTRA_DESIGNERCOMPONENTS_LIBS}" + "KtCore KtGui KtXml KtDesigner KtScript" +) + +install( + TARGETS KtDesignerComponents + EXPORT KatieLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} + DESTINATION ${QT_LIBRARIES_PATH_INST} +) + +install( + DIRECTORY ${CMAKE_BINARY_DIR}/include/QtDesignerComponents + DESTINATION ${QT_HEADERS_PATH_INST} + COMPONENT Devel +) diff --git a/src/designer/components/buddyeditor/buddyeditor.cmake b/src/designer/components/buddyeditor/buddyeditor.cmake new file mode 100644 index 000000000..c83c9642a --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor.cmake @@ -0,0 +1,16 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_plugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_tool.h + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_global.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_tool.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_plugin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/buddyeditor/buddyeditor_instance.cpp +) + diff --git a/src/designer/components/buddyeditor/buddyeditor.cpp b/src/designer/components/buddyeditor/buddyeditor.cpp new file mode 100644 index 000000000..dee44738b --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor.cpp @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buddyeditor.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *buddyPropertyC = "buddy"; + +static bool canBeBuddy(QWidget *w, QDesignerFormWindowInterface *form) +{ + if (qobject_cast(w) || qobject_cast(w)) + return false; + if (w == form->mainContainer() || w->isHidden() ) + return false; + + QExtensionManager *ext = form->core()->extensionManager(); + if (QDesignerPropertySheetExtension *sheet = qt_extension(ext, w)) { + const int index = sheet->indexOf(QLatin1String("focusPolicy")); + if (index != -1) { + bool ok = false; + const Qt::FocusPolicy q = static_cast(qdesigner_internal::Utils::valueOf(sheet->property(index), &ok)); + // Refuse No-focus unless the widget is promoted. + return (ok && q != Qt::NoFocus) || qdesigner_internal::isPromoted(form->core(), w); + } + } + return false; +} + +static QString buddy(QLabel *label, QDesignerFormEditorInterface *core) +{ + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), label); + if (sheet == 0) + return QString(); + const int prop_idx = sheet->indexOf(QLatin1String(buddyPropertyC)); + if (prop_idx == -1) + return QString(); + return sheet->property(prop_idx).toString(); +} + +typedef QList LabelList; + +namespace qdesigner_internal { + +/******************************************************************************* +** BuddyEditor +*/ + +BuddyEditor::BuddyEditor(QDesignerFormWindowInterface *form, QWidget *parent) : + ConnectionEdit(parent, form), + m_formWindow(form), + m_updating(false) +{ +} + + +QWidget *BuddyEditor::widgetAt(const QPoint &pos) const +{ + QWidget *w = ConnectionEdit::widgetAt(pos); + + while (w != 0 && !m_formWindow->isManaged(w)) + w = w->parentWidget(); + if (!w) + return w; + + if (state() == Editing) { + QLabel *label = qobject_cast(w); + if (label == 0) + return 0; + const int cnt = connectionCount(); + for (int i = 0; i < cnt; ++i) { + Connection *con = connection(i); + if (con->widget(EndPoint::Source) == w) + return 0; + } + } else { + if (!canBeBuddy(w, m_formWindow)) + return 0; + } + + return w; +} + +Connection *BuddyEditor::createConnection(QWidget *source, QWidget *destination) +{ + return new Connection(this, source, destination); +} + +QDesignerFormWindowInterface *BuddyEditor::formWindow() const +{ + return m_formWindow; +} + +void BuddyEditor::updateBackground() +{ + if (m_updating || background() == 0) + return; + ConnectionEdit::updateBackground(); + + m_updating = true; + QList newList; + const LabelList label_list = background()->findChildren(); + foreach (QLabel *label, label_list) { + const QString buddy_name = buddy(label, m_formWindow->core()); + if (buddy_name.isEmpty()) + continue; + + const QList targets = background()->findChildren(buddy_name); + if (targets.isEmpty()) + continue; + + QWidget *target = 0; + + QListIterator it(targets); + while (it.hasNext()) { + QWidget *widget = it.next(); + if (widget && !widget->isHidden()) { + target = widget; + break; + } + } + + if (target == 0) + continue; + + Connection *con = new Connection(this); + con->setEndPoint(EndPoint::Source, label, widgetRect(label).center()); + con->setEndPoint(EndPoint::Target, target, widgetRect(target).center()); + newList.append(con); + } + + QList toRemove; + + const int c = connectionCount(); + for (int i = 0; i < c; i++) { + Connection *con = connection(i); + QObject *source = con->object(EndPoint::Source); + QObject *target = con->object(EndPoint::Target); + bool found = false; + QListIterator it(newList); + while (it.hasNext()) { + Connection *newConn = it.next(); + if (newConn->object(EndPoint::Source) == source && newConn->object(EndPoint::Target) == target) { + found = true; + break; + } + } + if (found == false) + toRemove.append(con); + } + if (!toRemove.isEmpty()) { + DeleteConnectionsCommand command(this, toRemove); + command.redo(); + foreach (Connection *con, toRemove) + delete takeConnection(con); + } + + QListIterator it(newList); + while (it.hasNext()) { + Connection *newConn = it.next(); + + bool found = false; + const int c = connectionCount(); + for (int i = 0; i < c; i++) { + Connection *con = connection(i); + if (con->object(EndPoint::Source) == newConn->object(EndPoint::Source) && + con->object(EndPoint::Target) == newConn->object(EndPoint::Target)) { + found = true; + break; + } + } + if (found == false) { + AddConnectionCommand command(this, newConn); + command.redo(); + } else { + delete newConn; + } + } + m_updating = false; +} + +void BuddyEditor::setBackground(QWidget *background) +{ + clear(); + ConnectionEdit::setBackground(background); + + const LabelList label_list = background->findChildren(); + foreach (QLabel *label, label_list) { + const QString buddy_name = buddy(label, m_formWindow->core()); + if (buddy_name.isEmpty()) + continue; + QWidget *target = background->findChild(buddy_name); + if (target == 0) + continue; + + Connection *con = new Connection(this); + con->setEndPoint(EndPoint::Source, label, widgetRect(label).center()); + con->setEndPoint(EndPoint::Target, target, widgetRect(target).center()); + addConnection(con); + } +} + +static QUndoCommand *createBuddyCommand(QDesignerFormWindowInterface *fw, QLabel *label, QWidget *buddy) +{ + SetPropertyCommand *command = new SetPropertyCommand(fw); + command->init(label, QLatin1String(buddyPropertyC), buddy->objectName()); + command->setText(BuddyEditor::tr("Add buddy")); + return command; +} + +void BuddyEditor::endConnection(QWidget *target, const QPoint &pos) +{ + Connection *tmp_con = newlyAddedConnection(); + Q_ASSERT(tmp_con != 0); + + tmp_con->setEndPoint(EndPoint::Target, target, pos); + + QWidget *source = 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, tmp_con->endPointPos(EndPoint::Source)); + new_con->setEndPoint(EndPoint::Target, target, tmp_con->endPointPos(EndPoint::Target)); + + selectNone(); + addConnection(new_con); + QLabel *source = qobject_cast(new_con->widget(EndPoint::Source)); + QWidget *target = new_con->widget(EndPoint::Target); + if (source) { + undoStack()->push(createBuddyCommand(m_formWindow, source, target)); + } else { + qDebug("BuddyEditor::endConnection(): not a label"); + } + setSelected(new_con, true); + } + + clearNewlyAddedConnection(); + findObjectsUnderMouse(mapFromGlobal(QCursor::pos())); +} + +void BuddyEditor::widgetRemoved(QWidget *widget) +{ + QList child_list = widget->findChildren(); + child_list.prepend(widget); + + ConnectionSet remove_set; + foreach (QWidget *w, child_list) { + const ConnectionList &cl = connectionList(); + foreach (Connection *con, cl) { + if (con->widget(EndPoint::Source) == w || con->widget(EndPoint::Target) == w) + remove_set.insert(con, con); + } + } + + if (!remove_set.isEmpty()) { + undoStack()->beginMacro(tr("Remove buddies")); + foreach (Connection *con, remove_set) { + setSelected(con, false); + con->update(); + QWidget *source = con->widget(EndPoint::Source); + if (qobject_cast(source) == 0) { + qDebug("BuddyConnection::widgetRemoved(): not a label"); + } else { + ResetPropertyCommand *command = new ResetPropertyCommand(formWindow()); + command->init(source, QLatin1String(buddyPropertyC)); + undoStack()->push(command); + } + delete takeConnection(con); + } + undoStack()->endMacro(); + } +} + +void BuddyEditor::deleteSelected() +{ + const ConnectionSet selectedConnections = selection(); // want copy for unselect + if (selectedConnections.isEmpty()) + return; + + undoStack()->beginMacro(tr("Remove %n buddies", 0, selectedConnections.size())); + foreach (Connection *con, selectedConnections) { + setSelected(con, false); + con->update(); + QWidget *source = con->widget(EndPoint::Source); + if (qobject_cast(source) == 0) { + qDebug("BuddyConnection::deleteSelected(): not a label"); + } else { + ResetPropertyCommand *command = new ResetPropertyCommand(formWindow()); + command->init(source, QLatin1String(buddyPropertyC)); + undoStack()->push(command); + } + delete takeConnection(con); + } + undoStack()->endMacro(); +} + +void BuddyEditor::autoBuddy() +{ + // Any labels? + LabelList labelList = background()->findChildren(); + if (labelList.empty()) + return; + // Find already used buddies + QWidgetList usedBuddies; + const ConnectionList &beforeConnections = connectionList(); + foreach (const Connection *c, beforeConnections) + usedBuddies.push_back(c->widget(EndPoint::Target)); + // Find potential new buddies, keep lists in sync + QWidgetList buddies; + for (LabelList::iterator it = labelList.begin(); it != labelList.end(); ) { + QLabel *label = *it; + QWidget *newBuddy = 0; + if (m_formWindow->isManaged(label)) { + const QString buddy_name = buddy(label, m_formWindow->core()); + if (buddy_name.isEmpty()) + newBuddy = findBuddy(label, usedBuddies); + } + if (newBuddy) { + buddies.push_back(newBuddy); + usedBuddies.push_back(newBuddy); + ++it; + } else { + it = labelList.erase(it); + } + } + // Add the list in one go. + if (labelList.empty()) + return; + const int count = labelList.size(); + Q_ASSERT(count == buddies.size()); + undoStack()->beginMacro(tr("Add %n buddies", 0, count)); + for (int i = 0; i < count; i++) + undoStack()->push(createBuddyCommand(m_formWindow, labelList.at(i), buddies.at(i))); + undoStack()->endMacro(); + // Now select all new ones + const ConnectionList &connections = connectionList(); + foreach (Connection *con, connections) + setSelected(con, buddies.contains(con->widget(EndPoint::Target))); +} + +// Geometrically find a potential buddy for label by checking neighbouring children of parent +QWidget *BuddyEditor::findBuddy(QLabel *l, const QWidgetList &existingBuddies) const +{ + enum { DeltaX = 5 }; + const QWidget *parent = l->parentWidget(); + // Try to find next managed neighbour on horizontal line + const QRect geom = l->geometry(); + const int y = geom.center().y(); + QWidget *neighbour = 0; + switch (l->layoutDirection()) { + case Qt::LayoutDirectionAuto: + case Qt::LeftToRight: { // Walk right to find next managed neighbour + const int xEnd = parent->size().width(); + for (int x = geom.right() + 1; x < xEnd; x += DeltaX) + if (QWidget *c = parent->childAt (x, y)) + if (m_formWindow->isManaged(c)) { + neighbour = c; + break; + } + } + break; + case Qt::RightToLeft: // Walk left to find next managed neighbour + for (int x = geom.x() - 1; x >= 0; x -= DeltaX) + if (QWidget *c = parent->childAt (x, y)) + if (m_formWindow->isManaged(c)) { + neighbour = c; + break; + } + break; + } + if (neighbour && !existingBuddies.contains(neighbour) && canBeBuddy(neighbour, m_formWindow)) + return neighbour; + + return 0; +} + +void BuddyEditor::createContextMenu(QMenu &menu) +{ + QAction *autoAction = menu.addAction(tr("Set automatically")); + connect(autoAction, SIGNAL(triggered()), this, SLOT(autoBuddy())); + menu.addSeparator(); + ConnectionEdit::createContextMenu(menu); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/buddyeditor/buddyeditor.h b/src/designer/components/buddyeditor/buddyeditor.h new file mode 100644 index 000000000..88d10f39a --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BUDDYEDITOR_H +#define BUDDYEDITOR_H + +#include "buddyeditor_global.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QLabel; + +namespace qdesigner_internal { + +class QT_BUDDYEDITOR_EXPORT BuddyEditor : public ConnectionEdit +{ + Q_OBJECT + +public: + BuddyEditor(QDesignerFormWindowInterface *form, QWidget *parent); + + QDesignerFormWindowInterface *formWindow() const; + virtual void setBackground(QWidget *background); + virtual void deleteSelected(); + +public slots: + virtual void updateBackground(); + virtual void widgetRemoved(QWidget *w); + void autoBuddy(); + +protected: + virtual QWidget *widgetAt(const QPoint &pos) const; + virtual Connection *createConnection(QWidget *source, QWidget *destination); + virtual void endConnection(QWidget *target, const QPoint &pos); + virtual void createContextMenu(QMenu &menu); + +private: + QWidget *findBuddy(QLabel *l, const QWidgetList &existingBuddies) const; + + QPointer m_formWindow; + bool m_updating; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/src/designer/components/buddyeditor/buddyeditor_global.h b/src/designer/components/buddyeditor/buddyeditor_global.h new file mode 100644 index 000000000..042576869 --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BUDDYEDITOR_GLOBAL_H +#define BUDDYEDITOR_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_BUDDYEDITOR_LIBRARY +# define QT_BUDDYEDITOR_EXPORT +#else +# define QT_BUDDYEDITOR_EXPORT +#endif +#else +#define QT_BUDDYEDITOR_EXPORT +#endif + +#endif // BUDDYEDITOR_GLOBAL_H diff --git a/src/designer/components/buddyeditor/buddyeditor_instance.cpp b/src/designer/components/buddyeditor/buddyeditor_instance.cpp new file mode 100644 index 000000000..f946bf149 --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_instance.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "buddyeditor_plugin.h" + +QT_USE_NAMESPACE + +using namespace qdesigner_internal; + +Q_EXPORT_PLUGIN(BuddyEditorPlugin) diff --git a/src/designer/components/buddyeditor/buddyeditor_plugin.cpp b/src/designer/components/buddyeditor/buddyeditor_plugin.cpp new file mode 100644 index 000000000..96404c41d --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_plugin.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "buddyeditor_plugin.h" +#include "buddyeditor_tool.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +BuddyEditorPlugin::BuddyEditorPlugin() + : m_initialized(false) +{ +} + +BuddyEditorPlugin::~BuddyEditorPlugin() +{ +} + +bool BuddyEditorPlugin::isInitialized() const +{ + return m_initialized; +} + +void BuddyEditorPlugin::initialize(QDesignerFormEditorInterface *core) +{ + Q_ASSERT(!isInitialized()); + + m_action = new QAction(tr("Edit Buddies"), this); + m_action->setObjectName(QLatin1String("__qt_edit_buddies_action")); + QIcon buddyIcon = QIcon::fromTheme("designer-edit-buddy", + QIcon(core->resourceLocation() + QLatin1String("/buddytool.png"))); + m_action->setIcon(buddyIcon); + m_action->setEnabled(false); + + setParent(core); + m_core = core; + m_initialized = true; + + connect(core->formWindowManager(), SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), + this, SLOT(addFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(formWindowRemoved(QDesignerFormWindowInterface*)), + this, SLOT(removeFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface*))); +} + +QDesignerFormEditorInterface *BuddyEditorPlugin::core() const +{ + return m_core; +} + +void BuddyEditorPlugin::addFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == false); + + BuddyEditorTool *tool = new BuddyEditorTool(formWindow, this); + m_tools[formWindow] = tool; + connect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + formWindow->registerTool(tool); +} + +void BuddyEditorPlugin::removeFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == true); + + BuddyEditorTool *tool = m_tools.value(formWindow); + m_tools.remove(formWindow); + disconnect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + // ### FIXME disable the tool + + delete tool; +} + +QAction *BuddyEditorPlugin::action() const +{ + return m_action; +} + +void BuddyEditorPlugin::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + m_action->setEnabled(formWindow != 0); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/buddyeditor/buddyeditor_plugin.h b/src/designer/components/buddyeditor/buddyeditor_plugin.h new file mode 100644 index 000000000..e126e56ea --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_plugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BUDDYEDITOR_PLUGIN_H +#define BUDDYEDITOR_PLUGIN_H + +#include "buddyeditor_global.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QAction; + +namespace qdesigner_internal { + +class BuddyEditorTool; + +class QT_BUDDYEDITOR_EXPORT BuddyEditorPlugin: public QObject, public QDesignerFormEditorPluginInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerFormEditorPluginInterface) +public: + BuddyEditorPlugin(); + virtual ~BuddyEditorPlugin(); + + virtual bool isInitialized() const; + virtual void initialize(QDesignerFormEditorInterface *core); + QAction *action() const; + + virtual QDesignerFormEditorInterface *core() const; + +public slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + +private slots: + void addFormWindow(QDesignerFormWindowInterface *formWindow); + void removeFormWindow(QDesignerFormWindowInterface *formWindow); + +private: + QPointer m_core; + QHash m_tools; + bool m_initialized; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // BUDDYEDITOR_PLUGIN_H diff --git a/src/designer/components/buddyeditor/buddyeditor_tool.cpp b/src/designer/components/buddyeditor/buddyeditor_tool.cpp new file mode 100644 index 000000000..060eb908c --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_tool.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buddyeditor_tool.h" +#include "buddyeditor.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +BuddyEditorTool::BuddyEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent) + : QDesignerFormWindowToolInterface(parent), + m_formWindow(formWindow), + m_action(new QAction(tr("Edit Buddies"), this)) +{ +} + +BuddyEditorTool::~BuddyEditorTool() +{ +} + +QDesignerFormEditorInterface *BuddyEditorTool::core() const +{ + return m_formWindow->core(); +} + +QDesignerFormWindowInterface *BuddyEditorTool::formWindow() const +{ + return m_formWindow; +} + +bool BuddyEditorTool::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) +{ + Q_UNUSED(widget); + Q_UNUSED(managedWidget); + Q_UNUSED(event); + + return false; +} + +QWidget *BuddyEditorTool::editor() const +{ + if (!m_editor) { + Q_ASSERT(formWindow() != 0); + m_editor = new BuddyEditor(formWindow(), 0); + connect(formWindow(), SIGNAL(mainContainerChanged(QWidget*)), m_editor, SLOT(setBackground(QWidget*))); + connect(formWindow(), SIGNAL(changed()), + m_editor, SLOT(updateBackground())); + } + + return m_editor; +} + +void BuddyEditorTool::activated() +{ + m_editor->enableUpdateBackground(true); +} + +void BuddyEditorTool::deactivated() +{ + m_editor->enableUpdateBackground(false); +} + +QAction *BuddyEditorTool::action() const +{ + return m_action; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/buddyeditor/buddyeditor_tool.h b/src/designer/components/buddyeditor/buddyeditor_tool.h new file mode 100644 index 000000000..c175812f5 --- /dev/null +++ b/src/designer/components/buddyeditor/buddyeditor_tool.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BUDDYEDITOR_TOOL_H +#define BUDDYEDITOR_TOOL_H + +#include "buddyeditor_global.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QAction; + +namespace qdesigner_internal { + +class BuddyEditor; + +class QT_BUDDYEDITOR_EXPORT BuddyEditorTool: public QDesignerFormWindowToolInterface +{ + Q_OBJECT +public: + explicit BuddyEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent = 0); + virtual ~BuddyEditorTool(); + + virtual QDesignerFormEditorInterface *core() const; + virtual QDesignerFormWindowInterface *formWindow() const; + + virtual QWidget *editor() const; + virtual QAction *action() const; + + virtual void activated(); + virtual void deactivated(); + + virtual bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event); + +private: + QDesignerFormWindowInterface *m_formWindow; + mutable QPointer m_editor; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // BUDDYEDITOR_TOOL_H diff --git a/src/designer/components/formeditor/brushmanagerproxy.cpp b/src/designer/components/formeditor/brushmanagerproxy.cpp new file mode 100644 index 000000000..868acca83 --- /dev/null +++ b/src/designer/components/formeditor/brushmanagerproxy.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtbrushmanager.h" +#include "brushmanagerproxy.h" +#include "qsimpleresource_p.h" +#include "qdesigner_utils_p.h" +#include "ui4_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class BrushManagerProxyPrivate +{ + BrushManagerProxy *q_ptr; + Q_DECLARE_PUBLIC(BrushManagerProxy) + +public: + BrushManagerProxyPrivate(BrushManagerProxy *bp, QDesignerFormEditorInterface *core); + void brushAdded(const QString &name, const QBrush &brush); + void brushRemoved(const QString &name); + QString uniqueBrushFileName(const QString &brushName) const; + + QtBrushManager *m_Manager; + QString m_designerFolder; + const QString m_BrushFolder; + QString m_BrushPath; + QDesignerFormEditorInterface *m_Core; + QMap m_FileToBrush; + QMap m_BrushToFile; +}; + +BrushManagerProxyPrivate::BrushManagerProxyPrivate(BrushManagerProxy *bp, QDesignerFormEditorInterface *core) : + q_ptr(bp), + m_Manager(0), + m_BrushFolder(QLatin1String("brushes")), + m_Core(core) +{ + m_designerFolder = QDir::homePath(); + m_designerFolder += QDir::separator(); + m_designerFolder += QLatin1String(".designer"); + m_BrushPath = m_designerFolder; + m_BrushPath += QDir::separator(); + m_BrushPath += m_BrushFolder; +} +} // namespace qdesigner_internal + +using namespace qdesigner_internal; + +void BrushManagerProxyPrivate::brushAdded(const QString &name, const QBrush &brush) +{ + const QString filename = uniqueBrushFileName(name); + + QDir designerDir(m_designerFolder); + if (!designerDir.exists(m_BrushFolder)) + designerDir.mkdir(m_BrushFolder); + + QFile file(m_BrushPath + QDir::separator() +filename); + if (file.open(QIODevice::WriteOnly)) { + QSimpleResource resource(m_Core); + + DomBrush *dom = resource.saveBrush(brush); + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + writer.writeStartElement(QLatin1String("description")); + writer.writeAttribute(QLatin1String("name"), name); + dom->write(writer); + writer.writeEndElement(); + writer.writeEndDocument(); + + delete dom; + file.close(); + + m_FileToBrush[filename] = name; + m_BrushToFile[name] = filename; + } +} + +void BrushManagerProxyPrivate::brushRemoved(const QString &name) +{ + QDir brushDir(m_BrushPath); + + QString filename = m_BrushToFile[name]; + brushDir.remove(filename); + m_BrushToFile.remove(name); + m_FileToBrush.remove(filename); +} + +QString BrushManagerProxyPrivate::uniqueBrushFileName(const QString &brushName) const +{ + const QString extension = QLatin1String(".br"); + QString filename = brushName.toLower(); + filename += extension; + int i = 0; + while (m_FileToBrush.contains(filename)) { + filename = brushName.toLower(); + filename += QString::number(++i); + filename += extension; + } + return filename; +} + + +BrushManagerProxy::BrushManagerProxy(QDesignerFormEditorInterface *core, QObject *parent) + : QObject(parent), d_ptr(new BrushManagerProxyPrivate(this, core)) +{ +} + +BrushManagerProxy::~BrushManagerProxy() +{ +} + +void BrushManagerProxy::setBrushManager(QtBrushManager *manager) +{ + if (d_ptr->m_Manager == manager) + return; + + if (d_ptr->m_Manager) { + disconnect(d_ptr->m_Manager, SIGNAL(brushAdded(QString,QBrush)), + this, SLOT(brushAdded(QString,QBrush))); + disconnect(d_ptr->m_Manager, SIGNAL(brushRemoved(QString)), + this, SLOT(brushRemoved(QString))); + } + + d_ptr->m_Manager = manager; + + if (!d_ptr->m_Manager) + return; + + // clear the manager + QMap brushes = d_ptr->m_Manager->brushes(); + QMap::ConstIterator it = brushes.constBegin(); + while (it != brushes.constEnd()) { + QString name = it.key(); + d_ptr->m_Manager->removeBrush(name); + + it++; + } + + // fill up the manager from compiled resources or from brush folder here + const QString nameAttribute = QLatin1String("name"); + const QString brush = QLatin1String("brush"); + const QString description = QLatin1String("description"); + + QDir brushDir(d_ptr->m_BrushPath); + bool customBrushesExist = brushDir.exists(); + if (customBrushesExist) { + // load brushes from brush folder + QStringList nameFilters; + nameFilters.append(QLatin1String("*.br")); + + QFileInfoList infos = brushDir.entryInfoList(nameFilters); + QListIterator it(infos); + while (it.hasNext()) { + const QFileInfo fi = it.next(); + + QString filename = fi.absoluteFilePath(); + + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + QXmlStreamReader reader(&file); + + // + // + // + // + // + + QString descname; + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + const QString tag = reader.name().toString().toLower(); + if (tag == description) { + if (!reader.attributes().hasAttribute(nameAttribute)) + reader.raiseError(tr("The element '%1' is missing the required attribute '%2'.") + .arg(tag, nameAttribute)); + else + descname = reader.attributes().value(nameAttribute).toString(); + continue; + } + if (tag == brush) { + DomBrush brush; + brush.read(reader); + + if (descname.isEmpty()) { + reader.raiseError(tr("Empty brush name encountered.")); + } else { + QSimpleResource resource(d_ptr->m_Core); + QBrush br = resource.setupBrush(&brush); + d_ptr->m_Manager->addBrush(descname, br); + d_ptr->m_FileToBrush[filename] = descname; + d_ptr->m_BrushToFile[descname] = filename; + } + continue; + } + reader.raiseError(tr("An unexpected element '%1' was encountered.").arg(tag)); + } + } + + file.close(); + + if (reader.hasError()) { + qdesigner_internal::designerWarning(tr("An error occurred when reading the brush definition file '%1' at line line %2, column %3: %4") + .arg(fi.fileName()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + continue; + } + } + } + } + + connect(d_ptr->m_Manager, SIGNAL(brushAdded(QString,QBrush)), + this, SLOT(brushAdded(QString,QBrush))); + connect(d_ptr->m_Manager, SIGNAL(brushRemoved(QString)), + this, SLOT(brushRemoved(QString))); + + if (!customBrushesExist) { + // load brushes from resources + QFile qrcFile(QLatin1String(":trolltech/brushes/defaultbrushes.xml")); + if (!qrcFile.open(QIODevice::ReadOnly)) + Q_ASSERT(0); + + QXmlStreamReader reader(&qrcFile); + + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name().toString().toLower() == QLatin1String("description")) { + const QString name = reader.attributes().value(nameAttribute).toString(); + do { // forward to element, which DomBrush expects + reader.readNext(); + } while (!reader.atEnd() && reader.tokenType() != QXmlStreamReader::StartElement); + DomBrush brushDom; + brushDom.read(reader); + if (!reader.hasError()) { + QSimpleResource resource(d_ptr->m_Core); + QBrush br = resource.setupBrush(&brushDom); + d_ptr->m_Manager->addBrush(name, br); + } + } + } + } + if (reader.hasError()) { + // Should never happen + qdesigner_internal::designerWarning(tr("An error occurred when reading the resource file '%1' at line %2, column %3: %4") + .arg(qrcFile.fileName()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber()) + .arg(reader.errorString())); + } + + qrcFile.close(); + } +} + +QT_END_NAMESPACE + +#include "moc_brushmanagerproxy.cpp" +#include diff --git a/src/designer/components/formeditor/brushmanagerproxy.h b/src/designer/components/formeditor/brushmanagerproxy.h new file mode 100644 index 000000000..fa5bd95cf --- /dev/null +++ b/src/designer/components/formeditor/brushmanagerproxy.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BRUSHMANAGERPROXY_H +#define BRUSHMANAGERPROXY_H + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QtBrushManager; +class BrushManagerProxyPrivate; + +class BrushManagerProxy : public QObject +{ + Q_OBJECT +public: + explicit BrushManagerProxy(QDesignerFormEditorInterface *core, QObject *parent = 0); + ~BrushManagerProxy(); + + void setBrushManager(QtBrushManager *manager); + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(BrushManagerProxy) + Q_DISABLE_COPY(BrushManagerProxy) + Q_PRIVATE_SLOT(d_func(), void brushAdded(const QString &, const QBrush &)) + Q_PRIVATE_SLOT(d_func(), void brushRemoved(const QString &name)) +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/src/designer/components/formeditor/default_actionprovider.cpp b/src/designer/components/formeditor/default_actionprovider.cpp new file mode 100644 index 000000000..84560e06e --- /dev/null +++ b/src/designer/components/formeditor/default_actionprovider.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "default_actionprovider.h" +#include "invisible_widget_p.h" +#include "qdesigner_toolbar_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ------------ ActionProviderBase: +// Draws the drag indicator when dragging an action over a widget +// that receives action Dnd, such as ToolBar, Menu or MenuBar. +ActionProviderBase::ActionProviderBase(QWidget *widget) : + m_indicator(new InvisibleWidget(widget)) +{ + Q_ASSERT(widget != 0); + + m_indicator->setAutoFillBackground(true); + m_indicator->setBackgroundRole(QPalette::Window); + + QPalette p; + p.setColor(m_indicator->backgroundRole(), Qt::red); + m_indicator->setPalette(p); + m_indicator->hide(); +} + +enum { indicatorSize = 2 }; + +// Position an indicator horizontally over the rectangle, indicating +// 'Insert before' (left or right according to layout direction) +static inline QRect horizontalIndicatorRect(const QRect &rect, Qt::LayoutDirection layoutDirection) +{ + // Position right? + QRect rc = QRect(rect.x(), 0, indicatorSize, rect.height() - 1); + if (layoutDirection == Qt::RightToLeft) + rc.moveLeft(rc.x() + rect.width() - indicatorSize); + return rc; +} + +// Position an indicator vertically over the rectangle, indicating 'Insert before' (top) +static inline QRect verticalIndicatorRect(const QRect &rect) +{ + return QRect(0, rect.top(), rect.width() - 1, indicatorSize); +} + +// Determine the geometry of the indicator by retrieving +// the action under mouse and positioning the bar within its geometry. +QRect ActionProviderBase::indicatorGeometry(const QPoint &pos, Qt::LayoutDirection layoutDirection) const +{ + QAction *action = actionAt(pos); + if (!action) + return QRect(); + QRect rc = actionGeometry(action); + return orientation() == Qt::Horizontal ? horizontalIndicatorRect(rc, layoutDirection) : verticalIndicatorRect(rc); +} + +// Adjust the indicator while dragging. (-1,1) is called to finish a DND operation +void ActionProviderBase::adjustIndicator(const QPoint &pos) +{ + if (pos == QPoint(-1, -1)) { + m_indicator->hide(); + return; + } + const QRect ig = indicatorGeometry(pos, m_indicator->layoutDirection()); + if (ig.isValid()) { + m_indicator->setGeometry(ig); + QPalette p = m_indicator->palette(); + if (p.color(m_indicator->backgroundRole()) != Qt::red) { + p.setColor(m_indicator->backgroundRole(), Qt::red); + m_indicator->setPalette(p); + } + m_indicator->show(); + m_indicator->raise(); + } else { + m_indicator->hide(); + } +} + +// ------------- QToolBarActionProvider +QToolBarActionProvider::QToolBarActionProvider(QToolBar *widget, QObject *parent) : + QObject(parent), + ActionProviderBase(widget), + m_widget(widget) +{ +} + +QRect QToolBarActionProvider::actionGeometry(QAction *action) const +{ + return m_widget->actionGeometry(action); +} + +QAction *QToolBarActionProvider::actionAt(const QPoint &pos) const +{ + return ToolBarEventFilter::actionAt(m_widget, pos); +} + +Qt::Orientation QToolBarActionProvider::orientation() const +{ + return m_widget->orientation(); +} + +QRect QToolBarActionProvider::indicatorGeometry(const QPoint &pos, Qt::LayoutDirection layoutDirection) const +{ + const QRect actionRect = ActionProviderBase::indicatorGeometry(pos, layoutDirection); + if (actionRect.isValid()) + return actionRect; + // Toolbar differs in that is has no dummy placeholder to 'insert before' + // when intending to append. Check the free area. + const QRect freeArea = ToolBarEventFilter::freeArea(m_widget); + if (!freeArea.contains(pos)) + return QRect(); + return orientation() == Qt::Horizontal ? horizontalIndicatorRect(freeArea, layoutDirection) : verticalIndicatorRect(freeArea); +} + +// ------------- QMenuBarActionProvider +QMenuBarActionProvider::QMenuBarActionProvider(QMenuBar *widget, QObject *parent) : + QObject(parent), + ActionProviderBase(widget), + m_widget(widget) +{ +} + +QRect QMenuBarActionProvider::actionGeometry(QAction *action) const +{ + return m_widget->actionGeometry(action); +} + +QAction *QMenuBarActionProvider::actionAt(const QPoint &pos) const +{ + return m_widget->actionAt(pos); +} + +Qt::Orientation QMenuBarActionProvider::orientation() const +{ + return Qt::Horizontal; +} + +// ------------- QMenuActionProvider +QMenuActionProvider::QMenuActionProvider(QMenu *widget, QObject *parent) : + QObject(parent), + ActionProviderBase(widget), + m_widget(widget) +{ +} + +QRect QMenuActionProvider::actionGeometry(QAction *action) const +{ + return m_widget->actionGeometry(action); +} + +QAction *QMenuActionProvider::actionAt(const QPoint &pos) const +{ + return m_widget->actionAt(pos); +} + +Qt::Orientation QMenuActionProvider::orientation() const +{ + return Qt::Vertical; +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/default_actionprovider.h b/src/designer/components/formeditor/default_actionprovider.h new file mode 100644 index 000000000..9f0ff113a --- /dev/null +++ b/src/designer/components/formeditor/default_actionprovider.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEFAULT_ACTIONPROVIDER_H +#define DEFAULT_ACTIONPROVIDER_H + +#include "formeditor_global.h" +#include "actionprovider_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class FormWindow; + +class QT_FORMEDITOR_EXPORT ActionProviderBase: public QDesignerActionProviderExtension +{ +protected: + explicit ActionProviderBase(QWidget *widget); + +public: + virtual void adjustIndicator(const QPoint &pos); + virtual Qt::Orientation orientation() const = 0; + +protected: + virtual QRect indicatorGeometry(const QPoint &pos, Qt::LayoutDirection layoutDirection) const; + +private: + QWidget *m_indicator; +}; + +class QT_FORMEDITOR_EXPORT QToolBarActionProvider: public QObject, public ActionProviderBase +{ + Q_OBJECT + Q_INTERFACES(QDesignerActionProviderExtension) +public: + explicit QToolBarActionProvider(QToolBar *widget, QObject *parent = 0); + + virtual QRect actionGeometry(QAction *action) const; + virtual QAction *actionAt(const QPoint &pos) const; + Qt::Orientation orientation() const; + +protected: + virtual QRect indicatorGeometry(const QPoint &pos, Qt::LayoutDirection layoutDirection) const; + +private: + QToolBar *m_widget; +}; + +class QT_FORMEDITOR_EXPORT QMenuBarActionProvider: public QObject, public ActionProviderBase +{ + Q_OBJECT + Q_INTERFACES(QDesignerActionProviderExtension) +public: + explicit QMenuBarActionProvider(QMenuBar *widget, QObject *parent = 0); + + virtual QRect actionGeometry(QAction *action) const; + virtual QAction *actionAt(const QPoint &pos) const; + Qt::Orientation orientation() const; + +private: + QMenuBar *m_widget; +}; + +class QT_FORMEDITOR_EXPORT QMenuActionProvider: public QObject, public ActionProviderBase +{ + Q_OBJECT + Q_INTERFACES(QDesignerActionProviderExtension) +public: + explicit QMenuActionProvider(QMenu *widget, QObject *parent = 0); + + virtual QRect actionGeometry(QAction *action) const; + virtual QAction *actionAt(const QPoint &pos) const; + Qt::Orientation orientation() const; + +private: + QMenu *m_widget; +}; + +typedef ExtensionFactory QToolBarActionProviderFactory; +typedef ExtensionFactory QMenuBarActionProviderFactory; +typedef ExtensionFactory QMenuActionProviderFactory; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DEFAULT_ACTIONPROVIDER_H diff --git a/src/designer/components/formeditor/default_container.cpp b/src/designer/components/formeditor/default_container.cpp new file mode 100644 index 000000000..3cbea5e25 --- /dev/null +++ b/src/designer/components/formeditor/default_container.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "default_container.h" +#include + +QT_BEGIN_NAMESPACE + +template +static inline void setCurrentContainerIndex(int index, Container *container) +{ + const bool blocked = container->signalsBlocked(); + container->blockSignals(true); + container->setCurrentIndex(index); + container->blockSignals(blocked); +} + +static inline void ensureNoParent(QWidget *widget) +{ + if (widget->parentWidget()) + widget->setParent(0); +} + +static const char *PageLabel = "Page"; + +namespace qdesigner_internal { + +// --------- QStackedWidgetContainer +QStackedWidgetContainer::QStackedWidgetContainer(QStackedWidget *widget, QObject *parent) : + QObject(parent), + m_widget(widget) +{ +} + +void QStackedWidgetContainer::setCurrentIndex(int index) +{ + setCurrentContainerIndex(index, m_widget); +} + +void QStackedWidgetContainer::addWidget(QWidget *widget) +{ + ensureNoParent(widget); + m_widget->addWidget(widget); +} + +void QStackedWidgetContainer::insertWidget(int index, QWidget *widget) +{ + ensureNoParent(widget); + m_widget->insertWidget(index, widget); +} + +void QStackedWidgetContainer::remove(int index) +{ + m_widget->removeWidget(widget(index)); +} + +// --------- QTabWidgetContainer +QTabWidgetContainer::QTabWidgetContainer(QTabWidget *widget, QObject *parent) : + QObject(parent), + m_widget(widget) +{ +} + +void QTabWidgetContainer::setCurrentIndex(int index) +{ + setCurrentContainerIndex(index, m_widget); +} + +void QTabWidgetContainer::addWidget(QWidget *widget) +{ + ensureNoParent(widget); + m_widget->addTab(widget, QString::fromUtf8(PageLabel)); +} + +void QTabWidgetContainer::insertWidget(int index, QWidget *widget) +{ + ensureNoParent(widget); + m_widget->insertTab(index, widget, QString::fromUtf8(PageLabel)); +} + +void QTabWidgetContainer::remove(int index) +{ + m_widget->removeTab(index); +} + +// ------------------- QToolBoxContainer +QToolBoxContainer::QToolBoxContainer(QToolBox *widget, QObject *parent) : + QObject(parent), + m_widget(widget) +{ +} + +void QToolBoxContainer::setCurrentIndex(int index) +{ + setCurrentContainerIndex(index, m_widget); +} + +void QToolBoxContainer::addWidget(QWidget *widget) +{ + ensureNoParent(widget); + m_widget->addItem(widget, QString::fromUtf8(PageLabel)); +} + +void QToolBoxContainer::insertWidget(int index, QWidget *widget) +{ + ensureNoParent(widget); + m_widget->insertItem(index, widget, QString::fromUtf8(PageLabel)); +} + +void QToolBoxContainer::remove(int index) +{ + m_widget->removeItem(index); +} + +// ------------------- QScrollAreaContainer +// We pass on active=true only if there are no children yet. +// If there are children, it is a legacy custom widget QScrollArea that has an internal, +// unmanaged child, in which case we deactivate the extension (otherwise we crash). +// The child will then not show up in the task menu + +QScrollAreaContainer::QScrollAreaContainer(QScrollArea *widget, QObject *parent) : + QObject(parent), + SingleChildContainer(widget, widget->widget() == 0) +{ +} +// ------------------- QDockWidgetContainer +QDockWidgetContainer::QDockWidgetContainer(QDockWidget *widget, QObject *parent) : + QObject(parent), + SingleChildContainer(widget) +{ +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/default_container.h b/src/designer/components/formeditor/default_container.h new file mode 100644 index 000000000..01c9aeec8 --- /dev/null +++ b/src/designer/components/formeditor/default_container.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEFAULT_CONTAINER_H +#define DEFAULT_CONTAINER_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ------------ QStackedWidgetContainer +class QStackedWidgetContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QStackedWidgetContainer(QStackedWidget *widget, QObject *parent = 0); + + virtual int count() const { return m_widget->count(); } + virtual QWidget *widget(int index) const { return m_widget->widget(index); } + + virtual int currentIndex() const { return m_widget->currentIndex(); } + virtual void setCurrentIndex(int index); + + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QStackedWidget *m_widget; +}; + +// ------------ QTabWidgetContainer +class QTabWidgetContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QTabWidgetContainer(QTabWidget *widget, QObject *parent = 0); + + virtual int count() const { return m_widget->count(); } + virtual QWidget *widget(int index) const { return m_widget->widget(index); } + + virtual int currentIndex() const { return m_widget->currentIndex(); } + virtual void setCurrentIndex(int index); + + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QTabWidget *m_widget; +}; + +// ------------ QToolBoxContainer +class QToolBoxContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QToolBoxContainer(QToolBox *widget, QObject *parent = 0); + + virtual int count() const { return m_widget->count(); } + virtual QWidget *widget(int index) const { return m_widget->widget(index); } + + virtual int currentIndex() const { return m_widget->currentIndex(); } + virtual void setCurrentIndex(int index); + + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QToolBox *m_widget; +}; + +// ------------ SingleChildContainer: +// Template for containers that have a single child widget using widget()/setWidget(). + +template +class SingleChildContainer: public QDesignerContainerExtension +{ +protected: + explicit SingleChildContainer(Container *widget, bool active = true); +public: + virtual int count() const; + virtual QWidget *widget(int index) const; + virtual int currentIndex() const; + virtual void setCurrentIndex(int /*index*/) {} + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int /*index*/) {} + +private: + const bool m_active; + Container *m_container; +}; + +template +SingleChildContainer::SingleChildContainer(Container *widget, bool active) : + m_active(active), + m_container(widget) +{ +} + +template +int SingleChildContainer::count() const +{ + return m_active && m_container->widget() ? 1 : 0; +} + +template +QWidget *SingleChildContainer::widget(int /* index */) const +{ + return m_container->widget(); +} + +template +int SingleChildContainer::currentIndex() const +{ + return m_active && m_container->widget() ? 0 : -1; +} + +template +void SingleChildContainer::addWidget(QWidget *widget) +{ + Q_ASSERT(m_container->widget() == 0); + widget->setParent(m_container); + m_container->setWidget(widget); +} + +template +void SingleChildContainer::insertWidget(int /* index */, QWidget *widget) +{ + addWidget(widget); +} + +// ------------ QScrollAreaContainer +class QScrollAreaContainer: public QObject, public SingleChildContainer +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QScrollAreaContainer(QScrollArea *widget, QObject *parent = 0); +}; + +// --------------- QDockWidgetContainer +class QDockWidgetContainer: public QObject, public SingleChildContainer +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QDockWidgetContainer(QDockWidget *widget, QObject *parent = 0); +}; + +typedef ExtensionFactory QDesignerStackedWidgetContainerFactory; +typedef ExtensionFactory QDesignerTabWidgetContainerFactory; +typedef ExtensionFactory QDesignerToolBoxContainerFactory; +typedef ExtensionFactory QScrollAreaContainerFactory; +typedef ExtensionFactory QDockWidgetContainerFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DEFAULT_CONTAINER_H diff --git a/src/designer/components/formeditor/default_layoutdecoration.cpp b/src/designer/components/formeditor/default_layoutdecoration.cpp new file mode 100644 index 000000000..f0b13f4fe --- /dev/null +++ b/src/designer/components/formeditor/default_layoutdecoration.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "default_layoutdecoration.h" +#include "qlayout_widget_p.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---- QDesignerLayoutDecorationFactory ---- +QDesignerLayoutDecorationFactory::QDesignerLayoutDecorationFactory(QExtensionManager *parent) + : QExtensionFactory(parent) +{ +} + +QObject *QDesignerLayoutDecorationFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const +{ + if (!object->isWidgetType() || iid != Q_TYPEID(QDesignerLayoutDecorationExtension)) + return 0; + + QWidget *widget = qobject_cast(object); + + if (const QLayoutWidget *layoutWidget = qobject_cast(widget)) + return QLayoutSupport::createLayoutSupport(layoutWidget->formWindow(), widget, parent); + + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(widget)) + if (LayoutInfo::managedLayout(fw->core(), widget)) + return QLayoutSupport::createLayoutSupport(fw, widget, parent); + + return 0; +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/default_layoutdecoration.h b/src/designer/components/formeditor/default_layoutdecoration.h new file mode 100644 index 000000000..086264ac3 --- /dev/null +++ b/src/designer/components/formeditor/default_layoutdecoration.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DEFAULT_LAYOUTDECORATION_H +#define DEFAULT_LAYOUTDECORATION_H + +#include "formeditor_global.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +class QDesignerLayoutDecorationFactory: public QExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) +public: + explicit QDesignerLayoutDecorationFactory(QExtensionManager *parent = 0); + +protected: + virtual QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DEFAULT_LAYOUTDECORATION_H diff --git a/src/designer/components/formeditor/defaultbrushes.xml b/src/designer/components/formeditor/defaultbrushes.xml new file mode 100644 index 000000000..88035c3a6 --- /dev/null +++ b/src/designer/components/formeditor/defaultbrushes.xml @@ -0,0 +1,542 @@ + + + + + + + 0 + 0 + 255 + + + + + 0 + 0 + 255 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + + + + + + + 0 + 0 + 0 + + + + + 0 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 255 + 0 + + + + + 255 + 255 + 0 + + + + + + + + + + + 0 + 176 + 221 + + + + + 0 + 176 + 221 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 0 + 176 + 221 + + + + + 0 + 176 + 221 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 0 + 176 + 221 + + + + + 0 + 176 + 221 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 0 + 176 + 221 + + + + + 0 + 176 + 221 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 0 + 176 + 221 + + + + + 0 + 176 + 221 + + + + + + + + + + + 60 + 160 + 0 + + + + + 60 + 160 + 0 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + + + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + + + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 5 + 0 + 70 + + + + + 5 + 0 + 70 + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + + + + + + + 255 + 255 + 255 + + + + + 255 + 255 + 255 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + + + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 255 + 0 + + + + + 255 + 255 + 0 + + + + + 255 + 0 + 0 + + + + + 255 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 255 + + + + + + + 0 + 255 + 255 + + + + + + + 0 + 255 + 0 + + + + + + + 255 + 0 + 255 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 0 + + + + diff --git a/src/designer/components/formeditor/deviceprofiledialog.cpp b/src/designer/components/formeditor/deviceprofiledialog.cpp new file mode 100644 index 000000000..e503c08dc --- /dev/null +++ b/src/designer/components/formeditor/deviceprofiledialog.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "deviceprofiledialog.h" +#include "ui_deviceprofiledialog.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +static const char *profileExtensionC = "qdp"; + +static inline QString fileFilter() +{ + return qdesigner_internal::DeviceProfileDialog::tr("Device Profiles (*.%1)").arg(QLatin1String(profileExtensionC)); +} + +// Populate a combo with a sequence of integers, also set them as data. +template + static void populateNumericCombo(IntIterator i1, IntIterator i2, QComboBox *cb) +{ + QString s; + cb->setEditable(false); + for ( ; i1 != i2 ; ++i1) { + const int n = *i1; + s.setNum(n); + cb->addItem(s, QVariant(n)); + } +} + +namespace qdesigner_internal { + +DeviceProfileDialog::DeviceProfileDialog(QDesignerDialogGuiInterface *dlgGui, QWidget *parent) : + QDialog(parent), + m_ui(new Ui::DeviceProfileDialog), + m_dlgGui(dlgGui) +{ + setModal(true); + m_ui->setupUi(this); + + const QList standardFontSizes = QFontDatabase::standardSizes(); + populateNumericCombo(standardFontSizes.constBegin(), standardFontSizes.constEnd(), m_ui->m_systemFontSizeCombo); + + // Styles + const QStringList styles = QStyleFactory::keys(); + m_ui->m_styleCombo->addItem(tr("Default"), QVariant(QString())); + const QStringList::const_iterator cend = styles.constEnd(); + for (QStringList::const_iterator it = styles.constBegin(); it != cend; ++it) + m_ui->m_styleCombo->addItem(*it, *it); + + connect(m_ui->m_nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(nameChanged(QString))); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(m_ui->buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(accept())); + // Note that Load/Save emit accepted() of the button box.. + connect(m_ui->buttonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(save())); + connect(m_ui->buttonBox->button(QDialogButtonBox::Open), SIGNAL(clicked()), this, SLOT(open())); +} + +DeviceProfileDialog::~DeviceProfileDialog() +{ + delete m_ui; +} + +DeviceProfile DeviceProfileDialog::deviceProfile() const +{ + DeviceProfile rc; + rc.setName(m_ui->m_nameLineEdit->text()); + rc.setFontFamily(m_ui->m_systemFontComboBox->currentFont().family()); + rc.setFontPointSize(m_ui->m_systemFontSizeCombo->itemData(m_ui->m_systemFontSizeCombo->currentIndex()).toInt()); + + int dpiX, dpiY; + m_ui->m_dpiChooser->getDPI(&dpiX, &dpiY); + rc.setDpiX(dpiX); + rc.setDpiY(dpiY); + + rc.setStyle(m_ui->m_styleCombo->itemData(m_ui->m_styleCombo->currentIndex()).toString()); + + return rc; +} + +void DeviceProfileDialog::setDeviceProfile(const DeviceProfile &s) +{ + m_ui->m_nameLineEdit->setText(s.name()); + m_ui->m_systemFontComboBox->setCurrentFont(QFont(s.fontFamily())); + const int fontSizeIndex = m_ui->m_systemFontSizeCombo->findData(QVariant(s.fontPointSize())); + m_ui->m_systemFontSizeCombo->setCurrentIndex(fontSizeIndex != -1 ? fontSizeIndex : 0); + m_ui->m_dpiChooser->setDPI(s.dpiX(), s.dpiY()); + const int styleIndex = m_ui->m_styleCombo->findData(s.style()); + m_ui->m_styleCombo->setCurrentIndex(styleIndex != -1 ? styleIndex : 0); +} + +void DeviceProfileDialog::setOkButtonEnabled(bool v) +{ + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(v); +} + +bool DeviceProfileDialog::showDialog(const QStringList &existingNames) +{ + m_existingNames = existingNames; + m_ui->m_nameLineEdit->setFocus(Qt::OtherFocusReason); + nameChanged(m_ui->m_nameLineEdit->text()); + return exec() == Accepted; +} + +void DeviceProfileDialog::nameChanged(const QString &name) +{ + const bool invalid = name.isEmpty() || m_existingNames.indexOf(name) != -1; + setOkButtonEnabled(!invalid); +} + +void DeviceProfileDialog::save() +{ + QString fn = m_dlgGui->getSaveFileName(this, tr("Save Profile"), QString(), fileFilter()); + if (fn.isEmpty()) + return; + if (QFileInfo(fn).completeSuffix().isEmpty()) { + fn += QLatin1Char('.'); + fn += QLatin1String(profileExtensionC); + } + + QFile file(fn); + if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) { + critical(tr("Save Profile - Error"), tr("Unable to open the file '%1' for writing: %2").arg(fn, file.errorString())); + return; + } + file.write(deviceProfile().toXml().toUtf8()); +} + +void DeviceProfileDialog::open() +{ + const QString fn = m_dlgGui->getOpenFileName(this, tr("Open profile"), QString(), fileFilter()); + if (fn.isEmpty()) + return; + + QFile file(fn); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + critical(tr("Open Profile - Error"), tr("Unable to open the file '%1' for reading: %2").arg(fn, file.errorString())); + return; + } + QString errorMessage; + DeviceProfile newSettings; + if (!newSettings.fromXml(QString::fromUtf8(file.readAll()), &errorMessage)) { + critical(tr("Open Profile - Error"), tr("'%1' is not a valid profile: %2").arg(fn, errorMessage)); + return; + } + setDeviceProfile(newSettings); +} + +void DeviceProfileDialog::critical(const QString &title, const QString &msg) +{ + m_dlgGui->message(this, QDesignerDialogGuiInterface::OtherMessage, QMessageBox::Critical, title, msg); +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/deviceprofiledialog.h b/src/designer/components/formeditor/deviceprofiledialog.h new file mode 100644 index 000000000..62f78220b --- /dev/null +++ b/src/designer/components/formeditor/deviceprofiledialog.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 SYSTEMSETTINGSDIALOG_H +#define SYSTEMSETTINGSDIALOG_H + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace Ui { + class DeviceProfileDialog; +} + +class QDesignerDialogGuiInterface; + +class QDialogButtonBox; + +namespace qdesigner_internal { + +class DeviceProfile; + +/* DeviceProfileDialog: Widget to edit system settings for embedded design */ + +class DeviceProfileDialog : public QDialog +{ + Q_DISABLE_COPY(DeviceProfileDialog) + Q_OBJECT +public: + explicit DeviceProfileDialog(QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + ~DeviceProfileDialog(); + + DeviceProfile deviceProfile() const; + void setDeviceProfile(const DeviceProfile &s); + + bool showDialog(const QStringList &existingNames); + +private slots: + void setOkButtonEnabled(bool); + void nameChanged(const QString &name); + void save(); + void open(); + +private: + void critical(const QString &title, const QString &msg); + Ui::DeviceProfileDialog *m_ui; + QDesignerDialogGuiInterface *m_dlgGui; + QStringList m_existingNames; +}; +} + +QT_END_NAMESPACE + +#endif // SYSTEMSETTINGSDIALOG_H diff --git a/src/designer/components/formeditor/deviceprofiledialog.ui b/src/designer/components/formeditor/deviceprofiledialog.ui new file mode 100644 index 000000000..d7a298c67 --- /dev/null +++ b/src/designer/components/formeditor/deviceprofiledialog.ui @@ -0,0 +1,108 @@ + + + DeviceProfileDialog + + + + 0 + 0 + 348 + 209 + + + + + + + + + + &Family + + + m_systemFontComboBox + + + + + + + + + + &Point Size + + + m_systemFontSizeCombo + + + + + + + + + + Style + + + m_styleCombo + + + + + + + + + + Device DPI + + + + + + + + + + Name + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Open|QDialogButtonBox::Save + + + + + + + + qdesigner_internal::DPI_Chooser + QWidget +
dpi_chooser.h
+ 1 +
+
+ + m_nameLineEdit + m_systemFontComboBox + m_systemFontSizeCombo + m_styleCombo + buttonBox + + + +
diff --git a/src/designer/components/formeditor/dpi_chooser.cpp b/src/designer/components/formeditor/dpi_chooser.cpp new file mode 100644 index 000000000..c8620ed43 --- /dev/null +++ b/src/designer/components/formeditor/dpi_chooser.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dpi_chooser.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum { minDPI = 50, maxDPI = 400 }; + +namespace qdesigner_internal { + +// Entry struct for predefined values +struct DPI_Entry { + int dpiX; + int dpiY; + const char *description; +}; + +const struct DPI_Entry dpiEntries[] = { + //: Embedded device standard screen resolution + { 96, 96, QT_TRANSLATE_NOOP("DPI_Chooser", "Standard (96 x 96)") }, + //: Embedded device screen resolution + { 179, 185, QT_TRANSLATE_NOOP("DPI_Chooser", "Greenphone (179 x 185)") }, + //: Embedded device high definition screen resolution + { 192, 192, QT_TRANSLATE_NOOP("DPI_Chooser", "High (192 x 192)") } +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(const struct qdesigner_internal::DPI_Entry*); + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ------------- DPI_Chooser + +DPI_Chooser::DPI_Chooser(QWidget *parent) : + QWidget(parent), + m_systemEntry(new DPI_Entry), + m_predefinedCombo(new QComboBox), + m_dpiXSpinBox(new QSpinBox), + m_dpiYSpinBox(new QSpinBox) +{ + // Predefined settings: System + DeviceProfile::systemResolution(&(m_systemEntry->dpiX), &(m_systemEntry->dpiY)); + m_systemEntry->description = 0; + const struct DPI_Entry *systemEntry = m_systemEntry; + //: System resolution + m_predefinedCombo->addItem(tr("System (%1 x %2)").arg(m_systemEntry->dpiX).arg(m_systemEntry->dpiY), QVariant::fromValue(systemEntry)); + // Devices. Exclude the system values as not to duplicate the entries + const int predefinedCount = sizeof(dpiEntries)/sizeof(DPI_Entry); + const struct DPI_Entry *ecend = dpiEntries + predefinedCount; + for (const struct DPI_Entry *it = dpiEntries; it < ecend; ++it) + if (it->dpiX != m_systemEntry->dpiX || it->dpiY != m_systemEntry->dpiY) + m_predefinedCombo->addItem(tr(it->description), QVariant::fromValue(it)); + m_predefinedCombo->addItem(tr("User defined")); + + setFocusProxy(m_predefinedCombo); + m_predefinedCombo->setEditable(false); + m_predefinedCombo->setCurrentIndex(0); + connect(m_predefinedCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(syncSpinBoxes())); + // top row with predefined settings + QVBoxLayout *vBoxLayout = new QVBoxLayout; + vBoxLayout->setMargin(0); + vBoxLayout->addWidget(m_predefinedCombo); + // Spin box row + QHBoxLayout *hBoxLayout = new QHBoxLayout; + hBoxLayout->setMargin(0); + + m_dpiXSpinBox->setMinimum(minDPI); + m_dpiXSpinBox->setMaximum(maxDPI); + hBoxLayout->addWidget(m_dpiXSpinBox); + //: DPI X/Y separator + hBoxLayout->addWidget(new QLabel(tr(" x "))); + + m_dpiYSpinBox->setMinimum(minDPI); + m_dpiYSpinBox->setMaximum(maxDPI); + hBoxLayout->addWidget(m_dpiYSpinBox); + + hBoxLayout->addStretch(); + vBoxLayout->addLayout(hBoxLayout); + setLayout(vBoxLayout); + + syncSpinBoxes(); +} + +DPI_Chooser::~DPI_Chooser() +{ + delete m_systemEntry; +} + +void DPI_Chooser::getDPI(int *dpiX, int *dpiY) const +{ + *dpiX = m_dpiXSpinBox->value(); + *dpiY = m_dpiYSpinBox->value(); +} + +void DPI_Chooser::setDPI(int dpiX, int dpiY) +{ + // Default to system if it is something weird + const bool valid = dpiX >= minDPI && dpiX <= maxDPI && dpiY >= minDPI && dpiY <= maxDPI; + if (!valid) { + m_predefinedCombo->setCurrentIndex(0); + return; + } + // Try to find the values among the predefined settings + const int count = m_predefinedCombo->count(); + int predefinedIndex = -1; + for (int i = 0; i < count; i++) { + const QVariant data = m_predefinedCombo->itemData(i); + if (data.type() != QVariant::Invalid) { + const struct DPI_Entry *entry = qvariant_cast(data); + if (entry->dpiX == dpiX && entry->dpiY == dpiY) { + predefinedIndex = i; + break; + } + } + } + if (predefinedIndex != -1) { + m_predefinedCombo->setCurrentIndex(predefinedIndex); // triggers syncSpinBoxes() + } else { + setUserDefinedValues(dpiX, dpiY); + } +} + +void DPI_Chooser::setUserDefinedValues(int dpiX, int dpiY) +{ + const bool blocked = m_predefinedCombo->blockSignals(true); + m_predefinedCombo->setCurrentIndex(m_predefinedCombo->count() - 1); + m_predefinedCombo->blockSignals(blocked); + + m_dpiXSpinBox->setEnabled(true); + m_dpiYSpinBox->setEnabled(true); + m_dpiXSpinBox->setValue(dpiX); + m_dpiYSpinBox->setValue(dpiY); +} + +void DPI_Chooser::syncSpinBoxes() +{ + const int predefIdx = m_predefinedCombo->currentIndex(); + const QVariant data = m_predefinedCombo->itemData(predefIdx); + + // Predefined mode in which spin boxes are disabled or user defined? + const bool userSetting = data.type() == QVariant::Invalid; + m_dpiXSpinBox->setEnabled(userSetting); + m_dpiYSpinBox->setEnabled(userSetting); + + if (!userSetting) { + const struct DPI_Entry *entry = qvariant_cast(data); + m_dpiXSpinBox->setValue(entry->dpiX); + m_dpiYSpinBox->setValue(entry->dpiY); + } +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/dpi_chooser.h b/src/designer/components/formeditor/dpi_chooser.h new file mode 100644 index 000000000..c528a5455 --- /dev/null +++ b/src/designer/components/formeditor/dpi_chooser.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 DPICHOOSER_H +#define DPICHOOSER_H + +#include + +QT_BEGIN_NAMESPACE + +class QSpinBox; +class QComboBox; + +namespace qdesigner_internal { + +struct DPI_Entry; + +/* Let the user choose a DPI settings */ +class DPI_Chooser : public QWidget { + Q_DISABLE_COPY(DPI_Chooser) + Q_OBJECT + +public: + explicit DPI_Chooser(QWidget *parent = 0); + ~DPI_Chooser(); + + void getDPI(int *dpiX, int *dpiY) const; + void setDPI(int dpiX, int dpiY); + +private slots: + void syncSpinBoxes(); + +private: + void setUserDefinedValues(int dpiX, int dpiY); + + struct DPI_Entry *m_systemEntry; + QComboBox *m_predefinedCombo; + QSpinBox *m_dpiXSpinBox; + QSpinBox *m_dpiYSpinBox; +}; +} + +QT_END_NAMESPACE + +#endif // DPICHOOSER_H diff --git a/src/designer/components/formeditor/embeddedoptionspage.cpp b/src/designer/components/formeditor/embeddedoptionspage.cpp new file mode 100644 index 000000000..98201c5a4 --- /dev/null +++ b/src/designer/components/formeditor/embeddedoptionspage.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "embeddedoptionspage.h" +#include "deviceprofiledialog.h" +#include "widgetfactory_p.h" +#include "formwindowmanager.h" + +#include +#include +#include +#include +#include + + +// SDK +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +typedef QList DeviceProfileList; + +enum { profileComboIndexOffset = 1 }; + +// Sort by name. Used by template, do not make it static! +bool deviceProfileLessThan(const DeviceProfile &d1, const DeviceProfile &d2) +{ + return d1.name().toLower() < d2.name().toLower(); +} + +static bool ask(QWidget *parent, + QDesignerDialogGuiInterface *dlgui, + const QString &title, + const QString &what) +{ + return dlgui->message(parent, QDesignerDialogGuiInterface::OtherMessage, + QMessageBox::Question, title, what, + QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; +} + +// ------------ EmbeddedOptionsControlPrivate +class EmbeddedOptionsControlPrivate { + Q_DISABLE_COPY(EmbeddedOptionsControlPrivate) +public: + EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core); + void init(EmbeddedOptionsControl *q); + + bool isDirty() const { return m_dirty; } + + void loadSettings(); + void saveSettings(); + void slotAdd(); + void slotEdit(); + void slotDelete(); + void slotProfileIndexChanged(int); + +private: + QStringList existingProfileNames() const; + void sortAndPopulateProfileCombo(); + void updateState(); + void updateDescriptionLabel(); + + QDesignerFormEditorInterface *m_core; + QComboBox *m_profileCombo; + QToolButton *m_addButton; + QToolButton *m_editButton; + QToolButton *m_deleteButton; + QLabel *m_descriptionLabel; + + DeviceProfileList m_sortedProfiles; + EmbeddedOptionsControl *m_q; + bool m_dirty; + QSet m_usedProfiles; +}; + +EmbeddedOptionsControlPrivate::EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core) : + m_core(core), + m_profileCombo(new QComboBox), + m_addButton(new QToolButton), + m_editButton(new QToolButton), + m_deleteButton(new QToolButton), + m_descriptionLabel(new QLabel), + m_q(0), + m_dirty(false) +{ + m_descriptionLabel->setMinimumHeight(80); + // Determine used profiles to lock them + const QDesignerFormWindowManagerInterface *fwm = core->formWindowManager(); + if (const int fwCount = fwm->formWindowCount()) { + for (int i = 0; i < fwCount; i++) + if (const FormWindowBase *fwb = qobject_cast(fwm->formWindow(i))) { + const QString deviceProfileName = fwb->deviceProfileName(); + if (!deviceProfileName.isEmpty()) + m_usedProfiles.insert(deviceProfileName); + } + } +} + +void EmbeddedOptionsControlPrivate::init(EmbeddedOptionsControl *q) +{ + m_q = q; + QVBoxLayout *vLayout = new QVBoxLayout; + QHBoxLayout *hLayout = new QHBoxLayout; + m_profileCombo->setMinimumWidth(200); + m_profileCombo->setEditable(false); + hLayout->addWidget(m_profileCombo); + m_profileCombo->addItem(EmbeddedOptionsControl::tr("None")); + EmbeddedOptionsControl::connect(m_profileCombo, SIGNAL(currentIndexChanged(int)), m_q, SLOT(slotProfileIndexChanged(int))); + + m_addButton->setIcon(createIconSet(QString::fromUtf8("plus.png"))); + m_addButton->setToolTip(EmbeddedOptionsControl::tr("Add a profile")); + EmbeddedOptionsControl::connect(m_addButton, SIGNAL(clicked()), m_q, SLOT(slotAdd())); + hLayout->addWidget(m_addButton); + + EmbeddedOptionsControl::connect(m_editButton, SIGNAL(clicked()), m_q, SLOT(slotEdit())); + m_editButton->setIcon(createIconSet(QString::fromUtf8("edit.png"))); + m_editButton->setToolTip(EmbeddedOptionsControl::tr("Edit the selected profile")); + hLayout->addWidget(m_editButton); + + m_deleteButton->setIcon(createIconSet(QString::fromUtf8("minus.png"))); + m_deleteButton->setToolTip(EmbeddedOptionsControl::tr("Delete the selected profile")); + EmbeddedOptionsControl::connect(m_deleteButton, SIGNAL(clicked()), m_q, SLOT(slotDelete())); + hLayout->addWidget(m_deleteButton); + + hLayout->addStretch(); + vLayout->addLayout(hLayout); + vLayout->addWidget(m_descriptionLabel); + m_q->setLayout(vLayout); +} + +QStringList EmbeddedOptionsControlPrivate::existingProfileNames() const +{ + QStringList rc; + const DeviceProfileList::const_iterator dcend = m_sortedProfiles.constEnd(); + for (DeviceProfileList::const_iterator it = m_sortedProfiles.constBegin(); it != dcend; ++it) + rc.push_back(it->name()); + return rc; +} + +void EmbeddedOptionsControlPrivate::slotAdd() +{ + DeviceProfileDialog dlg(m_core->dialogGui(), m_q); + dlg.setWindowTitle(EmbeddedOptionsControl::tr("Add Profile")); + // Create a new profile with a new, unique name + DeviceProfile settings; + settings.fromSystem(); + dlg.setDeviceProfile(settings); + + const QStringList names = existingProfileNames(); + const QString newNamePrefix = EmbeddedOptionsControl::tr("New profile"); + QString newName = newNamePrefix; + for (int i = 2; names.contains(newName); i++) { + newName = newNamePrefix; + newName += QString::number(i); + } + + settings.setName(newName); + dlg.setDeviceProfile(settings); + if (dlg.showDialog(names)) { + const DeviceProfile newProfile = dlg.deviceProfile(); + m_sortedProfiles.push_back(newProfile); + // Maintain sorted order + sortAndPopulateProfileCombo(); + const int index = m_profileCombo->findText(newProfile.name()); + m_profileCombo->setCurrentIndex(index); + m_dirty = true; + } +} + +void EmbeddedOptionsControlPrivate::slotEdit() +{ + const int index = m_profileCombo->currentIndex() - profileComboIndexOffset; + if (index < 0) + return; + + // Edit the profile, compile a list of existing names + // excluding current one. re-insert if changed, + // re-sort if name changed. + const DeviceProfile oldProfile = m_sortedProfiles.at(index); + const QString oldName = oldProfile.name(); + QStringList names = existingProfileNames(); + names.removeAll(oldName); + + DeviceProfileDialog dlg(m_core->dialogGui(), m_q); + dlg.setWindowTitle(EmbeddedOptionsControl::tr("Edit Profile")); + dlg.setDeviceProfile(oldProfile); + if (dlg.showDialog(names)) { + const DeviceProfile newProfile = dlg.deviceProfile(); + if (newProfile != oldProfile) { + m_dirty = true; + m_sortedProfiles[index] = newProfile; + if (newProfile.name() != oldName) { + sortAndPopulateProfileCombo(); + const int index = m_profileCombo->findText(newProfile.name()); + m_profileCombo->setCurrentIndex(index); + } else { + updateDescriptionLabel(); + } + + } + } +} + +void EmbeddedOptionsControlPrivate::slotDelete() +{ + const int index = m_profileCombo->currentIndex() - profileComboIndexOffset; + if (index < 0) + return; + const QString name = m_sortedProfiles.at(index).name(); + if (ask(m_q, m_core->dialogGui(), + EmbeddedOptionsControl::tr("Delete Profile"), + EmbeddedOptionsControl::tr("Would you like to delete the profile '%1'?").arg(name))) { + m_profileCombo->setCurrentIndex(0); + m_sortedProfiles.removeAt(index); + m_profileCombo->removeItem(index + profileComboIndexOffset); + m_dirty = true; + } +} + +void EmbeddedOptionsControlPrivate::sortAndPopulateProfileCombo() +{ + // Clear items until only "None" is left + for (int i = m_profileCombo->count() - 1; i > 0; i--) + m_profileCombo->removeItem(i); + if (!m_sortedProfiles.empty()) { + qSort(m_sortedProfiles.begin(), m_sortedProfiles.end(), deviceProfileLessThan); + m_profileCombo->addItems(existingProfileNames()); + } +} + +void EmbeddedOptionsControlPrivate::loadSettings() +{ + const QDesignerSharedSettings settings(m_core); + m_sortedProfiles = settings.deviceProfiles(); + sortAndPopulateProfileCombo(); + // Index: 0 is "None" + const int settingsIndex = settings.currentDeviceProfileIndex(); + const int profileIndex = settingsIndex >= 0 && settingsIndex < m_sortedProfiles.size() ? settingsIndex + profileComboIndexOffset : 0; + m_profileCombo->setCurrentIndex(profileIndex); + updateState(); + m_dirty = false; +} + +void EmbeddedOptionsControlPrivate::saveSettings() +{ + QDesignerSharedSettings settings(m_core); + settings.setDeviceProfiles(m_sortedProfiles); + // Index: 0 is "None" + settings.setCurrentDeviceProfileIndex(m_profileCombo->currentIndex() - profileComboIndexOffset); + m_dirty = false; +} + +//: Format embedded device profile description +static const char *descriptionFormat = QT_TRANSLATE_NOOP("EmbeddedOptionsControl", +"" +"" +"" +"" +"" +"
Font%1, %2
Style%3
Resolution%4 x %5
" +""); + +static inline QString description(const DeviceProfile& p) +{ + QString styleName = p.style(); + if (styleName.isEmpty()) + styleName = EmbeddedOptionsControl::tr("Default"); + return EmbeddedOptionsControl::tr(descriptionFormat). + arg(p.fontFamily()).arg(p.fontPointSize()).arg(styleName).arg(p.dpiX()).arg(p.dpiY()); +} + +void EmbeddedOptionsControlPrivate::updateDescriptionLabel() +{ + const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset; + if (profileIndex >= 0) { + m_descriptionLabel->setText(description(m_sortedProfiles.at(profileIndex))); + } else { + m_descriptionLabel->clear(); + } +} + +void EmbeddedOptionsControlPrivate::updateState() +{ + const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset; + // Allow for changing/deleting only if it is not in use + bool modifyEnabled = false; + if (profileIndex >= 0) + modifyEnabled = !m_usedProfiles.contains(m_sortedProfiles.at(profileIndex).name()); + m_editButton->setEnabled(modifyEnabled); + m_deleteButton->setEnabled(modifyEnabled); + updateDescriptionLabel(); +} + +void EmbeddedOptionsControlPrivate::slotProfileIndexChanged(int) +{ + updateState(); + m_dirty = true; +} + +// ------------- EmbeddedOptionsControl +EmbeddedOptionsControl::EmbeddedOptionsControl(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + m_d(new EmbeddedOptionsControlPrivate(core)) +{ + m_d->init(this); +} + +EmbeddedOptionsControl::~EmbeddedOptionsControl() +{ + delete m_d; +} + +void EmbeddedOptionsControl::slotAdd() +{ + m_d->slotAdd(); +} + +void EmbeddedOptionsControl::slotEdit() +{ + m_d->slotEdit(); +} + +void EmbeddedOptionsControl::slotDelete() +{ + m_d->slotDelete(); +} + +void EmbeddedOptionsControl::loadSettings() +{ + m_d->loadSettings(); +} + +void EmbeddedOptionsControl::saveSettings() +{ + m_d->saveSettings(); +} + +void EmbeddedOptionsControl::slotProfileIndexChanged(int i) +{ + m_d->slotProfileIndexChanged(i); +} + +bool EmbeddedOptionsControl::isDirty() const +{ + return m_d->isDirty(); +} + +// EmbeddedOptionsPage: +EmbeddedOptionsPage::EmbeddedOptionsPage(QDesignerFormEditorInterface *core) : + m_core(core) +{ +} + +QString EmbeddedOptionsPage::name() const +{ + //: Tab in preferences dialog + return QCoreApplication::translate("EmbeddedOptionsPage", "Embedded Design"); +} + +QWidget *EmbeddedOptionsPage::createPage(QWidget *parent) +{ + QWidget *optionsWidget = new QWidget(parent); + + QVBoxLayout *optionsVLayout = new QVBoxLayout(); + + //: EmbeddedOptionsControl group box" + QGroupBox *gb = new QGroupBox(QCoreApplication::translate("EmbeddedOptionsPage", "Device Profiles")); + QVBoxLayout *gbVLayout = new QVBoxLayout(); + m_embeddedOptionsControl = new EmbeddedOptionsControl(m_core); + m_embeddedOptionsControl->loadSettings(); + gbVLayout->addWidget(m_embeddedOptionsControl); + gb->setLayout(gbVLayout); + optionsVLayout->addWidget(gb); + + optionsVLayout->addStretch(1); + + // Outer layout to give it horizontal stretch + QHBoxLayout *optionsHLayout = new QHBoxLayout(); + optionsHLayout->addLayout(optionsVLayout); + optionsHLayout->addStretch(1); + optionsWidget->setLayout(optionsHLayout); + return optionsWidget; +} + +void EmbeddedOptionsPage::apply() +{ + if (!m_embeddedOptionsControl || !m_embeddedOptionsControl->isDirty()) + return; + + m_embeddedOptionsControl->saveSettings(); + if (FormWindowManager *fw = qobject_cast(m_core->formWindowManager())) + fw->deviceProfilesChanged(); +} + +void EmbeddedOptionsPage::finish() +{ +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/embeddedoptionspage.h b/src/designer/components/formeditor/embeddedoptionspage.h new file mode 100644 index 000000000..09f6ba974 --- /dev/null +++ b/src/designer/components/formeditor/embeddedoptionspage.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef EMBEDDEDOPTIONSPAGE_H +#define EMBEDDEDOPTIONSPAGE_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class EmbeddedOptionsControlPrivate; + +/* EmbeddedOptions Control. Presents the user with a list of embedded + * device profiles he can modify/add/delete. */ +class EmbeddedOptionsControl : public QWidget { + Q_DISABLE_COPY(EmbeddedOptionsControl) + Q_OBJECT +public: + explicit EmbeddedOptionsControl(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~EmbeddedOptionsControl(); + + bool isDirty() const; + +public slots: + void loadSettings(); + void saveSettings(); + +private slots: + void slotAdd(); + void slotEdit(); + void slotDelete(); + void slotProfileIndexChanged(int); + +private: + EmbeddedOptionsControlPrivate *m_d; +}; + +// EmbeddedOptionsPage +class EmbeddedOptionsPage : public QDesignerOptionsPageInterface +{ + Q_DISABLE_COPY(EmbeddedOptionsPage) +public: + explicit EmbeddedOptionsPage(QDesignerFormEditorInterface *core); + + QString name() const; + QWidget *createPage(QWidget *parent); + virtual void finish(); + virtual void apply(); + +private: + QDesignerFormEditorInterface *m_core; + QPointer m_embeddedOptionsControl; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // EMBEDDEDOPTIONSPAGE_H diff --git a/src/designer/components/formeditor/formeditor.cmake b/src/designer/components/formeditor/formeditor.cmake new file mode 100644 index 000000000..822e794a6 --- /dev/null +++ b/src/designer/components/formeditor/formeditor.cmake @@ -0,0 +1,77 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qdesigner_resource.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qdesignerundostack.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow_widgetstack.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow_dnditem.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowcursor.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/widgetselection.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowmanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor_global.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qlayoutwidget_propertysheet.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/layout_propertysheet.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/spacer_propertysheet.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/line_propertysheet.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_container.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_actionprovider.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qmainwindow_container.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qworkspace_container.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qmdiarea_container.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qwizard_container.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_layoutdecoration.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qtbrushmanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/brushmanagerproxy.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/iconcache.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/tool_widgeteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor_optionspage.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/embeddedoptionspage.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowsettings.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/deviceprofiledialog.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/dpi_chooser.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/previewactiongroup.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/itemview_propertysheet.h + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/templateoptionspage.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/deviceprofiledialog.ui + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowsettings.ui + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/templateoptionspage.ui + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qdesigner_resource.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qdesignerundostack.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow_widgetstack.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindow_dnditem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowcursor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/widgetselection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qlayoutwidget_propertysheet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/layout_propertysheet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/spacer_propertysheet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/line_propertysheet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qmainwindow_container.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qworkspace_container.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qmdiarea_container.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qwizard_container.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_container.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_layoutdecoration.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/default_actionprovider.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/tool_widgeteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/qtbrushmanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/brushmanagerproxy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/iconcache.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor_optionspage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/embeddedoptionspage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formwindowsettings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/deviceprofiledialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/dpi_chooser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/previewactiongroup.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/itemview_propertysheet.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/templateoptionspage.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/formeditor/formeditor.qrc +) + diff --git a/src/designer/components/formeditor/formeditor.cpp b/src/designer/components/formeditor/formeditor.cpp new file mode 100644 index 000000000..9c9d530df --- /dev/null +++ b/src/designer/components/formeditor/formeditor.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formeditor.h" +#include "formeditor_optionspage.h" +#include "embeddedoptionspage.h" +#include "templateoptionspage.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" +#include "widgetfactory_p.h" +#include "formwindowmanager.h" +#include "qmainwindow_container.h" +#include "qworkspace_container.h" +#include "qmdiarea_container.h" +#include "qwizard_container.h" +#include "default_container.h" +#include "default_layoutdecoration.h" +#include "default_actionprovider.h" +#include "qlayoutwidget_propertysheet.h" +#include "spacer_propertysheet.h" +#include "line_propertysheet.h" +#include "layout_propertysheet.h" +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_toolbox_p.h" +#include "qdesigner_tabwidget_p.h" +#include "qtbrushmanager.h" +#include "brushmanagerproxy.h" +#include "iconcache.h" +#include "qtresourcemodel_p.h" +#include "qdesigner_integration_p.h" +#include "itemview_propertysheet.h" + +// sdk +#include + +// shared +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +FormEditor::FormEditor(QObject *parent) + : QDesignerFormEditorInterface(parent) +{ + setIntrospection(new QDesignerIntrospection); + setDialogGui(new DialogGui); + QDesignerPluginManager *pluginManager = new QDesignerPluginManager(this); + setPluginManager(pluginManager); + + WidgetDataBase *widgetDatabase = new WidgetDataBase(this, this); + setWidgetDataBase(widgetDatabase); + + MetaDataBase *metaDataBase = new MetaDataBase(this, this); + setMetaDataBase(metaDataBase); + + WidgetFactory *widgetFactory = new WidgetFactory(this, this); + setWidgetFactory(widgetFactory); + + FormWindowManager *formWindowManager = new FormWindowManager(this, this); + setFormManager(formWindowManager); + connect(formWindowManager, SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), widgetFactory, SLOT(formWindowAdded(QDesignerFormWindowInterface*))); + connect(formWindowManager, SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), widgetFactory, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface*))); + + QExtensionManager *mgr = new QExtensionManager(this); + const QString containerExtensionId = Q_TYPEID(QDesignerContainerExtension); + + QDesignerStackedWidgetContainerFactory::registerExtension(mgr, containerExtensionId); + QDesignerTabWidgetContainerFactory::registerExtension(mgr, containerExtensionId); + QDesignerToolBoxContainerFactory::registerExtension(mgr, containerExtensionId); + QMainWindowContainerFactory::registerExtension(mgr, containerExtensionId); + QDockWidgetContainerFactory::registerExtension(mgr, containerExtensionId); + QScrollAreaContainerFactory::registerExtension(mgr, containerExtensionId); + QWorkspaceContainerFactory::registerExtension(mgr, containerExtensionId); + QMdiAreaContainerFactory::registerExtension(mgr, containerExtensionId); + QWizardContainerFactory::registerExtension(mgr, containerExtensionId); + + mgr->registerExtensions(new QDesignerLayoutDecorationFactory(mgr), + Q_TYPEID(QDesignerLayoutDecorationExtension)); + + const QString actionProviderExtensionId = Q_TYPEID(QDesignerActionProviderExtension); + QToolBarActionProviderFactory::registerExtension(mgr, actionProviderExtensionId); + QMenuBarActionProviderFactory::registerExtension(mgr, actionProviderExtensionId); + QMenuActionProviderFactory::registerExtension(mgr, actionProviderExtensionId); + + QDesignerDefaultPropertySheetFactory::registerExtension(mgr); + QLayoutWidgetPropertySheetFactory::registerExtension(mgr); + SpacerPropertySheetFactory::registerExtension(mgr); + LinePropertySheetFactory::registerExtension(mgr); + LayoutPropertySheetFactory::registerExtension(mgr); + QStackedWidgetPropertySheetFactory::registerExtension(mgr); + QToolBoxWidgetPropertySheetFactory::registerExtension(mgr); + QTabWidgetPropertySheetFactory::registerExtension(mgr); + QMdiAreaPropertySheetFactory::registerExtension(mgr); + QWorkspacePropertySheetFactory::registerExtension(mgr); + QWizardPagePropertySheetFactory::registerExtension(mgr); + QWizardPropertySheetFactory::registerExtension(mgr); + + QTreeViewPropertySheetFactory::registerExtension(mgr); + QTableViewPropertySheetFactory::registerExtension(mgr); + + const QString internalTaskMenuId = QLatin1String("QDesignerInternalTaskMenuExtension"); + QDesignerTaskMenuFactory::registerExtension(mgr, internalTaskMenuId); + + mgr->registerExtensions(new QDesignerMemberSheetFactory(mgr), + Q_TYPEID(QDesignerMemberSheetExtension)); + + setExtensionManager(mgr); + + setIconCache(new IconCache(this)); + + QtBrushManager *brushManager = new QtBrushManager(this); + setBrushManager(brushManager); + + BrushManagerProxy *brushProxy = new BrushManagerProxy(this, this); + brushProxy->setBrushManager(brushManager); + setPromotion(new QDesignerPromotion(this)); + + QtResourceModel *resourceModel = new QtResourceModel(this); + setResourceModel(resourceModel); + connect(resourceModel, SIGNAL(qrcFileModifiedExternally(QString)), + this, SLOT(slotQrcFileChangedExternally(QString))); + + QList optionsPages; + optionsPages << new TemplateOptionsPage(this) << new FormEditorOptionsPage(this) << new EmbeddedOptionsPage(this); + setOptionsPages(optionsPages); + + setSettingsManager(new QDesignerQSettings()); +} + +FormEditor::~FormEditor() +{ +} + +void FormEditor::slotQrcFileChangedExternally(const QString &path) +{ + QDesignerIntegration *designerIntegration = qobject_cast(integration()); + if (!designerIntegration) + return; + + QDesignerIntegration::ResourceFileWatcherBehaviour behaviour = designerIntegration->resourceFileWatcherBehaviour(); + if (behaviour == QDesignerIntegration::NoWatcher) { + return; + } else if (behaviour == QDesignerIntegration::PromptAndReload) { + QMessageBox::StandardButton button = dialogGui()->message(topLevel(), QDesignerDialogGuiInterface::FileChangedMessage, QMessageBox::Warning, + tr("Resource File Changed"), + tr("The file \"%1\" has changed outside Designer. Do you want to reload it?").arg(path), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (button != QMessageBox::Yes) + return; + } + + resourceModel()->reload(path); +} + +} + +QT_END_NAMESPACE + +#include +#include diff --git a/src/designer/components/formeditor/formeditor.h b/src/designer/components/formeditor/formeditor.h new file mode 100644 index 000000000..80156360a --- /dev/null +++ b/src/designer/components/formeditor/formeditor.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMEDITOR_H +#define FORMEDITOR_H + +#include "formeditor_global.h" + +#include + +QT_BEGIN_NAMESPACE + +class QObject; + +namespace qdesigner_internal { + +class QT_FORMEDITOR_EXPORT FormEditor: public QDesignerFormEditorInterface +{ + Q_OBJECT +public: + FormEditor(QObject *parent = 0); + virtual ~FormEditor(); +public slots: + void slotQrcFileChangedExternally(const QString &path); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMEDITOR_H diff --git a/src/designer/components/formeditor/formeditor.qrc b/src/designer/components/formeditor/formeditor.qrc new file mode 100644 index 000000000..e42cc66ec --- /dev/null +++ b/src/designer/components/formeditor/formeditor.qrc @@ -0,0 +1,175 @@ + + + images/submenu.png + images/cursors/arrow.png + images/cursors/busy.png + images/cursors/closedhand.png + images/cursors/cross.png + images/cursors/hand.png + images/cursors/hsplit.png + images/cursors/ibeam.png + images/cursors/no.png + images/cursors/openhand.png + images/cursors/sizeall.png + images/cursors/sizeb.png + images/cursors/sizef.png + images/cursors/sizeh.png + images/cursors/sizev.png + images/cursors/uparrow.png + images/cursors/vsplit.png + images/cursors/wait.png + images/cursors/whatsthis.png + images/emptyicon.png + images/filenew-16.png + images/fileopen-16.png + images/editdelete-16.png + images/plus-16.png + images/minus-16.png + images/prefix-add.png + images/downplus.png + images/leveldown.png + images/levelup.png + images/mac/adjustsize.png + images/mac/widgettool.png + images/mac/signalslottool.png + images/mac/tabordertool.png + images/mac/buddytool.png + images/mac/editbreaklayout.png + images/mac/editcopy.png + images/mac/editcut.png + images/mac/editdelete.png + images/mac/editgrid.png + images/mac/editform.png + images/mac/edithlayout.png + images/mac/edithlayoutsplit.png + images/mac/editlower.png + images/mac/editpaste.png + images/mac/editraise.png + images/mac/editvlayout.png + images/mac/editvlayoutsplit.png + images/mac/filenew.png + images/mac/insertimage.png + images/mac/undo.png + images/mac/redo.png + images/mac/fileopen.png + images/mac/filesave.png + images/mac/resourceeditortool.png + images/mac/plus.png + images/mac/minus.png + images/mac/back.png + images/mac/forward.png + images/mac/down.png + images/mac/up.png + images/qtlogo.png + images/qt3logo.png + images/resetproperty.png + images/cleartext.png + images/sort.png + images/edit.png + images/reload.png + images/configure.png + images/color.png + images/dropdownbutton.png + images/widgets/calendarwidget.png + images/widgets/checkbox.png + images/widgets/columnview.png + images/widgets/combobox.png + images/widgets/commandlinkbutton.png + images/widgets/dateedit.png + images/widgets/datetimeedit.png + images/widgets/dial.png + images/widgets/dialogbuttonbox.png + images/widgets/dockwidget.png + images/widgets/doublespinbox.png + images/widgets/fontcombobox.png + images/widgets/frame.png + images/widgets/graphicsview.png + images/widgets/groupbox.png + images/widgets/hscrollbar.png + images/widgets/hslider.png + images/widgets/hsplit.png + images/widgets/label.png + images/widgets/lcdnumber.png + images/widgets/line.png + images/widgets/lineedit.png + images/widgets/listbox.png + images/widgets/listview.png + images/widgets/mdiarea.png + images/widgets/plaintextedit.png + images/widgets/progress.png + images/widgets/pushbutton.png + images/widgets/radiobutton.png + images/widgets/scrollarea.png + images/widgets/spacer.png + images/widgets/spinbox.png + images/widgets/table.png + images/widgets/tabwidget.png + images/widgets/textedit.png + images/widgets/timeedit.png + images/widgets/toolbox.png + images/widgets/toolbutton.png + images/widgets/vline.png + images/widgets/vscrollbar.png + images/widgets/vslider.png + images/widgets/vspacer.png + images/widgets/widget.png + images/widgets/widgetstack.png + images/widgets/wizard.png + images/win/adjustsize.png + images/win/widgettool.png + images/win/signalslottool.png + images/win/tabordertool.png + images/win/buddytool.png + images/win/editbreaklayout.png + images/win/editcopy.png + images/win/editcut.png + images/win/editdelete.png + images/win/editgrid.png + images/win/editform.png + images/win/edithlayout.png + images/win/edithlayoutsplit.png + images/win/editlower.png + images/win/editpaste.png + images/win/editraise.png + images/win/editvlayout.png + images/win/editvlayoutsplit.png + images/win/filenew.png + images/win/insertimage.png + images/win/undo.png + images/win/redo.png + images/win/fileopen.png + images/win/filesave.png + images/win/resourceeditortool.png + images/win/plus.png + images/win/minus.png + images/win/textanchor.png + images/win/textbold.png + images/win/textitalic.png + images/win/textunder.png + images/win/textleft.png + images/win/textcenter.png + images/win/textright.png + images/win/textjustify.png + images/win/textsuperscript.png + images/win/textsubscript.png + images/win/simplifyrichtext.png + images/win/back.png + images/win/forward.png + images/win/down.png + images/win/up.png + images/mac/textanchor.png + images/mac/textbold.png + images/mac/textitalic.png + images/mac/textunder.png + images/mac/textleft.png + images/mac/textcenter.png + images/mac/textright.png + images/mac/textjustify.png + images/mac/textsuperscript.png + images/mac/textsubscript.png + images/mac/simplifyrichtext.png + + + defaultbrushes.xml + + diff --git a/src/designer/components/formeditor/formeditor_global.h b/src/designer/components/formeditor/formeditor_global.h new file mode 100644 index 000000000..68a4d696b --- /dev/null +++ b/src/designer/components/formeditor/formeditor_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMEDITOR_GLOBAL_H +#define FORMEDITOR_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_FORMEDITOR_LIBRARY +# define QT_FORMEDITOR_EXPORT +#else +# define QT_FORMEDITOR_EXPORT +#endif +#else +#define QT_FORMEDITOR_EXPORT +#endif + +#endif // FORMEDITOR_GLOBAL_H diff --git a/src/designer/components/formeditor/formeditor_optionspage.cpp b/src/designer/components/formeditor/formeditor_optionspage.cpp new file mode 100644 index 000000000..01305380f --- /dev/null +++ b/src/designer/components/formeditor/formeditor_optionspage.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formeditor_optionspage.h" + +// shared +#include "formwindowbase_p.h" +#include "gridpanel_p.h" +#include "grid_p.h" +#include "previewconfigurationwidget_p.h" +#include "shared_settings_p.h" +#include "zoomwidget_p.h" + +// SDK +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QList IntList; + +namespace qdesigner_internal { + +// Zoom, currently for preview only +class ZoomSettingsWidget : public QGroupBox { + Q_DISABLE_COPY(ZoomSettingsWidget) +public: + explicit ZoomSettingsWidget(QWidget *parent = 0); + + void fromSettings(const QDesignerSharedSettings &s); + void toSettings(QDesignerSharedSettings &s) const; + +private: + QComboBox *m_zoomCombo; +}; + +ZoomSettingsWidget::ZoomSettingsWidget(QWidget *parent) : + QGroupBox(parent), + m_zoomCombo(new QComboBox) +{ + m_zoomCombo->setEditable(false); + const IntList zoomValues = ZoomMenu::zoomValues(); + const IntList::const_iterator cend = zoomValues.constEnd(); + + for (IntList::const_iterator it = zoomValues.constBegin(); it != cend; ++it) { + //: Zoom percentage + m_zoomCombo->addItem(QCoreApplication::translate("FormEditorOptionsPage", "%1 %").arg(*it), QVariant(*it)); + } + + // Layout + setCheckable(true); + setTitle(QCoreApplication::translate("FormEditorOptionsPage", "Preview Zoom")); + QFormLayout *lt = new QFormLayout; + lt->addRow(QCoreApplication::translate("FormEditorOptionsPage", "Default Zoom"), m_zoomCombo); + setLayout(lt); +} + +void ZoomSettingsWidget::fromSettings(const QDesignerSharedSettings &s) +{ + setChecked(s.zoomEnabled()); + const int idx = m_zoomCombo->findData(QVariant(s.zoom())); + m_zoomCombo->setCurrentIndex(qMax(0, idx)); +} + +void ZoomSettingsWidget::toSettings(QDesignerSharedSettings &s) const +{ + s.setZoomEnabled(isChecked()); + const int zoom = m_zoomCombo->itemData(m_zoomCombo->currentIndex()).toInt(); + s.setZoom(zoom); +} + + + +// FormEditorOptionsPage: +FormEditorOptionsPage::FormEditorOptionsPage(QDesignerFormEditorInterface *core) + : m_core(core) +{ +} + +QString FormEditorOptionsPage::name() const +{ + //: Tab in preferences dialog + return QCoreApplication::translate("FormEditorOptionsPage", "Forms"); +} + +QWidget *FormEditorOptionsPage::createPage(QWidget *parent) +{ + QWidget *optionsWidget = new QWidget(parent); + + const QDesignerSharedSettings settings(m_core); + m_previewConf = new PreviewConfigurationWidget(m_core); + m_zoomSettingsWidget = new ZoomSettingsWidget; + m_zoomSettingsWidget->fromSettings(settings); + + m_defaultGridConf = new GridPanel(); + m_defaultGridConf->setTitle(QCoreApplication::translate("FormEditorOptionsPage", "Default Grid")); + m_defaultGridConf->setGrid(settings.defaultGrid()); + + QVBoxLayout *optionsVLayout = new QVBoxLayout(); + optionsVLayout->addWidget(m_defaultGridConf); + optionsVLayout->addWidget(m_previewConf); + optionsVLayout->addWidget(m_zoomSettingsWidget); + optionsVLayout->addStretch(1); + + // Outer layout to give it horizontal stretch + QHBoxLayout *optionsHLayout = new QHBoxLayout(); + optionsHLayout->addLayout(optionsVLayout); + optionsHLayout->addStretch(1); + optionsWidget->setLayout(optionsHLayout); + + return optionsWidget; +} + +void FormEditorOptionsPage::apply() +{ + QDesignerSharedSettings settings(m_core); + if (m_defaultGridConf) { + const Grid defaultGrid = m_defaultGridConf->grid(); + settings.setDefaultGrid(defaultGrid); + + FormWindowBase::setDefaultDesignerGrid(defaultGrid); + // Update grid settings in all existing form windows + QDesignerFormWindowManagerInterface *fwm = m_core->formWindowManager(); + if (const int numWindows = fwm->formWindowCount()) { + for (int i = 0; i < numWindows; i++) + if (qdesigner_internal::FormWindowBase *fwb + = qobject_cast( fwm->formWindow(i))) + if (!fwb->hasFormGrid()) + fwb->setDesignerGrid(defaultGrid); + } + } + if (m_previewConf) { + m_previewConf->saveState(); + } + + if (m_zoomSettingsWidget) + m_zoomSettingsWidget->toSettings(settings); +} + +void FormEditorOptionsPage::finish() +{ +} + +} + +QT_END_NAMESPACE diff --git a/src/designer/components/formeditor/formeditor_optionspage.h b/src/designer/components/formeditor/formeditor_optionspage.h new file mode 100644 index 000000000..776b15449 --- /dev/null +++ b/src/designer/components/formeditor/formeditor_optionspage.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMEDITOR_OPTIONSPAGE_H +#define FORMEDITOR_OPTIONSPAGE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class PreviewConfigurationWidget; +class GridPanel; +class ZoomSettingsWidget; + +class FormEditorOptionsPage : public QDesignerOptionsPageInterface +{ +public: + explicit FormEditorOptionsPage(QDesignerFormEditorInterface *core); + + QString name() const; + QWidget *createPage(QWidget *parent); + virtual void apply(); + virtual void finish(); + +private: + QDesignerFormEditorInterface *m_core; + QPointer m_previewConf; + QPointer m_defaultGridConf; + QPointer m_zoomSettingsWidget; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMEDITOR_OPTIONSPAGE_H diff --git a/src/designer/components/formeditor/formwindow.cpp b/src/designer/components/formeditor/formwindow.cpp new file mode 100644 index 000000000..f941e7411 --- /dev/null +++ b/src/designer/components/formeditor/formwindow.cpp @@ -0,0 +1,2983 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindow.h" +#include "formeditor.h" +#include "formwindow_dnditem.h" +#include "formwindow_widgetstack.h" +#include "formwindowcursor.h" +#include "formwindowmanager.h" +#include "tool_widgeteditor.h" +#include "widgetselection.h" +#include "qtresourcemodel_p.h" +#include "widgetfactory_p.h" + +// shared +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QWidget*) + +QT_BEGIN_NAMESPACE + +namespace { +class BlockSelection +{ +public: + BlockSelection(qdesigner_internal::FormWindow *fw) + : m_formWindow(fw), + m_blocked(m_formWindow->blockSelectionChanged(true)) + { + } + + ~BlockSelection() + { + if (m_formWindow) + m_formWindow->blockSelectionChanged(m_blocked); + } + +private: + QPointer m_formWindow; + const bool m_blocked; +}; + +enum { debugFormWindow = 0 }; +} + +namespace qdesigner_internal { + +// ------------------------ FormWindow::Selection +// Maintains a pool of WidgetSelections to be used for selected widgets. + +class FormWindow::Selection +{ +public: + Selection(); + ~Selection(); + + // Clear + void clear(); + + // Also clear out the pool. Call if reparenting of the main container occurs. + void clearSelectionPool(); + + void repaintSelection(QWidget *w); + void repaintSelection(); + + bool isWidgetSelected(QWidget *w) const; + QWidgetList selectedWidgets() const; + + WidgetSelection *addWidget(FormWindow* fw, QWidget *w); + // remove widget, return new current widget or 0 + QWidget* removeWidget(QWidget *w); + + void raiseList(const QWidgetList& l); + void raiseWidget(QWidget *w); + + void updateGeometry(QWidget *w); + + void hide(QWidget *w); + void show(QWidget *w); + +private: + + typedef QList SelectionPool; + SelectionPool m_selectionPool; + + typedef QHash SelectionHash; + SelectionHash m_usedSelections; +}; + +FormWindow::Selection::Selection() +{ +} + +FormWindow::Selection::~Selection() +{ + clearSelectionPool(); +} + +void FormWindow::Selection::clear() +{ + if (!m_usedSelections.empty()) { + const SelectionHash::iterator mend = m_usedSelections.end(); + for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) { + it.value()->setWidget(0); + } + m_usedSelections.clear(); + } +} + +void FormWindow::Selection::clearSelectionPool() +{ + clear(); + qDeleteAll(m_selectionPool); + m_selectionPool.clear(); +} + +WidgetSelection *FormWindow::Selection::addWidget(FormWindow* fw, QWidget *w) +{ + WidgetSelection *rc = m_usedSelections.value(w); + if (rc != 0) { + rc->show(); + rc->updateActive(); + return rc; + } + // find a free one in the pool + const SelectionPool::iterator pend = m_selectionPool.end(); + for (SelectionPool::iterator it = m_selectionPool.begin(); it != pend; ++it) { + if (! (*it)->isUsed()) { + rc = *it; + break; + } + } + + if (rc == 0) { + rc = new WidgetSelection(fw); + m_selectionPool.push_back(rc); + } + + m_usedSelections.insert(w, rc); + rc->setWidget(w); + return rc; +} + +QWidget* FormWindow::Selection::removeWidget(QWidget *w) +{ + WidgetSelection *s = m_usedSelections.value(w); + if (!s) + return w; + + s->setWidget(0); + m_usedSelections.remove(w); + + if (m_usedSelections.isEmpty()) + return 0; + + return (*m_usedSelections.begin())->widget(); +} + +void FormWindow::Selection::repaintSelection(QWidget *w) +{ + if (WidgetSelection *s = m_usedSelections.value(w)) + s->update(); +} + +void FormWindow::Selection::repaintSelection() +{ + const SelectionHash::iterator mend = m_usedSelections.end(); + for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) { + it.value()->update(); + } +} + +bool FormWindow::Selection::isWidgetSelected(QWidget *w) const{ + return m_usedSelections.contains(w); +} + +QWidgetList FormWindow::Selection::selectedWidgets() const +{ + return m_usedSelections.keys(); +} + +void FormWindow::Selection::raiseList(const QWidgetList& l) +{ + const SelectionHash::iterator mend = m_usedSelections.end(); + for (SelectionHash::iterator it = m_usedSelections.begin(); it != mend; ++it) { + WidgetSelection *w = it.value(); + if (l.contains(w->widget())) + w->show(); + } +} + +void FormWindow::Selection::raiseWidget(QWidget *w) +{ + if (WidgetSelection *s = m_usedSelections.value(w)) + s->show(); +} + +void FormWindow::Selection::updateGeometry(QWidget *w) +{ + if (WidgetSelection *s = m_usedSelections.value(w)) { + s->updateGeometry(); + } +} + +void FormWindow::Selection::hide(QWidget *w) +{ + if (WidgetSelection *s = m_usedSelections.value(w)) + s->hide(); +} + +void FormWindow::Selection::show(QWidget *w) +{ + if (WidgetSelection *s = m_usedSelections.value(w)) + s->show(); +} + +// ------------------------ FormWindow +FormWindow::FormWindow(FormEditor *core, QWidget *parent, Qt::WindowFlags flags) : + FormWindowBase(core, parent, flags), + m_mouseState(NoMouseState), + m_core(core), + m_selection(new Selection), + m_widgetStack(new FormWindowWidgetStack(this)), + m_contextMenuPosition(-1, -1) +{ + // Apply settings to formcontainer + deviceProfile().apply(core, m_widgetStack->formContainer(), qdesigner_internal::DeviceProfile::ApplyFormParent); + + setLayout(m_widgetStack->layout()); + init(); + + m_cursor = new FormWindowCursor(this, this); + + core->formWindowManager()->addFormWindow(this); + + setDirty(false); + setAcceptDrops(true); +} + +FormWindow::~FormWindow() +{ + Q_ASSERT(core() != 0); + Q_ASSERT(core()->metaDataBase() != 0); + Q_ASSERT(core()->formWindowManager() != 0); + + core()->formWindowManager()->removeFormWindow(this); + core()->metaDataBase()->remove(this); + + QWidgetList l = widgets(); + foreach (QWidget *w, l) + core()->metaDataBase()->remove(w); + + m_widgetStack = 0; + m_rubberBand = 0; + if (resourceSet()) + core()->resourceModel()->removeResourceSet(resourceSet()); + delete m_selection; +} + +QDesignerFormEditorInterface *FormWindow::core() const +{ + return m_core; +} + +QDesignerFormWindowCursorInterface *FormWindow::cursor() const +{ + return m_cursor; +} + +void FormWindow::updateWidgets() +{ + if (!m_mainContainer) + return; +} + +int FormWindow::widgetDepth(const QWidget *w) +{ + int d = -1; + while (w && !w->isWindow()) { + d++; + w = w->parentWidget(); + } + + return d; +} + +bool FormWindow::isChildOf(const QWidget *c, const QWidget *p) +{ + while (c) { + if (c == p) + return true; + c = c->parentWidget(); + } + return false; +} + +void FormWindow::setCursorToAll(const QCursor &c, QWidget *start) +{ +#ifndef QT_NO_CURSOR + start->setCursor(c); + const QWidgetList widgets = start->findChildren(); + foreach (QWidget *widget, widgets) { + if (!qobject_cast(widget)) { + widget->setCursor(c); + } + } +#endif +} + +void FormWindow::init() +{ + if (FormWindowManager *manager = qobject_cast (core()->formWindowManager())) { + manager->undoGroup()->addStack(m_undoStack.qundoStack()); + } + + m_blockSelectionChanged = false; + + m_defaultMargin = INT_MIN; + m_defaultSpacing = INT_MIN; + + connect(m_widgetStack, SIGNAL(currentToolChanged(int)), this, SIGNAL(toolChanged(int))); + + m_selectionChangedTimer = new QTimer(this); + m_selectionChangedTimer->setSingleShot(true); + connect(m_selectionChangedTimer, SIGNAL(timeout()), this, SLOT(selectionChangedTimerDone())); + + m_checkSelectionTimer = new QTimer(this); + m_checkSelectionTimer->setSingleShot(true); + connect(m_checkSelectionTimer, SIGNAL(timeout()), this, SLOT(checkSelectionNow())); + + m_geometryChangedTimer = new QTimer(this); + m_geometryChangedTimer->setSingleShot(true); + connect(m_geometryChangedTimer, SIGNAL(timeout()), this, SIGNAL(geometryChanged())); + + m_rubberBand = 0; + + setFocusPolicy(Qt::StrongFocus); + + m_mainContainer = 0; + m_currentWidget = 0; + + connect(&m_undoStack, SIGNAL(changed()), this, SIGNAL(changed())); + connect(&m_undoStack, SIGNAL(changed()), this, SLOT(checkSelection())); + + core()->metaDataBase()->add(this); + + initializeCoreTools(); + + QAction *a = new QAction(this); + a->setText(tr("Edit contents")); + a->setShortcut(tr("F2")); + connect(a, SIGNAL(triggered()), this, SLOT(editContents())); + addAction(a); +} + +QWidget *FormWindow::mainContainer() const +{ + return m_mainContainer; +} + + +void FormWindow::clearMainContainer() +{ + if (m_mainContainer) { + setCurrentTool(0); + m_widgetStack->setMainContainer(0); + core()->metaDataBase()->remove(m_mainContainer); + unmanageWidget(m_mainContainer); + delete m_mainContainer; + m_mainContainer = 0; + } +} + +void FormWindow::setMainContainer(QWidget *w) +{ + if (w == m_mainContainer) { + // nothing to do + return; + } + + clearMainContainer(); + + m_mainContainer = w; + const QSize sz = m_mainContainer->size(); + + m_widgetStack->setMainContainer(m_mainContainer); + m_widgetStack->setCurrentTool(m_widgetEditor); + + setCurrentWidget(m_mainContainer); + manageWidget(m_mainContainer); + + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), m_mainContainer)) { + sheet->setVisible(sheet->indexOf(QLatin1String("windowTitle")), true); + sheet->setVisible(sheet->indexOf(QLatin1String("windowIcon")), true); + sheet->setVisible(sheet->indexOf(QLatin1String("windowModality")), true); + sheet->setVisible(sheet->indexOf(QLatin1String("windowOpacity")), true); + sheet->setVisible(sheet->indexOf(QLatin1String("windowFilePath")), true); + // ### generalize + } + + m_mainContainer->setFocusPolicy(Qt::StrongFocus); + m_mainContainer->resize(sz); + + emit mainContainerChanged(m_mainContainer); +} + +QWidget *FormWindow::findTargetContainer(QWidget *widget) const +{ + Q_ASSERT(widget); + + while (QWidget *parentWidget = widget->parentWidget()) { + if (LayoutInfo::layoutType(m_core, parentWidget) == LayoutInfo::NoLayout && isManaged(widget)) + return widget; + + widget = parentWidget; + } + + return mainContainer(); +} + +static inline void clearObjectInspectorSelection(const QDesignerFormEditorInterface *core) +{ + if (QDesignerObjectInspector *oi = qobject_cast(core->objectInspector())) + oi->clearSelection(); +} + +// Find a parent of a desired selection state +static QWidget *findSelectedParent(QDesignerFormWindowInterface *fw, const QWidget *w, bool selected) +{ + const QDesignerFormWindowCursorInterface *cursor = fw->cursor(); + QWidget *mainContainer = fw->mainContainer(); + for (QWidget *p = w->parentWidget(); p && p != mainContainer; p = p->parentWidget()) + if (fw->isManaged(p)) + if (cursor->isWidgetSelected(p) == selected) + return p; + return 0; +} + +// Mouse modifiers. + +enum MouseFlags { ToggleSelectionModifier = 0x1, CycleParentModifier=0x2, CopyDragModifier=0x4 }; + +static inline unsigned mouseFlags(Qt::KeyboardModifiers mod) +{ + switch (mod) { + case Qt::ShiftModifier: + return CycleParentModifier; + break; +#ifdef Q_WS_MAC + case Qt::AltModifier: // "Alt" or "option" key on Mac means copy + return CopyDragModifier; +#endif + case Qt::ControlModifier: + return CopyDragModifier|ToggleSelectionModifier; + break; + default: + break; + } + return 0; +} + +// Handle the click selection: Do toggling/cycling +// of parents according to the modifiers. +void FormWindow::handleClickSelection(QWidget *managedWidget, unsigned mouseMode) +{ + const bool sameWidget = managedWidget == m_lastClickedWidget; + m_lastClickedWidget = managedWidget; + + const bool selected = isWidgetSelected(managedWidget); + if (debugFormWindow) + qDebug() << "handleClickSelection" << managedWidget << " same=" << sameWidget << " mouse= " << mouseMode << " selected=" << selected; + + // // toggle selection state of widget + if (mouseMode & ToggleSelectionModifier) { + selectWidget(managedWidget, !selected); + return; + } + + QWidget *selectionCandidate = 0; + // Hierarchy cycling: If the same widget clicked again: Attempt to cycle + // trough the hierarchy. Find the next currently selected parent + if (sameWidget && (mouseMode & CycleParentModifier)) + if (QWidget *currentlySelectedParent = selected ? managedWidget : findSelectedParent(this, managedWidget, true)) + selectionCandidate = findSelectedParent(this, currentlySelectedParent, false); + // Not the same widget, list wrapped over or there was no unselected parent + if (!selectionCandidate && !selected) + selectionCandidate = managedWidget; + + if (selectionCandidate) + selectSingleWidget(selectionCandidate); +} + +void FormWindow::selectSingleWidget(QWidget *w) +{ + clearSelection(false); + selectWidget(w, true); + raiseChildSelections(w); +} + +bool FormWindow::handleMousePressEvent(QWidget * widget, QWidget *managedWidget, QMouseEvent *e) +{ + m_mouseState = NoMouseState; + m_startPos = QPoint(); + e->accept(); + + BlockSelection blocker(this); + + if (core()->formWindowManager()->activeFormWindow() != this) + core()->formWindowManager()->setActiveFormWindow(this); + + const Qt::MouseButtons buttons = e->buttons(); + if (buttons != Qt::LeftButton && buttons != Qt::MidButton) + return true; + + m_startPos = mapFromGlobal(e->globalPos()); + + if (debugFormWindow) + qDebug() << "handleMousePressEvent:" << widget << ',' << managedWidget; + + if (buttons == Qt::MidButton || isMainContainer(managedWidget) == true) { // press was on the formwindow + clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector. + clearSelection(false); + + m_mouseState = MouseDrawRubber; + m_currRect = QRect(); + startRectDraw(mapFromGlobal(e->globalPos()), this, Rubber); + return true; + } + if (buttons != Qt::LeftButton) + return true; + + const unsigned mouseMode = mouseFlags(e->modifiers()); + + /* Normally, we want to be able to click /select-on-press to drag away + * the widget in the next step. However, in the case of a widget which + * itself or whose parent is selected, we defer the selection to the + * release event. + * This is to prevent children from being dragged away from layouts + * when their layouts are selected and one wants to move the layout. + * Note that toggle selection is only deferred if the widget is already + * selected, so, it is still possible to just Ctrl+Click and CopyDrag. */ + const bool deferSelection = isWidgetSelected(managedWidget) || findSelectedParent(this, managedWidget, true); + if (deferSelection) { + m_mouseState = MouseDeferredSelection; + } else { + // Cycle the parent unless we explicitly want toggle + const unsigned effectiveMouseMode = (mouseMode & ToggleSelectionModifier) ? mouseMode : static_cast(CycleParentModifier); + handleClickSelection(managedWidget, effectiveMouseMode); + } + return true; +} + +// We can drag widget in managed layouts except splitter. +static bool canDragWidgetInLayout(const QDesignerFormEditorInterface *core, QWidget *w) +{ + bool managed; + const LayoutInfo::Type type = LayoutInfo::laidoutWidgetType(core ,w, &managed); + if (!managed) + return false; + switch (type) { + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + return false; + default: + break; + } + return true; +} + +bool FormWindow::handleMouseMoveEvent(QWidget *, QWidget *, QMouseEvent *e) +{ + e->accept(); + if (m_startPos.isNull()) + return true; + + const QPoint pos = mapFromGlobal(e->globalPos()); + + switch (m_mouseState) { + case MouseDrawRubber: // Rubber band with left/middle mouse + continueRectDraw(pos, this, Rubber); + return true; + case MouseMoveDrag: // Spurious move event after drag started? + return true; + default: + break; + } + + if (e->buttons() != Qt::LeftButton) + return true; + + const bool canStartDrag = (m_startPos - pos).manhattanLength() > QApplication::startDragDistance(); + + if (canStartDrag == false) { + // nothing to do + return true; + } + + m_mouseState = MouseMoveDrag; + const bool blocked = blockSelectionChanged(true); + + QWidgetList sel = selectedWidgets(); + simplifySelection(&sel); + + QSet widget_set; + + foreach (QWidget *child, sel) { // Move parent layout or container? + QWidget *current = child; + + bool done = false; + while (!isMainContainer(current) && !done) { + if (!isManaged(current)) { + current = current->parentWidget(); + continue; + } else if (LayoutInfo::isWidgetLaidout(core(), current)) { + // Go up to parent of layout if shift pressed, else do that only for splitters + if (!canDragWidgetInLayout(core(), current)) { + current = current->parentWidget(); + continue; + } + } + done = true; + } + + if (current == mainContainer()) + continue; + + widget_set.insert(current); + } + + sel = widget_set.toList(); + QDesignerFormWindowCursorInterface *c = cursor(); + QWidget *current = c->current(); + if (sel.contains(current)) { + sel.removeAll(current); + sel.prepend(current); + } + + QList item_list; + const QPoint globalPos = mapToGlobal(m_startPos); + const QDesignerDnDItemInterface::DropType dropType = (mouseFlags(e->modifiers()) & CopyDragModifier) ? + QDesignerDnDItemInterface::CopyDrop : QDesignerDnDItemInterface::MoveDrop; + foreach (QWidget *widget, sel) { + item_list.append(new FormWindowDnDItem(dropType, this, widget, globalPos)); + if (dropType == QDesignerDnDItemInterface::MoveDrop) { + m_selection->hide(widget); + widget->hide(); + } + } + + blockSelectionChanged(blocked); + + if (!sel.empty()) // reshow selection? + if (QDesignerMimeData::execDrag(item_list, core()->topLevel()) == Qt::IgnoreAction && dropType == QDesignerDnDItemInterface::MoveDrop) + foreach (QWidget *widget, sel) + m_selection->show(widget); + + m_startPos = QPoint(); + + return true; +} + +bool FormWindow::handleMouseReleaseEvent(QWidget *w, QWidget *mw, QMouseEvent *e) +{ + const MouseState oldState = m_mouseState; + m_mouseState = NoMouseState; + + if (debugFormWindow) + qDebug() << "handleMouseeleaseEvent:" << w << ',' << mw << "state=" << oldState; + + if (oldState == MouseDoubleClicked) + return true; + + e->accept(); + + switch (oldState) { + case MouseDrawRubber: { // we were drawing a rubber selection + endRectDraw(); // get rid of the rectangle + const bool blocked = blockSelectionChanged(true); + selectWidgets(); // select widgets which intersect the rect + blockSelectionChanged(blocked); + } + break; + // Deferred select: Select the child here unless the parent was moved. + case MouseDeferredSelection: + handleClickSelection(mw, mouseFlags(e->modifiers())); + break; + default: + break; + } + + m_startPos = QPoint(); + + /* Inform about selection changes (left/mid or context menu). Also triggers + * in the case of an empty rubber drag that cleared the selection in + * MousePressEvent. */ + switch (e->button()) { + case Qt::LeftButton: + case Qt::MidButton: + case Qt::RightButton: + emitSelectionChanged(); + break; + default: + break; + } + + return true; +} + +void FormWindow::checkPreviewGeometry(QRect &r) +{ + if (!rect().contains(r)) { + if (r.left() < rect().left()) + r.moveTopLeft(QPoint(0, r.top())); + if (r.right() > rect().right()) + r.moveBottomRight(QPoint(rect().right(), r.bottom())); + if (r.top() < rect().top()) + r.moveTopLeft(QPoint(r.left(), rect().top())); + if (r.bottom() > rect().bottom()) + r.moveBottomRight(QPoint(r.right(), rect().bottom())); + } +} + +void FormWindow::startRectDraw(const QPoint &pos, QWidget *, RectType t) +{ + m_rectAnchor = (t == Insert) ? designerGrid().snapPoint(pos) : pos; + + m_currRect = QRect(m_rectAnchor, QSize(0, 0)); + if (!m_rubberBand) + m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this); + m_rubberBand->setGeometry(m_currRect); + m_rubberBand->show(); +} + +void FormWindow::continueRectDraw(const QPoint &pos, QWidget *, RectType t) +{ + const QPoint p2 = (t == Insert) ? designerGrid().snapPoint(pos) : pos; + + QRect r(m_rectAnchor, p2); + r = r.normalized(); + + if (m_currRect == r) + return; + + if (r.width() > 1 || r.height() > 1) { + m_currRect = r; + if (m_rubberBand) + m_rubberBand->setGeometry(m_currRect); + } +} + +void FormWindow::endRectDraw() +{ + if (m_rubberBand) { + delete m_rubberBand; + m_rubberBand = 0; + } +} + +QWidget *FormWindow::currentWidget() const +{ + return m_currentWidget; +} + +bool FormWindow::setCurrentWidget(QWidget *currentWidget) +{ + if (debugFormWindow) + qDebug() << "setCurrentWidget:" << m_currentWidget << " --> " << currentWidget; + if (currentWidget == m_currentWidget) + return false; + // repaint the old widget unless it is the main window + if (m_currentWidget && m_currentWidget != mainContainer()) { + m_selection->repaintSelection(m_currentWidget); + } + // set new and repaint + m_currentWidget = currentWidget; + if (m_currentWidget && m_currentWidget != mainContainer()) { + m_selection->repaintSelection(m_currentWidget); + } + return true; +} + +void FormWindow::selectWidget(QWidget* w, bool select) +{ + if (trySelectWidget(w, select)) + emitSelectionChanged(); +} + +// Selects a widget and determines the new current one. Returns true if a change occurs. +bool FormWindow::trySelectWidget(QWidget *w, bool select) +{ + if (debugFormWindow) + qDebug() << "trySelectWidget:" << w << select; + if (!isManaged(w) && !isCentralWidget(w)) + return false; + + if (!select && !isWidgetSelected(w)) + return false; + + if (!mainContainer()) + return false; + + if (isMainContainer(w) || isCentralWidget(w)) { + setCurrentWidget(mainContainer()); + return true; + } + + if (select) { + setCurrentWidget(w); + m_selection->addWidget(this, w); + } else { + QWidget *newCurrent = m_selection->removeWidget(w); + if (!newCurrent) + newCurrent = mainContainer(); + setCurrentWidget(newCurrent); + } + return true; +} + +void FormWindow::clearSelection(bool changePropertyDisplay) +{ + if (debugFormWindow) + qDebug() << "clearSelection(" << changePropertyDisplay << ')'; + // At all events, we need a current widget. + m_selection->clear(); + setCurrentWidget(mainContainer()); + + if (changePropertyDisplay) + emitSelectionChanged(); +} + +void FormWindow::emitSelectionChanged() +{ + if (m_blockSelectionChanged == true) { + // nothing to do + return; + } + + m_selectionChangedTimer->start(0); +} + +void FormWindow::selectionChangedTimerDone() +{ + emit selectionChanged(); +} + +bool FormWindow::isWidgetSelected(QWidget *w) const +{ + return m_selection->isWidgetSelected(w); +} + +bool FormWindow::isMainContainer(const QWidget *w) const +{ + return w && (w == this || w == mainContainer()); +} + +void FormWindow::updateChildSelections(QWidget *w) +{ + const QWidgetList l = w->findChildren(); + if (!l.empty()) { + const QWidgetList::const_iterator lcend = l.constEnd(); + for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it) { + QWidget *w = *it; + if (isManaged(w)) + updateSelection(w); + } + } +} + +void FormWindow::repaintSelection() +{ + m_selection->repaintSelection(); +} + +void FormWindow::raiseSelection(QWidget *w) +{ + m_selection->raiseWidget(w); +} + +void FormWindow::updateSelection(QWidget *w) +{ + if (!w->isVisibleTo(this)) { + selectWidget(w, false); + } else { + m_selection->updateGeometry(w); + } +} + +QWidget *FormWindow::designerWidget(QWidget *w) const +{ + while ((w && !isMainContainer(w) && !isManaged(w)) || isCentralWidget(w)) + w = w->parentWidget(); + + return w; +} + +bool FormWindow::isCentralWidget(QWidget *w) const +{ + if (QMainWindow *mainWindow = qobject_cast(mainContainer())) + return w == mainWindow->centralWidget(); + + return false; +} + +void FormWindow::ensureUniqueObjectName(QObject *object) +{ + QString name = object->objectName(); + if (name.isEmpty()) { + QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + if (QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(object))) + name = qdesigner_internal::qtify(item->name()); + } + unify(object, name, true); + object->setObjectName(name); +} + +template +static inline void insertNames(const QDesignerMetaDataBaseInterface *metaDataBase, + Iterator it, const Iterator &end, + QObject *excludedObject, QSet &nameSet) +{ + for ( ; it != end; ++it) + if (excludedObject != *it && metaDataBase->item(*it)) + nameSet.insert((*it)->objectName()); +} + +static QSet languageKeywords() +{ + static QSet keywords; + if (keywords.isEmpty()) { + // C++ keywords + keywords.insert(QLatin1String("asm")); + keywords.insert(QLatin1String("auto")); + keywords.insert(QLatin1String("bool")); + keywords.insert(QLatin1String("break")); + keywords.insert(QLatin1String("case")); + keywords.insert(QLatin1String("catch")); + keywords.insert(QLatin1String("char")); + keywords.insert(QLatin1String("class")); + keywords.insert(QLatin1String("const")); + keywords.insert(QLatin1String("const_cast")); + keywords.insert(QLatin1String("continue")); + keywords.insert(QLatin1String("default")); + keywords.insert(QLatin1String("delete")); + keywords.insert(QLatin1String("do")); + keywords.insert(QLatin1String("double")); + keywords.insert(QLatin1String("dynamic_cast")); + keywords.insert(QLatin1String("else")); + keywords.insert(QLatin1String("enum")); + keywords.insert(QLatin1String("explicit")); + keywords.insert(QLatin1String("export")); + keywords.insert(QLatin1String("extern")); + keywords.insert(QLatin1String("false")); + keywords.insert(QLatin1String("float")); + keywords.insert(QLatin1String("for")); + keywords.insert(QLatin1String("friend")); + keywords.insert(QLatin1String("goto")); + keywords.insert(QLatin1String("if")); + keywords.insert(QLatin1String("inline")); + keywords.insert(QLatin1String("int")); + keywords.insert(QLatin1String("long")); + keywords.insert(QLatin1String("mutable")); + keywords.insert(QLatin1String("namespace")); + keywords.insert(QLatin1String("new")); + keywords.insert(QLatin1String("NULL")); + keywords.insert(QLatin1String("operator")); + keywords.insert(QLatin1String("private")); + keywords.insert(QLatin1String("protected")); + keywords.insert(QLatin1String("public")); + keywords.insert(QLatin1String("register")); + keywords.insert(QLatin1String("reinterpret_cast")); + keywords.insert(QLatin1String("return")); + keywords.insert(QLatin1String("short")); + keywords.insert(QLatin1String("signed")); + keywords.insert(QLatin1String("sizeof")); + keywords.insert(QLatin1String("static")); + keywords.insert(QLatin1String("static_cast")); + keywords.insert(QLatin1String("struct")); + keywords.insert(QLatin1String("switch")); + keywords.insert(QLatin1String("template")); + keywords.insert(QLatin1String("this")); + keywords.insert(QLatin1String("throw")); + keywords.insert(QLatin1String("true")); + keywords.insert(QLatin1String("try")); + keywords.insert(QLatin1String("typedef")); + keywords.insert(QLatin1String("typeid")); + keywords.insert(QLatin1String("typename")); + keywords.insert(QLatin1String("union")); + keywords.insert(QLatin1String("unsigned")); + keywords.insert(QLatin1String("using")); + keywords.insert(QLatin1String("virtual")); + keywords.insert(QLatin1String("void")); + keywords.insert(QLatin1String("volatile")); + keywords.insert(QLatin1String("wchar_t")); + keywords.insert(QLatin1String("while")); + + // java keywords + keywords.insert(QLatin1String("abstract")); + keywords.insert(QLatin1String("assert")); + 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("continue")); + keywords.insert(QLatin1String("default")); + keywords.insert(QLatin1String("do")); + keywords.insert(QLatin1String("double")); + keywords.insert(QLatin1String("else")); + keywords.insert(QLatin1String("enum")); + keywords.insert(QLatin1String("extends")); + keywords.insert(QLatin1String("false")); + keywords.insert(QLatin1String("final")); + keywords.insert(QLatin1String("finality")); + keywords.insert(QLatin1String("float")); + keywords.insert(QLatin1String("for")); + keywords.insert(QLatin1String("goto")); + keywords.insert(QLatin1String("if")); + keywords.insert(QLatin1String("implements")); + keywords.insert(QLatin1String("import")); + 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("null")); + 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("strictfp")); + 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("void")); + keywords.insert(QLatin1String("volatile")); + keywords.insert(QLatin1String("while")); + } + return keywords; +} + +bool FormWindow::unify(QObject *w, QString &s, bool changeIt) +{ + typedef QSet StringSet; + + QWidget *main = mainContainer(); + if (!main) + return true; + + StringSet existingNames = languageKeywords(); + // build a set of existing names of other widget excluding self + if (!(w->isWidgetType() && isMainContainer(qobject_cast(w)))) + existingNames.insert(main->objectName()); + + const QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase(); + const QWidgetList widgetChildren = main->findChildren(); + if (!widgetChildren.empty()) + insertNames(metaDataBase, widgetChildren.constBegin(), widgetChildren.constEnd(), w, existingNames); + + const QList layoutChildren = main->findChildren(); + if (!layoutChildren.empty()) + insertNames(metaDataBase, layoutChildren.constBegin(), layoutChildren.constEnd(), w, existingNames); + + const QList actionChildren = main->findChildren(); + if (!actionChildren.empty()) + insertNames(metaDataBase, actionChildren.constBegin(), actionChildren.constEnd(), w, existingNames); + + const QList buttonGroupChildren = main->findChildren(); + if (!buttonGroupChildren.empty()) + insertNames(metaDataBase, buttonGroupChildren.constBegin(), buttonGroupChildren.constEnd(), w, existingNames); + + const StringSet::const_iterator enEnd = existingNames.constEnd(); + if (existingNames.constFind(s) == enEnd) + return true; + else + if (!changeIt) + return false; + + // split 'name_number' + qlonglong num = 0; + qlonglong factor = 1; + int idx = s.length()-1; + const ushort zeroUnicode = QLatin1Char('0').unicode(); + for ( ; idx > 0 && s.at(idx).isDigit(); --idx) { + num += (s.at(idx).unicode() - zeroUnicode) * factor; + factor *= 10; + } + // Position index past '_'. + const QChar underscore = QLatin1Char('_'); + if (idx >= 0 && s.at(idx) == underscore) { + idx++; + } else { + num = 1; + s += underscore; + idx = s.length(); + } + // try 'name_n', 'name_n+1' + for (num++ ; ;num++) { + s.truncate(idx); + s += QString::number(num); + if (existingNames.constFind(s) == enEnd) + break; + } + return false; +} +/* already_in_form is true when we are moving a widget from one parent to another inside the same + * form. All this means is that InsertWidgetCommand::undo() must not unmanage it. */ + +void FormWindow::insertWidget(QWidget *w, const QRect &rect, QWidget *container, bool already_in_form) +{ + clearSelection(false); + + beginCommand(tr("Insert widget '%1'").arg(WidgetFactory::classNameOf(m_core, w))); // ### use the WidgetDatabaseItem + + /* Reparenting into a QSplitter automatically adjusts child's geometry. We create the geometry + * command before we push the reparent command, so that the geometry command has the original + * geometry of the widget. */ + QRect r = rect; + Q_ASSERT(r.isValid()); + SetPropertyCommand *geom_cmd = new SetPropertyCommand(this); + geom_cmd->init(w, QLatin1String("geometry"), r); // ### use rc.size() + + if (w->parentWidget() != container) { + ReparentWidgetCommand *cmd = new ReparentWidgetCommand(this); + cmd->init(w, container); + m_undoStack.push(cmd); + } + + m_undoStack.push(geom_cmd); + + InsertWidgetCommand *cmd = new InsertWidgetCommand(this); + cmd->init(w, already_in_form); + m_undoStack.push(cmd); + + endCommand(); + + w->show(); +} + +QWidget *FormWindow::createWidget(DomUI *ui, const QRect &rc, QWidget *target) +{ + QWidget *container = findContainer(target, false); + if (!container) + return 0; + if (isMainContainer(container)) { + if (QMainWindow *mw = qobject_cast(container)) { + Q_ASSERT(mw->centralWidget() != 0); + container = mw->centralWidget(); + } + } + QDesignerResource resource(this); + const FormBuilderClipboard clipboard = resource.paste(ui, container); + if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet + return 0; + QWidget *widget = clipboard.m_widgets.first(); + insertWidget(widget, rc, container); + return widget; +} + +#ifndef QT_NO_DEBUG +static bool isDescendant(const QWidget *parent, const QWidget *child) +{ + for (; child != 0; child = child->parentWidget()) { + if (child == parent) + return true; + } + return false; +} +#endif + +void FormWindow::resizeWidget(QWidget *widget, const QRect &geometry) +{ + Q_ASSERT(isDescendant(this, widget)); + + QRect r = geometry; + SetPropertyCommand *cmd = new SetPropertyCommand(this); + cmd->init(widget, QLatin1String("geometry"), r); + cmd->setText(tr("Resize")); + m_undoStack.push(cmd); +} + +void FormWindow::raiseChildSelections(QWidget *w) +{ + const QWidgetList l = w->findChildren(); + if (l.isEmpty()) + return; + m_selection->raiseList(l); +} + +QWidget *FormWindow::containerAt(const QPoint &pos, QWidget *notParentOf) +{ + QWidget *container = 0; + int depth = -1; + const QWidgetList selected = selectedWidgets(); + if (rect().contains(mapFromGlobal(pos))) { + container = mainContainer(); + depth = widgetDepth(container); + } + + QListIterator it(m_widgets); + while (it.hasNext()) { + QWidget *wit = it.next(); + if (qobject_cast(wit) || qobject_cast(wit)) + continue; + if (!wit->isVisibleTo(this)) + continue; + if (selected.indexOf(wit) != -1) + continue; + if (!core()->widgetDataBase()->isContainer(wit) && + wit != mainContainer()) + continue; + + // the rectangles of all ancestors of the container must contain the insert position + QWidget *w = wit; + while (w && !w->isWindow()) { + if (!w->rect().contains((w->mapFromGlobal(pos)))) + break; + w = w->parentWidget(); + } + if (!(w == 0 || w->isWindow())) + continue; // we did not get through the full while loop + + int wd = widgetDepth(wit); + if (wd == depth && container) { + if (wit->parentWidget()->children().indexOf(wit) > + container->parentWidget()->children().indexOf(container)) + wd++; + } + if (wd > depth && !isChildOf(wit, notParentOf)) { + depth = wd; + container = wit; + } + } + return container; +} + +QWidgetList FormWindow::selectedWidgets() const +{ + return m_selection->selectedWidgets(); +} + +void FormWindow::selectWidgets() +{ + bool selectionChanged = false; + const QWidgetList l = mainContainer()->findChildren(); + QListIterator it(l); + const QRect selRect(mapToGlobal(m_currRect.topLeft()), m_currRect.size()); + while (it.hasNext()) { + QWidget *w = it.next(); + if (w->isVisibleTo(this) && isManaged(w)) { + const QPoint p = w->mapToGlobal(QPoint(0,0)); + const QRect r(p, w->size()); + if (r.intersects(selRect) && !r.contains(selRect) && trySelectWidget(w, true)) + selectionChanged = true; + } + } + + if (selectionChanged) + emitSelectionChanged(); +} + +bool FormWindow::handleKeyPressEvent(QWidget *widget, QWidget *, QKeyEvent *e) +{ + if (qobject_cast(widget) || qobject_cast(widget)) + return false; + + e->accept(); // we always accept! + + switch (e->key()) { + default: break; // we don't care about the other keys + + case Qt::Key_Delete: + case Qt::Key_Backspace: + if (e->modifiers() == Qt::NoModifier) + deleteWidgets(); + break; + + case Qt::Key_Tab: + if (e->modifiers() == Qt::NoModifier) + cursor()->movePosition(QDesignerFormWindowCursorInterface::Next); + break; + + case Qt::Key_Backtab: + if (e->modifiers() == Qt::NoModifier) + cursor()->movePosition(QDesignerFormWindowCursorInterface::Prev); + break; + + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Up: + case Qt::Key_Down: + handleArrowKeyEvent(e->key(), e->modifiers()); + break; + } + + return true; +} + +int FormWindow::getValue(const QRect &rect, int key, bool size) const +{ + if (size) { + if (key == Qt::Key_Left || key == Qt::Key_Right) + return rect.width(); + return rect.height(); + } + if (key == Qt::Key_Left || key == Qt::Key_Right) + return rect.x(); + return rect.y(); +} + +int FormWindow::calcValue(int val, bool forward, bool snap, int snapOffset) const +{ + if (snap) { + const int rest = val % snapOffset; + if (rest) { + const int offset = forward ? snapOffset : 0; + const int newOffset = rest < 0 ? offset - snapOffset : offset; + return val + newOffset - rest; + } + return (forward ? val + snapOffset : val - snapOffset); + } + return (forward ? val + 1 : val - 1); +} + +// ArrowKeyOperation: Stores a keyboard move or resize (Shift pressed) +// operation. +struct ArrowKeyOperation { + ArrowKeyOperation() : resize(false), distance(0), arrowKey(Qt::Key_Left) {} + + QRect apply(const QRect &in) const; + + bool resize; // Resize: Shift-Key->drag bottom/right corner, else just move + int distance; + int arrowKey; +}; + +} // namespace + +QT_END_NAMESPACE +Q_DECLARE_METATYPE(qdesigner_internal::ArrowKeyOperation) +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QRect ArrowKeyOperation::apply(const QRect &rect) const +{ + QRect r = rect; + if (resize) { + if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right) + r.setWidth(r.width() + distance); + else + r.setHeight(r.height() + distance); + } else { + if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right) + r.moveLeft(r.x() + distance); + else + r.moveTop(r.y() + distance); + } + return r; +} + +QDebug operator<<(QDebug in, const ArrowKeyOperation &op) +{ + in.nospace() << "Resize=" << op.resize << " dist=" << op.distance << " Key=" << op.arrowKey << ' '; + return in; +} + +// ArrowKeyPropertyHelper: Applies a struct ArrowKeyOperation +// (stored as new value) to a list of widgets using to calculate the +// changed geometry of the widget in setValue(). Thus, the 'newValue' +// of the property command is the relative move distance, which is the same +// for all widgets (although resulting in different geometries for the widgets). +// The command merging can then work as it would when applying the same text +// to all QLabels. + +class ArrowKeyPropertyHelper : public PropertyHelper { +public: + ArrowKeyPropertyHelper(QObject* o, SpecialProperty sp, + QDesignerPropertySheetExtension *s, int i) : + PropertyHelper(o, sp, s, i) {} + + virtual Value setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask); +}; + +PropertyHelper::Value ArrowKeyPropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask) +{ + // Apply operation to obtain the new geometry value. + QWidget *w = qobject_cast(object()); + const ArrowKeyOperation operation = qvariant_cast(value); + const QRect newGeom = operation.apply(w->geometry()); + return PropertyHelper::setValue(fw, QVariant(newGeom), changed, subPropertyMask); +} + +// ArrowKeyPropertyCommand: Helper factory overwritten to create +// ArrowKeyPropertyHelper and a merge operation that merges values of +// the same direction. +class ArrowKeyPropertyCommand: public SetPropertyCommand { +public: + explicit ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw, + QUndoCommand *p = 0); + + void init(QWidgetList &l, const ArrowKeyOperation &op); + +protected: + virtual PropertyHelper *createPropertyHelper(QObject *o, SpecialProperty sp, + QDesignerPropertySheetExtension *s, int i) const + { return new ArrowKeyPropertyHelper(o, sp, s, i); } + virtual QVariant mergeValue(const QVariant &newValue); +}; + +ArrowKeyPropertyCommand::ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw, + QUndoCommand *p) : + SetPropertyCommand(fw, p) +{ + static const int mid = qRegisterMetaType(); + Q_UNUSED(mid) +} + +void ArrowKeyPropertyCommand::init(QWidgetList &l, const ArrowKeyOperation &op) +{ + QObjectList ol; + foreach(QWidget *w, l) + ol.push_back(w); + SetPropertyCommand::init(ol, QLatin1String("geometry"), QVariant::fromValue(op)); + + setText(op.resize ? FormWindow::tr("Key Resize") : FormWindow::tr("Key Move")); +} + +QVariant ArrowKeyPropertyCommand::mergeValue(const QVariant &newMergeValue) +{ + // Merge move operations of the same arrow key + if (!newMergeValue.canConvert()) + return QVariant(); + ArrowKeyOperation mergedOperation = qvariant_cast(newValue()); + const ArrowKeyOperation newMergeOperation = qvariant_cast(newMergeValue); + if (mergedOperation.resize != newMergeOperation.resize || mergedOperation.arrowKey != newMergeOperation.arrowKey) + return QVariant(); + mergedOperation.distance += newMergeOperation.distance; + return QVariant::fromValue(mergedOperation); +} + +void FormWindow::handleArrowKeyEvent(int key, Qt::KeyboardModifiers modifiers) +{ + const QDesignerFormWindowCursorInterface *c = cursor(); + if (!c->hasSelection()) + return; + + QWidgetList selection; + + // check if a laid out widget is selected + const int count = c->selectedWidgetCount(); + for (int index = 0; index < count; ++index) { + QWidget *w = c->selectedWidget(index); + if (!LayoutInfo::isWidgetLaidout(m_core, w)) + selection.append(w); + } + + if (selection.isEmpty()) + return; + + QWidget *current = c->current(); + if (!current || LayoutInfo::isWidgetLaidout(m_core, current)) { + current = selection.first(); + } + + const bool size = modifiers & Qt::ShiftModifier; + + const bool snap = !(modifiers & Qt::ControlModifier); + const bool forward = (key == Qt::Key_Right || key == Qt::Key_Down); + const int snapPoint = (key == Qt::Key_Left || key == Qt::Key_Right) ? grid().x() : grid().y(); + + const int oldValue = getValue(current->geometry(), key, size); + + const int newValue = calcValue(oldValue, forward, snap, snapPoint); + + ArrowKeyOperation operation; + operation.resize = modifiers & Qt::ShiftModifier; + operation.distance = newValue - oldValue; + operation.arrowKey = key; + + ArrowKeyPropertyCommand *cmd = new ArrowKeyPropertyCommand(this); + cmd->init(selection, operation); + m_undoStack.push(cmd); +} + +bool FormWindow::handleKeyReleaseEvent(QWidget *, QWidget *, QKeyEvent *e) +{ + e->accept(); + return true; +} + +void FormWindow::selectAll() +{ + bool selectionChanged = false; + foreach (QWidget *widget, m_widgets) { + if (widget->isVisibleTo(this) && trySelectWidget(widget, true)) + selectionChanged = true; + } + if (selectionChanged) + emitSelectionChanged(); +} + +void FormWindow::createLayout(int type, QWidget *container) +{ + if (container) { + layoutContainer(container, type); + } else { + LayoutCommand *cmd = new LayoutCommand(this); + cmd->init(mainContainer(), selectedWidgets(), static_cast(type)); + commandHistory()->push(cmd); + } +} + +void FormWindow::morphLayout(QWidget *container, int newType) +{ + MorphLayoutCommand *cmd = new MorphLayoutCommand(this); + if (cmd->init(container, newType)) { + commandHistory()->push(cmd); + } else { + qDebug() << "** WARNING Unable to morph layout."; + delete cmd; + } +} + +void FormWindow::deleteWidgets() +{ + QWidgetList selection = selectedWidgets(); + simplifySelection(&selection); + + deleteWidgetList(selection); +} + +QString FormWindow::fileName() const +{ + return m_fileName; +} + +void FormWindow::setFileName(const QString &fileName) +{ + if (m_fileName == fileName) + return; + + m_fileName = fileName; + emit fileNameChanged(fileName); +} + +QString FormWindow::contents() const +{ + QBuffer b; + if (!mainContainer() || !b.open(QIODevice::WriteOnly)) + return QString(); + + QDesignerResource resource(const_cast(this)); + resource.save(&b, mainContainer()); + + return QString::fromUtf8(b.buffer()); +} + +void FormWindow::copy() +{ + QBuffer b; + if (!b.open(QIODevice::WriteOnly)) + return; + + FormBuilderClipboard clipboard; + QDesignerResource resource(this); + resource.setSaveRelative(false); + clipboard.m_widgets = selectedWidgets(); + simplifySelection(&clipboard.m_widgets); + resource.copy(&b, clipboard); + + qApp->clipboard()->setText(QString::fromUtf8(b.buffer()), QClipboard::Clipboard); +} + +void FormWindow::cut() +{ + copy(); + deleteWidgets(); +} + +// for cases like QMainWindow (central widget is an inner container) or QStackedWidget (page is an inner container) +QWidget *FormWindow::innerContainer(QWidget *outerContainer) const +{ + if (m_core->widgetDataBase()->isContainer(outerContainer)) + if (const QDesignerContainerExtension *container = qt_extension(m_core->extensionManager(), outerContainer)) { + const int currentIndex = container->currentIndex(); + return currentIndex >= 0 ? + container->widget(currentIndex) : + static_cast(0); + } + return outerContainer; +} + +QWidget *FormWindow::containerForPaste() const +{ + QWidget *w = mainContainer(); + if (!w) + return 0; + do { + // Try to find a close parent, for example a non-laid-out + // QFrame/QGroupBox when a widget within it is selected. + QWidgetList selection = selectedWidgets(); + if (selection.empty()) + break; + simplifySelection(&selection); + + QWidget *containerOfW = findContainer(selection.first(), /* exclude layouts */ true); + if (!containerOfW || containerOfW == mainContainer()) + break; + // No layouts, must be container. No empty page-based containers. + containerOfW = innerContainer(containerOfW); + if (!containerOfW) + break; + if (LayoutInfo::layoutType(m_core, containerOfW) != LayoutInfo::NoLayout || !m_core->widgetDataBase()->isContainer(containerOfW)) + break; + w = containerOfW; + } while (false); + // First check for layout (note that it does not cover QMainWindow + // and the like as the central widget has the layout). + + w = innerContainer(w); + if (!w) + return 0; + if (LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout) + return 0; + // Go up via container extension (also includes step from QMainWindow to its central widget) + w = m_core->widgetFactory()->containerOfWidget(w); + if (w == 0 || LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout) + return 0; + + if (debugFormWindow) + qDebug() <<"containerForPaste() " << w; + return w; +} + +void FormWindow::paste() +{ + paste(PasteAll); +} + +// Construct DomUI from clipboard (paste) and determine number of widgets/actions. +static inline DomUI *domUIFromClipboard(int *widgetCount, int *actionCount) +{ + *widgetCount = *actionCount = 0; + const QString clipboardText = qApp->clipboard()->text(); + if (clipboardText.isEmpty() || clipboardText.indexOf(QLatin1Char('<')) == -1) + return 0; + + QXmlStreamReader reader(clipboardText); + DomUI *ui = 0; + const QString uiElement = QLatin1String("ui"); + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0 && !ui) { + ui = new DomUI(); + ui->read(reader); + break; + } else { + reader.raiseError(QCoreApplication::translate("FormWindow", "Unexpected element <%1>").arg(reader.name().toString())); + } + } + } + if (reader.hasError()) { + delete ui; + ui = 0; + designerWarning(QCoreApplication::translate("FormWindow", "Error while pasting clipboard contents at line %1, column %2: %3"). + arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString())); + return 0; + } + + if (const DomWidget *topLevel = ui->elementWidget()) { + *widgetCount = topLevel->elementWidget().size(); + *actionCount = topLevel->elementAction().size(); + } + if (*widgetCount == 0 && *actionCount == 0) { + delete ui; + return 0; + } + return ui; +} + +static inline QString pasteCommandDescription(int widgetCount, int actionCount) +{ + if (widgetCount == 0) + return FormWindow::tr("Paste %n action(s)", 0, actionCount); + if (actionCount == 0) + return FormWindow::tr("Paste %n widget(s)", 0, widgetCount); + return FormWindow::tr("Paste (%1 widgets, %2 actions)").arg(widgetCount).arg(actionCount); +} + +static void positionPastedWidgetsAtMousePosition(FormWindow *fw, const QPoint &contextMenuPosition, QWidget *parent, const QWidgetList &l) +{ + // Try to position pasted widgets at mouse position (current mouse position for Ctrl-V or position of context menu) + // if it fits. If it is completely outside, force it to 0,0 + // If it fails, the old coordinates relative to the previous parent will be used. + QPoint currentPos = contextMenuPosition.x() >=0 ? parent->mapFrom(fw, contextMenuPosition) : parent->mapFromGlobal(QCursor::pos()); + const Grid &grid = fw->designerGrid(); + QPoint cursorPos = grid.snapPoint(currentPos); + const QRect parentGeometry = QRect(QPoint(0, 0), parent->size()); + const bool outside = !parentGeometry.contains(cursorPos); + if (outside) + cursorPos = grid.snapPoint(QPoint(0, 0)); + // Determine area of pasted widgets + QRect pasteArea; + const QWidgetList::const_iterator lcend = l.constEnd(); + for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it) + pasteArea =pasteArea.isNull() ? (*it)->geometry() : pasteArea.united((*it)->geometry()); + + // Mouse on some child? (try to position bottomRight on a free spot to + // get the stacked-offset effect of Designer 4.3, that is, offset by grid if Ctrl-V is pressed continuously + do { + const QPoint bottomRight = cursorPos + QPoint(pasteArea.width(), pasteArea.height()) - QPoint(1, 1); + if (bottomRight.y() > parentGeometry.bottom() || parent->childAt(bottomRight) == 0) + break; + cursorPos += QPoint(grid.deltaX(), grid.deltaY()); + } while (true); + // Move. + const QPoint offset = cursorPos - pasteArea.topLeft(); + for (QWidgetList::const_iterator it = l.constBegin(); it != lcend; ++it) + (*it)->move((*it)->pos() + offset); +} + +void FormWindow::paste(PasteMode pasteMode) +{ + // Avoid QDesignerResource constructing widgets that are not used as + // QDesignerResource manages the widgets it creates (creating havoc if one remains unused) + DomUI *ui = 0; + do { + int widgetCount; + int actionCount; + ui = domUIFromClipboard(&widgetCount, &actionCount); + if (!ui) + break; + + // Check for actions + if (pasteMode == PasteActionsOnly) + if (widgetCount != 0 || actionCount == 0) + break; + + // Check for widgets: need a container + QWidget *pasteContainer = widgetCount ? containerForPaste() : 0; + if (widgetCount && pasteContainer == 0) { + + const QString message = tr("Cannot paste widgets. Designer could not find a container " + "without a layout to paste into."); + const QString infoMessage = tr("Break the layout of the " + "container you want to paste into, select this container " + "and then paste again."); + core()->dialogGui()->message(this, QDesignerDialogGuiInterface::FormEditorMessage, QMessageBox::Information, + tr("Paste error"), message, infoMessage, QMessageBox::Ok); + break; + } + + QDesignerResource resource(this); + // Note that the widget factory must be able to locate the + // form window (us) via parent, otherwise, it will not able to construct QLayoutWidgets + // (It will then default to widgets) among other issues. + const FormBuilderClipboard clipboard = resource.paste(ui, pasteContainer, this); + + clearSelection(false); + // Create command sequence + beginCommand(pasteCommandDescription(widgetCount, actionCount)); + + if (widgetCount) { + positionPastedWidgetsAtMousePosition(this, m_contextMenuPosition, pasteContainer, clipboard.m_widgets); + foreach (QWidget *w, clipboard.m_widgets) { + InsertWidgetCommand *cmd = new InsertWidgetCommand(this); + cmd->init(w); + m_undoStack.push(cmd); + selectWidget(w); + } + } + + if (actionCount) + foreach (QAction *a, clipboard.m_actions) { + ensureUniqueObjectName(a); + AddActionCommand *cmd = new AddActionCommand(this); + cmd->init(a); + m_undoStack.push(cmd); + } + endCommand(); + } while (false); + delete ui; +} + +// Draw a dotted frame around containers +bool FormWindow::frameNeeded(QWidget *w) const +{ + if (!core()->widgetDataBase()->isContainer(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + if (qobject_cast(w)) + return false; + return true; +} + +bool FormWindow::eventFilter(QObject *watched, QEvent *event) +{ + const bool ret = FormWindowBase::eventFilter(watched, event); + if (event->type() != QEvent::Paint) + return ret; + + Q_ASSERT(watched->isWidgetType()); + QWidget *w = static_cast(watched); + QPaintEvent *pe = static_cast(event); + const QRect widgetRect = w->rect(); + const QRect paintRect = pe->rect(); + // Does the paint rectangle touch the borders of the widget rectangle + if (paintRect.x() > widgetRect.x() && paintRect.y() > widgetRect.y() && + paintRect.right() < widgetRect.right() && paintRect.bottom() < widgetRect.bottom()) + return ret; + QPainter p(w); + const QPen pen(QColor(0, 0, 0, 32), 0, Qt::DotLine); + p.setPen(pen); + p.setBrush(QBrush(Qt::NoBrush)); + p.drawRect(widgetRect.adjusted(0, 0, -1, -1)); + return ret; +} + +void FormWindow::manageWidget(QWidget *w) +{ + if (isManaged(w)) + return; + + Q_ASSERT(qobject_cast(w) == 0); + + if (w->hasFocus()) + setFocus(); + + core()->metaDataBase()->add(w); + + m_insertedWidgets.insert(w); + m_widgets.append(w); + +#ifndef QT_NO_CURSOR + setCursorToAll(Qt::ArrowCursor, w); +#endif + + emit changed(); + emit widgetManaged(w); + + if (frameNeeded(w)) + w->installEventFilter(this); +} + +void FormWindow::unmanageWidget(QWidget *w) +{ + if (!isManaged(w)) + return; + + m_selection->removeWidget(w); + + emit aboutToUnmanageWidget(w); + + if (w == m_currentWidget) + setCurrentWidget(mainContainer()); + + core()->metaDataBase()->remove(w); + + m_insertedWidgets.remove(w); + m_widgets.removeAt(m_widgets.indexOf(w)); + + emit changed(); + emit widgetUnmanaged(w); + + if (frameNeeded(w)) + w->removeEventFilter(this); +} + +bool FormWindow::isManaged(QWidget *w) const +{ + return m_insertedWidgets.contains(w); +} + +void FormWindow::breakLayout(QWidget *w) +{ + if (w == this) + w = mainContainer(); + // Find the first-order managed child widgets + QWidgetList widgets; + + const QObjectList children = w->children(); + const QObjectList::const_iterator cend = children.constEnd(); + const QDesignerMetaDataBaseInterface *mdb = core()->metaDataBase(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) + if ( (*it)->isWidgetType()) { + QWidget *w = static_cast(*it); + if (mdb->item(w)) + widgets.push_back(w); + } + + BreakLayoutCommand *cmd = new BreakLayoutCommand(this); + cmd->init(widgets, w); + commandHistory()->push(cmd); + clearSelection(false); +} + +void FormWindow::beginCommand(const QString &description) +{ + m_undoStack.beginMacro(description); +} + +void FormWindow::endCommand() +{ + m_undoStack.endMacro(); +} + +void FormWindow::raiseWidgets() +{ + QWidgetList widgets = selectedWidgets(); + simplifySelection(&widgets); + + if (widgets.isEmpty()) + return; + + beginCommand(tr("Raise widgets")); + foreach (QWidget *widget, widgets) { + RaiseWidgetCommand *cmd = new RaiseWidgetCommand(this); + cmd->init(widget); + m_undoStack.push(cmd); + } + endCommand(); +} + +void FormWindow::lowerWidgets() +{ + QWidgetList widgets = selectedWidgets(); + simplifySelection(&widgets); + + if (widgets.isEmpty()) + return; + + beginCommand(tr("Lower widgets")); + foreach (QWidget *widget, widgets) { + LowerWidgetCommand *cmd = new LowerWidgetCommand(this); + cmd->init(widget); + m_undoStack.push(cmd); + } + endCommand(); +} + +bool FormWindow::handleMouseButtonDblClickEvent(QWidget *w, QWidget *managedWidget, QMouseEvent *e) +{ + if (debugFormWindow) + qDebug() << "handleMouseButtonDblClickEvent:" << w << ',' << managedWidget << "state=" << m_mouseState; + + e->accept(); + + // Might be out of sync due cycling of the parent selection + // In that case, do nothing + if (isWidgetSelected(managedWidget)) + emit activated(managedWidget); + + m_mouseState = MouseDoubleClicked; + return true; +} + + +QMenu *FormWindow::initializePopupMenu(QWidget *managedWidget) +{ + if (!isManaged(managedWidget) || currentTool()) + return 0; + + // Make sure the managedWidget is selected and current since + // the SetPropertyCommands must use the right reference + // object obtained from the property editor for the property group + // of a multiselection to be correct. + const bool selected = isWidgetSelected(managedWidget); + bool update = false; + if (selected == false) { + clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector. + clearSelection(false); + update = trySelectWidget(managedWidget, true); + raiseChildSelections(managedWidget); // raise selections and select widget + } else { + update = setCurrentWidget(managedWidget); + } + + if (update) { + emitSelectionChanged(); + QMetaObject::invokeMethod(core()->formWindowManager(), "slotUpdateActions"); + } + + QWidget *contextMenuWidget = 0; + + if (isMainContainer(managedWidget)) { // press on a child widget + contextMenuWidget = mainContainer(); + } else { // press on a child widget + // if widget is laid out, find the first non-laid out super-widget + QWidget *realWidget = managedWidget; // but store the original one + QMainWindow *mw = qobject_cast(mainContainer()); + + if (mw && mw->centralWidget() == realWidget) { + contextMenuWidget = managedWidget; + } else { + contextMenuWidget = realWidget; + } + } + + if (!contextMenuWidget) + return 0; + + QMenu *contextMenu = createPopupMenu(contextMenuWidget); + if (!contextMenu) + return 0; + + emit contextMenuRequested(contextMenu, contextMenuWidget); + return contextMenu; +} + +bool FormWindow::handleContextMenu(QWidget *, QWidget *managedWidget, QContextMenuEvent *e) +{ + QMenu *contextMenu = initializePopupMenu(managedWidget); + if (!contextMenu) + return false; + const QPoint globalPos = e->globalPos(); + m_contextMenuPosition = mapFromGlobal (globalPos); + contextMenu->exec(globalPos); + delete contextMenu; + e->accept(); + m_contextMenuPosition = QPoint(-1, -1); + return true; +} + +void FormWindow::setContents(QIODevice *dev) +{ + UpdateBlocker ub(this); + clearSelection(); + m_selection->clearSelectionPool(); + m_insertedWidgets.clear(); + m_widgets.clear(); + // The main container is cleared as otherwise + // the names of the newly loaded objects will be unified. + clearMainContainer(); + emit changed(); + + QDesignerResource r(this); + QWidget *w = r.load(dev, formContainer()); + setMainContainer(w); + emit changed(); +} + +void FormWindow::setContents(const QString &contents) +{ + QByteArray data = contents.toUtf8(); + QBuffer b(&data); + if (b.open(QIODevice::ReadOnly)) + setContents(&b); +} + +void FormWindow::layoutContainer(QWidget *w, int type) +{ + if (w == this) + w = mainContainer(); + + w = core()->widgetFactory()->containerOfWidget(w); + + const QObjectList l = w->children(); + if (l.isEmpty()) + return; + // find managed widget children + QWidgetList widgets; + const QObjectList::const_iterator ocend = l.constEnd(); + for (QObjectList::const_iterator it = l.constBegin(); it != ocend; ++it) + if ( (*it)->isWidgetType() ) { + QWidget *widget = static_cast(*it); + if (widget->isVisibleTo(this) && isManaged(widget)) + widgets.append(widget); + } + + LayoutCommand *cmd = new LayoutCommand(this); + cmd->init(mainContainer(), widgets, static_cast(type), w); + clearSelection(false); + commandHistory()->push(cmd); +} + +bool FormWindow::hasInsertedChildren(QWidget *widget) const // ### move +{ + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), widget)) { + const int index = container->currentIndex(); + if (index < 0) + return false; + widget = container->widget(index); + } + + const QWidgetList l = widgets(widget); + + foreach (QWidget *child, l) { + if (isManaged(child) && !LayoutInfo::isWidgetLaidout(core(), child) && child->isVisibleTo(const_cast(this))) + return true; + } + + return false; +} + +// "Select Ancestor" sub menu code +void FormWindow::slotSelectWidget(QAction *a) +{ + if (QWidget *w = qvariant_cast(a->data())) + selectSingleWidget(w); +} + +static inline QString objectNameOf(const QWidget *w) +{ + if (const QLayoutWidget *lw = qobject_cast(w)) { + const QLayout *layout = lw->layout(); + const QString rc = layout->objectName(); + if (!rc.isEmpty()) + return rc; + // Fall thru for 4.3 forms which have a name on the widget: Display the class name + return QString::fromUtf8(layout->metaObject()->className()); + } + return w->objectName(); +} + +QAction *FormWindow::createSelectAncestorSubMenu(QWidget *w) +{ + // Find the managed, unselected parents + QWidgetList parents; + QWidget *mc = mainContainer(); + for (QWidget *p = w->parentWidget(); p && p != mc; p = p->parentWidget()) + if (isManaged(p) && !isWidgetSelected(p)) + parents.push_back(p); + if (parents.empty()) + return 0; + // Create a submenu listing the managed, unselected parents + QMenu *menu = new QMenu; + QActionGroup *ag = new QActionGroup(menu); + QObject::connect(ag, SIGNAL(triggered(QAction*)), this, SLOT(slotSelectWidget(QAction*))); + const int size = parents.size(); + for (int i = 0; i < size; i++) { + QWidget *w = parents.at(i); + QAction *a = ag->addAction(objectNameOf(w)); + a->setData(QVariant::fromValue(w)); + menu->addAction(a); + } + QAction *ma = new QAction(tr("Select Ancestor"), 0); + ma->setMenu(menu); + return ma; +} + +QMenu *FormWindow::createPopupMenu(QWidget *w) +{ + QMenu *popup = createExtensionTaskMenu(this, w, true); + if (!popup) + popup = new QMenu; + // if w doesn't have a QDesignerTaskMenu as a child create one and make it a child. + // insert actions from QDesignerTaskMenu + + QDesignerFormWindowManagerInterface *manager = core()->formWindowManager(); + const bool isFormWindow = qobject_cast(w); + + // Check for special containers and obtain the page menu from them to add layout actions. + if (!isFormWindow) { + if (QStackedWidget *stackedWidget = qobject_cast(w)) { + QStackedWidgetEventFilter::addStackedWidgetContextMenuActions(stackedWidget, popup); + } else if (QTabWidget *tabWidget = qobject_cast(w)) { + QTabWidgetEventFilter::addTabWidgetContextMenuActions(tabWidget, popup); + } else if (QToolBox *toolBox = qobject_cast(w)) { + QToolBoxHelper::addToolBoxContextMenuActions(toolBox, popup); + } + + if (manager->actionLower()->isEnabled()) { + popup->addAction(manager->actionLower()); + popup->addAction(manager->actionRaise()); + popup->addSeparator(); + } + popup->addAction(manager->actionCut()); + popup->addAction(manager->actionCopy()); + } + + popup->addAction(manager->actionPaste()); + + if (QAction *selectAncestorAction = createSelectAncestorSubMenu(w)) + popup->addAction(selectAncestorAction); + popup->addAction(manager->actionSelectAll()); + + if (!isFormWindow) { + popup->addAction(manager->actionDelete()); + } + + popup->addSeparator(); + QMenu *layoutMenu = popup->addMenu(tr("Lay out")); + layoutMenu->addAction(manager->actionAdjustSize()); + layoutMenu->addAction(manager->actionHorizontalLayout()); + layoutMenu->addAction(manager->actionVerticalLayout()); + if (!isFormWindow) { + layoutMenu->addAction(manager->actionSplitHorizontal()); + layoutMenu->addAction(manager->actionSplitVertical()); + } + layoutMenu->addAction(manager->actionGridLayout()); + layoutMenu->addAction(manager->actionFormLayout()); + layoutMenu->addAction(manager->actionBreakLayout()); + layoutMenu->addAction(manager->actionSimplifyLayout()); + + return popup; +} + +void FormWindow::resizeEvent(QResizeEvent *e) +{ + m_geometryChangedTimer->start(10); + + QWidget::resizeEvent(e); +} + +/*! + Maps \a pos in \a w's coordinates to the form's coordinate system. + + This is the equivalent to mapFromGlobal(w->mapToGlobal(pos)) but + avoids the two roundtrips to the X-Server on Unix/X11. + */ +QPoint FormWindow::mapToForm(const QWidget *w, const QPoint &pos) const +{ + QPoint p = pos; + const QWidget* i = w; + while (i && !i->isWindow() && !isMainContainer(i)) { + p = i->mapToParent(p); + i = i->parentWidget(); + } + + return mapFromGlobal(w->mapToGlobal(pos)); +} + +bool FormWindow::canBeBuddy(QWidget *w) const // ### rename me. +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), w)) { + const int index = sheet->indexOf(QLatin1String("focusPolicy")); + if (index != -1) { + bool ok = false; + const Qt::FocusPolicy q = static_cast(Utils::valueOf(sheet->property(index), &ok)); + return ok && q != Qt::NoFocus; + } + } + + return false; +} + +QWidget *FormWindow::findContainer(QWidget *w, bool excludeLayout) const +{ + if (!isChildOf(w, this) + || const_cast(w) == this) + return 0; + + QDesignerWidgetFactoryInterface *widgetFactory = core()->widgetFactory(); + QDesignerWidgetDataBaseInterface *widgetDataBase = core()->widgetDataBase(); + QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase(); + + QWidget *container = widgetFactory->containerOfWidget(mainContainer()); // default parent for new widget is the formwindow + if (!isMainContainer(w)) { // press was not on formwindow, check if we can find another parent + while (w) { + if (qobject_cast(w) || !metaDataBase->item(w)) { + w = w->parentWidget(); + continue; + } + + const bool isContainer = widgetDataBase->isContainer(w, true) || w == mainContainer(); + + if (!isContainer || (excludeLayout && qobject_cast(w))) { // ### skip QSplitter + w = w->parentWidget(); + } else { + container = w; + break; + } + } + } + + return container; +} + +void FormWindow::simplifySelection(QWidgetList *sel) const +{ + if (sel->size() < 2) + return; + // Figure out which widgets should be removed from selection. + // We want to remove those whose parent widget is also in the + // selection (because the child widgets are contained by + // their parent, they shouldn't be in the selection -- + // they are "implicitly" selected). + QWidget *mainC = mainContainer(); // Quick check for main container first + if (sel->contains(mainC)) { + sel->clear(); + sel->push_back(mainC); + return; + } + typedef QVector WidgetVector; + WidgetVector toBeRemoved; + toBeRemoved.reserve(sel->size()); + const QWidgetList::const_iterator scend = sel->constEnd(); + for (QWidgetList::const_iterator it = sel->constBegin(); it != scend; ++it) { + QWidget *child = *it; + for (QWidget *w = child; true ; ) { // Is any of the parents also selected? + QWidget *parent = w->parentWidget(); + if (!parent || parent == mainC) + break; + if (sel->contains(parent)) { + toBeRemoved.append(child); + break; + } + w = parent; + } + } + // Now we can actually remove the widgets that were marked + // for removal in the previous pass. + if (!toBeRemoved.isEmpty()) { + const WidgetVector::const_iterator rcend = toBeRemoved.constEnd(); + for (WidgetVector::const_iterator it = toBeRemoved.constBegin(); it != rcend; ++it) + sel->removeAll(*it); + } +} + +FormWindow *FormWindow::findFormWindow(QWidget *w) +{ + return qobject_cast(QDesignerFormWindowInterface::findFormWindow(w)); +} + +bool FormWindow::isDirty() const +{ + return m_undoStack.isDirty(); +} + +void FormWindow::setDirty(bool dirty) +{ + m_undoStack.setDirty(dirty); +} + +QWidget *FormWindow::containerAt(const QPoint &pos) +{ + QWidget *widget = widgetAt(pos); + return findContainer(widget, true); +} + +static QWidget *childAt_SkipDropLine(QWidget *w, QPoint pos) +{ + const QObjectList child_list = w->children(); + for (int i = child_list.size() - 1; i >= 0; --i) { + QObject *child_obj = child_list[i]; + if (qobject_cast(child_obj) != 0) + continue; + QWidget *child = qobject_cast(child_obj); + if (!child || child->isWindow() || !child->isVisible() || + !child->geometry().contains(pos) || child->testAttribute(Qt::WA_TransparentForMouseEvents)) + continue; + const QPoint childPos = child->mapFromParent(pos); + if (QWidget *res = childAt_SkipDropLine(child, childPos)) + return res; + if (child->testAttribute(Qt::WA_MouseNoMask) || child->mask().contains(pos) + || child->mask().isEmpty()) + return child; + } + + return 0; +} + +QWidget *FormWindow::widgetAt(const QPoint &pos) +{ + QWidget *w = childAt(pos); + if (qobject_cast(w) != 0) + w = childAt_SkipDropLine(this, pos); + return (w == 0 || w == formContainer()) ? this : w; +} + +void FormWindow::highlightWidget(QWidget *widget, const QPoint &pos, HighlightMode mode) +{ + Q_ASSERT(widget); + + if (QMainWindow *mainWindow = qobject_cast (widget)) { + widget = mainWindow->centralWidget(); + } + + QWidget *container = findContainer(widget, false); + + if (container == 0 || core()->metaDataBase()->item(container) == 0) + return; + + if (QDesignerActionProviderExtension *g = qt_extension(core()->extensionManager(), container)) { + if (mode == Restore) { + g->adjustIndicator(QPoint()); + } else { + const QPoint pt = widget->mapTo(container, pos); + g->adjustIndicator(pt); + } + } else if (QDesignerLayoutDecorationExtension *g = qt_extension(core()->extensionManager(), container)) { + if (mode == Restore) { + g->adjustIndicator(QPoint(), -1); + } else { + const QPoint pt = widget->mapTo(container, pos); + const int index = g->findItemAt(pt); + g->adjustIndicator(pt, index); + } + } + + QMainWindow *mw = qobject_cast (container); + if (container == mainContainer() || (mw && mw->centralWidget() && mw->centralWidget() == container)) + return; + + if (mode == Restore) { + const WidgetPaletteMap::iterator pit = m_palettesBeforeHighlight.find(container); + if (pit != m_palettesBeforeHighlight.end()) { + container->setPalette(pit.value().first); + container->setAutoFillBackground(pit.value().second); + m_palettesBeforeHighlight.erase(pit); + } + } else { + QPalette p = container->palette(); + if (!m_palettesBeforeHighlight.contains(container)) { + PaletteAndFill paletteAndFill; + if (container->testAttribute(Qt::WA_SetPalette)) + paletteAndFill.first = p; + paletteAndFill.second = container->autoFillBackground(); + m_palettesBeforeHighlight.insert(container, paletteAndFill); + } + + p.setColor(backgroundRole(), p.midlight().color()); + container->setPalette(p); + container->setAutoFillBackground(true); + } +} + +QWidgetList FormWindow::widgets(QWidget *widget) const +{ + const QObjectList children = widget->children(); + if (children.empty()) + return QWidgetList(); + QWidgetList rc; + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) + if ((*it)->isWidgetType()) { + QWidget *w = qobject_cast(*it); + if (isManaged(w)) + rc.push_back(w); + } + return rc; +} + +int FormWindow::toolCount() const +{ + return m_widgetStack->count(); +} + +QDesignerFormWindowToolInterface *FormWindow::tool(int index) const +{ + return m_widgetStack->tool(index); +} + +void FormWindow::registerTool(QDesignerFormWindowToolInterface *tool) +{ + Q_ASSERT(tool != 0); + + m_widgetStack->addTool(tool); + + if (m_mainContainer) + m_mainContainer->update(); +} + +void FormWindow::setCurrentTool(int index) +{ + m_widgetStack->setCurrentTool(index); +} + +int FormWindow::currentTool() const +{ + return m_widgetStack->currentIndex(); +} + +bool FormWindow::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) +{ + if (m_widgetStack == 0) + return false; + + QDesignerFormWindowToolInterface *tool = m_widgetStack->currentTool(); + if (tool == 0) + return false; + + return tool->handleEvent(widget, managedWidget, event); +} + +void FormWindow::initializeCoreTools() +{ + m_widgetEditor = new WidgetEditorTool(this); + registerTool(m_widgetEditor); +} + +void FormWindow::checkSelection() +{ + m_checkSelectionTimer->start(0); +} + +void FormWindow::checkSelectionNow() +{ + m_checkSelectionTimer->stop(); + + foreach (QWidget *widget, selectedWidgets()) { + updateSelection(widget); + + if (LayoutInfo::layoutType(core(), widget) != LayoutInfo::NoLayout) + updateChildSelections(widget); + } +} + +QString FormWindow::author() const +{ + return m_author; +} + +QString FormWindow::comment() const +{ + return m_comment; +} + +void FormWindow::setAuthor(const QString &author) +{ + m_author = author; +} + +void FormWindow::setComment(const QString &comment) +{ + m_comment = comment; +} + +void FormWindow::editWidgets() +{ + m_widgetEditor->action()->trigger(); +} + +QStringList FormWindow::resourceFiles() const +{ + return m_resourceFiles; +} + +void FormWindow::addResourceFile(const QString &path) +{ + if (!m_resourceFiles.contains(path)) { + m_resourceFiles.append(path); + setDirty(true); + emit resourceFilesChanged(); + } +} + +void FormWindow::removeResourceFile(const QString &path) +{ + if (m_resourceFiles.removeAll(path) > 0) { + setDirty(true); + emit resourceFilesChanged(); + } +} + +bool FormWindow::blockSelectionChanged(bool b) +{ + const bool blocked = m_blockSelectionChanged; + m_blockSelectionChanged = b; + return blocked; +} + +void FormWindow::editContents() +{ + const QWidgetList sel = selectedWidgets(); + if (sel.count() == 1) { + QWidget *widget = sel.first(); + + if (QAction *a = preferredEditAction(core(), widget)) + a->trigger(); + } +} + +void FormWindow::dragWidgetWithinForm(QWidget *widget, const QRect &targetGeometry, QWidget *targetContainer) +{ + const bool fromLayout = canDragWidgetInLayout(core(), widget); + const QDesignerLayoutDecorationExtension *targetDeco = qt_extension(core()->extensionManager(), targetContainer); + const bool toLayout = targetDeco != 0; + + if (fromLayout) { + // Drag from Layout: We need to delete the widget properly to store the layout state + // Do not simplify the layout when dragging onto a layout + // as this might invalidate the insertion position if it is the same layout + DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this); + unsigned deleteFlags = DeleteWidgetCommand::DoNotUnmanage; + if (toLayout) + deleteFlags |= DeleteWidgetCommand::DoNotSimplifyLayout; + cmd->init(widget, deleteFlags); + commandHistory()->push(cmd); + } + + if (toLayout) { + // Drag from form to layout: just insert. Do not manage + insertWidget(widget, targetGeometry, targetContainer, true); + } else { + // into container without layout + if (targetContainer != widget->parent()) { // different parent + ReparentWidgetCommand *cmd = new ReparentWidgetCommand(this); + cmd->init(widget, targetContainer ); + commandHistory()->push(cmd); + } + resizeWidget(widget, targetGeometry); + selectWidget(widget, true); + widget->show(); + } +} + +static Qt::DockWidgetArea detectDropArea(QMainWindow *mainWindow, const QRect &area, const QPoint &drop) +{ + QPoint offset = area.topLeft(); + QRect rect = area; + rect.moveTopLeft(QPoint(0, 0)); + QPoint point = drop - offset; + const int x = point.x(); + const int y = point.y(); + const int w = rect.width(); + const int h = rect.height(); + + if (rect.contains(point)) { + bool topRight = false; + bool topLeft = false; + if (w * y < h * x) // top and right, oterwise bottom and left + topRight = true; + if (w * y < h * (w - x)) // top and left, otherwise bottom and right + topLeft = true; + + if (topRight && topLeft) + return Qt::TopDockWidgetArea; + else if (topRight && !topLeft) + return Qt::RightDockWidgetArea; + else if (!topRight && topLeft) + return Qt::LeftDockWidgetArea; + return Qt::BottomDockWidgetArea; + } + + if (x < 0) { + if (y < 0) + return mainWindow->corner(Qt::TopLeftCorner); + else if (y > h) + return mainWindow->corner(Qt::BottomLeftCorner); + else + return Qt::LeftDockWidgetArea; + } else if (x > w) { + if (y < 0) + return mainWindow->corner(Qt::TopRightCorner); + else if (y > h) + return mainWindow->corner(Qt::BottomRightCorner); + else + return Qt::RightDockWidgetArea; + } else { + if (y < 0) + return Qt::TopDockWidgetArea; + else + return Qt::BottomDockWidgetArea; + } + return Qt::LeftDockWidgetArea; +} + +bool FormWindow::dropDockWidget(QDesignerDnDItemInterface *item, const QPoint &global_mouse_pos) +{ + DomUI *dom_ui = item->domUi(); + + QMainWindow *mw = qobject_cast(mainContainer()); + if (!mw) + return false; + + QDesignerResource resource(this); + const FormBuilderClipboard clipboard = resource.paste(dom_ui, mw); + if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet + return false; + + QWidget *centralWidget = mw->centralWidget(); + QPoint localPos = centralWidget->mapFromGlobal(global_mouse_pos); + const QRect centralWidgetAreaRect = centralWidget->rect(); + Qt::DockWidgetArea area = detectDropArea(mw, centralWidgetAreaRect, localPos); + + beginCommand(tr("Drop widget")); + + clearSelection(false); + highlightWidget(mw, QPoint(0, 0), FormWindow::Restore); + + QWidget *widget = clipboard.m_widgets.first(); + + insertWidget(widget, QRect(0, 0, 1, 1), mw); + + selectWidget(widget, true); + mw->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector + + core()->formWindowManager()->setActiveFormWindow(this); + mainContainer()->activateWindow(); + + QDesignerPropertySheetExtension *propertySheet = qobject_cast(m_core->extensionManager()->extension(widget, Q_TYPEID(QDesignerPropertySheetExtension))); + if (propertySheet) { + const QString dockWidgetAreaName = QLatin1String("dockWidgetArea"); + PropertySheetEnumValue e = qvariant_cast(propertySheet->property(propertySheet->indexOf(dockWidgetAreaName))); + e.value = area; + QVariant v; + v.setValue(e); + SetPropertyCommand *cmd = new SetPropertyCommand(this); + cmd->init(widget, dockWidgetAreaName, v); + m_undoStack.push(cmd); + } + + endCommand(); + return true; +} + +bool FormWindow::dropWidgets(const QList &item_list, QWidget *target, + const QPoint &global_mouse_pos) +{ + + QWidget *parent = target; + if (parent == 0) + parent = mainContainer(); + // You can only drop stuff onto the central widget of a QMainWindow + // ### generalize to use container extension + if (QMainWindow *main_win = qobject_cast(target)) { + if (!main_win->centralWidget()) { + designerWarning(tr("A QMainWindow-based form does not contain a central widget.")); + return false; + } + const QPoint main_win_pos = main_win->mapFromGlobal(global_mouse_pos); + const QRect central_wgt_geo = main_win->centralWidget()->geometry(); + if (!central_wgt_geo.contains(main_win_pos)) + return false; + } + + QWidget *container = findContainer(parent, false); + if (container == 0) + return false; + + beginCommand(tr("Drop widget")); + + clearSelection(false); + highlightWidget(target, target->mapFromGlobal(global_mouse_pos), FormWindow::Restore); + + QPoint offset; + QDesignerDnDItemInterface *current = 0; + QDesignerFormWindowCursorInterface *c = cursor(); + foreach (QDesignerDnDItemInterface *item, item_list) { + QWidget *w = item->widget(); + if (!current) + current = item; + if (c->current() == w) { + current = item; + break; + } + } + if (current) { + QRect geom = current->decoration()->geometry(); + QPoint topLeft = container->mapFromGlobal(geom.topLeft()); + offset = designerGrid().snapPoint(topLeft) - topLeft; + } + + foreach (QDesignerDnDItemInterface *item, item_list) { + DomUI *dom_ui = item->domUi(); + QRect geometry = item->decoration()->geometry(); + Q_ASSERT(dom_ui != 0); + + geometry.moveTopLeft(container->mapFromGlobal(geometry.topLeft()) + offset); + if (item->type() == QDesignerDnDItemInterface::CopyDrop) { // from widget box or CTRL + mouse move + QWidget *widget = createWidget(dom_ui, geometry, parent); + if (!widget) { + endCommand(); + return false; + } + selectWidget(widget, true); + mainContainer()->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector + } else { // same form move + QWidget *widget = item->widget(); + Q_ASSERT(widget != 0); + QDesignerFormWindowInterface *dest = findFormWindow(widget); + if (dest == this) { + dragWidgetWithinForm(widget, geometry, container); + } else { // from other form + FormWindow *source = qobject_cast(item->source()); + Q_ASSERT(source != 0); + + source->deleteWidgetList(QWidgetList() << widget); + QWidget *new_widget = createWidget(dom_ui, geometry, parent); + + selectWidget(new_widget, true); + } + } + } + + core()->formWindowManager()->setActiveFormWindow(this); + mainContainer()->activateWindow(); + endCommand(); + return true; +} + +QDir FormWindow::absoluteDir() const +{ + if (fileName().isEmpty()) + return QDir::current(); + + return QFileInfo(fileName()).absoluteDir(); +} + +void FormWindow::layoutDefault(int *margin, int *spacing) +{ + *margin = m_defaultMargin; + *spacing = m_defaultSpacing; +} + +void FormWindow::setLayoutDefault(int margin, int spacing) +{ + m_defaultMargin = margin; + m_defaultSpacing = spacing; +} + +void FormWindow::layoutFunction(QString *margin, QString *spacing) +{ + *margin = m_marginFunction; + *spacing = m_spacingFunction; +} + +void FormWindow::setLayoutFunction(const QString &margin, const QString &spacing) +{ + m_marginFunction = margin; + m_spacingFunction = spacing; +} + +QString FormWindow::pixmapFunction() const +{ + return m_pixmapFunction; +} + +void FormWindow::setPixmapFunction(const QString &pixmapFunction) +{ + m_pixmapFunction = pixmapFunction; +} + +QStringList FormWindow::includeHints() const +{ + return m_includeHints; +} + +void FormWindow::setIncludeHints(const QStringList &includeHints) +{ + m_includeHints = includeHints; +} + +QString FormWindow::exportMacro() const +{ + return m_exportMacro; +} + +void FormWindow::setExportMacro(const QString &exportMacro) +{ + m_exportMacro = exportMacro; +} + +QEditorFormBuilder *FormWindow::createFormBuilder() +{ + return new QDesignerResource(this); +} + +QWidget *FormWindow::formContainer() const +{ + return m_widgetStack->formContainer(); +} + +QUndoStack *FormWindow::commandHistory() const +{ + return const_cast(m_undoStack).qundoStack(); +} + +} // namespace + +QT_END_NAMESPACE + +#include diff --git a/src/designer/components/formeditor/formwindow.h b/src/designer/components/formeditor/formwindow.h new file mode 100644 index 000000000..8f0e6ec46 --- /dev/null +++ b/src/designer/components/formeditor/formwindow.h @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOW_H +#define FORMWINDOW_H + +#include "formeditor_global.h" +#include "qdesignerundostack.h" +#include + +// Qt +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerDnDItemInterface; +class QDesignerTaskMenuExtension; +class DomConnections; + +class QWidget; +class QAction; +class QLabel; +class QTimer; +class QAction; +class QMenu; +class QRubberBand; + +namespace qdesigner_internal { + +class FormEditor; +class FormWindowCursor; +class WidgetEditorTool; +class FormWindowWidgetStack; +class FormWindowManager; +class FormWindowDnDItem; +class SetPropertyCommand; + +class QT_FORMEDITOR_EXPORT FormWindow: public FormWindowBase +{ + Q_OBJECT + +public: + explicit FormWindow(FormEditor *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~FormWindow(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual QDesignerFormWindowCursorInterface *cursor() const; + + // Overwritten: FormWindowBase + virtual QWidget *formContainer() const; + + virtual int toolCount() const; + virtual int currentTool() const; + virtual void setCurrentTool(int index); + virtual QDesignerFormWindowToolInterface *tool(int index) const; + virtual void registerTool(QDesignerFormWindowToolInterface *tool); + + virtual QString author() const; + virtual void setAuthor(const QString &author); + + virtual QString comment() const; + virtual void setComment(const QString &comment); + + virtual void layoutDefault(int *margin, int *spacing); + virtual void setLayoutDefault(int margin, int spacing); + + virtual void layoutFunction(QString *margin, QString *spacing); + virtual void setLayoutFunction(const QString &margin, const QString &spacing); + + virtual QString pixmapFunction() const; + virtual void setPixmapFunction(const QString &pixmapFunction); + + virtual QString exportMacro() const; + virtual void setExportMacro(const QString &exportMacro); + + virtual QStringList includeHints() const; + virtual void setIncludeHints(const QStringList &includeHints); + + virtual QString fileName() const; + virtual void setFileName(const QString &fileName); + + virtual QString contents() const; + virtual void setContents(const QString &contents); + virtual void setContents(QIODevice *dev); + + virtual QDir absoluteDir() const; + + virtual void simplifySelection(QWidgetList *sel) const; + + virtual void ensureUniqueObjectName(QObject *object); + + virtual QWidget *mainContainer() const; + void setMainContainer(QWidget *mainContainer); + bool isMainContainer(const QWidget *w) const; + + QWidget *currentWidget() const; + + bool hasInsertedChildren(QWidget *w) const; + + QList selectedWidgets() const; + void clearSelection(bool changePropertyDisplay=true); + bool isWidgetSelected(QWidget *w) const; + void selectWidget(QWidget *w, bool select=true); + + void selectWidgets(); + void repaintSelection(); + void updateSelection(QWidget *w); + void updateChildSelections(QWidget *w); + void raiseChildSelections(QWidget *w); + void raiseSelection(QWidget *w); + + inline const QList& widgets() const { return m_widgets; } + inline int widgetCount() const { return m_widgets.count(); } + inline QWidget *widgetAt(int index) const { return m_widgets.at(index); } + + QList widgets(QWidget *widget) const; + + QWidget *createWidget(DomUI *ui, const QRect &rect, QWidget *target); + + bool isManaged(QWidget *w) const; + + void manageWidget(QWidget *w); + void unmanageWidget(QWidget *w); + + virtual QUndoStack *commandHistory() const; + void beginCommand(const QString &description); + void endCommand(); + + virtual bool blockSelectionChanged(bool blocked); + virtual void emitSelectionChanged(); + + bool unify(QObject *w, QString &s, bool changeIt); + + bool isDirty() const; + void setDirty(bool dirty); + + static FormWindow *findFormWindow(QWidget *w); + + virtual QWidget *containerAt(const QPoint &pos); + virtual QWidget *widgetAt(const QPoint &pos); + virtual void highlightWidget(QWidget *w, const QPoint &pos, + HighlightMode mode = Highlight); + + void updateOrderIndicators(); + + bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event); + + QStringList resourceFiles() const; + void addResourceFile(const QString &path); + void removeResourceFile(const QString &path); + + void resizeWidget(QWidget *widget, const QRect &geometry); + + bool dropDockWidget(QDesignerDnDItemInterface *item, const QPoint &global_mouse_pos); + bool dropWidgets(const QList &item_list, QWidget *target, + const QPoint &global_mouse_pos); + + virtual QWidget *findContainer(QWidget *w, bool excludeLayout) const; + // for WidgetSelection only. + QWidget *designerWidget(QWidget *w) const; + + // Initialize and return a popup menu for a managed widget + QMenu *initializePopupMenu(QWidget *managedWidget); + + virtual void paste(PasteMode pasteMode); + virtual QEditorFormBuilder *createFormBuilder(); + + bool eventFilter(QObject *watched, QEvent *event); + +signals: + void contextMenuRequested(QMenu *menu, QWidget *widget); + +public slots: + void deleteWidgets(); + void raiseWidgets(); + void lowerWidgets(); + void copy(); + void cut(); + void paste(); + void selectAll(); + + void createLayout(int type, QWidget *container = 0); + void morphLayout(QWidget *container, int newType); + void breakLayout(QWidget *w); + + void editContents(); + +protected: + virtual QMenu *createPopupMenu(QWidget *w); + virtual void resizeEvent(QResizeEvent *e); + + void insertWidget(QWidget *w, const QRect &rect, QWidget *target, bool already_in_form = false); + +private slots: + void selectionChangedTimerDone(); + void checkSelection(); + void checkSelectionNow(); + void slotSelectWidget(QAction *); + +private: + enum MouseState { + NoMouseState, + // Double click received + MouseDoubleClicked, + // Drawing selection rubber band rectangle + MouseDrawRubber, + // Started a move operation + MouseMoveDrag, + // Click on a widget whose parent is selected. Defer selection to release + MouseDeferredSelection + }; + MouseState m_mouseState; + QPointer m_lastClickedWidget; + + void init(); + void initializeCoreTools(); + + int getValue(const QRect &rect, int key, bool size) const; + int calcValue(int val, bool forward, bool snap, int snapOffset) const; + void handleClickSelection(QWidget *managedWidget, unsigned mouseFlags); + + bool frameNeeded(QWidget *w) const; + + enum RectType { Insert, Rubber }; + + void startRectDraw(const QPoint &global, QWidget *, RectType t); + void continueRectDraw(const QPoint &global, QWidget *, RectType t); + void endRectDraw(); + + QWidget *containerAt(const QPoint &pos, QWidget *notParentOf); + + void checkPreviewGeometry(QRect &r); + + bool handleContextMenu(QWidget *widget, QWidget *managedWidget, QContextMenuEvent *e); + bool handleMouseButtonDblClickEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMousePressEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMouseMoveEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMouseReleaseEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleKeyPressEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e); + bool handleKeyReleaseEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e); + + bool isCentralWidget(QWidget *w) const; + + bool setCurrentWidget(QWidget *currentWidget); + bool trySelectWidget(QWidget *w, bool select); + + void dragWidgetWithinForm(QWidget *widget, const QRect &targetGeometry, QWidget *targetContainer); + + void setCursorToAll(const QCursor &c, QWidget *start); + + QPoint mapToForm(const QWidget *w, const QPoint &pos) const; + bool canBeBuddy(QWidget *w) const; + + QWidget *findTargetContainer(QWidget *widget) const; + + void clearMainContainer(); + + static int widgetDepth(const QWidget *w); + static bool isChildOf(const QWidget *c, const QWidget *p); + + void editWidgets(); + + void updateWidgets(); + + void handleArrowKeyEvent(int key, Qt::KeyboardModifiers modifiers); + + void layoutSelection(int type); + void layoutContainer(QWidget *w, int type); + +private: + QWidget *innerContainer(QWidget *outerContainer) const; + QWidget *containerForPaste() const; + QAction *createSelectAncestorSubMenu(QWidget *w); + void selectSingleWidget(QWidget *w); + + FormEditor *m_core; + FormWindowCursor *m_cursor; + QWidget *m_mainContainer; + QWidget *m_currentWidget; + + bool m_blockSelectionChanged; + + QPoint m_rectAnchor; + QRect m_currRect; + + QWidgetList m_widgets; + QSet m_insertedWidgets; + + class Selection; + Selection *m_selection; + + QPoint m_startPos; + + QDesignerUndoStack m_undoStack; + + QString m_fileName; + + typedef QPair PaletteAndFill; + typedef QMap WidgetPaletteMap; + WidgetPaletteMap m_palettesBeforeHighlight; + + QRubberBand *m_rubberBand; + + QTimer *m_selectionChangedTimer; + QTimer *m_checkSelectionTimer; + QTimer *m_geometryChangedTimer; + + FormWindowWidgetStack *m_widgetStack; + WidgetEditorTool *m_widgetEditor; + + QStringList m_resourceFiles; + + QString m_comment; + QString m_author; + QString m_pixmapFunction; + int m_defaultMargin, m_defaultSpacing; + QString m_marginFunction, m_spacingFunction; + QString m_exportMacro; + QStringList m_includeHints; + + QPoint m_contextMenuPosition; + +private: + friend class WidgetEditorTool; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOW_H diff --git a/src/designer/components/formeditor/formwindow_dnditem.cpp b/src/designer/components/formeditor/formwindow_dnditem.cpp new file mode 100644 index 000000000..0d93f0539 --- /dev/null +++ b/src/designer/components/formeditor/formwindow_dnditem.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindow_dnditem.h" +#include "formwindow.h" + +#include +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +static QWidget *decorationFromWidget(QWidget *w) +{ + QLabel *label = new QLabel(0, Qt::ToolTip); + QPixmap pm = QPixmap::grabWidget(w); + label->setPixmap(pm); + label->resize(pm.size()); + + return label; +} + +static DomUI *widgetToDom(QWidget *widget, FormWindow *form) +{ + QDesignerResource builder(form); + builder.setSaveRelative(false); + return builder.copy(FormBuilderClipboard(widget)); +} + +FormWindowDnDItem::FormWindowDnDItem(QDesignerDnDItemInterface::DropType type, FormWindow *form, + QWidget *widget, const QPoint &global_mouse_pos) + : QDesignerDnDItem(type, form) +{ + QWidget *decoration = decorationFromWidget(widget); + QPoint pos = widget->mapToGlobal(QPoint(0, 0)); + decoration->move(pos); + + init(0, widget, decoration, global_mouse_pos); +} + +DomUI *FormWindowDnDItem::domUi() const +{ + DomUI *result = QDesignerDnDItem::domUi(); + if (result != 0) + return result; + FormWindow *form = qobject_cast(source()); + if (widget() == 0 || form == 0) + return 0; + + QtResourceModel *resourceModel = form->core()->resourceModel(); + QtResourceSet *currentResourceSet = resourceModel->currentResourceSet(); + /* Short: + * We need to activate the original resourceSet associated with a form + * to properly generate the dom resource includes. + * Long: + * widgetToDom() calls copy() on QDesignerResource. It generates the + * Dom structure. In order to create DomResources properly we need to + * have the associated ResourceSet active (QDesignerResource::saveResources() + * queries the resource model for a qrc path for the given resource file: + * qrcFile = m_core->resourceModel()->qrcPath(ri->text()); + * This works only when the resource file comes from the active + * resourceSet */ + resourceModel->setCurrentResourceSet(form->resourceSet()); + + result = widgetToDom(widget(), form); + const_cast(this)->setDomUi(result); + resourceModel->setCurrentResourceSet(currentResourceSet); + return result; +} + +QT_END_NAMESPACE diff --git a/src/designer/components/formeditor/formwindow_dnditem.h b/src/designer/components/formeditor/formwindow_dnditem.h new file mode 100644 index 000000000..d7297e7d5 --- /dev/null +++ b/src/designer/components/formeditor/formwindow_dnditem.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOW_DNDITEM_H +#define FORMWINDOW_DNDITEM_H + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class FormWindow; + +class FormWindowDnDItem : public QDesignerDnDItem +{ +public: + FormWindowDnDItem(QDesignerDnDItemInterface::DropType type, FormWindow *form, + QWidget *widget, const QPoint &global_mouse_pos); + virtual DomUI *domUi() const; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOW_DNDITEM_H diff --git a/src/designer/components/formeditor/formwindow_widgetstack.cpp b/src/designer/components/formeditor/formwindow_widgetstack.cpp new file mode 100644 index 000000000..3c51e5acd --- /dev/null +++ b/src/designer/components/formeditor/formwindow_widgetstack.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindow_widgetstack.h" +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +FormWindowWidgetStack::FormWindowWidgetStack(QObject *parent) : + QObject(parent), + m_formContainer(new QWidget), + m_formContainerLayout(new QStackedLayout), + m_layout(new QStackedLayout) +{ + m_layout->setMargin(0); + m_layout->setSpacing(0); + m_layout->setStackingMode(QStackedLayout::StackAll); + + // We choose a QStackedLayout as immediate layout for + // the form windows as it ignores the sizePolicy of + // its child (for example, Fixed would cause undesired side effects). + m_formContainerLayout->setMargin(0); + m_formContainer->setObjectName(QLatin1String("formContainer")); + m_formContainer->setLayout(m_formContainerLayout); + m_formContainerLayout->setStackingMode(QStackedLayout::StackAll); + // System settings might have different background colors, autofill them + // (affects for example mainwindow status bars) + m_formContainer->setAutoFillBackground(true); +} + +FormWindowWidgetStack::~FormWindowWidgetStack() +{ +} + +int FormWindowWidgetStack::count() const +{ + return m_tools.count(); +} + +QDesignerFormWindowToolInterface *FormWindowWidgetStack::currentTool() const +{ + return tool(currentIndex()); +} + +void FormWindowWidgetStack::setCurrentTool(int index) +{ + const int cnt = count(); + if (index < 0 || index >= cnt) { + qDebug("FormWindowWidgetStack::setCurrentTool(): invalid index: %d", index); + return; + } + + const int cur = currentIndex(); + if (index == cur) + return; + + if (cur != -1) + m_tools.at(cur)->deactivated(); + + + m_layout->setCurrentIndex(index); + // Show the widget editor and the current tool + for (int i = 0; i < cnt; i++) + m_tools.at(i)->editor()->setVisible(i == 0 || i == index); + + QDesignerFormWindowToolInterface *tool = m_tools.at(index); + tool->activated(); + + emit currentToolChanged(index); +} + +void FormWindowWidgetStack::setSenderAsCurrentTool() +{ + QDesignerFormWindowToolInterface *tool = 0; + QAction *action = qobject_cast(sender()); + if (action == 0) { + qDebug("FormWindowWidgetStack::setSenderAsCurrentTool(): sender is not a QAction"); + return; + } + + foreach (QDesignerFormWindowToolInterface *t, m_tools) { + if (action == t->action()) { + tool = t; + break; + } + } + + if (tool == 0) { + qDebug("FormWindowWidgetStack::setSenderAsCurrentTool(): unknown tool"); + return; + } + + setCurrentTool(tool); +} + +int FormWindowWidgetStack::indexOf(QDesignerFormWindowToolInterface *tool) const +{ + return m_tools.indexOf(tool); +} + +void FormWindowWidgetStack::setCurrentTool(QDesignerFormWindowToolInterface *tool) +{ + int index = indexOf(tool); + if (index == -1) { + qDebug("FormWindowWidgetStack::setCurrentTool(): unknown tool"); + return; + } + + setCurrentTool(index); +} + +void FormWindowWidgetStack::setMainContainer(QWidget *w) +{ + // This code is triggered once by the formwindow and + // by integrations doing "revert to saved". Anything changing? + const int previousCount = m_formContainerLayout->count(); + QWidget *previousMainContainer = previousCount ? m_formContainerLayout->itemAt(0)->widget() : static_cast(0); + if (previousMainContainer == w) + return; + // Swap + if (previousCount) + delete m_formContainerLayout->takeAt(0); + if (w) + m_formContainerLayout->addWidget(w); +} + +void FormWindowWidgetStack::addTool(QDesignerFormWindowToolInterface *tool) +{ + if (QWidget *w = tool->editor()) { + w->setVisible(m_layout->count() == 0); // Initially only form editor is visible + m_layout->addWidget(w); + } else { + // The form editor might not have a tool initially, use dummy. Assert on anything else + Q_ASSERT(m_tools.empty()); + m_layout->addWidget(m_formContainer); + } + + m_tools.append(tool); + + connect(tool->action(), SIGNAL(triggered()), this, SLOT(setSenderAsCurrentTool())); +} + +QDesignerFormWindowToolInterface *FormWindowWidgetStack::tool(int index) const +{ + if (index < 0 || index >= count()) + return 0; + + return m_tools.at(index); +} + +int FormWindowWidgetStack::currentIndex() const +{ + return m_layout->currentIndex(); +} + +QWidget *FormWindowWidgetStack::defaultEditor() const +{ + if (m_tools.isEmpty()) + return 0; + + return m_tools.at(0)->editor(); +} + +QLayout *FormWindowWidgetStack::layout() const +{ + return m_layout; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/formwindow_widgetstack.h b/src/designer/components/formeditor/formwindow_widgetstack.h new file mode 100644 index 000000000..b4ef59209 --- /dev/null +++ b/src/designer/components/formeditor/formwindow_widgetstack.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOW_WIDGETSTACK_H +#define FORMWINDOW_WIDGETSTACK_H + +#include "formeditor_global.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowToolInterface; + +class QStackedLayout; +class QWidget; + +namespace qdesigner_internal { + +class QT_FORMEDITOR_EXPORT FormWindowWidgetStack: public QObject +{ + Q_OBJECT +public: + FormWindowWidgetStack(QObject *parent = 0); + virtual ~FormWindowWidgetStack(); + + QLayout *layout() const; + + int count() const; + QDesignerFormWindowToolInterface *tool(int index) const; + QDesignerFormWindowToolInterface *currentTool() const; + int currentIndex() const; + int indexOf(QDesignerFormWindowToolInterface *tool) const; + + void setMainContainer(QWidget *w = 0); + + // Return the widget containing the form which can be used to apply embedded design settings to. + // These settings should not affect the other editing tools. + QWidget *formContainer() const { return m_formContainer; } + +signals: + void currentToolChanged(int index); + +public slots: + void addTool(QDesignerFormWindowToolInterface *tool); + void setCurrentTool(QDesignerFormWindowToolInterface *tool); + void setCurrentTool(int index); + void setSenderAsCurrentTool(); + +protected: + QWidget *defaultEditor() const; + +private: + QList m_tools; + QWidget *m_formContainer; + QStackedLayout *m_formContainerLayout; + QStackedLayout *m_layout; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOW_WIDGETSTACK_H diff --git a/src/designer/components/formeditor/formwindowcursor.cpp b/src/designer/components/formeditor/formwindowcursor.cpp new file mode 100644 index 000000000..7601b9cd3 --- /dev/null +++ b/src/designer/components/formeditor/formwindowcursor.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindowcursor.h" +#include "formwindow.h" + +// sdk +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +FormWindowCursor::FormWindowCursor(FormWindow *fw, QObject *parent) + : QObject(parent), + m_formWindow(fw) +{ + update(); + connect(fw, SIGNAL(changed()), this, SLOT(update())); +} + +FormWindowCursor::~FormWindowCursor() +{ +} + +QDesignerFormWindowInterface *FormWindowCursor::formWindow() const +{ + return m_formWindow; +} + +bool FormWindowCursor::movePosition(MoveOperation op, MoveMode mode) +{ + if (widgetCount() == 0) + return false; + + int iterator = position(); + + if (mode == MoveAnchor) + m_formWindow->clearSelection(false); + + switch (op) { + case Next: + ++iterator; + if (iterator >= widgetCount()) + iterator = 0; + + m_formWindow->selectWidget(m_formWindow->widgetAt(iterator), true); + return true; + + case Prev: + --iterator; + if (iterator < 0) + iterator = widgetCount() - 1; + + if (iterator < 0) + return false; + + m_formWindow->selectWidget(m_formWindow->widgetAt(iterator), true); + return true; + + default: + return false; + } +} + +int FormWindowCursor::position() const +{ + const int index = m_formWindow->widgets().indexOf(current()); + return index == -1 ? 0 : index; +} + +void FormWindowCursor::setPosition(int pos, MoveMode mode) +{ + if (!widgetCount()) + return; + + if (mode == MoveAnchor) + m_formWindow->clearSelection(false); + + if (pos >= widgetCount()) + pos = 0; + + m_formWindow->selectWidget(m_formWindow->widgetAt(pos), true); +} + +QWidget *FormWindowCursor::current() const +{ + return m_formWindow->currentWidget(); +} + +bool FormWindowCursor::hasSelection() const +{ + return !m_formWindow->selectedWidgets().isEmpty(); +} + +int FormWindowCursor::selectedWidgetCount() const +{ + int N = m_formWindow->selectedWidgets().count(); + return N ? N : 1; +} + +QWidget *FormWindowCursor::selectedWidget(int index) const +{ + return hasSelection() + ? m_formWindow->selectedWidgets().at(index) + : m_formWindow->mainContainer(); +} + +void FormWindowCursor::update() +{ + // ### todo +} + +int FormWindowCursor::widgetCount() const +{ + return m_formWindow->widgetCount(); +} + +QWidget *FormWindowCursor::widget(int index) const +{ + return m_formWindow->widgetAt(index); +} + +void FormWindowCursor::setProperty(const QString &name, const QVariant &value) +{ + + // build selection + const int N = selectedWidgetCount(); + Q_ASSERT(N); + + SetPropertyCommand::ObjectList selection; + for (int i=0; iinit(selection, name, value, current())) { + m_formWindow->commandHistory()->push(setPropertyCommand); + } else { + delete setPropertyCommand; + qDebug() << "Unable to set property " << name << '.'; + } +} + +void FormWindowCursor::setWidgetProperty(QWidget *widget, const QString &name, const QVariant &value) +{ + SetPropertyCommand *cmd = new SetPropertyCommand(m_formWindow); + if (cmd->init(widget, name, value)) { + m_formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "Unable to set property " << name << '.'; + } +} + +void FormWindowCursor::resetWidgetProperty(QWidget *widget, const QString &name) +{ + ResetPropertyCommand *cmd = new ResetPropertyCommand(m_formWindow); + if (cmd->init(widget, name)) { + m_formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "Unable to reset property " << name << '.'; + } +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/formwindowcursor.h b/src/designer/components/formeditor/formwindowcursor.h new file mode 100644 index 000000000..80fe1714e --- /dev/null +++ b/src/designer/components/formeditor/formwindowcursor.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOWCURSOR_H +#define FORMWINDOWCURSOR_H + +#include "formeditor_global.h" +#include "formwindow.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QT_FORMEDITOR_EXPORT FormWindowCursor: public QObject, public QDesignerFormWindowCursorInterface +{ + Q_OBJECT +public: + explicit FormWindowCursor(FormWindow *fw, QObject *parent = 0); + virtual ~FormWindowCursor(); + + virtual QDesignerFormWindowInterface *formWindow() const; + + virtual bool movePosition(MoveOperation op, MoveMode mode); + + virtual int position() const; + virtual void setPosition(int pos, MoveMode mode); + + virtual QWidget *current() const; + + virtual int widgetCount() const; + virtual QWidget *widget(int index) const; + + virtual bool hasSelection() const; + virtual int selectedWidgetCount() const; + virtual QWidget *selectedWidget(int index) const; + + virtual void setProperty(const QString &name, const QVariant &value); + virtual void setWidgetProperty(QWidget *widget, const QString &name, const QVariant &value); + virtual void resetWidgetProperty(QWidget *widget, const QString &name); + +public slots: + void update(); + +private: + FormWindow *m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOWCURSOR_H diff --git a/src/designer/components/formeditor/formwindowmanager.cpp b/src/designer/components/formeditor/formwindowmanager.cpp new file mode 100644 index 000000000..0d72003c3 --- /dev/null +++ b/src/designer/components/formeditor/formwindowmanager.cpp @@ -0,0 +1,1037 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// components/formeditor +#include "formwindowmanager.h" +#include "formwindow_dnditem.h" +#include "formwindow.h" +#include "formeditor.h" +#include "widgetselection.h" +#include "previewactiongroup.h" +#include "formwindowsettings.h" + +// shared +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// SDK +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + enum { debugFWM = 0 }; +} + +static inline QString whatsThisFrom(const QString &str) { /// ### implement me! + return str; +} + +// find the first child of w in a sequence +template +static inline Iterator findFirstChildOf(Iterator it,Iterator end, const QWidget *w) +{ + for (;it != end; ++it) { + if (w->isAncestorOf(*it)) + return it; + } + return it; +} + +namespace qdesigner_internal { + +FormWindowManager::FormWindowManager(QDesignerFormEditorInterface *core, QObject *parent) : + QDesignerFormWindowManager(parent), + m_core(core), + m_activeFormWindow(0), + m_previewManager(new PreviewManager(PreviewManager::SingleFormNonModalPreview, this)), + m_createLayoutContext(LayoutContainer), + m_morphLayoutContainer(0), + m_actionGroupPreviewInStyle(0), + m_actionShowFormWindowSettingsDialog(0) +{ + setupActions(); + qApp->installEventFilter(this); +} + +FormWindowManager::~FormWindowManager() +{ + qDeleteAll(m_formWindows); +} + +QDesignerFormEditorInterface *FormWindowManager::core() const +{ + return m_core; +} + +QDesignerFormWindowInterface *FormWindowManager::activeFormWindow() const +{ + return m_activeFormWindow; +} + +int FormWindowManager::formWindowCount() const +{ + return m_formWindows.size(); +} + +QDesignerFormWindowInterface *FormWindowManager::formWindow(int index) const +{ + return m_formWindows.at(index); +} + +bool FormWindowManager::eventFilter(QObject *o, QEvent *e) +{ + if (!o->isWidgetType()) + return false; + + // If we don't have an active form, we only listen for WindowActivate to speed up integrations + const QEvent::Type eventType = e->type(); + if (m_activeFormWindow == 0 && eventType != QEvent::WindowActivate) + return false; + + switch (eventType) { // Uninteresting events + case QEvent::Create: + case QEvent::Destroy: + case QEvent::AccessibilityDescription: + case QEvent::AccessibilityHelp: + case QEvent::AccessibilityPrepare: + case QEvent::ActionAdded: + case QEvent::ActionChanged: + case QEvent::ActionRemoved: + case QEvent::ChildAdded: + case QEvent::ChildPolished: + case QEvent::ChildRemoved: + case QEvent::Clipboard: + case QEvent::ContentsRectChange: + case QEvent::DeferredDelete: + case QEvent::FileOpen: + case QEvent::LanguageChange: + case QEvent::MetaCall: + case QEvent::ModifiedChange: + case QEvent::Paint: + case QEvent::PaletteChange: + case QEvent::ParentAboutToChange: + case QEvent::ParentChange: + case QEvent::Polish: + case QEvent::PolishRequest: + case QEvent::QueryWhatsThis: + case QEvent::StatusTip: + case QEvent::StyleChange: + case QEvent::Timer: + case QEvent::ToolBarChange: + case QEvent::ToolTip: + case QEvent::WhatsThis: + case QEvent::WhatsThisClicked: + case QEvent::WinIdChange: + case QEvent::DynamicPropertyChange: + case QEvent::HoverEnter: + case QEvent::HoverLeave: + case QEvent::HoverMove: + case QEvent::AcceptDropsChange: + return false; + default: + break; + } + + QWidget *widget = static_cast(o); + + if (qobject_cast(widget)) { // ### remove me + return false; + } + + FormWindow *fw = FormWindow::findFormWindow(widget); + if (fw == 0) { + return false; + } + + if (QWidget *managedWidget = findManagedWidget(fw, widget)) { + // Prevent MDI and QWorkspace subwindows from being closed by clicking at the title bar + if (managedWidget != widget && eventType == QEvent::Close) { + e->ignore(); + return true; + } + switch (eventType) { + + case QEvent::WindowActivate: { + if (fw->parentWidget()->isWindow() && fw->isMainContainer(managedWidget) && activeFormWindow() != fw) { + setActiveFormWindow(fw); + } + } break; + + case QEvent::WindowDeactivate: { + if (o == fw && o == activeFormWindow()) + fw->repaintSelection(); + } break; + + case QEvent::KeyPress: { + QKeyEvent *ke = static_cast(e); + if (ke->key() == Qt::Key_Escape) { + ke->accept(); + return true; + } + } + // don't break... + // Embedded Design: Drop on different form: Make sure the right form + // window/device is active before having the widget created by the factory + case QEvent::Drop: + if (activeFormWindow() != fw) + setActiveFormWindow(fw); + // don't break... + default: { + if (fw->handleEvent(widget, managedWidget, e)) { + return true; + } + } break; + + } // end switch + } + + return false; +} + +void FormWindowManager::addFormWindow(QDesignerFormWindowInterface *w) +{ + FormWindow *formWindow = qobject_cast(w); + if (!formWindow || m_formWindows.contains(formWindow)) + return; + + connect(formWindow, SIGNAL(selectionChanged()), this, SLOT(slotUpdateActions())); + connect(formWindow->commandHistory(), SIGNAL(indexChanged(int)), this, SLOT(slotUpdateActions())); + connect(formWindow, SIGNAL(toolChanged(int)), this, SLOT(slotUpdateActions())); + + if (ActionEditor *ae = qobject_cast(m_core->actionEditor())) + connect(w, SIGNAL(mainContainerChanged(QWidget*)), ae, SLOT(mainContainerChanged())); + if (QDesignerObjectInspector *oi = qobject_cast(m_core->objectInspector())) + connect(w, SIGNAL(mainContainerChanged(QWidget*)), oi, SLOT(mainContainerChanged())); + + m_formWindows.append(formWindow); + emit formWindowAdded(formWindow); +} + +void FormWindowManager::removeFormWindow(QDesignerFormWindowInterface *w) +{ + FormWindow *formWindow = qobject_cast(w); + + int idx = m_formWindows.indexOf(formWindow); + if (!formWindow || idx == -1) + return; + + formWindow->disconnect(this); + m_formWindows.removeAt(idx); + emit formWindowRemoved(formWindow); + + if (formWindow == m_activeFormWindow) + setActiveFormWindow(0); + + if (m_formWindows.size() == 0 + && m_core->widgetBox()) { + // Make sure that widget box is enabled by default + m_core->widgetBox()->setEnabled(true); + } + +} + +void FormWindowManager::setActiveFormWindow(QDesignerFormWindowInterface *w) +{ + FormWindow *formWindow = qobject_cast(w); + + if (formWindow == m_activeFormWindow) + return; + + FormWindow *old = m_activeFormWindow; + + m_activeFormWindow = formWindow; + + QtResourceSet *resourceSet = 0; + if (formWindow) + resourceSet = formWindow->resourceSet(); + m_core->resourceModel()->setCurrentResourceSet(resourceSet); + + slotUpdateActions(); + + if (m_activeFormWindow) { + m_activeFormWindow->repaintSelection(); + if (old) + old->repaintSelection(); + } + + emit activeFormWindowChanged(m_activeFormWindow); + + if (m_activeFormWindow) { + m_activeFormWindow->emitSelectionChanged(); + m_activeFormWindow->commandHistory()->setActive(); + // Trigger setActiveSubWindow on mdi area unless we are in toplevel mode + QMdiSubWindow *mdiSubWindow = 0; + if (QWidget *formwindow = m_activeFormWindow->parentWidget()) { + mdiSubWindow = qobject_cast(formwindow->parentWidget()); + } + if (mdiSubWindow) { + for (QWidget *parent = mdiSubWindow->parentWidget(); parent; parent = parent->parentWidget()) { + if (QMdiArea *mdiArea = qobject_cast(parent)) { + mdiArea->setActiveSubWindow(mdiSubWindow); + break; + } + } + } + } +} + +void FormWindowManager::closeAllPreviews() +{ + m_previewManager->closeAllPreviews(); +} + +QWidget *FormWindowManager::findManagedWidget(FormWindow *fw, QWidget *w) +{ + while (w && w != fw) { + if (fw->isManaged(w)) + break; + w = w->parentWidget(); + } + return w; +} + +void FormWindowManager::setupActions() +{ + m_actionCut = new QAction(createIconSet(QLatin1String("editcut.png")), tr("Cu&t"), this); + m_actionCut->setObjectName(QLatin1String("__qt_cut_action")); + m_actionCut->setShortcut(QKeySequence::Cut); + m_actionCut->setStatusTip(tr("Cuts the selected widgets and puts them on the clipboard")); + m_actionCut->setWhatsThis(whatsThisFrom(QLatin1String("Edit|Cut"))); + connect(m_actionCut, SIGNAL(triggered()), this, SLOT(slotActionCutActivated())); + m_actionCut->setEnabled(false); + + m_actionCopy = new QAction(createIconSet(QLatin1String("editcopy.png")), tr("&Copy"), this); + m_actionCopy->setObjectName(QLatin1String("__qt_copy_action")); + m_actionCopy->setShortcut(QKeySequence::Copy); + m_actionCopy->setStatusTip(tr("Copies the selected widgets to the clipboard")); + m_actionCopy->setWhatsThis(whatsThisFrom(QLatin1String("Edit|Copy"))); + connect(m_actionCopy, SIGNAL(triggered()), this, SLOT(slotActionCopyActivated())); + m_actionCopy->setEnabled(false); + + m_actionPaste = new QAction(createIconSet(QLatin1String("editpaste.png")), tr("&Paste"), this); + m_actionPaste->setObjectName(QLatin1String("__qt_paste_action")); + m_actionPaste->setShortcut(QKeySequence::Paste); + m_actionPaste->setStatusTip(tr("Pastes the clipboard's contents")); + m_actionPaste->setWhatsThis(whatsThisFrom(QLatin1String("Edit|Paste"))); + connect(m_actionPaste, SIGNAL(triggered()), this, SLOT(slotActionPasteActivated())); + m_actionPaste->setEnabled(false); + + m_actionDelete = new QAction(tr("&Delete"), this); + m_actionDelete->setObjectName(QLatin1String("__qt_delete_action")); + m_actionDelete->setStatusTip(tr("Deletes the selected widgets")); + m_actionDelete->setWhatsThis(whatsThisFrom(QLatin1String("Edit|Delete"))); + connect(m_actionDelete, SIGNAL(triggered()), this, SLOT(slotActionDeleteActivated())); + m_actionDelete->setEnabled(false); + + m_actionSelectAll = new QAction(tr("Select &All"), this); + m_actionSelectAll->setObjectName(QLatin1String("__qt_select_all_action")); + m_actionSelectAll->setShortcut(QKeySequence::SelectAll); + m_actionSelectAll->setStatusTip(tr("Selects all widgets")); + m_actionSelectAll->setWhatsThis(whatsThisFrom(QLatin1String("Edit|Select All"))); + connect(m_actionSelectAll, SIGNAL(triggered()), this, SLOT(slotActionSelectAllActivated())); + m_actionSelectAll->setEnabled(false); + + m_actionRaise = new QAction(createIconSet(QLatin1String("editraise.png")), tr("Bring to &Front"), this); + m_actionRaise->setObjectName(QLatin1String("__qt_raise_action")); + m_actionRaise->setShortcut(Qt::CTRL + Qt::Key_L); + m_actionRaise->setStatusTip(tr("Raises the selected widgets")); + m_actionRaise->setWhatsThis(tr("Raises the selected widgets")); + connect(m_actionRaise, SIGNAL(triggered()), this, SLOT(slotActionRaiseActivated())); + m_actionRaise->setEnabled(false); + + m_actionLower = new QAction(createIconSet(QLatin1String("editlower.png")), tr("Send to &Back"), this); + m_actionLower->setObjectName(QLatin1String("__qt_lower_action")); + m_actionLower->setShortcut(Qt::CTRL + Qt::Key_K); + m_actionLower->setStatusTip(tr("Lowers the selected widgets")); + m_actionLower->setWhatsThis(tr("Lowers the selected widgets")); + connect(m_actionLower, SIGNAL(triggered()), this, SLOT(slotActionLowerActivated())); + m_actionLower->setEnabled(false); + + m_actionAdjustSize = new QAction(createIconSet(QLatin1String("adjustsize.png")), tr("Adjust &Size"), this); + m_actionAdjustSize->setObjectName(QLatin1String("__qt_adjust_size_action")); + m_actionAdjustSize->setShortcut(Qt::CTRL + Qt::Key_J); + m_actionAdjustSize->setStatusTip(tr("Adjusts the size of the selected widget")); + m_actionAdjustSize->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Adjust Size"))); + connect(m_actionAdjustSize, SIGNAL(triggered()), this, SLOT(slotActionAdjustSizeActivated())); + m_actionAdjustSize->setEnabled(false); + + + m_actionHorizontalLayout = new QAction(createIconSet(QLatin1String("edithlayout.png")), tr("Lay Out &Horizontally"), this); + m_actionHorizontalLayout->setObjectName(QLatin1String("__qt_horizontal_layout_action")); + m_actionHorizontalLayout->setShortcut(Qt::CTRL + Qt::Key_1); + m_actionHorizontalLayout->setStatusTip(tr("Lays out the selected widgets horizontally")); + m_actionHorizontalLayout->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out Horizontally"))); + m_actionHorizontalLayout->setData(LayoutInfo::HBox); + m_actionHorizontalLayout->setEnabled(false); + connect(m_actionHorizontalLayout, SIGNAL(triggered()), this, SLOT(createLayout())); + + m_actionVerticalLayout = new QAction(createIconSet(QLatin1String("editvlayout.png")), tr("Lay Out &Vertically"), this); + m_actionVerticalLayout->setObjectName(QLatin1String("__qt_vertical_layout_action")); + m_actionVerticalLayout->setShortcut(Qt::CTRL + Qt::Key_2); + m_actionVerticalLayout->setStatusTip(tr("Lays out the selected widgets vertically")); + m_actionVerticalLayout->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out Vertically"))); + m_actionVerticalLayout->setData(LayoutInfo::VBox); + m_actionVerticalLayout->setEnabled(false); + connect(m_actionVerticalLayout, SIGNAL(triggered()), this, SLOT(createLayout())); + + QIcon formIcon = QIcon::fromTheme("designer-form-layout", createIconSet(QLatin1String("editform.png"))); + QAction *actionFormLayout = new QAction(formIcon, tr("Lay Out in a &Form Layout"), this); + actionFormLayout->setObjectName(QLatin1String("__qt_form_layout_action")); + actionFormLayout->setShortcut(Qt::CTRL + Qt::Key_6); + actionFormLayout->setStatusTip(tr("Lays out the selected widgets in a form layout")); + actionFormLayout->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out in a Form"))); + actionFormLayout->setData(LayoutInfo::Form); + actionFormLayout->setEnabled(false); + setActionFormLayout(actionFormLayout); + connect(actionFormLayout, SIGNAL(triggered()), this, SLOT(createLayout())); + + m_actionGridLayout = new QAction(createIconSet(QLatin1String("editgrid.png")), tr("Lay Out in a &Grid"), this); + m_actionGridLayout->setObjectName(QLatin1String("__qt_grid_layout_action")); + m_actionGridLayout->setShortcut(Qt::CTRL + Qt::Key_5); + m_actionGridLayout->setStatusTip(tr("Lays out the selected widgets in a grid")); + m_actionGridLayout->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out in a Grid"))); + m_actionGridLayout->setData(LayoutInfo::Grid); + m_actionGridLayout->setEnabled(false); + connect(m_actionGridLayout, SIGNAL(triggered()), this, SLOT(createLayout())); + + m_actionSplitHorizontal = new QAction(createIconSet(QLatin1String("edithlayoutsplit.png")), + tr("Lay Out Horizontally in S&plitter"), this); + m_actionSplitHorizontal->setObjectName(QLatin1String("__qt_split_horizontal_action")); + m_actionSplitHorizontal->setShortcut(Qt::CTRL + Qt::Key_3); + m_actionSplitHorizontal->setStatusTip(tr("Lays out the selected widgets horizontally in a splitter")); + m_actionSplitHorizontal->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out Horizontally in Splitter"))); + m_actionSplitHorizontal->setData(LayoutInfo::HSplitter); + m_actionSplitHorizontal->setEnabled(false); + connect(m_actionSplitHorizontal, SIGNAL(triggered()), this, SLOT(createLayout())); + + m_actionSplitVertical = new QAction(createIconSet(QLatin1String("editvlayoutsplit.png")), + tr("Lay Out Vertically in Sp&litter"), this); + m_actionSplitVertical->setObjectName(QLatin1String("__qt_split_vertical_action")); + m_actionSplitVertical->setShortcut(Qt::CTRL + Qt::Key_4); + m_actionSplitVertical->setStatusTip(tr("Lays out the selected widgets vertically in a splitter")); + m_actionSplitVertical->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Lay Out Vertically in Splitter"))); + connect(m_actionSplitVertical, SIGNAL(triggered()), this, SLOT(createLayout())); + m_actionSplitVertical->setData(LayoutInfo::VSplitter); + + m_actionSplitVertical->setEnabled(false); + + m_actionBreakLayout = new QAction(createIconSet(QLatin1String("editbreaklayout.png")), tr("&Break Layout"), this); + m_actionBreakLayout->setObjectName(QLatin1String("__qt_break_layout_action")); + m_actionBreakLayout->setShortcut(Qt::CTRL + Qt::Key_0); + m_actionBreakLayout->setStatusTip(tr("Breaks the selected layout")); + m_actionBreakLayout->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Break Layout"))); + connect(m_actionBreakLayout, SIGNAL(triggered()), this, SLOT(slotActionBreakLayoutActivated())); + m_actionBreakLayout->setEnabled(false); + + QAction *simplifyLayoutAction = new QAction(tr("Si&mplify Grid Layout"), this); + simplifyLayoutAction->setObjectName(QLatin1String("__qt_simplify_layout_action")); + simplifyLayoutAction->setStatusTip(tr("Removes empty columns and rows")); + simplifyLayoutAction->setWhatsThis(whatsThisFrom(QLatin1String("Layout|Simplify Layout"))); + connect(simplifyLayoutAction, SIGNAL(triggered()), this, SLOT(slotActionSimplifyLayoutActivated())); + simplifyLayoutAction->setEnabled(false); + setActionSimplifyLayout(simplifyLayoutAction); + + m_actionDefaultPreview = new QAction(tr("&Preview..."), this); + m_actionDefaultPreview->setObjectName(QLatin1String("__qt_default_preview_action")); + m_actionDefaultPreview->setStatusTip(tr("Preview current form")); + m_actionDefaultPreview->setWhatsThis(whatsThisFrom(QLatin1String("Form|Preview"))); + connect(m_actionDefaultPreview, SIGNAL(triggered()), + this, SLOT(slotActionDefaultPreviewActivated())); + + m_undoGroup = new QUndoGroup(this); + + m_actionUndo = m_undoGroup->createUndoAction(this); + m_actionUndo->setEnabled(false); + + m_actionUndo->setIcon(QIcon::fromTheme("edit-undo", createIconSet(QLatin1String("undo.png")))); + m_actionRedo = m_undoGroup->createRedoAction(this); + m_actionRedo->setEnabled(false); + m_actionRedo->setIcon(QIcon::fromTheme("edit-redo", createIconSet(QLatin1String("redo.png")))); + + m_actionShowFormWindowSettingsDialog = new QAction(tr("Form &Settings..."), this); + m_actionShowFormWindowSettingsDialog->setObjectName(QLatin1String("__qt_form_settings_action")); + connect(m_actionShowFormWindowSettingsDialog, SIGNAL(triggered()), this, SLOT(slotActionShowFormWindowSettingsDialog())); + m_actionShowFormWindowSettingsDialog->setEnabled(false); + +#ifdef Q_WS_X11 + m_actionCopy->setIcon(QIcon::fromTheme("edit-copy", m_actionCopy->icon())); + m_actionCut->setIcon(QIcon::fromTheme("edit-cut", m_actionCut->icon())); + m_actionPaste->setIcon(QIcon::fromTheme("edit-paste", m_actionPaste->icon())); + m_actionDelete->setIcon(QIcon::fromTheme("edit-delete", m_actionDelete->icon())); + + // These do not currently exist, but will allow theme authors to fill in the gaps + m_actionBreakLayout->setIcon(QIcon::fromTheme("designer-break-layout", m_actionBreakLayout->icon())); + m_actionGridLayout->setIcon(QIcon::fromTheme("designer-grid-layout", m_actionGridLayout->icon())); + m_actionHorizontalLayout->setIcon(QIcon::fromTheme("designer-horizontal-layout", m_actionHorizontalLayout->icon())); + m_actionVerticalLayout->setIcon(QIcon::fromTheme("designer-vertical-layout", m_actionVerticalLayout->icon())); + m_actionSplitHorizontal->setIcon(QIcon::fromTheme("designer-split-horizontal", m_actionSplitHorizontal->icon())); + m_actionSplitVertical->setIcon(QIcon::fromTheme("designer-split-vertical", m_actionSplitVertical->icon())); + m_actionAdjustSize->setIcon(QIcon::fromTheme("designer-adjust-size", m_actionAdjustSize->icon())); +#endif +} + +void FormWindowManager::slotActionCutActivated() +{ + m_activeFormWindow->cut(); +} + +void FormWindowManager::slotActionCopyActivated() +{ + m_activeFormWindow->copy(); + slotUpdateActions(); +} + +void FormWindowManager::slotActionPasteActivated() +{ + m_activeFormWindow->paste(); +} + +void FormWindowManager::slotActionDeleteActivated() +{ + m_activeFormWindow->deleteWidgets(); +} + +void FormWindowManager::slotActionLowerActivated() +{ + m_activeFormWindow->lowerWidgets(); +} + +void FormWindowManager::slotActionRaiseActivated() +{ + m_activeFormWindow->raiseWidgets(); +} + +static inline QWidget *findLayoutContainer(const FormWindow *fw) +{ + QList l(fw->selectedWidgets()); + fw->simplifySelection(&l); + return l.empty() ? fw->mainContainer() : l.front(); +} + +void FormWindowManager::createLayout() +{ + QAction *a = qobject_cast(sender()); + if (!a) + return; + const int type = a->data().toInt(); + switch (m_createLayoutContext) { + case LayoutContainer: + // Cannot create a splitter on a container + if (type != LayoutInfo::HSplitter && type != LayoutInfo::VSplitter) + m_activeFormWindow->createLayout(type, findLayoutContainer(m_activeFormWindow)); + break; + case LayoutSelection: + m_activeFormWindow->createLayout(type); + break; + case MorphLayout: + m_activeFormWindow->morphLayout(m_morphLayoutContainer, type); + break; + } +} + +void FormWindowManager::slotActionBreakLayoutActivated() +{ + const QList layouts = layoutsToBeBroken(); + if (layouts.isEmpty()) + return; + + if (debugFWM) { + qDebug() << "slotActionBreakLayoutActivated: " << layouts.size(); + foreach (QWidget *w, layouts) { + qDebug() << w; + } + } + + m_activeFormWindow->beginCommand(tr("Break Layout")); + foreach (QWidget *layout, layouts) { + m_activeFormWindow->breakLayout(layout); + } + m_activeFormWindow->endCommand(); +} + +void FormWindowManager::slotActionSimplifyLayoutActivated() +{ + Q_ASSERT(m_activeFormWindow != 0); + QWidgetList selectedWidgets = m_activeFormWindow->selectedWidgets(); + m_activeFormWindow->simplifySelection(&selectedWidgets); + if (selectedWidgets.size() != 1) + return; + SimplifyLayoutCommand *cmd = new SimplifyLayoutCommand(m_activeFormWindow); + if (cmd->init(selectedWidgets.front())) { + m_activeFormWindow->commandHistory()->push(cmd); + } else { + delete cmd; + } +} + +void FormWindowManager::slotActionAdjustSizeActivated() +{ + Q_ASSERT(m_activeFormWindow != 0); + + m_activeFormWindow->beginCommand(tr("Adjust Size")); + + QList selectedWidgets = m_activeFormWindow->selectedWidgets(); + m_activeFormWindow->simplifySelection(&selectedWidgets); + + if (selectedWidgets.isEmpty()) { + Q_ASSERT(m_activeFormWindow->mainContainer() != 0); + selectedWidgets.append(m_activeFormWindow->mainContainer()); + } + + // Always count the main container as unlaid-out + foreach (QWidget *widget, selectedWidgets) { + bool unlaidout = LayoutInfo::layoutType(core(), widget->parentWidget()) == LayoutInfo::NoLayout; + bool isMainContainer = m_activeFormWindow->isMainContainer(widget); + + if (unlaidout || isMainContainer) { + AdjustWidgetSizeCommand *cmd = new AdjustWidgetSizeCommand(m_activeFormWindow); + cmd->init(widget); + m_activeFormWindow->commandHistory()->push(cmd); + } + } + + m_activeFormWindow->endCommand(); +} + +void FormWindowManager::slotActionSelectAllActivated() +{ + m_activeFormWindow->selectAll(); +} + +void FormWindowManager::slotActionDefaultPreviewActivated() +{ + slotActionGroupPreviewInStyle(QString(), -1); +} + +void FormWindowManager::slotActionGroupPreviewInStyle(const QString &style, int deviceProfileIndex) +{ + QDesignerFormWindowInterface *fw = activeFormWindow(); + if (!fw) + return; + + QString errorMessage; + if (!m_previewManager->showPreview(fw, style, deviceProfileIndex, &errorMessage)) { + const QString title = tr("Could not create form preview", "Title of warning message box"); + core()->dialogGui()->message(fw, QDesignerDialogGuiInterface::FormEditorMessage, QMessageBox::Warning, + title, errorMessage); + } +} + +// The user might click on a layout child or the actual layout container. +QWidgetList FormWindowManager::layoutsToBeBroken(QWidget *w) const +{ + if (!w) + return QList(); + + if (debugFWM) + qDebug() << "layoutsToBeBroken: " << w; + + QWidget *parent = w->parentWidget(); + if (m_activeFormWindow->isMainContainer(w)) + parent = 0; + + QWidget *widget = core()->widgetFactory()->containerOfWidget(w); + + // maybe we want to remove following block + const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase(); + const QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(widget)); + if (!item) { + if (debugFWM) + qDebug() << "layoutsToBeBroken: Don't have an item, recursing for parent"; + return layoutsToBeBroken(parent); + } + + const bool layoutContainer = (item->isContainer() || m_activeFormWindow->isMainContainer(widget)); + + if (!layoutContainer) { + if (debugFWM) + qDebug() << "layoutsToBeBroken: Not a container, recursing for parent"; + return layoutsToBeBroken(parent); + } + + QLayout *widgetLayout = widget->layout(); + QLayout *managedLayout = LayoutInfo::managedLayout(m_core, widgetLayout); + if (!managedLayout) { + if (qobject_cast(widget)) { + if (debugFWM) + qDebug() << "layoutsToBeBroken: Splitter special"; + QList list = layoutsToBeBroken(parent); + list.append(widget); + return list; + } + if (debugFWM) + qDebug() << "layoutsToBeBroken: Is a container but doesn't have a managed layout (has an internal layout), returning 0"; + return QList(); + } + + if (managedLayout) { + QList list; + if (debugFWM) + qDebug() << "layoutsToBeBroken: Is a container and has a layout"; + if (qobject_cast(widget)) { + if (debugFWM) + qDebug() << "layoutsToBeBroken: red layout special case"; + list = layoutsToBeBroken(parent); + } + list.append(widget); + return list; + } + if (debugFWM) + qDebug() << "layoutsToBeBroken: Is a container but doesn't have a layout at all, returning 0"; + return QList(); + +} + +QMap FormWindowManager::getUnsortedLayoutsToBeBroken(bool firstOnly) const +{ + // Return a set of layouts to be broken. + QMap layouts; + + QList selection = m_activeFormWindow->selectedWidgets(); + if (selection.isEmpty() && m_activeFormWindow->mainContainer()) + selection.append(m_activeFormWindow->mainContainer()); + + const QList::const_iterator scend = selection.constEnd(); + for (QList::const_iterator sit = selection.constBegin(); sit != scend; ++sit) { + // find all layouts + const QList list = layoutsToBeBroken(*sit); + if (!list.empty()) { + const QList::const_iterator lbcend = list.constEnd(); + for (QList::const_iterator lbit = list.constBegin(); lbit != lbcend; ++lbit) { + layouts.insert(*lbit, true); + } + if (firstOnly) + return layouts; + } + } + return layouts; +} + +bool FormWindowManager::hasLayoutsToBeBroken() const +{ + // Quick check for layouts to be broken + return !getUnsortedLayoutsToBeBroken(true).isEmpty(); +} + +QWidgetList FormWindowManager::layoutsToBeBroken() const +{ + // Get all layouts. This is a list of all 'red' layouts (QLayoutWidgets) + // up to the first 'real' widget with a layout in hierarchy order. + QMap unsortedLayouts = getUnsortedLayoutsToBeBroken(false); + // Sort in order of hierarchy + QList orderedLayoutList; + const QMap::const_iterator lscend = unsortedLayouts.constEnd(); + for (QMap::const_iterator itLay = unsortedLayouts.constBegin(); itLay != lscend; ++itLay) { + QWidget *wToBeInserted = itLay.key(); + if (!orderedLayoutList.contains(wToBeInserted)) { + // try to find first child, use as insertion position, else append + const QList::iterator firstChildPos = findFirstChildOf(orderedLayoutList.begin(), orderedLayoutList.end(), wToBeInserted); + if (firstChildPos == orderedLayoutList.end()) { + orderedLayoutList.push_back(wToBeInserted); + } else { + orderedLayoutList.insert(firstChildPos, wToBeInserted); + } + } + } + return orderedLayoutList; +} + +static inline bool hasManagedLayoutItems(const QDesignerFormEditorInterface *core, QWidget *w) +{ + if (const QLayout *ml = LayoutInfo::managedLayout(core, w)) { + // Try to find managed items, ignore dummy grid spacers + const int count = ml->count(); + for (int i = 0; i < count; i++) + if (!LayoutInfo::isEmptyItem(ml->itemAt(i))) + return true; + } + return false; +} + +void FormWindowManager::slotUpdateActions() +{ + m_createLayoutContext = LayoutSelection; + m_morphLayoutContainer = 0; + bool canMorphIntoVBoxLayout = false; + bool canMorphIntoHBoxLayout = false; + bool canMorphIntoGridLayout = false; + bool canMorphIntoFormLayout = false; + int selectedWidgetCount = 0; + int laidoutWidgetCount = 0; + int unlaidoutWidgetCount = 0; + bool pasteAvailable = false; + bool layoutAvailable = false; + bool breakAvailable = false; + bool simplifyAvailable = false; + bool layoutContainer = false; + bool canChangeZOrder = true; + + do { + if (m_activeFormWindow == 0 || m_activeFormWindow->currentTool() != 0) + break; + + breakAvailable = hasLayoutsToBeBroken(); + + QWidgetList simplifiedSelection = m_activeFormWindow->selectedWidgets(); + + selectedWidgetCount = simplifiedSelection.count(); + pasteAvailable = qApp->clipboard()->mimeData() && qApp->clipboard()->mimeData()->hasText(); + + m_activeFormWindow->simplifySelection(&simplifiedSelection); + QWidget *mainContainer = m_activeFormWindow->mainContainer(); + if (simplifiedSelection.isEmpty() && mainContainer) + simplifiedSelection.append(mainContainer); + + // Always count the main container as unlaid-out + const QWidgetList::const_iterator cend = simplifiedSelection.constEnd(); + for (QWidgetList::const_iterator it = simplifiedSelection.constBegin(); it != cend; ++it) { + if (*it != mainContainer && LayoutInfo::isWidgetLaidout(m_core, *it)) { + ++laidoutWidgetCount; + } else { + ++unlaidoutWidgetCount; + } + if (qobject_cast(*it) || qobject_cast(*it)) + canChangeZOrder = false; + } + + // Figure out layouts: Looking at a group of dangling widgets + if (simplifiedSelection.count() != 1) { + layoutAvailable = unlaidoutWidgetCount > 1; + //breakAvailable = false; + break; + } + // Manipulate layout of a single widget + m_createLayoutContext = LayoutSelection; + QWidget *widget = core()->widgetFactory()->containerOfWidget(simplifiedSelection.first()); + if (widget == 0) // We are looking at a page-based container with 0 pages + break; + + const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase(); + const QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(widget)); + if (!item) + break; + + QLayout *widgetLayout = LayoutInfo::internalLayout(widget); + QLayout *managedLayout = LayoutInfo::managedLayout(m_core, widgetLayout); + // We don't touch a layout createds by a custom widget + if (widgetLayout && !managedLayout) + break; + + layoutContainer = (item->isContainer() || m_activeFormWindow->isMainContainer(widget)); + + layoutAvailable = layoutContainer && m_activeFormWindow->hasInsertedChildren(widget) && managedLayout == 0; + simplifyAvailable = SimplifyLayoutCommand::canSimplify(m_core, widget); + if (layoutAvailable) { + m_createLayoutContext = LayoutContainer; + } else { + /* Cannot create a layout, have some layouts to be broken and + * exactly one, non-empty layout with selected: check the morph layout options + * (Note that there might be > 1 layouts to broken if the selection + * is a red layout, however, we want the inner-most layout here). */ + if (breakAvailable && simplifiedSelection.size() == 1 + && hasManagedLayoutItems(m_core, widget)) { + int type; + m_morphLayoutContainer = widget; // Was: page of first selected + m_createLayoutContext = MorphLayout; + if (MorphLayoutCommand::canMorph(m_activeFormWindow, m_morphLayoutContainer, &type)) { + canMorphIntoVBoxLayout = type != LayoutInfo::VBox; + canMorphIntoHBoxLayout = type != LayoutInfo::HBox; + canMorphIntoGridLayout = type != LayoutInfo::Grid; + canMorphIntoFormLayout = type != LayoutInfo::Form; + } + } + } + } while(false); + + m_actionCut->setEnabled(selectedWidgetCount > 0); + m_actionCopy->setEnabled(selectedWidgetCount > 0); + m_actionDelete->setEnabled(selectedWidgetCount > 0); + m_actionLower->setEnabled(canChangeZOrder && selectedWidgetCount > 0); + m_actionRaise->setEnabled(canChangeZOrder && selectedWidgetCount > 0); + + m_actionPaste->setEnabled(pasteAvailable); + + m_actionSelectAll->setEnabled(m_activeFormWindow != 0); + + m_actionAdjustSize->setEnabled(unlaidoutWidgetCount > 0); + + m_actionHorizontalLayout->setEnabled(layoutAvailable || canMorphIntoHBoxLayout); + m_actionVerticalLayout->setEnabled(layoutAvailable || canMorphIntoVBoxLayout); + m_actionSplitHorizontal->setEnabled(layoutAvailable && !layoutContainer); + m_actionSplitVertical->setEnabled(layoutAvailable && !layoutContainer); + actionFormLayout()->setEnabled(layoutAvailable || canMorphIntoFormLayout); + m_actionGridLayout->setEnabled(layoutAvailable || canMorphIntoGridLayout); + + m_actionBreakLayout->setEnabled(breakAvailable); + actionSimplifyLayout()->setEnabled(simplifyAvailable); + m_actionShowFormWindowSettingsDialog->setEnabled(m_activeFormWindow != 0); +} + +QDesignerFormWindowInterface *FormWindowManager::createFormWindow(QWidget *parentWidget, Qt::WindowFlags flags) +{ + FormWindow *formWindow = new FormWindow(qobject_cast(core()), parentWidget, flags); + formWindow->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + addFormWindow(formWindow); + return formWindow; +} + +QPixmap FormWindowManager::createPreviewPixmap(QString *errorMessage) +{ + QPixmap pixmap; + QDesignerFormWindowInterface *fw = activeFormWindow(); + if (!fw) + return pixmap; + + pixmap = m_previewManager->createPreviewPixmap(fw, QString(), errorMessage); + return pixmap; +} + +QAction *FormWindowManager::actionUndo() const +{ + return m_actionUndo; +} + +QAction *FormWindowManager::actionRedo() const +{ + return m_actionRedo; +} + +QActionGroup *FormWindowManager::actionGroupPreviewInStyle() const +{ + if (m_actionGroupPreviewInStyle == 0) { + // Wish we could make the 'this' pointer mutable ;-) + QObject *parent = const_cast(this); + m_actionGroupPreviewInStyle = new PreviewActionGroup(m_core, parent); + connect(m_actionGroupPreviewInStyle, SIGNAL(preview(QString,int)), + this, SLOT(slotActionGroupPreviewInStyle(QString,int))); + } + return m_actionGroupPreviewInStyle; +} + +void FormWindowManager::deviceProfilesChanged() +{ + if (m_actionGroupPreviewInStyle) + m_actionGroupPreviewInStyle->updateDeviceProfiles(); +} + +// DnD stuff + +void FormWindowManager::dragItems(const QList &item_list) +{ + QDesignerMimeData::execDrag(item_list, m_core->topLevel()); +} + +QUndoGroup *FormWindowManager::undoGroup() const +{ + return m_undoGroup; +} + +QAction *FormWindowManager::actionShowFormWindowSettingsDialog() const +{ + return m_actionShowFormWindowSettingsDialog; +} + +void FormWindowManager::slotActionShowFormWindowSettingsDialog() +{ + QDesignerFormWindowInterface *fw = activeFormWindow(); + if (!fw) + return; + + QDialog *settingsDialog = 0; + const bool wasDirty = fw->isDirty(); + + // Ask the language extension for a dialog. If not, create our own + if (QDesignerLanguageExtension *lang = qt_extension(m_core->extensionManager(), m_core)) + settingsDialog = lang->createFormWindowSettingsDialog(fw, /*parent=*/ 0); + + if (!settingsDialog) + settingsDialog = new FormWindowSettings(fw); + + QString title = QFileInfo(fw->fileName()).fileName(); + if (title.isEmpty()) // Grab the title from the outer window if no filename + if (const QWidget *window = m_core->integration()->containerWindow(fw)) + title = window->windowTitle(); + + settingsDialog->setWindowTitle(tr("Form Settings - %1").arg(title)); + if (settingsDialog->exec()) + if (fw->isDirty() != wasDirty) + emit formWindowSettingsChanged(fw); + + delete settingsDialog; +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/formwindowmanager.h b/src/designer/components/formeditor/formwindowmanager.h new file mode 100644 index 000000000..a3411683c --- /dev/null +++ b/src/designer/components/formeditor/formwindowmanager.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOWMANAGER_H +#define FORMWINDOWMANAGER_H + +#include "formeditor_global.h" + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QAction; +class QActionGroup; +class QUndoGroup; +class QDesignerFormEditorInterface; +class QDesignerWidgetBoxInterface; + +namespace qdesigner_internal { + +class FormWindow; +class PreviewManager; +class PreviewActionGroup; + +class QT_FORMEDITOR_EXPORT FormWindowManager + : public QDesignerFormWindowManager +{ + Q_OBJECT +public: + explicit FormWindowManager(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~FormWindowManager(); + + virtual QDesignerFormEditorInterface *core() const; + + inline QAction *actionCut() const { return m_actionCut; } + inline QAction *actionCopy() const { return m_actionCopy; } + inline QAction *actionPaste() const { return m_actionPaste; } + inline QAction *actionDelete() const { return m_actionDelete; } + inline QAction *actionSelectAll() const { return m_actionSelectAll; } + inline QAction *actionLower() const { return m_actionLower; } + inline QAction *actionRaise() const { return m_actionRaise; } + QAction *actionUndo() const; + QAction *actionRedo() const; + + inline QAction *actionHorizontalLayout() const { return m_actionHorizontalLayout; } + inline QAction *actionVerticalLayout() const { return m_actionVerticalLayout; } + inline QAction *actionSplitHorizontal() const { return m_actionSplitHorizontal; } + inline QAction *actionSplitVertical() const { return m_actionSplitVertical; } + inline QAction *actionGridLayout() const { return m_actionGridLayout; } + inline QAction *actionBreakLayout() const { return m_actionBreakLayout; } + inline QAction *actionAdjustSize() const { return m_actionAdjustSize; } + + inline QAction *actionDefaultPreview() const { return m_actionDefaultPreview; } + QActionGroup *actionGroupPreviewInStyle() const; + virtual QAction *actionShowFormWindowSettingsDialog() const; + + QDesignerFormWindowInterface *activeFormWindow() const; + + int formWindowCount() const; + QDesignerFormWindowInterface *formWindow(int index) const; + + QDesignerFormWindowInterface *createFormWindow(QWidget *parentWidget = 0, Qt::WindowFlags flags = 0); + + QPixmap createPreviewPixmap(QString *errorMessage); + + bool eventFilter(QObject *o, QEvent *e); + + void dragItems(const QList &item_list); + + QUndoGroup *undoGroup() const; + + virtual PreviewManager *previewManager() const { return m_previewManager; } + +public slots: + void addFormWindow(QDesignerFormWindowInterface *formWindow); + void removeFormWindow(QDesignerFormWindowInterface *formWindow); + void setActiveFormWindow(QDesignerFormWindowInterface *formWindow); + void closeAllPreviews(); + void deviceProfilesChanged(); + +private slots: + void slotActionCutActivated(); + void slotActionCopyActivated(); + void slotActionPasteActivated(); + void slotActionDeleteActivated(); + void slotActionSelectAllActivated(); + void slotActionLowerActivated(); + void slotActionRaiseActivated(); + void createLayout(); + void slotActionBreakLayoutActivated(); + void slotActionAdjustSizeActivated(); + void slotActionSimplifyLayoutActivated(); + void slotActionDefaultPreviewActivated(); + void slotActionGroupPreviewInStyle(const QString &style, int deviceProfileIndex); + void slotActionShowFormWindowSettingsDialog(); + + void slotUpdateActions(); + +private: + void setupActions(); + FormWindow *findFormWindow(QWidget *w); + QWidget *findManagedWidget(FormWindow *fw, QWidget *w); + + void setCurrentUndoStack(QUndoStack *stack); + +private: + enum CreateLayoutContext { LayoutContainer, LayoutSelection, MorphLayout }; + + QDesignerFormEditorInterface *m_core; + FormWindow *m_activeFormWindow; + QList m_formWindows; + + PreviewManager *m_previewManager; + + /* Context of the layout actions and base for morphing layouts. Determined + * in slotUpdateActions() and used later on in the action slots. */ + CreateLayoutContext m_createLayoutContext; + QWidget *m_morphLayoutContainer; + + // edit actions + QAction *m_actionCut; + QAction *m_actionCopy; + QAction *m_actionPaste; + QAction *m_actionSelectAll; + QAction *m_actionDelete; + QAction *m_actionLower; + QAction *m_actionRaise; + // layout actions + QAction *m_actionHorizontalLayout; + QAction *m_actionVerticalLayout; + QAction *m_actionSplitHorizontal; + QAction *m_actionSplitVertical; + QAction *m_actionGridLayout; + QAction *m_actionBreakLayout; + QAction *m_actionAdjustSize; + // preview actions + QAction *m_actionDefaultPreview; + mutable PreviewActionGroup *m_actionGroupPreviewInStyle; + QAction *m_actionShowFormWindowSettingsDialog; + + QAction *m_actionUndo; + QAction *m_actionRedo; + + QMap getUnsortedLayoutsToBeBroken(bool firstOnly) const; + bool hasLayoutsToBeBroken() const; + QWidgetList layoutsToBeBroken(QWidget *w) const; + QWidgetList layoutsToBeBroken() const; + + QUndoGroup *m_undoGroup; + +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOWMANAGER_H diff --git a/src/designer/components/formeditor/formwindowsettings.cpp b/src/designer/components/formeditor/formwindowsettings.cpp new file mode 100644 index 000000000..8e1844349 --- /dev/null +++ b/src/designer/components/formeditor/formwindowsettings.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formwindowsettings.h" +#include "ui_formwindowsettings.h" + +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Data structure containing form dialog data providing comparison +struct FormWindowData { + FormWindowData(); + + bool equals(const FormWindowData&) const; + + void fromFormWindow(FormWindowBase* fw); + void applyToFormWindow(FormWindowBase* fw) const; + + bool layoutDefaultEnabled; + int defaultMargin; + int defaultSpacing; + + bool layoutFunctionsEnabled; + QString marginFunction; + QString spacingFunction; + + QString pixFunction; + + QString author; + + QStringList includeHints; + + bool hasFormGrid; + Grid grid; +}; + +inline bool operator==(const FormWindowData &fd1, const FormWindowData &fd2) { return fd1.equals(fd2); } +inline bool operator!=(const FormWindowData &fd1, const FormWindowData &fd2) { return !fd1.equals(fd2); } + +QDebug operator<<(QDebug str, const FormWindowData &d) +{ + str.nospace() << "LayoutDefault=" << d.layoutDefaultEnabled << ',' << d.defaultMargin + << ',' << d.defaultSpacing << " LayoutFunctions=" << d.layoutFunctionsEnabled << ',' + << d.marginFunction << ',' << d.spacingFunction << " PixFunction=" + << d.pixFunction << " Author=" << d.author << " Hints=" << d.includeHints + << " Grid=" << d.hasFormGrid << d.grid.deltaX() << d.grid.deltaY() << '\n'; + return str; +} + +FormWindowData::FormWindowData() : + layoutDefaultEnabled(false), + defaultMargin(0), + defaultSpacing(0), + layoutFunctionsEnabled(false), + hasFormGrid(false) +{ +} + +bool FormWindowData::equals(const FormWindowData &rhs) const +{ + return layoutDefaultEnabled == rhs.layoutDefaultEnabled && + defaultMargin == rhs.defaultMargin && + defaultSpacing == rhs.defaultSpacing && + layoutFunctionsEnabled == rhs.layoutFunctionsEnabled && + marginFunction == rhs.marginFunction && + spacingFunction == rhs.spacingFunction && + pixFunction == rhs.pixFunction && + author == rhs.author && + includeHints == rhs.includeHints && + hasFormGrid == rhs.hasFormGrid && + grid == rhs.grid; +} + +void FormWindowData::fromFormWindow(FormWindowBase* fw) +{ + defaultMargin = defaultSpacing = INT_MIN; + fw->layoutDefault(&defaultMargin, &defaultSpacing); + + QStyle *style = fw->formContainer()->style(); + layoutDefaultEnabled = defaultMargin != INT_MIN || defaultMargin != INT_MIN; + if (defaultMargin == INT_MIN) + defaultMargin = style->pixelMetric(QStyle::PM_DefaultChildMargin, 0); + if (defaultSpacing == INT_MIN) + defaultSpacing = style->pixelMetric(QStyle::PM_DefaultLayoutSpacing, 0); + + + marginFunction.clear(); + spacingFunction.clear(); + fw->layoutFunction(&marginFunction, &spacingFunction); + layoutFunctionsEnabled = !marginFunction.isEmpty() || !spacingFunction.isEmpty(); + + pixFunction = fw->pixmapFunction(); + + author = fw->author(); + + includeHints = fw->includeHints(); + includeHints.removeAll(QString()); + + hasFormGrid = fw->hasFormGrid(); + grid = hasFormGrid ? fw->designerGrid() : FormWindowBase::defaultDesignerGrid(); +} + +void FormWindowData::applyToFormWindow(FormWindowBase* fw) const +{ + fw->setAuthor(author); + fw->setPixmapFunction(pixFunction); + + if (layoutDefaultEnabled) { + fw->setLayoutDefault(defaultMargin, defaultSpacing); + } else { + fw->setLayoutDefault(INT_MIN, INT_MIN); + } + + if (layoutFunctionsEnabled) { + fw->setLayoutFunction(marginFunction, spacingFunction); + } else { + fw->setLayoutFunction(QString(), QString()); + } + + fw->setIncludeHints(includeHints); + + const bool hadFormGrid = fw->hasFormGrid(); + fw->setHasFormGrid(hasFormGrid); + if (hasFormGrid || hadFormGrid != hasFormGrid) + fw->setDesignerGrid(hasFormGrid ? grid : FormWindowBase::defaultDesignerGrid()); +} + +// -------------------------- FormWindowSettings + +FormWindowSettings::FormWindowSettings(QDesignerFormWindowInterface *parent) : + QDialog(parent), + m_ui(new ::Ui::FormWindowSettings), + m_formWindow(qobject_cast(parent)), + m_oldData(new FormWindowData) +{ + Q_ASSERT(m_formWindow); + + m_ui->setupUi(this); + m_ui->gridPanel->setCheckable(true); + m_ui->gridPanel->setResetButtonVisible(false); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QString deviceProfileName = m_formWindow->deviceProfileName(); + if (deviceProfileName.isEmpty()) + deviceProfileName = tr("None"); + m_ui->deviceProfileLabel->setText(tr("Device Profile: %1").arg(deviceProfileName)); + + m_oldData->fromFormWindow(m_formWindow); + setData(*m_oldData); +} + +FormWindowSettings::~FormWindowSettings() +{ + delete m_oldData; + delete m_ui; +} + +FormWindowData FormWindowSettings::data() const +{ + FormWindowData rc; + rc.author = m_ui->authorLineEdit->text(); + + if (m_ui->pixmapFunctionGroupBox->isChecked()) { + rc.pixFunction = m_ui->pixmapFunctionLineEdit->text(); + } else { + rc.pixFunction.clear(); + } + + rc.layoutDefaultEnabled = m_ui->layoutDefaultGroupBox->isChecked(); + rc.defaultMargin = m_ui->defaultMarginSpinBox->value(); + rc.defaultSpacing = m_ui->defaultSpacingSpinBox->value(); + + rc.layoutFunctionsEnabled = m_ui->layoutFunctionGroupBox->isChecked(); + rc.marginFunction = m_ui->marginFunctionLineEdit->text(); + rc.spacingFunction = m_ui->spacingFunctionLineEdit->text(); + + const QString hints = m_ui->includeHintsTextEdit->toPlainText(); + if (!hints.isEmpty()) { + rc.includeHints = hints.split(QString(QLatin1Char('\n'))); + // Purge out any lines consisting of blanks only + const QRegExp blankLine = QRegExp(QLatin1String("^\\s*$")); + Q_ASSERT(blankLine.isValid()); + for (QStringList::iterator it = rc.includeHints.begin(); it != rc.includeHints.end(); ) + if (blankLine.exactMatch(*it)) { + it = rc.includeHints.erase(it); + } else { + ++it; + } + rc.includeHints.removeAll(QString()); + } + + rc.hasFormGrid = m_ui->gridPanel->isChecked(); + rc.grid = m_ui->gridPanel->grid(); + return rc; +} + +void FormWindowSettings::setData(const FormWindowData &data) +{ + m_ui->layoutDefaultGroupBox->setChecked(data.layoutDefaultEnabled); + m_ui->defaultMarginSpinBox->setValue(data.defaultMargin); + m_ui->defaultSpacingSpinBox->setValue(data.defaultSpacing); + + m_ui->layoutFunctionGroupBox->setChecked(data.layoutFunctionsEnabled); + m_ui->marginFunctionLineEdit->setText(data.marginFunction); + m_ui->spacingFunctionLineEdit->setText(data.spacingFunction); + + m_ui->pixmapFunctionLineEdit->setText(data.pixFunction); + m_ui->pixmapFunctionGroupBox->setChecked(!data.pixFunction.isEmpty()); + + m_ui->authorLineEdit->setText(data.author); + + if (data.includeHints.empty()) { + m_ui->includeHintsTextEdit->clear(); + } else { + m_ui->includeHintsTextEdit->setText(data.includeHints.join(QLatin1String("\n"))); + } + + m_ui->gridPanel->setChecked(data.hasFormGrid); + m_ui->gridPanel->setGrid(data.grid); +} + +void FormWindowSettings::accept() +{ + // Anything changed? -> Apply and set dirty + const FormWindowData newData = data(); + if (newData != *m_oldData) { + newData.applyToFormWindow(m_formWindow); + m_formWindow->setDirty(true); + } + + QDialog::accept(); +} +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/formwindowsettings.h b/src/designer/components/formeditor/formwindowsettings.h new file mode 100644 index 000000000..8bcf7b297 --- /dev/null +++ b/src/designer/components/formeditor/formwindowsettings.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMWINDOWSETTINGS_H +#define FORMWINDOWSETTINGS_H + +#include + +QT_BEGIN_NAMESPACE + +namespace Ui { + class FormWindowSettings; +} + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +struct FormWindowData; +class FormWindowBase; + +/* Dialog to edit the settings of a QDesignerFormWindowInterface. + * It sets the dirty flag on the form window if something was changed. */ + +class FormWindowSettings: public QDialog +{ + Q_DISABLE_COPY(FormWindowSettings) + Q_OBJECT +public: + explicit FormWindowSettings(QDesignerFormWindowInterface *formWindow); + virtual ~FormWindowSettings(); + + virtual void accept(); + +private: + FormWindowData data() const; + void setData(const FormWindowData&); + + Ui::FormWindowSettings *m_ui; + FormWindowBase *m_formWindow; + FormWindowData *m_oldData; +}; +} + +QT_END_NAMESPACE + +#endif // FORMWINDOWSETTINGS_H diff --git a/src/designer/components/formeditor/formwindowsettings.ui b/src/designer/components/formeditor/formwindowsettings.ui new file mode 100644 index 000000000..01caac047 --- /dev/null +++ b/src/designer/components/formeditor/formwindowsettings.ui @@ -0,0 +1,328 @@ + + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + FormWindowSettings + + + + 0 + 0 + 470 + 466 + + + + Form Settings + + + + + + 6 + + + 0 + + + + + Layout &Default + + + true + + + + 8 + + + 6 + + + + + &Spacing: + + + defaultSpacingSpinBox + + + + + + + &Margin: + + + defaultMarginSpinBox + + + + + + + + + + + + + + + + &Layout Function + + + true + + + + 8 + + + 6 + + + + + + + + + + + Ma&rgin: + + + marginFunctionLineEdit + + + + + + + Spa&cing: + + + spacingFunctionLineEdit + + + + + + + + + + + + 6 + + + 0 + + + + + &Pixmap Function + + + true + + + + 6 + + + 8 + + + + + + + + + + + + + Qt::Vertical + + + + 111 + 115 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Qt::Horizontal + + + + + + + &Include Hints + + + + 6 + + + 8 + + + + + + + + + + + Grid + + + + + + + Embedded Design + + + + + + TextLabel + + + + + + + + + + &Author + + + + 6 + + + 8 + + + + + + + + + + + + qdesigner_internal::GridPanel + QGroupBox +
gridpanel_p.h
+ 1 +
+
+ + authorLineEdit + defaultMarginSpinBox + defaultSpacingSpinBox + marginFunctionLineEdit + spacingFunctionLineEdit + pixmapFunctionLineEdit + + + + + buttonBox + accepted() + FormWindowSettings + accept() + + + 294 + 442 + + + 150 + 459 + + + + + buttonBox + rejected() + FormWindowSettings + reject() + + + 373 + 444 + + + 357 + 461 + + + + +
diff --git a/src/designer/components/formeditor/iconcache.cpp b/src/designer/components/formeditor/iconcache.cpp new file mode 100644 index 000000000..e11b41506 --- /dev/null +++ b/src/designer/components/formeditor/iconcache.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "iconcache.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +IconCache::IconCache(QObject *parent) + : QDesignerIconCacheInterface(parent) +{ +} + +QIcon IconCache::nameToIcon(const QString &path, const QString &resourcePath) +{ + Q_UNUSED(path) + Q_UNUSED(resourcePath) + qWarning() << "IconCache::nameToIcon(): IconCache is obsoleted"; + return QIcon(); +} + +QString IconCache::iconToFilePath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "IconCache::iconToFilePath(): IconCache is obsoleted"; + return QString(); +} + +QString IconCache::iconToQrcPath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "IconCache::iconToQrcPath(): IconCache is obsoleted"; + return QString(); +} + +QPixmap IconCache::nameToPixmap(const QString &path, const QString &resourcePath) +{ + Q_UNUSED(path) + Q_UNUSED(resourcePath) + qWarning() << "IconCache::nameToPixmap(): IconCache is obsoleted"; + return QPixmap(); +} + +QString IconCache::pixmapToFilePath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "IconCache::pixmapToFilePath(): IconCache is obsoleted"; + return QString(); +} + +QString IconCache::pixmapToQrcPath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "IconCache::pixmapToQrcPath(): IconCache is obsoleted"; + return QString(); +} + +QList IconCache::pixmapList() const +{ + qWarning() << "IconCache::pixmapList(): IconCache is obsoleted"; + return QList(); +} + +QList IconCache::iconList() const +{ + qWarning() << "IconCache::iconList(): IconCache is obsoleted"; + return QList(); +} + +QString IconCache::resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &wd) const +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + Q_UNUSED(wd) + qWarning() << "IconCache::resolveQrcPath(): IconCache is obsoleted"; + return QString(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/iconcache.h b/src/designer/components/formeditor/iconcache.h new file mode 100644 index 000000000..3e083b438 --- /dev/null +++ b/src/designer/components/formeditor/iconcache.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ICONCACHE_H +#define ICONCACHE_H + +#include "formeditor_global.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QT_FORMEDITOR_EXPORT IconCache : public QDesignerIconCacheInterface +{ + Q_OBJECT +public: + explicit IconCache(QObject *parent); + + virtual QIcon nameToIcon(const QString &path, const QString &resourcePath = QString()); + virtual QString iconToFilePath(const QIcon &pm) const; + virtual QString iconToQrcPath(const QIcon &pm) const; + virtual QPixmap nameToPixmap(const QString &path, const QString &resourcePath = QString()); + virtual QString pixmapToFilePath(const QPixmap &pm) const; + virtual QString pixmapToQrcPath(const QPixmap &pm) const; + + virtual QList pixmapList() const; + virtual QList iconList() const; + + virtual QString resolveQrcPath(const QString &filePath, const QString &qrcPath, const QString &workingDirectory = QString()) const; + +private: +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ICONCACHE_H diff --git a/src/designer/components/formeditor/images/cleartext.png b/src/designer/components/formeditor/images/cleartext.png new file mode 100644 index 0000000000000000000000000000000000000000..74133bafffc9dc28c4b0b3cea112dc0ff70b01a8 GIT binary patch literal 760 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igM? z4jwb3&I9fM00MYPL_t(I%dL~YY9m(^hd5E=|gVK5SETrOT?aHql# zpw7h>f)vJ&u!Y#d;B@;Mjvhc3u7VdAHXGzd9wf$~W+6njHJUp&O)N;lR>=A;f_Yq6v`a`CXppgDlJXqtWQ*yMRWc@j+|-ljnK6p64Ni z;4c8n<&x!c`87?`pYQMQKfm?Jx1`x@ekhenpNui}YPE_n24f7RQVHMp@qHgD?U z0IfB{;gF-FBeu7%byG$k% z!Z0KZLnf06-ENope9oKC^Sma20+3R!!Wcsk1pnIi+S(dYN}O{m?`i_)++CjMSZguH z(ChVxqKG(-iQ||kisT05}TlBOxGR*QPQ&Uieg)9KLZbQq7v z)a!LxtrlsTVy(qmJ6Nr;R;&G?lzLYv6t-?}Z+Uup;^N{0Ap}wi`u#phl5lx>iIkEw zO~1apynLEYr-=lZPNzu}MOFy0FNE+%qY+9eDwPVwVv#J%czAd~YmIa6_t)3gkJs1N zzx?eIA0HpTL{aojDfOe)dP{4)I%O#(N-2Qf7K_El!C>(DyU(=${{5b>bz4e#K%vkq q04{fTS)L7+>9T)yb#=r4sXqbVI`}h>_`S0L0000D)XO`;E7-c$%> zEyAGhrp|0diMaeo(UM8ooU@psW=nH(UpA&WXQtC~rb*6esAXk^Qbr8-^lo5Oka=bo z59b{I=lq`kdH>J*vH-B$RcUGIVx!S`WoT$V!$z-F#tHO=*HptA(+|c{7*D*dmj*p){qQ1WVoxi_-9*e~~N?JW{ zgPfe4B%5sj!z05O86AOAseDG@O{8U{1@ktbQfRunyV2kO3jNks=;?X!H99(a)y#VK zjqCuo4Wgo=ywqy-WLsMs1_rF??d?IA^($f};#gUbriHeE}h{a-OdF&#Q=-sWto3BrvI$7c2;jx?GD;TGU zQX}Z>>~vhZdT@^#gm#qwg@$yHTVSIr$}2redL-@d&l zFE4kbq@*Ybww|=mRqJ1}&&Z&rrY7HHYDUrpKK4X&P*PGtb|o^9k6wYX>NvC@Lzl5O||=*K_&%zb+8CvQAZpM1DLXwnrd6JsoroHg9k5&41RJ zkwIf);{ub(RQKS49Px?g5FQZ*KA#V{Tt4RK=eN^Yb0*3A?Li`uc$v*+1I>AyOE`n@ zZDHVYxlkw+pMrvd4ins)Y2W<3p-eBLjJHZ8MFc;Ku<%fDI2>rT+OLt3k>?4%)Sa%M znJr8D`ue&=1tJ$bG;AwELPDU^>AuIr#9SlzN_VF&tFW+e1-1SQ+C8{O4r9yKU<3vR zI(Kf5i;EKze2u$sm%-rRpn;;-nw6E6(bCc~5gZ&$6S3C8Dhm-S)bK_V)>rZqo;Bl((Cm@xw*N{E)Dn}aF+qi_WjiQ mZ%GqrX=yD4W;1D|f9VGpO9jynHk=Xw0000Zw~@EOfQR+sk_Oou&-fjcEVpj<&$!X3+c~E>J1*JV{%`IM zZgwlFFx}G*?uS>UO1?P#t>x;u4TbmfTg6u$kN3O$qTon=;uum9xAyEt-c|zv=78lpdFvQ2-{H`#GwYbpmwD+D`;ru|4Sly6 z!u|DfH10id;EhPQ?!aC0$4imznq|kzoDQ|k!q4v~%5Ey$Ds|~#XU>6Rfw7|8GuZ2t ub|G literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/closedhand.png b/src/designer/components/formeditor/images/cursors/closedhand.png new file mode 100644 index 0000000000000000000000000000000000000000..b78dd1dac5a827fa698f1993718f22c282019505 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?iy0WWJ3*My{N(AiKtWee z7sn8d^J_1j=3-FbaJl&N|NMQrDg{-+lDl3uEOL6iyxxIhJsV5IbqBU04WsP|EmPEI sG+%tqc!TF=!@MiLH_pW!e7ccq3Qxekuk)up2O7xW>FVdQ&MBb@057mJumAu6 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/cross.png b/src/designer/components/formeditor/images/cursors/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..fe38e744805f61abefcc555e07d399725c3fd7e6 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz=_jGX#;h346aDZ1vLF6&ZrWY3kt_3XM>{%UFuy$6%gUPvacWfgV8N9#n V_FPj?S`E~~;OXk;vd$@?2>`R9Dxv@Y literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/hand.png b/src/designer/components/formeditor/images/cursors/hand.png new file mode 100644 index 0000000000000000000000000000000000000000..d2004aefa7337edc3e15327992e1d69fda5b1831 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMr!L@pN$v;fPL7NDxSDXz&}>iqs75rOW+zd6_S+ yS|~6}xHpZ9x7T5rLt)D22WQ%mvv4FO#t=)GwlEX literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/ibeam.png b/src/designer/components/formeditor/images/cursors/ibeam.png new file mode 100644 index 0000000000000000000000000000000000000000..097fc5fa7287da71ffd907b3a11adbda4516aca6 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq&ztDY@kvlRIOU`3qAsV; zTo+`Y{*UQrTXjS>sKxPHd$OYcrG25=M&E9}?0tT1Yy7oqDMB$hU$uYoMqOpDnka4~ qygb6(Z%*F(qx}EZO#W7L6_Nk?#pi+cGI+ZBxvXgTe~DWM4f DkdQT7 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/sizeall.png b/src/designer/components/formeditor/images/cursors/sizeall.png new file mode 100644 index 0000000000000000000000000000000000000000..69f13eb347a6c299e06844729a14f657b282fe8f GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq(Z!J(8lD*)YR5__{zaA2hTRnD?iBlbDB7>)^pUXO@geCxo C3NS_h literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/sizef.png b/src/designer/components/formeditor/images/cursors/sizef.png new file mode 100644 index 0000000000000000000000000000000000000000..f37d7b91e8cde73243fa78df45c7808aba1c5fa0 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qqmr4+Dcjj*u+}&-pB%i42~uelF{r5}E+x CJu-a& literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/sizeh.png b/src/designer/components/formeditor/images/cursors/sizeh.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f40cbc3d77c566c11c32c0631b4f94f44dd441 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq&V7RQzr9bPir8&?922WQ%mvv4FO#sHOC(Zx> literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/sizev.png b/src/designer/components/formeditor/images/cursors/sizev.png new file mode 100644 index 0000000000000000000000000000000000000000..1edbab27a5b05555aaf515931f69ad6bf7e417f0 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqM`U1 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/wait.png b/src/designer/components/formeditor/images/cursors/wait.png new file mode 100644 index 0000000000000000000000000000000000000000..69056c479e9b2f009e366dfd71999a7c74f97620 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2^mK6y;h346;J_ZVI&3Y=<%J7po~YS&;ev>WRgqc?pLgqp;N^OZ7kj)9 zt2OYRn)8&w&_Fn;AZ-e>OWiG&j$ap41wJoZG>h5TTvwNMesusN!}2q-U)D!*O8^aL N@O1TaS?83{1OR$JJ0<`C literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/cursors/whatsthis.png b/src/designer/components/formeditor/images/cursors/whatsthis.png new file mode 100644 index 0000000000000000000000000000000000000000..b47601c3780eec780fdae43bab7481bbfebdddae GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*Z*JuL59RLGuTpf8VsdEq+OT~Tw87C;38om{D5bL fNHFu^21W*zFYzQLHV>pgRTEmjmPmE=~L&Tz>HVT3*E| z_sA``d`xT>Vi6bF)_L?&`mu&Zok(f|5s#P1v@>xR?aS3$H1O|%J3Jb??A z#q;V%47E1}nu5Nun!%J&Gnn!`nj-MC<}iW28F}(?QEa8ySD^X)yl)HB8mW!jN+p8> z^PWbd)ef!5ySFd+Zo;2Jv03#1!a;ak2@id+zW0kSUsp}@*zUDj-i%$D*gW|8BZ#8G zrOCVv%ZHH8!oCHEW`QGRVk_(V#QKNXo}+y3Q$Z=c)QojS0dO3L<%K0S(mQKFhZxbd zBFO3Dq*JuBGk^Z`t5IO|!j0Wi0+7q*n49@V_IGX^=n*4oUK^K_pLeNtHg{Q=PnnM6 z^okKRya4|N6A^dgB(ZDjUUca@2F8JxH-Q5D1){UnjEjhw82|tP07*qoM6N<$f|--~ Ab^rhX literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/dropdownbutton.png b/src/designer/components/formeditor/images/dropdownbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..5dd964946ce821f00df9f939d9835e3dc15229e9 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6w*!1aT!A!6puN2vqFzf&3&`l~ z>;#hk|NjS)M~)o%{Q2{X7cbJ&(+?gzc=P7X3l}aF7Z*oFL`;}4;p)|^XU?4Y@ZrPX zzkh%I`gQ#H@%{VvZ`rct>C>lc*RI{TapU91kAM95F>TtkojZ5#*|X={w{PFSfB*C6 z&#G0cPMtcnY}vAF*RH*N`*zWyMVBvMe);m{{Q2|e%$c)m*RDHv?#!Gy^WnpXy}iAW zk&*ZA-Fx%q&HelL)06&z{47xt6O{Q9TSd_y1c8 zRCC(X#W6%8JoKWg*dYf2*NaU%9$lEgQeIwu|9{tuB@bG5P5$?sceUfmBd6c(_tMfl z;?NsEYsz#6#-2}$T-VN6*8H;XS;)Lju@mpsI8>gQrtnHkDx}@D;rX%dSxNV-`VQCq zn3rJ3vorr|x_I&Xx7=A9*koG|YncD)3g72_zOshXKV3O_ zzG&$?!RM>$g^fe^_Wi#rpIPUqJToE0>h$}yTnjqOR8qR7{>!SWy_m+Lc=FQE_-7(l h4W4FrnDK`GWN5u?#;rQ9I{@e{22WQ%mvv4FO#sQL_xu0= literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/edit.png b/src/designer/components/formeditor/images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..a5e49adf99d73cdb5f9c03affdb8ec8dfee34cbe GIT binary patch literal 929 zcmV;S177@zP)Bza>=E25^Bq0)|NzVTH$bnDc49RjwPL37L&9Vr(Cnv+RUBv^m|{2VdxykyFLHB z?|z@}v)`WQr2znKqpBoen_7*}_FBFd@w3T?@FL%B82;2lL*tFV&Yw@Wok93420lla z&z86o=QjjT2|c=%7oS!RRu?QP0s@z7_G2|$vP`&&p!)- z4~=l+TA0YfNIekYitCypF<#x1rVC>=rx0!p!}&~TPNu^Xw)M1?v1ZUbE_Fb-qw;2i zzY?Y!z2moGy=vOys6HIICD5R5}(?uj|HxUuNgq%&3Dv`$x$48dxpC6 zMfkLdKF0bVzG{Gz1ul>9B65kws%&5ibv~HGpz!=CeA<+OU6ur2F+hwLkoahs8N#h$ zFq;7agg=kQr_BtoP=wPZoxbJQf+3vy9WBw{QY#7{$kM0q0)(3zVqwzYO9D{Ga;=FP zoNt*X&|l?>7U|#uUKm5+k<#Kz0y@flJ-c6Doay7UprgbM+Bvq+kZM5UGy9lW)OzrR z0QAW9@fzW|yU_O;1q5<+aKGI4W!-;Q9IoG1K07QbIwW00000NkvXXu0mjf DXpf{- literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/editdelete-16.png b/src/designer/components/formeditor/images/editdelete-16.png new file mode 100644 index 0000000000000000000000000000000000000000..ef5c799c15f3764ddd48671d8e907b884ce5af5e GIT binary patch literal 553 zcmV+^0@nSBP)RANu;3PQULfnZ9(JSNtE+ju(s1wC>x7D3V$V^7Tf=6{%cfER5-CQXJ#@H%=6#_%o z;$E8|JkkBmKny(et8{ROKdjwuT@fcR76buo+lFOXT9tubODR#UR^jfr zS+DHf50dl9buuCL*EhZ>Xg9;+QkP=n+t@UhvBQUnSw1|ILq=jdy ruuXn6#8`;&a32spLwtq4PC4uc7VgOrHrKF}00000NkvXXu0mjf+dAul literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/emptyicon.png b/src/designer/components/formeditor/images/emptyicon.png new file mode 100644 index 0000000000000000000000000000000000000000..897220e2130cd0fb0bdb3aca460b4229b883f908 GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@O3?%^u89IVj3+}8-|2h{L$Cqi9*>|X z3fw*KUg@mInx2k9p66Iq71ni)P19i8wy3o>Gs`lwPo5ITL=L0kZz3n5loBWb_`VOD z$IW`1Y+tCh(fXs)u!OQKAx%^G+VA0ZwOZ5aj`-#nINe5w;~11u#wdy+UY+no&KXee zi2?6D&x0@wo&6+%=GSUXxceNa|Ga?_lTdf3jL|W;1|$|%47P@(-wR1_u|h94Z-vD6 zYdvr=0YBw|8(=S{X~fX=4}xGmfl~UAgvA^jxq?jq)?_XLP7+uneg@b_|HHuG>hc2q w0jI(T{lf#zQ_C~Ub9-M9E6Z;?KS?8g035XLO8$RU&;S4c07*qoM6N<$g6#gep8x;= literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/fileopen-16.png b/src/designer/components/formeditor/images/fileopen-16.png new file mode 100644 index 0000000000000000000000000000000000000000..d832c621cc566451dfa8683d373e1aec0d08c283 GIT binary patch literal 549 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GXl47+C{+LR|m<{|^*lNI3yQ5Rh`5 zIqf8f>oE~3LIedeC*{PlV{d!6zY3iE$e{X0!LsKvxfg!_{{8FMuaBQTN#vaW^5x6+ zOP>|WE>Styp|T(YIMUZSD8(-+%o0@&5h$KY#vw z{_^Gdi_&pS^hb^7WfHyDxpX^yu4%4ZE>&`FEfA&q;(E;9)#?yj06peqy^xP`u z;7}I2$l-V{`_D&*v>!X_S47=X+kAL#@ZqiO>F-w;OSl)DT{H8xKkvR3n`R$P6x5ti zHKp?R#%89yP8VJjR$JfCG=H%4S?BDIT~3#*JGMl#bgR@X7F|E%-UJ^m{il+Nolj0K z5c%FZ$Kw;@IgwSdf3B_++p{bF(W9?3+f4Q52u+ho|MQ*Mlg&#f&3fe-P*i%l`njxg HN@xNALPZC! literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/leveldown.png b/src/designer/components/formeditor/images/leveldown.png new file mode 100644 index 0000000000000000000000000000000000000000..742b7fb84b03cbe2d316b6187a90232fc2bdc79c GIT binary patch literal 557 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 P00000NkvXXu0mjf%~7mV literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/back.png b/src/designer/components/formeditor/images/mac/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/src/designer/components/formeditor/images/mac/buddytool.png b/src/designer/components/formeditor/images/mac/buddytool.png new file mode 100644 index 0000000000000000000000000000000000000000..2a4287089b07bddcd695085b95ba5c23c366177e GIT binary patch literal 2046 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/editcopy.png b/src/designer/components/formeditor/images/mac/editcopy.png new file mode 100644 index 0000000000000000000000000000000000000000..f551364464a7f4500a2660fd3aa450e79010b0cb GIT binary patch literal 1468 zcmV;t1w;CYP)j91nuo^rMU``FpK}lmcH2BoZ zAH4JCyEWmjwMzgT#=uOCdmd7JuO$v)s8lK_84mKr3f?+?`0VR1?tg<)*HQ}J{^;_d zx+S0Y_4P-WQtN}){+tE#xOPNB3Z5T~fVOJdJdnQKmA@NUp8wZ z?$zp{)J%XNrjyRSus42A?Cl?jb<@afrs%r1WnkxaM7vZ3@+)At< zl`TV!sF2PwGTtu5AKvyjs4rA-qt%${9h(@D41&{(T zn8XDH>P|Q8whCx0La*w<^E@zO22eD33b<_!Q`>H{03sKo>$;F2@wvzODg1sK81Se7 zXg2VlTtfnRF4nRvL9>8>%_G3Q_jpaTF#^JSA{Ha<@i)NsIHARWyv9?(=O$}&1Ssr8 zEDDn5bPZisMnMBy1?bt#Jx4g$#%n0TPNX!_qa+0JbW~kLfJ9nCPGg4pF{+ML3+gaHLO^`>>3d5AaWOXXLDedCV3tU)P&1)oPTCjX*K5}0@QqH zO(G~76n^TCB#t^`Ik$*k#>bF(lmH~zwB+suqqLR=5F{VyIR3p>h-svrPjk~WiIfCT zL!y!I&5m8e$d!l4mh?p>q)stJ7Fjj8$jsFE%#H5~03raG4_Dc!$cT(gi@c~ zLseB0#6L51ows7ID$$1^8eBO$E%%%;5Jcm#~)CE0Jxx)4}dz+2FRW35VI&O1b5c ztKVJ_qymyagMj8jXkHb6&l~<30XPL<7Qj^iH-Gr@Oj@d$Ao7XPguGq^8idrf;(q`V WtAfH(WC6_p0000P)<*VVWcuS0Ev&yc^UgmMxPuCq=o+h)UUDGbvCiz9KZ#z>#2T zH*7#DEhZZSSjH(6t!!&*4Y=|TLKTXy*{F8wRwLgUrhFc}3~-1;E4$B<4F?4UCH_=X(?2pYg3Kt<)7W_X9hIt7q`_@k0wj;P zCfM+)m>3-G@5iiwfL76-3y8I0zP_2K%FFu)2M1B&N>2}dn~=~air9DByWrkbv=$cr zW-u6#IhUKu`P>}h9olgM)(;$H&K!xq0;} zmZYZMsIIQY*49?+?Cdmh{`6@qiHp0);s~!9ZNqy7us5e`YOW0r52ISG#^mH=tgWrZ z;^Jb|YPER0Aph2D9v%mU*9&I6j%Do_vBJS&_ko0j>t$tS#t{4a`=gDG4Gs(p;PHkA zOo@)}p3bJ6%BYPfuy?umb~`)&d?Yt_vZA5_v$L}?FE0-(D=V?Sz8)`Kx@6?CPG<;E zsnQ=5_9+F>$CJI__W+)D`vJy`ZD(eN1y)w5NlU}S`T3K(Vq<%gZN12L2=UTp0B0!K&mQ7SXV z{VXv z>S`#owl-dS9bgBYJtX4^;?#e#IfgL1l?{0uT_METfDHM8dfVYJ(58)+?c}9jxoO5BWAeIx)5pQns^E+0Ukuh;h ztv1mAZ^%=ymh-Ri^1_23+|>mnbq&=)SAjx-yzfh#osAUHgyWqb0uWC>@)i$fL!<{nxj2az zWULo$M?Zl;A;wokgwx|B(xbTVAc`Ty+W|iR44{biW-+;(vhGgE-Txy2O3^8bsV1R< zm`6NJ*b_@c-e-~mXEE9>Oc7uvhRb}>at@-pm;H+(Tq2CyifPM^nN7Kjgot@UgtZ8t zy~v}fZOFnG2`AXNx!ikGwxJ|~!)7H?Vr{}8S}4N(h%js^o}-X^&-@MJSdp!hcmgc| O0000 zME5R?{U5q7s2fuutc-4qF{a%$rcLUjDgoj0xc$z+&~Ze#piL)^=ggU5&Uc=3Eyr=_ ze-2As7=|$hGX9AB*qTEab=467j4}Lpw^Qw)>-ihDuf0NJWzqJd%y|0lwGJkY_aBwh`N~I2bKHodr zwhtZ!Kw8`kh&pa>Z)s#?gxO#^olXXW!MRK(lYDXyJOt+Y`kK1Bx@c%h_FuxFA1whi zvRap^O6Bx=y)-;LOzZ3Gq9l3EAOe8^g+ig=#>Pe(=caMj?~Xknfi#+kHBC%R(CFx> zC{G5#Trq{2as@!Q`XU~u?BOAeEiDn5CjQL7RRBe&YikskoTR>) znR^w&p5#3jT1R*I~?g8&J!EeUt96-WL0D-fYYpY~$Z(`3R17PgqV=^NVSN+6w5{@h` zk_CAV%6-_fx8ihTR{dFl@-6@~Dh>rB^AwIQlA|ST+l@$dp8y2wp2uQzSyS#cOsc@; z&?^8zJ$27Vs4u!Cl&kS?8^D7<28d^iG0MPl2g-c_;lXwpTRz0RJGv~+7@F&weRDJh zpgfIwE=W1Er~&drVIHop$g?fp3U?upFPm-&YWoTaSK3S<=2gpY%XpS?un3tA8j4m+ z)a}F8G6eO3?Eba7N`-AG$2~>I5N>D7Z744?i1=r+frhI?2l0?6Tmv|T@?0V=%8;)P zK)L^8Oza8e*khUnEg%rwC)|vtIY0^!?r=9~MkZvz*$9OeV)at%1#efX%1c=Lr>4m5 z$8K{Eu!0)TosCES=!gauqC&ZhDD(ZO{XC8aUcaLLxrpnxrF95&5OZ@?57j=y>XpHL zg5L-incrCwwmBL-=Z^-!QiJ!nBxRq_`bE1&xCY?p?`RE3ol@)1w^t;@Lolxwr3 zOkKwO1!fQQ6u669HJ6J65FUe`5Q%PUYDyYWk9;W7%P5+FBC>dz&j{Y1Xa* zduBDF2E5^`{AsbJM+5e7&4fnG!2Y_!4fIyQZB+BOgJ^ZpVO!(Co;rvOXv9B+@tY*M zxFs5}XI3NrH3XHykX7&+wNBB|d@BdaLoE!w9I)^PY8FJ?V5Q0lC_i-qTN^9HIc3*? zJ+m6&AUclZPam6mG+<91Lb7ge{F&@xHuP z!Ws>o-$!x}2|vu~0y>0F;Anep+3$4@_`R;7fX_7&@VQ5UabWUn*W&|86IQ$jCV(*j zbgp68GkCPK;88tXGd`dZ0~B}#3cxe)s3OnZ^K1M%?tfvkibhU=sth~<$ z*}g0@RW?9BH9+w{b4gg>4B*fd-z>x7jR+<fdqFuU@6;bFe<4>13931E4Ej8_0VpF@7fw=!*&ARG$g6<`1(y#r-X zj8}k2cSn%|MgVQU3mLb~^dZ1_I{NCJHtB=8$PT=JTV;1^Gt+i|YI-P)Pe5(wAcFCG z*?>BT0BQqf+8cP)PWbN`(Tf$ofjpT%H<eq= zj}fi!MNr8a$ogK^WCq0iH=-mkR6Av6ZgJzNrTQZ&Ve&p|sm{zTZUDw*Lg(#c!h&(! zIlF@J>$i~6v8;idMIoR91fWvFjP_3_&ssV6Q)dq!(wWUm{fZEF{+c}eYFg2a7?gnauVn$1uQX^X3jbQYysH$lf z8+_PqnCSR>9jG)LhimmMHR$ckdg_}$X*LemfU#!dXtlnDh}=;SE5^iv*pHuD7I@Rs zh(yG+qn$Be9GC#E1J~LSPLSTZbH}Plu|S;s{!f7x@C!IjzE4%v79N2!m^(<|v>8W4 zUVwE<+V>PVOX{{X2Aq||z)3PmoD4K_0ReF`@d*$)0<@<9RaFs5SCIb2&l1+%{$59r h3P?qy#cJ-%{{p-I^mMwt(g^?n002ovPDHLkV1h4QQJeq( literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/edithlayout.png b/src/designer/components/formeditor/images/mac/edithlayout.png new file mode 100644 index 0000000000000000000000000000000000000000..ec880bb5cba9b40cefead20133a99780b0de49d3 GIT binary patch literal 1395 zcmV-(1&sQMP)*PS)Y z?CK4Cb)~@uVH?^ej68l0lvJn!0b=p`|HH~cC)_+5(g}ofI+}` zJVFv1z+>Y{hXfKc8VxX?mxwx^BBu*R!yzJ3A_tXl+cjZbS1OzLHGs|d69zBynn5ks z0%@9H$?w&ttR%PuUgFlxoc@GVPZNqc0}Rs`(+S}02u%Z+&ZHKV$IecKJc}?KX)hpZ z)?K+awVmk{i{*lT0;U3kh0nuHZKu6BA)FN)=a?ISxIU_7f;e?O0|HJoEG} zk~ktLbO;#j3xgn7{dOHs)&k3UPGTMnlNb+E676q7JzxbGgtY|m2qA1Z&x%r2swXC;*ufRe;+VP$h(I?g15z zg>L|7Hikf{KXq7c!GI@r63p**luuVPezlo$k(iw(;} zMPu|6V8$w)H8iU~Ai4s<0C2Jfc-=?P6=hj!z!nqB%5N>Fu|;^px4#*g4tP%O&`L@hD*s3)wrs$Twknv$noOk?j;Hi$*gFL1c_oW=0b1M4ekD zNMh1lL^gq|l9dQk!N|VRD?27Gpdkk6y$RK{V^y+BGe_-Z)lt}$Ai?Wcp{||GxXyPj zSnm%oqCA|I5ITx68J6Dn6FB|p$CzFGp&cco>vKns9%Q!zu4hkuq8Lq6#RAfaF;tGOE$pxl8*tyMJpBK)Tq!@%7$JYJJ3DC852G zcOZKJhKv21=g-Y#AdUpUcw=p2xYD{QV_RYg(&T%!f|R{{6T$xL3zh`cpfwms~zfq{X&_^u1-19fsgM)IVWx1p$5BO-*BL1bWU z{pyObv9S}w!^0=(10kI-M*Ad3@{AjHSOgC!w<)p^^0LJ?G&QWKjrYX2XEIdDP_0*% zwyt&@2YPyXwv&7%>XAOzyiha=38{ia!b{5mW)jcg?4P~MHL-1K(bd&ORnV^ZsZ`2a zko<|dT3Ehj3rr(R%{xm(NJ3RE^zpCn@4>-+on9I-{1Z$-V`F0*GTOY~xWL~RJRSNi z)F}lV*xL!GPM?RWx=ncyMXP{m3G?p7UHECoV_ptKjFt`(z!!{TdWLK`5}NL=G*FZTj0$3%aBYa^OQ&=t|ILu zYHn`+pw`6K*=)DDipR-%o z1KeM?UffXW<(m)5*4EY|-QC@Xk@HMbNown7v`=y*4@OV%yvifAVvGBUcp1kP&++N# z==ghdbTomNQI15Nb(Y>lLqkIoa{mR%huJDx1!k;7uC7@u78CNsOb#A;Ps=s5w|o7_ zi_x#GamHl}7ITAD+A4A%u=xDBE`(=ek(yU(P)Y!59tcJFaFxuat!Nh#Q%SgFY6;{9 z8rNuAn}`a4O8`P`6$B7)X~@lUFQk=Ho@+p6wL5{3tp)+OR+z2mHzJKkC&J}28E+Fn zr3nB%W-aJOG;-w}JbZB9C$RD7CPKjF=q)qCFsqGZUkE~N6#y`D=PBANTkqv786mp) zXv&(M{xTlY?x%v7-=_Y(HU5AJ#_DN4x{UKIPD}UplW*jqYre3U|C~~d%hQ$1#3NcP zf14qwM|wLi1UYhKSyJiF$b_Y}gWq%mLtwZvibd#!32<%tvV$`j@_j9ySD0I8IbvSt|)xUXgoXaE!lMU)>t0RIDXYp~}EGa(KD0000>6LNbIlz6B`2qQdv>k|G?0tYZ=1OjinM2VkkmY zp~4^(;2&g3B-)|vF~muTeRh-Bbp~$n`RqGC{@(9i|q_RnueE3+;tf5wkbA6KQV zjf`8qc<&F=5?I)petkb$dHMYDTD@MMSDAKlU@#caYPDVf2f+x008G&Bc4;&kg)5}r z@8=|;19O3p3d{^Ep+=Kyq#_`jB-*q;R0Lod5>x_E%K(tTIfrwuFhFvRR2BT210#2{ zssJpGLeh6lAgL4yB;^3uuo9Lg!RTW&_Sx8TD+5SXfhLDa0N(8Fa`O2&H7Pnd=ErtBGWLLc0RTxTx!KdCqw%?>FKfD=>x6l&)9DaJQDF(1&F1tw%<^+8=^n5~ hd2PHW)PMhX{1-0^X)=$OtQi0R002ovPDHLkV1g?%0$Bh6 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/editpaste.png b/src/designer/components/formeditor/images/mac/editpaste.png new file mode 100644 index 0000000000000000000000000000000000000000..64c0b2d6ab88a7edf7b4087622fdb3e58c57ce61 GIT binary patch literal 1906 zcmV-&2aWiNP)2`C_)0KC{3#V>P6y5 z{***fN|TDJMNOkdidvOO7HMchA*}*}7Gf?oHpW-*jkUcO@9yl*<#ae&W!3d!3;Ig0 z&S=gYeb4#6Z_dsNDJA73gxC(OXK{5&DgVxWN&%DvUaM(Z)C2pT<2W5GBJbHh0G$Pp zUhYv^>8=kvz3Hc~@7wwFCpNBm^5Vsd3=R!3H0XJ#;K4yPdiU|>+5P0^XFhm!=WA=5 z!q0i|5$;h&3BZQ-Xm(dsWsa*MNl#A?;c%F{DV0iLObygI-2-(?v~(&$`FtMRwyCYHr6N8>qC&%Vxi1+d^%O{OfySWV z!oU88nVCbwMFIkd=q|b@@Cku&Te2)-AaKE`2t4(|&%XZBFS{NBHULqe8E8R?&w+Oi z|6qMzS%4!)jtE`XHK{oYVAZM>_%b?@5K>CdAOzHhg9HqnfKOvQE4V%}$-gJZ2yWWB zXY00Ydn&>qmevLc1$3$_b?U-?qE&u+Mw0EvKAQ>v`%3`kccl<`ivz5QR+6jKxa;R+-IR|+(t?knF)Iehi$%|z?!Rd1A$>X&3PNiY~x&HVlC&Di#hB191Y z_U_#)J32aCLkN5t_;n4#(9sasx{E7O0EVU!Ff{dyna!#oKA(?rvMh^4B0)_}jRFV+ z0_Z#-&H6t3$^q3ak1s>THFTx-nbY~tx0*}RSANJ4&{*(}tW zCJV`N95R`VDg(tvXJbo%O9R>GfukBlAwB6D7&`cU0$WN3VkxdB4UAP=c(DChzP#gQ z>bE>g&BI%{)<44O)2E5WVyfn+PMzZL;lpYyp0IHI_;C&$I>f<)2kGqWr2obsww>36 zr}+T70$^x1%ehI1)*T&e`{7Y4x4y#o13S3vJjP}D1exV~XnA};mZ;;{hn?zF93GwI zn@{cK%9Se$K-YCbp%9gomFhYYiC`E8u4+>EE^mPlOlBqVrLBDDz%NMY^(0a`gl>T* zNatK)ag$UkPj}wsa#ethty{R#+s9b8jhV9(7%G7LB4|rXi#oJ~5L8uFF_8psPL>4_ z8Vsi-yI%Y;;U&u$>3o-|{>vDF3R+t>uzb~{^iG7iF`ObcV=_9GVa&538kveEXn!n7 zLlHpj-rF^Gb#(v=D&?fT(Ux~KKLZ-HLC z+c&t_6KBlMkV@uBr!CJm2tXFk1n&(|0EMGof%%L)LYH^Xz_N%SZx7(Fj8M0-mV#K( zP(?gr(SPb~lJ)=Lp@&}}nf75>4z}Ye8$Jzbstiyf1*J@Ju3QF`8zMe@p#Y_-t@QCg z*w5K3XIcNqZbCtWtY_Pno?Q%G15k}7C<+M(i7aM^yJkRHsBxao56mz)Yq6u_EqZ37 z=z$c$pss@3mVwXEP`h+pBqfp*<`x0aG=WfNp8BU!y!u`j{kPJHYLn*r3bLj{&a^ST zantG=&YUP;0$}kH2=TvJC{=4ynAdl&XDDH#H>_sOy056tE{HQD(AL(rFo0aF2No#7 zD_J_{-WPyG0%G9|WX8Gyia|}uXhL!=<}#L& z=3u@q@$IS=jrR)RIF2$H3%`bz12b7k|BUBpmx(!PPNil4Y*zLU$7RpDE$!>8n_8bK z0xTYQdU~4C(NQMSL10eq#J$~ z14yUS>VHI6R~I)1N71TR5^3HH8t;??82xnExzRl($H!B$%YzvLCV_Dv4fp^Z2wHj5 za2=-{fcgNLn3zyM%iZ1G#L_x}hP70!-iGE6{oc}yx4p09bL9ZK{@CUG1c(6xfC+?w zN+7^INeTce#WOQAT)%#uOP4M&IzCO_*F;6bX8d)Lv#xaB8@_h#Pk;U0k8XRwZrQiM zo9GonTn98DPzam{bU*@5VNUn?gSJF4Gds(~__zu>Ff>Zau0pSFK{P!&W~CD!4xIbw zgG;C1?*rxllM)%smn;S&2ME9^fEZr@1VXWqTT@*9>@Q4b1NfuO7>(NzX7ndJ0| s3m^aX zKl$a)fiL*(_~ws)_{Fb3SODdguK*q-fPeh+9mSo}GyuZ++st-5a{6daiiVgbHk%F3IZlo{&MyXLvknm$ zhArL)=DjeD7yI>qGqAiISuK^-rD6RuBZ^SQ8I!??-xvzE5CSPN+^`%l3* z4m>(-u_CmgdP>_ka*8#ca;D_Mu({&$Vq{7Y?=5|2F+k%X#=M_^gLy!-Fsw8E%#dOx zI4}ZeX1!X|2FJWNcxQ0VpqiPr!Z;>SI6sdy&psB^&R#5LI4i7{YrOY}fDr|qI9SYS zTPOvN4the^y)fbI?%uOnMS`={vK)s)5&4|E`Bqi`c3S|m)*?YTKi_b2+#>>;b)@YA zX3+zRAQ*vu1~F1A5w{avX9?Z{V2beRlgFp8U%&p6TdYJ9hzJ*NuV~wr!-G18j6rE~3uqi<11S~OtB4PQ z7$cuNK47~o^t~s>iMI7nK-e|xhNfu{JSYeX3h-e9ZrsIGa0Y@iJo%)>NMY7B10u24@_b&4dv{ znIgtmoHb~HuJHsXl%gCSSjLqB5v;Y`qVC;4h6#!&MiiZtm=ec_ftX;}MEX7e&^8Um zK#WSxnOWy43MocJ8$babEnw|f;p$2$(^TibZFjb0`re_%;vHPR+u*H$ z>`?(@gtX0s#-R#b>xkP-*9H`2dATKofQkXGD-#@3hTw&q3iG~WyWZ?)ffexHf`WI~ zhfDBS3prCK!dmJ`Ho-C_XgrK#uGN@QK~xY?WS=H{@%!KL*T4K3BZ4X*fKpKuv{nfK zF;uHih4;RK!jC@t3@{HC@Xepj1_(do7WIqe@%K(nt+9LvfB!c;{lSxeRRJG*5?=p+ z+^xGh_w!e;UVV9Vbi|(AZ@~jTSl7oAaN`)=pPFZX0ad+HQK80&BFOt*)VfJUK&c>F zFZ&A+k-FaJzHvbWZUzC?RgeEad`Xmz(bD~&)akAe2aCcs1i#NP)D)2BfM%teFgTaf(}Mjr2nyC*=5n^>hf zrTl#nJTs65p`wUlY##v!dp%CSoCMi|7cXDU6wr?^5?Fj5L_mCz0QB!9s%{kjdmwt> z4GW_6_6!i6{}^DSFd#Ci4~Wby3(A^Ss7?ip`fxx3o{^Sn8K~OScnH}4)FTcAq9q>? z9iJsI7Y0PfXa5(7w4@+JD}!ifyR#BaDLu`dw&GEu`S})Ddou$>KYtBq&W8cfDDeQ1 zNH|C2Wk@8TB@-V@W&&ow%MtZI^!OSGM9V%P`h1!|^O>lsx$QPW)qsjB^0)kS!nq`w z9TgQpL97^)3Sy7GU0GmvDu_fR^QBLK1=r`@xapon?pmxwo9koX1{eaDz;AayR#iK3 z3tS)tQsCMxlDc4x2M9=kk&BTyLAGev?+H*<6_M-PS^y8r+H literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/editvlayoutsplit.png b/src/designer/components/formeditor/images/mac/editvlayoutsplit.png new file mode 100644 index 0000000000000000000000000000000000000000..5a02c944e03885a1d144b5a4119420e2d0de2bd1 GIT binary patch literal 872 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 UB0_&5sQ>@~07*qoM6N<$f^ZKn4*&oF literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/forward.png b/src/designer/components/formeditor/images/mac/forward.png new file mode 100644 index 0000000000000000000000000000000000000000..34b91f09fa3aa8e0b329edc97054c11defba8016 GIT binary patch literal 655 zcmV;A0&x9_P)cJAZUz`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/src/designer/components/formeditor/images/mac/insertimage.png b/src/designer/components/formeditor/images/mac/insertimage.png new file mode 100644 index 0000000000000000000000000000000000000000..b8673e13bc617c4796e8a6a272cfc8938d17c152 GIT binary patch literal 1280 zcmV+b1^@bqP)Fy+L)HFeo_&^jt2#QIBAPyXeAUIJ3{}lfRL2x1{3OaX`fe?u# z<_q7aM58g7bRHc${iv$c``&YoRcz=A70Sva?SjRr+PAoC?X`DZu6XbHHxUAe$U$I* z-NiNU{q+#VL2K=I>;gmt0AtKaV8?*4OVE)|G{fl&``$lGYI$VX`tp_O9e5C}+HoEQ zDvdKtVJd;$8Ns{+6h$b%vxv#%9;^Alii8J=B%lZWByWfdvKu-%2zm*M#XP>kVj8~%3#TK%ypJv$>h zKxP<-llcj7WFMS*37$Iu)jVVt`ss)n{O2IN@C5XRuuy{KDiZ@EHo9=`+KeQCAW@yU zOW4n}tIiObUAVppzpcaVZTR9GSU0OnH9gCKk7p(@L3;paufk6^0f98rj=QM}ml9yc zyaRzGg1|r$!qDpSTC2O1Jxl^|sN3G?sQ?}Xox2gOO_op@8Z}mkVP9qZSx2p+#zIWW5^Q z<}v!6*U0PvL?%0-X2GEtZbA?M9$h{Mcuiz(0;S5wgymbbhiz){5hUAY_4*~kq`=bR zj}#X^CG|{3dDfue$7u{WBUTVI{~F~4oUyu z{0~==IKmDac(cf$J)}}=631sLRX@Q4b1W^CX=V*_@;O28Ii&Lvv3&<4Wdvv@ z^AjMm4(|h&mrGncbBV3hCIDiD*4jNP`%A1}-k`L%NUoYAE=I@@7WUNXHQPi!qMKSe z?!Od^7brwuQLsmm&Iv+Oo}a)EosURI&bjFEA#jKXQ3ATk9jpSv4yvK>WIp7Vmc@zV zgQpVed;`4t&k5Y@hP0X?GI`b_A^<@XS z+9zDUJU@ZbVjlB&8Bk%PA}TE4oP!Zf6cHFWoCwwlao~v%Hc~J#oT|g|+JJrL49oUg z0^8(gdw2d0YF5Ruh|?PS zI}CRIE7_aJUS~HEP6F`kGW`7pZq@7cH}pHf)TuRPIHQ&UR!KJIjGCRCtjQSncRnrv qX2OZcj59#tR7g23Hhum6xc>%pC}OM}#;hd(00008XoPuMz?Y|Rw{0vjgv+B!ch zy;dm_ut20@$;9K+a_T>ye{*<+*6l4ly3tG?44ev#Dh)O)`svI2yTab}@8^|GHkfho zNeOTfAYE#xV zTYdQ2V(!{EY<(cpv1OlC|Mk$nFJwxr5_&M{ssYb!Y!6iMT-Lnu z^%LX6O%qa2gs^lmow~IC?DKoCclAn#7p^FN%^<|Ei6_mNUm#sTTOc~pY5nu>t=(d~ zq~A~M3_fyFjkTusWO04OyzP?5OwSte2Tr^2{`Z+?r;S>P_p|Ifa>5t{+wz6q>78?A zjm)yTU*U1P=G*Jdx;OcCUKB)#cx$;a{ezuM^;+MJ)Bq-|n aKk(|Y38wz&&UFFC3WKMspUXO@geCxP=D{TZ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/plus.png b/src/designer/components/formeditor/images/mac/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..1ee45423e3958221bd545ee6f122d989302cab3f GIT binary patch literal 810 zcmV+_1J(SAP)B<|3F)+>u<}zfuZe7)NT=Ybf%p@9RIu@KocNNrs{{OFYs@fvL zX^u9hJWdQiy;9GCE&}Y4r^W#*azk#i!X z0tQeD-}3nENIJp}&;hlob6(uM5O*#)UjX|Y5+K#c2tsS zZMN$5yBRrqOa_ap_a;nIVsn9|z+v%N9CfB%If8gRp1^qXR>?32 z*3w6Ivpw-?m;j)qg+kXMEtJw|G)lG5(gLLcMIhjghO{k_bu#)CdcjL^)(HV4rIl#l9 z`bJLq*V>D~Rp82RirDxVP5}7Rn~Yjj!U$|-YlH0pxKM8{H$~096rTrE8Kjh7nlCq5 ziUE@h66K&=O}1m34oT2-J2r6&15LJLTiV(KGC9`_1Eo1-3Ch!yU=dz^csbc-8xNq17Gynhq07*qoM6N<$f^&^p>Hq)$ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/redo.png b/src/designer/components/formeditor/images/mac/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..8875bf246c5ec3fb50ba3e8b809152aa421227fc GIT binary patch literal 1752 zcmV;}1}FK6P)w#k*dH0@s?zwlm z0O;}}(dB;v|0x0=2BvrrNkpMUCb5LD`&S7tfe%rtTCFuCWa?WIGmWOiETbtk=jOfm z%(J%^t4pu*eVrs{`&A!+g#61X@%!_DkzT;K5LT=LJXbRjt5xO#LA-izU|SLZ#}&kKD)KqsB>}00 zz}kFWv)iH!9Z7^gGXWu`YCSN8y|-oWr!la92C%9W$U6e;JOyk$38Z}stSBZK5}LgR z_-GZqLNasIKm^IM~&O1z>aO*;|pRrPtGU z)-_h1GMe^ZWuoK2$~`n^9*OW3w~F_r_&zDDmJ(IC9=Qltui0T zKgP-r>09L3*%5)laPw7~`l_==Q{@#Py%q?kBL3MVKt(=gdqj*C^oFM7POD$IroJ;R z5d@Mp?$o!aGz^vwM12L6pQj307CD{TEh-m5M7^o%M)ehwdHeT3!a-o+c6wwZFq6LV zCW+4&M^k2QTM}=X6m|QgVwYHVisi;HLQ}8V&x$CadM{9^Bat-PlBtuv6 z-OYPd4IIll?uv3CU<=?zKX|bnVZ3IQ05NG=!=t{iCNC&{sIWDlI&+t2V23d`%K*rvVi`6|##YIIhmg4;R={PD#~qN#Jom3yD0fq2U&Ag0hOgYI~2U z0esa>qI(DE=I9?c-YFm{CA0kOt)hB*g6)VP2_<)iFMZ*~eCOBXyy`}Ud&B}hA4m+6ctgT^KX8ow zoJkJ8tI~^V8}3%z1PZvmH*u`j(-U8CLgKi%a=B?wUHdZuk-Uejun+ON3^>GY=8~n; z=c*56m!4`?#uVOoYohZkJ|9ci65}Ln<+FDtyG3l+RehnQ{w`4W3vh^DI`9*FPE(h2 zLdv3(s$9p@$NpvSh+tRl9>)uQM9gyd36dtwIRCKLJ*^9i9q5D`LzwnK<~7bw5=lV{yz zEPEMyh|JYf=6>-6o@)!_H}3L`tBf&4#vCbg^pQDjI%PsaK};nao@7G#&Ov^wkY^{z zSi@!PzA{&Lnfu9ONDL!J5Tl6EGFXRWD8Csc&kU0>2FaLxWR4!z>YuoX_E)M!VDz;D$H~RQtAOdA+G=b|DVXfKoI@;pQ;7a z!c!9D7tA1OVDFM#)bRFI`d!AXvO1u+s;7%%2#0KP!Un;Fgaq5gCk{*~e&)l-9KQS# jBO6=3qr$;M$qWpMPuN;S?ziRvH86O(`njxgN@xNAXMir1 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/resourceeditortool.png b/src/designer/components/formeditor/images/mac/resourceeditortool.png new file mode 100644 index 0000000000000000000000000000000000000000..7ef511c2b19e8dc7161d9bac239a05d66a7bcb16 GIT binary patch literal 2171 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 X&4>18D&iK100000NkvXXu0mjf*I?IE literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/simplifyrichtext.png b/src/designer/components/formeditor/images/mac/simplifyrichtext.png new file mode 100644 index 0000000000000000000000000000000000000000..a48e974bf5f14314d188630c53da6138a9cf193e GIT binary patch literal 1988 zcmV;#2RrzQP)Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RV2oVYm5CSR%EdT%j24YJ`L;(K) z{{a7>y{D4^00%@#L_t(o!<|-ZY*fb;K6BsuFkbKag|IQExL`0gl?nu+@Q{Lx8WMsi zQ9~kCP%0&Al^>NFl_vcW2_;f~B`Q^>qPKpV}25iIH zcy5F%BaQFQ_1rn%`ObIF8FJ#p3F>(sP)cE;gAfvYk554m@cI1y z{rms=FyBh9>q28=BUD#cgX1`}4#W`#Rtz${2Mt@61>N1$ z8Nf3T02=P;=>gleA07ar=bqz1g+c**-=EiG{u?Lm6-5zLRSg)x^`nGS3di3xO=xRt zgIq2Lk|fOw00$aFZ?|pR25Z)=3D$TX&PQT^do0T`Y~H*XP#0kx%;h~A;RaKwRNy^l zV{_dMVr@V#LevqXv$^}@ybAO`p#0x5rW z>&p9YpTFkMVgO@~3|zc;F%TmvJ4`=%ib`L&L?8$t>?OYEs>hs1qX>;o8OC?TSs=#58P{FE0VMGi zA*MVAKKsJ;YG2#koRYYj(E!GM$BrG}qaXL6L7ccRFpj{AL|M$h8^Xl#10HHq0k_%_+xF@e~c_l;`thfhg0L1XX%Th$yH5Ac_D~ zlk&dlq$NK@Aqwg2*|UMrcp>867Yi@UPE%78)YsRC7;p(U{^xhgp1*m~+2{Br^-`$@ zL?qy53gGGXRrc?sxIoLA>R8*Nk`Z);V=~-!rs?TP#(B9q-x~>O@qTuG!1`h#(#+FVg zqn23}NG{51SzWZuZCttbMJ60ki!VAssr->;>&anDS3dmBJKvp|1ppbqEZDelBiO92 zyX;`zR_CHTT!K@nO157PLKRoLA(}e>iau5fcD4!}rmM25O0ueD7bz=WCih=EmoV}J z2mgMoR{H6I+oz;(`b>KjDyI*M`Q(!+T3~IskcN?=Z@|c1r@tzTweJ zUz~=etCqvkl|Lp8tDlp`?_W=tg*#8V2Ha)`oSJ5U-)K5JJ0X+F1l~NT8S&x!j3vu} z)eROTqW=SZya#HQZHI=Yee80f!d3O3&~>^4hHv%}+ayr8X07j7MxGsVot<(sNbAFg z57XAxR%mHy31W_A7}N1V3``rRENzW@Fm&T6h%g3=Ynq{^?s4in!w_5xMF~76A$_6% zSN_#QMu$eAq+B+Y$aWhjli zkgQk-iNs>??S4Xilj(^uDyggh#YkC26xKez3P$giGm+l|-L!2ZmmbA)KKZZ#X4L@{ zB6^QNihIEf7y)vbe}gIa!lETU#FDiTiwVgIQ@V~;M(Jm9{3xuA`OyO z31;?dR;;oNIJ-+kRERQrD*Gb<-Z8LYu@@A32o^QgL2aEfk&OACWlaM>ue-XsptrYo z&Wpqh{j7obHkdk7yfOkyDiqLStEry3%)Bd*z#w*2o823tj183;o&q3>8gU#G^xR<1 z^XwbG=VrqI{&MTqt?3ZVKL{&U5rCvllO=R@Rv%lZMruHG#wapT$Y+_SE_kj>ya;RW zszW8kf@sMKR{uxfe)=0`+v>C|dT-{$1&;UMQh%mt{4sS zcE@5%UR0FCc3F&-icH_Jcm=yZVn(1it6^~Pzc8Np?z|lFe@%%0?Q_2z^cDml41|LS z*6&qrcMD!C^_0d%rOTGbN)|7XWtNlFC!$3sbI0FH))$<? W)Y5qsq3FN>0000T5TmvM+jjFjje(gl8^*brj8M8M?gU~iRNMmEh!j< zgcbx86A7T)f*~v>B$LT)bK51mxk4@k67D2~PJi@=GoAM7c}~9f<6FvNr$74RnfE=H z_nhaP_p;wc%t=|3Dd_k=ArnA^6A?5F3=H7%<;ysC?i?;$xS;ck7cb({rAs=VKYw2J zlsQs2Iy$QIl`B^;Ha2GK={qtqqA@t9AIFm?PlCB_N}#s37GAFxO-)Vc?Ceyuwzi_Z zycAPn5w z+lz*V23<^;;IxrIAkZNs>NPOu!C+96a%3Vyieq12pX!NSOV46)mkMhWcxVpkN})BSWxtSpI?)j zs`~r;RVFd|(vLDpQ0Ck(1dYt?2}_`|veIs!cERt{t#G!JBhLsEcXf5y9kb7X5b^tc zwm*}Soaq?1Ei8e$x;kaN^OGh#x#|;4S@f|cYH1H3b4x8EU)-mB>}dlr>6&zi}xWht3rbY13|5HSwR&J*Z5FY)rz_9 zaxBO^tiA;$wJ0t3qOrY4iABGBK(;W5p`jrmKY$Y_PUzKR4C0ozfCRX={^NcW?ryX@ zz#!aP+SuB+ifh$2I5?=)GBDqe4W;#}r%ZpYrN3GGxo5!KaRzv2XPq64!Oi|t&M$6& zYk8UK^=8Nx?W^=-UfN-#6;*}24XmDih6J}EECC)aRz=56A9N3U9~N%U z;NXy+f#e*Ig1*#m-&v-kiD_<7SOUE7JUm=fw5Q1?K<1}TokHr`PceJ>A?*SK(ndYk zoIZUToAzAq+WJ55!u%DbSi7+Vt!;jdNk49JSOUDSY!w4T=-u z)kl$AP$IW07}5bIf9+#XR-uCr2a_`Z4;;yGr>{}!gs`EE>q zW~Gjk@+uiuPR`@f#I^!iWs>xRNvx7qzg-C|&a1$K(JzckzB`$iHk&|aXV9*m?d8a? zqtVe3)z6S5@6`iGyFMQ!USORESXz1=SqFhlH9(4cH_QQ~-a(+#N;uhmCM6NJm7iKO z<1?^q#{}Gar6Pg*f50CO1Ka9=R|;D-G2?^~U~(oKIWuB^`;iF0_t;+dhZD$=*5({? zpv-|ox3CGa_WuKErGLfpcTa0#`q%{c;5a`Z%?Bvw{Lo|vvOk*81C{TwaKr%*LT#Q< z{o0CuA-(u45?AfQ+$DvYn0lK4pIY-3%ZfRYfSE@pypCs<6d-a=3L;}tu8&Swy7|A` zfLDuJu<;la1)k54zP%gCE8oX2=Dv#9MQb(j#%c!vKCdKTz~tlO<2t{3^(yiy(MR?t2LD9UDjC!6E#1Wg#M63ApD6Q<0UO6GGsR zAGFy79DmL70x{36?wq|ax7(GR)8|UcKIuxz>YtUEH83kNb5OA$bLgh9>~~Lj5}Wq- zB7OY@Jqqtf?o@s zaDei!1hImcsgI}r?e}kN#@ydzz_lO)4?HsG$D;oVo8XHU+iwWIE%=_` zM}h|}GCvbMDwyiPqn7?*!9#*6Ka6~0p4G)~3huB9yX}?)93*bHNPJCjr`3(`2<{bp xS8%`J0SE54h~8%x|UWDJrFd7@*pzS|20U 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 KMNUMnLSTZ+RFne% literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/textitalic.png b/src/designer/components/formeditor/images/mac/textitalic.png new file mode 100644 index 0000000000000000000000000000000000000000..0170ee26a6fc568e9b6490e4a45e04f4ff55a19e GIT binary patch literal 1164 zcmV;71atd|P)8>eW)l!AzGVM8#k=|EC4uK5YENM=>0xku0=q3%Tx@Cy=CPKKK1sA6|N30mSsXZy!Ca>jN5} z@ZcGS$#QE2LlE2rz5BYAo}Mll$j*K|%ZG;~fQZL?LqSQAOdFul0MPYJH;4ctijdG_ z5a~b(`Hfo!07M>23W&KczB~`e8_x_6C8wrdNy$o7u}}z)Kro07?K(Su>4*Gx7p`nl zYUZGg?Wzc=9^N~4?4`4#hsF{$6F`yxG!9=sR6)3Adm7^=0c_h{z*wD?ikr7E|LeX9p!`Qe zVeP4KSFcvH+yh7q@bIs-s^8}b3=O7Jp<2z&ze5H1Pq9G#@4tX-A{Eza)PCFo0f43J zf9+WWM&o)q!f!u|`adWGtSsjY%>SN5V5F-z$qNt|N&E^sblsUNiKxfZ@+X4Irqzh>Aw?C>se*5X~I8~7jLZNj*@*{BD zUO@ZJ6-0e@^8J@X;qYXrI~6f%?iI`!&wr`Ln7#Z+EHeNAQF?%ekr1Mh7@7R-EPzys zNMQ8U4<=rC^VDZ!Z=E?eG<P`TXFfSHJ*D@>`qd^VQnd=ezrG9QHJSSY3ZUEJ=g0fT zkBs-_mRAPPef`bB&Gp5TrIm#Z0I)p%8PY(gCfXOF5Rm204owR<;0UEc(d27P!)zAv z>vbwKP*hdwkH?iQ{H1)`U1(GcsNO9#DD)!I^av5}xeI=opS|HYaAtXF@pWvmAUI8_ z(XdUkdS?wBM?e9h6p%GVjie}d4j*n8Z(gq4Eg27b4mb$=_2TSh9Iqm(H;;%!qwt?K zO%DykBJ5W9ckTj*U`QP?kP1>pw(f&6t*at6Z~ogxz(J$Kqk#Y|vgv>H<;eCC@J4|V ewuk0+hta>;sLepRg`ge)0000=P)qhXCOuvAB1>1$~LJ*45g^Hl43wge}u?lK;3c7G(7uL^E5Eoro zL90--YNtA5+o_%DOxu}v?@f*&Sp>rMPN^99b8>IaBe^-hcTOUz%0ca^6AJ+Zpx8kv zWP9O@xpHtrR5ed>eDMZwjNv9D?{#!B!#aQA-27btM`_2-d@w(oGBLD$fDmHeVGMDMWL2|_QD!rE>$TGWX#s}PlC!gbz@_VO zXlCQ&mP>1bh_Gw}-~6m;@(`47*RpTpO!R@^eOm0-S2}th@F66hw(D4#!o^>eiIwpX z48&QA%JXpi(W8Y}d@=f@49>lJH+@2+_S0fis_ z1ZNGiY3#2HjdKw^!%u&Kk&poYKKfXr)DY&_D>a0*Ol5~443(_~pu-^$&H(QLHmN{2 z&0>}6J-a08bXeKbauYFzYj>x zgiT{m1<(kOu)WjatWEDBXTixtl8*+#H5t<2g=O0@om%=nXGsy*-Cgp;<5OOGc^E>m z0;tlq9anC^-d>9Um#+u*9s~}rH2iVbpgyPQtD8dTm5Gz+uXM|RApEka2}s{7Yug5A z!5aAOjsq$PpFIAy0HC}H90dfR+H&&#UeEVhv&0fe{Exzi#1R2+Ix(LipLo^VOaE0f5Hl^4d6q06r+j2;M6q zBq(?9ba+0tSy1QkLE7!iztm8o#@)Ix_Kr0oLJ#$bT!TX1M>z|$lMx8p1l}-&zADX(K}ZeVNJ(b&>r0=5Abk4sPv)y3z;fwv z&Jt7;JRt!Hh@=eZoJ0#66l3$aH3o!KF-0W78-c`#A!r6y12hdhck1*ogaUYkU7H{K>!d$NI)j&kTeFMkoic*J>|snr}p=#` zT8V&ZBHsvX?;8X~Ltt|!gQ^O*VqZ1uP6X8$a{oaFPf>2&Rb&Lh^Us`IP={bdb<+`1 zY{`Tp7nAC8QlNC@xw@E?Ojn?*E+zHFqz(ajus&4KXBEP;*efdOx2pZ4lLPz%BE0P^ TcPZ9h00000NkvXXu0mjf)&4_P literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/textleft.png b/src/designer/components/formeditor/images/mac/textleft.png new file mode 100644 index 0000000000000000000000000000000000000000..83a66d553556318edf017cbb0575eb767401d708 GIT binary patch literal 1235 zcmV;^1T6cBP)Z1U5Qg9C?isHQ7moNJguein{0qo|6CVP}oIv6dtu{dl;Q%rxkl?_DT!7q=5Qj)8 zND+`rferwbYuKRTN5k-R+*5?%J(qt7b?= zHA24$;&|oDv0$-51Bi!w55Y5iego?=6iko4x^ij!J^(l*d+y?gZ|9kQ;*<7;ggDJQQ!nwG)iE>FOg335L^+^nHP!m)KRdH;Rz^V++ zJb)J<__>32iZWf_M$f0_G{VK1>vuf}q}$$5AEU6{*d&zBh5HEu+$0ENp~)f4WzyISA@`)jo){G z`PUj-Z~g`VW0$c`!}^Z0*+CTa)eRfNTTv zm?`)!^U0f+f6i}NsRKn{79%3SMCF}YXJP*OOg+ISE z&y71Op^llp2H`?yCH(x8@w3w-)`h}qd(}>-yR*c~Cm;frO2Xm88q1>s0MyqS(k{p% zD4d1001JVcVTiCYBE0(M1OP5{L5Qm6K(W!qjR(NWl7R^0?mcK#BS@GFh=KrYO&uEV zwT*A?8x3H2WU+Y=%L52^_XMM2eocb^a2>ey#@cy>@X71Ia0)sD$RM=c5UR=n2&xWRyEUt#p7JvG0K(QkZq~O#SpN?0Vf!WN%Ijb-kq6i=WBCU|l%}40E zwbYJt2ZFY8B2su8&lr^kFTXzF*piV4Ve%^BeM|e+z8Af(w(ds{a-1#tYcpaVm0h&$0}{XPtcB=pgOOD zi`#MDR30C~G*?UG>6a#*PsOJ$eKuaL8^9XCD_Df8VtwhT8$sbni9zLc$cH}e3C_IM^J1dzBKiRO;1TJa)=~gW57z_sPmFFU%-B0rP!+h{X?w5) z-4CD|gLeK%KTr$vfq=|FC4BLf$#$NC0p%ur>UuHB-i)%h;QZwzLFuAw{$i3UovO|% xU-mGX%w%-tFmc*65gv7%l*(`PYlaU`_8+QxueyjM5HSD%002ovPDHLkV1fu7LRJ6( literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/textright.png b/src/designer/components/formeditor/images/mac/textright.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c04645cf4db5caf5558a356d6969eeefb5c17c GIT binary patch literal 1406 zcmV-^1%djBP)#K_=t~%38c?zswBGxN0hto8X>(%-@OYLs&MeQvTjL6967!|V%)O|VyAq5IN{!vN*p=9|4X>W{$sWl*arhfcuVi#>`kcSw2lrN;Q!XnpAn0gX3yZEPlr_kqYMs=44K4TKPgBS#zy zApit?5Yp6Biz5J9Ny^|r%;lzMV3y;;WXjNBOe;0|YeFl5*#q!e2La_2NSn>=3`hsd zt?k?@Q&N?JHrTX?s1z|!E5@k!e2RepWIzfe#h8^M6k%hdb=$P1Q6C$vH>Nvq_0Mww z#tz_-Y4@t#sCw#9;mFmo62j?E?=ZgHx?}hHl|w=>ICRVy8TL4*{CY|m8O~>adn9B1 zazRA-^^|bOqKsM;@x$M+W@$iC4jnf}mUx^hLxal8+o~A2D!>&2zIf~H`uYo#o?7g< z-11;XUoFCbB=tm2$+CcRN*qJ873hl{W`+;ooZ!9ToX~7#%o&Vu%1i-m_PZhluXIR( zc7ZTdq`+pRqdf%`0$d5Yz9RaIu~x(ss8fI?MYMosF}6xj`XUV2JvLh3GgE-E6L_=@ zHthhRRJ?-L!-*aa7~j6Qr@rBqfjqB295Y6i3aR(}c2ZckLJ1yz8HY8?q1p5tKBa89 zO=z|PKm84BZxy0Q`0*I59uCAc<(F|-H9t_RDJp^O+xJ#7pcvom+gIN(dC{<7s8du8 zpFxsks49_DLNMZ5UM4SHN|-xT<8sr$z(oakk$^#(<|48r05Eq*dG>`8g!gaWGiBw} zyZ_jg>*;B@-V}tk>vA193p_YBTDR#AOs{@Zu=Te=*jE7RN`=T5ptaf{Y?k0GSH)db z5Ox#LkMS(O_P@XQO14iNSb@cz5?)oBK)2NgpE($t8e+>k5_ z_z)PJ9g(I6NIoYEI8_YjtHIOH-7ujPY~A z@K7L%9OLKV*7;xtNB<2hnZXEphKz-whGscp6d9Ke~R2I%QMB(Iwv21~o zW`VPpl!b#}!8mqf%-=8 z4Kv9fPjZS?bNxO9KuUmtYCcyTgpgOi5DXPavs{1(`3S^uX8}7pq@YuD+L&}+3tq>@ zq^dH;r6T1jHYUE*a?`fTcZG}gU9+@r&pU{46(J=7R-(KdUH=^5KeRqG?paFC4*&oF M07*qoM6N<$g4^GUS^xk5 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/textsubscript.png b/src/designer/components/formeditor/images/mac/textsubscript.png new file mode 100644 index 0000000000000000000000000000000000000000..ff431f396da9bfe074f7091f88d02edcfb68b928 GIT binary patch literal 1054 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/mac/undo.png b/src/designer/components/formeditor/images/mac/undo.png new file mode 100644 index 0000000000000000000000000000000000000000..a3bd5e0bf214adf2b0c780fd2f7de652395d555a GIT binary patch literal 1746 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/qt3logo.png b/src/designer/components/formeditor/images/qt3logo.png new file mode 100644 index 0000000000000000000000000000000000000000..720285001f819aa7522bc6161ccb063fb882fe2b GIT binary patch literal 1101 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/sort.png b/src/designer/components/formeditor/images/sort.png new file mode 100644 index 0000000000000000000000000000000000000000..883bfa9de53f583815335c11b73cf6d99f97f612 GIT binary patch literal 563 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6PXc^GT>t<74`dE+(BL`wFVKhH zB|(0{46}GS6kqXgjb}eyS*3NVG=Ygx;BLmfu5CHq@7DQl6)(UgozU#Y#a|t$U}fnEO5J>IK=CCZBhxm$P~J*#!fQ-R$Y&7*Y|}dhY6_W(S^z zi#g)gnM%vczyDkJ{=eN@Chnyn`*zPcnZne6Y4$H(Q=ykL92NC~&SfZ=YAohRjcd@5 zf27*YxcQ9f_J5uy6?S*?bTj*$U)G}FT{(O8tm6;o8UIwCo$SPuAl9Xt^z_ust5T^9 zG3tBn&FIK3w|V#NR}1@LK`H-be3R3(COqR_vgW|;ZBN#oy^~kJp@!|_3m*OTTtCj7 p%;(!*UlaG|@7KSN{r1-vY#08bDfFaHyOA3d@1CxHF6*2UngI0mkfi_s literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/submenu.png b/src/designer/components/formeditor/images/submenu.png new file mode 100644 index 0000000000000000000000000000000000000000..3deb28e3a889175411a1d6f06bc08e42adeb6014 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=%3?!FCJ6-`&(g8jpu0R?D7X1f;l$4aIQ>OxX zR#sLFY}}vDXnzE9m`Z~Df*G=Ie{rn?@?1S#978ywlM_6=lNlOSOc@nsPFuqENKI37 zCF4=YkPVW~QIn&hIwhHtCq-#YV80UZh-ryLaC2{?i1Vgy?iogGpD#5qFcfd(-~T#M RJQ`>qgQu&X%Q~loCICt{H2VMm literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/calendarwidget.png b/src/designer/components/formeditor/images/widgets/calendarwidget.png new file mode 100644 index 0000000000000000000000000000000000000000..26737b88389095fef9bcbc9b5ad053adef9b2192 GIT binary patch literal 968 zcmV;(12_DMP)LFKLqG7pmVbA94UGzu8Fbx1Vmyof&+(GL z<~db7VH^d+u;wZ~PWUTSeEK$45?^nC(a4x2LDO^*T+U8(EW_ftHPhzSw$$O^_Qhr6 zd3pg>I2U=XfJ$$9aOb7_u!$9R_r(X?>-kU~Y)qV#Qsv6#bX8WDH`d_ic;v5ZX%uIh zw83Xg6JVbzX@P)GU0CNrOZ}q10{m`{y5jsRhu+)U>tKqa7^><*JRbjNr8H-{-(ShP zySwWN42MH03oeHI2}){d3MQc7cI*K=^PUFd{ z)#y8P7_mo>EhvqFKv2$Rvk{@V7%ZSeL+Cqv1i4rYnc)#j?Z+!u@$vE%gxcGY`SJxX zckkir!vabk4Ep6`S=T`&sGR5@n$BlsY>FBn!7F_ymJR0uUH8!F$HcseahN;yr)OZso^legnLLx zK3~Tb%P_$-niko;i)WDF30WHK|@M7Sgt z>|j&9BUojX!aShl;2>TfI0R=Xgyw5kQQ&I?<+T+I{@cr}H=OViD`_hSSDa5IENFgg z41Ig|LE?wLT#H8nyM-@fokxUASgl&!c|NwJFALiF+6sds$QKsI_Z6aQs zH46>5q5{fWjh2*2Ff4Gfh1%P|+%9PR0om<_!V`s$c3SNv4$|qgpuE0BngE+t4qUx) z1A^;Ytx{BP+Y0}d&7!8}*2xJ-p3bBv?s9?6-&`x+_8ufy~p_z0#J^Twc6ia)m5QCfm0000)XgCX(I1ezh|N7TW0U5?rfuprA12#H z?Rge-({)gE83bLF;*I#HeER&3=agHfQwSQoVY1)vwX?(bcT@pT{fkupEzH*gmSI75 zi(UH#^Hs;xOv4(Ow#JOCxH~X_=grLmdd1o$ONQ0KU=U8H6Eib2H8njwjn2+aq)a9p zjgI0wukk6P6_qYdhBX3xv$uy#<~6d}EONOV@~lwEqgX7WR4SreE=g;m(J02p$0hW5 zVgf%1zGJf|@OySsfg8kC2y|s-1us`u5s5?yM6kZLhK-GN#A7ie5^+4Ao5S$%a0U97 zJvl)?vd64Nfwcn8W;17?8?~Zen1gk&#tK;ce1R@4F2ZKB!DuwX>+v8Fk74e7l($&* zg56`y?D`+DKuz2{-`k5Voeo1oL-2Uq=b@q`8)dg8R7}}vIG@kKHaRJw$5ty2+uIT6 z#v3c@SD^j< z{g|4XQs77IF1t~2@)TH%mD}xx!SE2<{79e&O-(pxX~DND)NZ%K<(gGs8|x9ynQ)14 zj8outxuE0y{lqKZudl}kF3VM+Vg&tuzXDrX_g~?1sl*A0OXFp=SP*Dz#8)mUD`;Pz zbm|0}PN%W35L95xx$s`OjLppiwzf7A4u>%^VuDjXQF`8IyInz3oS-a1q2=0Oaa;wu zv$Kt4GRadIhLzJ{Fc{!)IHX^aP7&piFBS{@^3cWb*w`52JP2N|7e1d)IuPQdiT6I= zGpRtp5Am=HEiWM)3SmAFxDako{T}wKWM*B$sfNXKSjY78BFiN*%lpKBO*O2RAKW_m vo4YSjm%LBxS69PV{STs**K6ecbqf3w-V8QQ2TjG^00000NkvXXu0mjfV}F1m literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/columnview.png b/src/designer/components/formeditor/images/widgets/columnview.png new file mode 100644 index 0000000000000000000000000000000000000000..4132ee6b1da27ddb1dcac96264323d64d8c711e0 GIT binary patch literal 518 zcmV+h0{Q)kP)r zmA`7jP#DEOZJJQDg@RJW#dpY}xVsg62%X)1fIfmtU&6&dxQUBz;2?sFi&PLH{z*)3 z@3A)_>eV(0(gR1lNAk<@GknGvV{{M2gdeycx#Pl7pc;&mnuN)oN@o7%=`}BzL=ARw|V+TOEz#Xj&)~sM&1Nd_MPD~=fp4X@>X#!c{cyOjd2!5x#%i_t5DO%f&=kP#A zV+INOS^QyynHt<*?!SjBfC=$F77~ojX1a|@!2ON_wuSGbZ++~5^|eSWe*gdg07*qo IM6N<$g1jj0P5=M^ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/combobox.png b/src/designer/components/formeditor/images/widgets/combobox.png new file mode 100644 index 0000000000000000000000000000000000000000..bf3ed79f7ec26c340158750fa2adba33bb090514 GIT binary patch literal 853 zcmV-b1FHOqP)In z<3^(G2WCQ7B!xq+vqrZ^Y4(_}_uZEX#r zD4KC76vFK6EH*YaTGk1K5C{T~VDSHYsHz6Getmr%`FtKs!qwGPAE*gDFwn2(a?^aH z(Kx#>^p#}=OG}SXE|sykxQNNgNsNt+VQy}&ZH^#G((jp>dkMzGUugovFw9Y{R)78fV|n>0l1a(U;rRgh zC-7-dUDu&$8pg*bkYBj({^ktbwl812D*J#B4r(S7rU-3z*P4ksv%2X^i6o6Rx{>bM zNF?HrIP?K;ec8g3Hx-1t#FLVl=++6GlMvg&-n=`|z|HIZNJK?=>(nB}As0jPa;Brc zJ}!R7QBy%w~gx!aOAf5I@&F6k7 z*tZ6+a)f_cGZUB^tA+B9V)}EyEcr^bYkpY zCtTw;boG|u_}K(xTLTfRkNh({JiMB}tMUjYXy(=@@KH+uE)9qnoU&tZ(vE=%JBQoR zf5(oS<94{l?C2Y{qwT7QqQhD|`BWG}LPF{}oGk;>_GzVc%^I}yq~pe@gzLi+z8{is z?Y4xAHzZuRF5${e364Gq-}Xwl?38e_Egc1t8V^T?-q-1L8{EgTxKZ7-{g2Nxoc&=l zF1SRT?-kMM6w%ZrV)EVyZjbiB)@ViR6%j2C5zUuH*xEN?cfEj@wrLR@96Z9oK?`6G zeSsQ9&?6lTwk|!Iy3J_(&Wtk-Gfp&SV)oY=&=?+b;)8}PeA;ftskRIpu*IRUPJrd0 zfY^jJ&}y~ivMiNjsG#l>w7#98u2qF&=OXb*hXD=e47^bW96WBuFSFA!ynnD8d-kQ^ z{gaPiR~?g~1(JYgp4Sq%nHni8pL9x+nuju!pJ1@^$(C9*thN5I9?;>P{eiGluES41 zPss2d=S95udH|m@4;5R+?e|%%-{LJpcf2d$wL%7sMl;KqkI7(i6p8;(6GLvP0M~#6|7FbmJ%=>2w+zjz z6!5Br!PnQ9z$O_?HYG>Zo?)o|jN#2PEq3mW!rPTm5X+;mqcjS6C6D5*;&m7qbIH)V zlf&4W6Nj9Vd1!8#fb=W|wOZ|Ruh@&cPd45EB}2^@4DZ<(s!lOfG%%DNV<f3Io*>U24(t%-2ccmR z0s)$M#V*N;MQ|0R6a@--e>Es=Us)+ZpIb5A7Qq>%6a@--pKMBwBJq<>NmA5(rBUO` z_G1@AJ>DS7y%t1)Lf$8vlA}ocq*IcV(tT=_Zq*xPrPcFN^IEHj-lRO&D!(&N@BRb# WWB+-F*HLBw0000u2v literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/dateedit.png b/src/designer/components/formeditor/images/widgets/dateedit.png new file mode 100644 index 0000000000000000000000000000000000000000..6827fa742ef25f13abc3b802e3c6396351d309c4 GIT binary patch literal 672 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 BpGyD$ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/dialogbuttonbox.png b/src/designer/components/formeditor/images/widgets/dialogbuttonbox.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f89fbb36f17920935f3118d2bf4cb7988eb16e GIT binary patch literal 1003 zcmVLKVx5A{$)_0k^@8dOldL@yQu z-3zRQNXxETm=#v5Rz|gMxohNJTz7V6PUp<*Of#EW1HV0U&Ybg^-}!!jXNMFQd%jjw zRJZ{Um@Z0@a#7PbO?gf&|+dA9E{;l3YlQmfvc9n(R~Xc|5dxKBgFB zT2R#0)kOpj(I;A=*6>Gr@LlvG9g|SxTxfa82tfs7Gh=`sd_Rl9^ezmMqj`$zJ)tD4|6js zM}?Y$=O}1ks~kLJSwd^@0($6u*aLZ3RdB!p)8@z)@Slv6Qlvupgce^tx}zYnB-nD9Ego)bGk}sLL6&6mk+z;B zy-rhO=*=5MZroI%(u@+dO+0yuSW~kKRN;EYW+_s)Z_bw2YoN9ci3l@_mDR-4r=ZN7 zp=LQ$e-4Szpay4sOI3^p*5gO*QHUQusd~B&9)Qf|Iri;U`wA<$_{r0wXPgSt$D$n_ zh&_7-;&#JXvjJqEH09PgNGzIgxu002ovPDHLkV1fhR#=HOk literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/dockwidget.png b/src/designer/components/formeditor/images/widgets/dockwidget.png new file mode 100644 index 0000000000000000000000000000000000000000..9eee04f701917da08dc036ee9da189dde473eaeb GIT binary patch literal 638 zcmV-^0)hRBP)u`|P@|xZm&B6u^j42M4{ih|qQ zUkD-SblM0ZNRkAtbCe2EO3`lL1EAOI)jckXVui=HvLKG5xdk~|=eVvz7=|of2ThJs z6a|Z`^f=2hrqd}>wvbZJ8>KayvMd8&=irPc$IJ7))+wAn$Kwe?h|0$_9bv5{3zq#ZmY%h)|U=kd!=Rlaj@R?ExUCPzpqH*+65j?e%jHDoeii-v)^4{e zr>Cb)U@`|FB9(Hv%<%AV^bQRTkB;&(BXjK0bJVf9LJ(jZUXS zr_*6#VggmI0ObBN?#cYK)G!PYkwo-;pY82!eBUPsg6N+}<2Z1Zem`Rn1Z-_>;kquK z=h197ql6LT?&P<%}E|-^=_`c88)fJ6KgD?!!8UGekM5x#6?Ck9HI3*6;qp_;; z`ufW8@o@t5(r7feySt-auOlLijg289tgWr#c^-RvdjoA5NfFn0b90kgtrjhMd3ixqX|-CJ zOyicz)VQB_A-QF`+=$|)oK;b^LTi8V0?U>m6et3mi{fM zh+vFiV`GEq>1o#2*D=PV+pT-H#?RQeL#(wuV^w8jWQ5h#)xIt5w%ct=r4o+gFgiL) zu~?*7EcTqwg+hURK94b`$M-=H@csSG{rx>6;sO|JEsKkbQ(+i3Rkbo`*9TltE>OF@ fy}dSnb(#JH+v>SwVvk$N00000NkvXXu0mjfbB$*s literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/fontcombobox.png b/src/designer/components/formeditor/images/widgets/fontcombobox.png new file mode 100644 index 0000000000000000000000000000000000000000..6848f15c2e6a0be5772062b7d072b61a45277a8e GIT binary patch literal 966 zcmV;%13CPOP)V?J8r#58qLiEXVyoQdf|M}iV89RwFr8ib;>RS~NecimY*=%Rw?G7BS8 z3$BXkNN{)U?LLshy;g=u9#*@6G$V*XN}(lQA(!`-eN{at40)@IU7~ zt+%(gGB7X@;UOfY1fWnTOifHoyaZsT)9H>(CWB>JJgg=sb1$F-CZM1X4E8*vVW89f{UH@MPKwn;_dg>JO*({e*DV9&1;CJBG z+8XO+Zri@i>))S>xSsnTjX}xxUFOCwp?y8VV!h6dp&^bRJ4QaACvoNsug2rd%*?dF z5!0-sl6^a*BD6~gL6}#uS}wD2{yd&-Gv3p~n+FcCva+K7i-*vE%iLmw)M>!yzxa|^ zsmw2?N#@;mFiivB_d;llrZmJ>;0CY>wF(7(8y&?4W?xM6+2Pj-0)Yqugb36<2xBgn zTiO6F7K@b1B?W36=hkg1SFTW-yvFuwm5QNrZN!r&L4O0w0pg}hBGyV1>ERM zk=A^c|GcLFNK|w^kEm%9sD97_O9?_q5}lpwbn5CZrD__L@4m-y9HwJYj-LEbJ>F=l z@>c|Cnuh0lg#H3-tax#8k&eV8h~WO1)oLtVyG|J>WDaupsl6%>GK@6urBVsk^YJ|o zxzP#%NQ5u3EeC+wH|L007FWBvc>DbiaO*ysNeGGW`?#(P>#YDavvh@k{GGgdL{u&8 z>}Bq18oS;)Le(lGYptc>pQtLQIm3?Q0J6y)kmlT&-Hew(Yjf(*_3z$CAn9E1IT>_1_7UMe5!<%4bv* ojhyly)uTOo_I^G*Jp2{F-%53J+_JrO%K!iX07*qoM6N<$f|R7xr2qf` literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/frame.png b/src/designer/components/formeditor/images/widgets/frame.png new file mode 100644 index 0000000000000000000000000000000000000000..68f5da0a363b599fdce0121ad67afbbe05a494d8 GIT binary patch literal 721 zcmV;?0xtcDP)d>E{C`wY){5j1G3pY1$(slz~a3wF!oVjO?=U!hcV+{8Z*1f0# z>glV)J1uKtFvhUDNdZbJETwME%K-d1c>YgZF5=niuZb|g%lBWQmLf)i;Lf!oSX{I zWFBk^XEGTom9h~1exG)`EqG_ANVQrKJRVPIHk*PS$03`|Z3<_zSy>zoheT0JaG{XL zbzQ;p`J5mK&{_jv+cv)MvD$qiyjYCmm@sTgQZ{Sjc^;OPY&FIZ1OagzU*{=2uaPpA z5?(q9naySiX<7Kbk8RtsCyH8JUJeDj?ry4>Hws5li}83YxY2lw<1AaK-Tpf)|ET!f`xfFc=6?t5w?w|Ju0j3N@CoMZ-00000NkvXXu0mjf DPhU54 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/graphicsview.png b/src/designer/components/formeditor/images/widgets/graphicsview.png new file mode 100644 index 0000000000000000000000000000000000000000..93fe7603ae8b4b9c8af8947afc8608369c579aed GIT binary patch literal 1182 zcmV;P1Y!G$P)b z)oV;sXB@|IP4>b!%d#wSS+bezs+(@vCY#e`b|cQj5dvG>4FN$ZlyWKb0!kJ^I$JJd zWy;0UmV)hR!CEd`p&melGQ~xlV1QCq91x)t8Hh4zOYLt@yRAVH#Vl*`P5PqebDli; z=igNTR{bBX=tr@!vFj8HWno4}M$>5h1(v227?bySP`6*l(bPMqm;S1{WtgSci@HB zo!A(39?x%^M8M8z$%YVSc259P%_F_KEKEjd9ru(Jtd>uZWMqJuswS8R`>M;a=lC?i zfT(gHMh%pWvuLt;z zfNQ}GZB-RSxdjAO{fm41?!K_HvAsV+3a}cBk)D+Uo}|u)CDsGU4L}MFaGQZ4+YBg; z7Ak`Y+nFVc;X9K0cfmVSbsqEJ)zr?; z&Tj<*!886h)ODI2@-tl!x7u+e{~KJfj-p6w#LSJG5S6HLr1d(6uG!Js+Y6n6#_a4Y zbah4qM9cAI=1=J9A3~-u=bY7Q9p>}-jefYP#b~zE$?$fuDiM`O55S}s!amv$N?i#X zoq~z6tI%q-kd~`Jovg?F{5%-phjFr{7eysf1jJNE5NkDq!Fa+CYgC$)k){Ja_*X>; zjCq0hz1xVnxm!>wm5?cv=rdbDQ53c^75FO40PiP?a~RjBXOSf?^j-;L$r9plI8XMp zm0_Ib;upFUPNxH@TrSL(-%u)*;XQ&~>Kg`4)7ZRUiTHwMa$*Mp!_r~uvY?~mXMDyI zuJ>Bq-Q64Rn!URETD!DN>Xt|(7rdv@)zhB2KW~$J?L%0a442I#n3|e`Addolz7N@A zGSAU6BubibYiTpCz4p w09r~~^T_Z&_+z9*QVnT>G)O8Z1ugf?Uqik&$H?zCga7~l07*qoM6N<$g1;^*CjbBd literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/groupbox.png b/src/designer/components/formeditor/images/widgets/groupbox.png new file mode 100644 index 0000000000000000000000000000000000000000..4025b4dc512b2314a43e1b6c0f2a853034fa1ba5 GIT binary patch literal 439 zcmV;o0Z9IdP)hzOj1mBp!eB9ImJ{$eykrQQFPn?Sg!K&Q9`Qkf`@%|Ns7<&m z;0TU+=3ONS9&eStK9lxa>ye+*zT2b5oKbJw>D@r7bN?i!= zaqEyN1ERvY3oPJq2x*gs#msO7u6SWWx`HdzFa#Va4(b*K4k)uvm9T;}JCp&32e1|e z9-nWlV~xfWvfgP6;BbJ#U={HFsKb!@TAO? hWZ%kQP+PFz@t@=lnvuN#3#tGB002ovPDHLkV1jyox{?3@ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/groupboxcollapsible.png b/src/designer/components/formeditor/images/widgets/groupboxcollapsible.png new file mode 100644 index 0000000000000000000000000000000000000000..62fd1ad56c2b2d2a14ee565f961328164f1741a9 GIT binary patch literal 702 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EaHVU^EKw332`Z|36TK7+`B_3sDvk z5n*a-YHVy=TU!eh2?_$44gtG%?Se3N@7}$0=T0>>HJA*8ot>SFi_41_FD6c$xP1BY zvuDrVxN&3Kwr#Giu0S=PKY#xI{rkFg>)yYA@9F7z_3G8RbLY;RH}B4!JCTu*TefVu zaN)xB>(?hunso8v#V1dmY~H*X=#0mYAHRC_>dKWX4gOuu1!Li!w|lIRgzfv6)rGGgq;*I0Z^fT1n!>Eakt z5jXY1(L82D5r>P<6FG`cJ#geOcAhy?^51{)SDnGGZ=hem)WhmKXxlK z8JYbKyKItcoqu~-rm6K@<}*%5EHtz2a9N1sOsJqVesH8k&Iw+xdy85}Sb4q9e0L>RV A-v9sr literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/hscrollbar.png b/src/designer/components/formeditor/images/widgets/hscrollbar.png new file mode 100644 index 0000000000000000000000000000000000000000..466c58de5bf9ef818002f3b6453fead298fb5256 GIT binary patch literal 408 zcmV;J0cZY+P)@lMQt}3;PA<3 zQ*n@nSkm=FL+8w}cWpSh$xy6(PL#?$Zdyp@cclVNq~@br!|A=@{2@cJ@_F$$&qB|M zS9T2Bmxkjz!^NZF>N!KP^1n>H*mKAIO#H)+zb1Yu_$s<#$GW@sg6{Z(oP?{W`rS3i z9BKKE`laRljc~^kF0YX(QYX^`Q1|{?`0|GGulNKIPwQ3En2RF-00005P)9G1&8sskV7c%Jv;`@ZMlzhr zW$}x$g5UUxGh+fjP=@ECAwEWQaa~LKRbA{JF~t4E9%3iUYrxUm zB3+vFnjEM_?D@PdKJ`H0JBfRT4RJl(BDM<|dPE`-WV2aKJUOS}7l}{a*Tsj=>x1I) zIMV4fR8>VjpWi^CP(Z0v(mYh#&Em~O%H?u_GOUT8eo*j}#3wQW-$;BYtc!gUlQP#V@qd@yJ%b5x$de)%G+@euph zXWS+?ocE=h#p7=j+$%Y8eL~Oh&}Bo+(q8!jyK2Bu+^xB4rDAMGL8j=$=s0Egw!mPl ziBwb{5VzZn%#_|AE~Es`UZuT_w0=OW4u@lI=#nnCH9AJf-=|wmr00000 LNkvXXu0mjfVa`}| literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/hsplit.png b/src/designer/components/formeditor/images/widgets/hsplit.png new file mode 100644 index 0000000000000000000000000000000000000000..1ea8f2ac0e0715d546b4748d6ec1a44dfdaedee9 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqndnN9uJT5pp$wj`elF{r G5}E*;-8RMm literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/label.png b/src/designer/components/formeditor/images/widgets/label.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7d7b4cc9c575724f7604354e5ec160730140bf GIT binary patch literal 953 zcmV;q14jIbP)3(&@4Y^os<2hzeSy5kygGk)bJ} zCTgLH3PLZCQE2lmC@Ly@Tk71F(`wtl^KAFpHt(!Hbl}GxE_c73opYWe0wDSy{62_8 zb8>QWpw()j)9Jim&s8cF$b!BpXqjmxqih;r4l1mnfO|v!Kd<#FkVf8p|KPOg8>$c1rCP; zlarG$zv{+leLmWcC9~{%!^6X8G}KGX>Rp@>h2hJ|E_$2JPD}YpR``b9Zim@yhVkW7e7seNJBQ;z z&3qpi7?{qNg-?4e_h30uQJB6Ay$vP!F)$rFbmUn${(%%;fmN{c;>;w8r6LFv%8TO(88?@a zL?Tf}*dATh!ZtdL+2Zw+yEwc(3JEbGOm>`5c#a2?2NH`2IhT2>RxL-@^`rRq<&B{9 zSw{^D)R9;nC1#QfL?*Gucc^7tNfvW*6{|Th(pItqrnmiG(%z10(V08Sze6q6K0pRr@b0^Dj8NSo0jHr*|I@c>MM@-IP>yoVmnbw zSQ}68gQ2qqxvB^uX#&@yg+j|Cg5S?Ra+Y}|@uP^wapi1MevXC20-3Y2WUz*E8}SmM z6_R=_dVg|{%=w0{<4VJB>^82ZUQ)kefgkdeN4v(!J?lzWqkN=)E%uSKWfkVSUGAGI bxa@xc$n3O$6DLQj00000NkvXXu0mjfZN0ZO literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/lcdnumber.png b/src/designer/components/formeditor/images/widgets/lcdnumber.png new file mode 100644 index 0000000000000000000000000000000000000000..c3cac182659b239a4f4bba0d073317140e22cb73 GIT binary patch literal 555 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6_X2!ET!A#HpuN3aOG^u)tFyBc z$oT*NKZ6TM;NZc7A3l6|@#4jsH*X$4eti7+@%#7hr>CbMIdbH}g$of85jSt%JagvE z=g*%fOqftyTzvKF)jfOmJbn6f=gytercL|x>({q$-}dj{zh%poA3uJqUAuPU#*Kge z{8_bX)u~gbmMvR$?b@}sZ{IFjwCM8X%P(KPoIij5oH=uL?b>zc&YhVvXFh!Ru(!82 zGBWbsy?cNE{{8;_`wRJ-%|K^Klmz(&tNHQ@s`6NQb8t(uD!DVUryn_S?nYD(!~C~7 zOMq&gc)B=-RK$f|NX=(9UdPF5Bu)U zwUg=)IrQ-NdQItOhWz5%q+L_}80xA&rq4QM!L-{pHnYRSUoDpV*ITXQHp{Pm_xa}V zbkC7ZORj!z`0U*1>bUy7;PdBPtGmMP7oThQZSLTWf7A{z^Q>*u#;mObbC0xJdpT)gzUu`>;Kd>cd&U$0T z*upgD#@o_6Hy9Z`wVF;e^@*}7weD5C#5QQ<|d}62BjvZR2H60wE-&H=;`7ZqH%ufdYl8!hO`$i{0`1rT_nDmGs<@2+>HlwRu-td2pjF-w72Cky%O&Zi*gz=FCf0 z_7+(=ot2gCv~I)E00q8NpI)xhez0MRhkp7+(<>n<#XA=*OXs+tpX%AUbSd`*z2p=9 z(suHcf?kEsVbp25@A&t;ucLK6UXEpB}P literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/lineedit.png b/src/designer/components/formeditor/images/widgets/lineedit.png new file mode 100644 index 0000000000000000000000000000000000000000..75fc890f407f698ee43245fd97dc3de1237bf2fc GIT binary patch literal 405 zcmV;G0c!qR4C8o zl08eqP#8uJDg6jdh76rL2!&)2|A1W_3)b#}tB|38qCY@E6bGT3larH+lMbR&LCBCH zPIhSrX=p(gd0cKw3Q2XB!*e+o9xl9>7x8!a2W3iBuyJq;aj>aSg3{^vVAfYy$gy@) zhBUCDGMFv&y^)ESvqJ;yzP|LC3nouO?6Cz-sJT)zL%Oo1r%Lr8#6CW_p+1DBnrbRc z*8N_H0|IbEO)~#8&e+(TcS7_``CTYcqC}Xi`)#)TCKM@BBowV)Ufv_!WXrKexnOd= zDm#aI)$;}(btyy_AL`pL7aHoSkTV^Lm<}zd5iT>E+U;Lz@5( zEUI`kY2g#(;?tsuM-_oZnHu%NC3UD%qm1l zXSRF2Z7~wrbThNp-DjVjd1hkyd|oUrF5>@(fDp>o>kVvgZ}Zp0{QNxD*T2d?MQ^M0 zgCO8ZzVG|!bUJ9>G!4k3-$$YER2+Q%^d(#;VzC%ImsgiKJakYly+ox_L8H-N76k75 zbcrAOzTc4-`D}@fJ!!*!ZX1^-v@?_#LMoN!!^vb4>GWs_NQ@PrVy-PsAp*9p>zcqi z1Q!?Q*xK5}^wbn;wJLrd9E2f>hDNdWLq8smqgWh)>-7QK_9P!1AJ1Xs{R(ehSa^X# zVW!Kp$Bw4wc|1eE<1yO-#<^Thf(e|So?>(BJKnx|hyDFMRH{`Yd75dz51FFbnOU^m z_F#WHV#z(}7+X z$abIZDKH9$9_e((1g57Q*N?HW@eLnVSMg(a2ae+mNG%Kjt-C6rCzOiC@D&Cyv3W9* zLa9{b&H1N!3`@z8R-=hywv;%Xv~Cikg*Cv~_1aQ$7nsORpxJCn|I#Rgs4CcYFX~A4 z(egHciRJaxHL6kxh3NuLPEJs*)gsmA_h#T5YqJ#?1)_kn*({b{FEgXLxjERj&CEzf zoUFDR`efT}HCwILovn!(XEIs2xsKhvT`av?;^ft8zd{X1rz&w(#zN{p`6wA`SoDXY zUv9(6$8tT2`;N2oGX*XT#$rHjngvtU<=a;GuHU)~LKaDY3P+kKDm@$5I;vx6|l1QV%yYX#@Q&P~UEEZ!ja< bryhR*e#vOLcrHAt00000NkvXXu0mjf<4kxu literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/listview.png b/src/designer/components/formeditor/images/widgets/listview.png new file mode 100644 index 0000000000000000000000000000000000000000..d1308d5758804bf47f5b9a70431d9590cd2a29c8 GIT binary patch literal 756 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/pushbutton.png b/src/designer/components/formeditor/images/widgets/pushbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..61f779ce2bf0c468f3c2e72c8b0d6cd15576be62 GIT binary patch literal 408 zcmV;J0cZY+P)=6A zNtD}P!MXXTj0QTumrDxf^Y0b8H3quGH)kw&M%3&Z!Vt# ztp0P)=8v%5p)27NN&e7YR|;DU2J;QYTZXGJT=WqD0000S literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/radiobutton.png b/src/designer/components/formeditor/images/widgets/radiobutton.png new file mode 100644 index 0000000000000000000000000000000000000000..10c1d8c3efdfc8c9285e79f5befa6e130e3e4871 GIT binary patch literal 586 zcmV-Q0=4~#P)e-}uYe3$jjbfsHmPMlsq}ERvv6ED#AYLMWrdDD_}rb;xWO2^JA#hczNf zg1QXqlD11khrxC!X%tE?WD!LjcNo4b{@)J97XOqw#&iAf@Vxwl{=M{HV%cQKE~i{_ z$tk-G*;c_aPN$9(c2?G!KC`{jT}lneBZrI?u$p>v+vi7_;x%O+GE8itds05R)nvgk zj^|F+UhthSeC88#ykm-GPW+*dFiQ9x1JJa&XAzK3* zhYBNn9GkUTOP*uJCkfH`J3S6`x!JhvEQb5(J9Wp7|-i&^3g zFNxIgMsnOB!x~cw`Q>b2SBv?KR}=}?aT|9S=PDjkNd@Hk4R5X7WSS@R*YQEJj4{ZD zsuepsO8pdg!~{JA337nTTw{oR#AdBy*!K^F7P8#uE)!(A&Nx>%%TfGG;f!@bL!Ld8 z9kmfIG0H_QFw7|q6Z;Vnspuc?UJjDn@+qXa5=ttmgyIUV7^Gj_9rDO4zXA%#FYl@m{d@R- Y0&Fzl}$?mQ51#`{e%8Qn?Uz1 z+eAb$k|a1_5C#$M3JL;Ug|v#2DIE72E@mOLE8N&7aN);BXCt@}+-L?Oh@S4bd1aa} zYG#x0aL*hs5AR%N&VZsQcgbXO7>mVFmSv1aqkJ_({oeopQN{|SQmJ91(ZKn9&Y8_- z7Q}yMn$6}93WWl)fbUGa4wQ5{onj`F!B8kM}S<%$rKRA3N?dE(J%#Kn}9 zq`&zYkHW}8e89zn!GMeD7AehN#PT`d;U(a<3|4W!-}fv&y9c~@1l&J0iK*!IdY;AC zPk=WX@Z^@n-EP;j`1Tp_;T7=c%0}Gjbole?MXbC4=H390Q#MDT>$>N}j^aop!e!L> zS7OIA_A7RLD*TEa-#ou!hgsoY>@eNF#qD;RZ%n7te5FlhsBgw)9)}?wkAD=4MXXdR zSglsEUa#{BCoo?dvHOBBsm|wdB9Tzp%kdcq1ds>@gD7foEqbDMgGo>L7la)bTVa*4 m#@JwNF?JZc4(z!>KH>{b2U6CCu7TMA0000EXE@}y1()^sS5MaViDF{o zvP`Z9O4((%=mw;iMCCb##6UE{NiQ!i6B833A0J&^T}MYpAqCYFx1aC|2-w@(FJ8QO z>$zKXb#={4wiirV5*ZnJ?8c+!=4NAK7woxk^N(xgcVJxexk-hBAV-J+r*Um)mM zSi5*fR(;paXYZoh=Q*ZU)YR1E=H~JW2`^o`)Ya8Bth&FVqQWk_dB%(x2^HU(^-R6aV0{Sa2fdOYzhDMl4sI4-Nf$;X<8XTot2=q$O8@=)`7_3{ z+;eta9kKo+jYUT7>LH9vqY}(|3uiugZYr^Kk<^S8jLDPFf0ImjJjfoKKg8 zeSLfng9z|UJ@*-tI&B>vfC c{5^jEzUwLD%#E!&poGNW>FVdQ&MBb@0C+?e?f?J) literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/spinbox.png b/src/designer/components/formeditor/images/widgets/spinbox.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd9fe141324c16758bd305b945339be013f1bc7 GIT binary patch literal 680 zcmV;Z0$2TsP)a zoyP8k=kpoub{j&7w^_LRPg2;xZjf9qhg2#BA%rhbyQp_k*il|-Y6E`)6+(Dx{A#j0 zi+7TE0(${GGWKHcHo(i`*^?)e2~<_Zdc8)eRPwF!TaAeb)oK-$O2xM$9+^LB?3dMO zG{Ss7cXn*CScIY|uYvs*iin^n3U<34w%aW-nG9;Rn)jCW3(7eMV+_q^6Zw1|jYb2E zF>hDctlbBa?F!p^?r+g(6!m)j?UvS8s}&N71d_=l;_*0Qu^3{p*xmUY3WX312EiD+ zbCGG9xZQ5pY&PJW>j1!n5Ga?+UzTNQL?nNz`iCn@2k^aKuYcHIU8cWiH_ACDVfTgr O0000M272 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/tabbar.png b/src/designer/components/formeditor/images/widgets/tabbar.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d37836b5322efb8c77e9740278eb313b72ae31 GIT binary patch literal 623 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EaHVU^EKw332`Z|36TK>OfUhwRVAs zq@-j<7vsXivITu2Sv`ye{UT8nj0+FRs;H<;-o+>;CRQ|2WcofvH8r)?RU+vfjJ|1% z*?o*lj>;BJWv!gUUpzs&a4KVRGoy}<&e9_q4J#R&)-blLXKdTZ*s+DNYa3(t4#wWy zjQx8Vs~0oYEo0QRW>8jEZeA`@I-9Y49%JPK#tHiw8<&ctwlQ|B6)By?=#|XqoxbnaJ3=UZi`INbgpW)(wog6Bsi( z8K>`MtY0KDZ4cvKlZT+7lqm`F3ufR|^JU@YkYrP0bm3t&Hej;S5DCgQu&X%Q~loCICZzy>kEn literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/table.png b/src/designer/components/formeditor/images/widgets/table.png new file mode 100644 index 0000000000000000000000000000000000000000..4bbd9c2d081f15879cd05dfb1818683af9c156ad GIT binary patch literal 483 zcmV<90UZ8`P)q?8P7XJO4mU&%5=EcEsjtyzh;WMH9E_A2T_g~qKL@8d zHB^Imi+J8~z3=aRd!EpG`Mlm8_a*=U0Im&B&-M&VL%ZI6+Pe?7bsYeJfoC|`e6Zly zRcMcU_ax|AmjECo7TK`uHnb<+YCy+|cL3m2jbE^&6AGS4aEKiM(AdFO2<=di5kyz&oxz%~Fl^cX(?0hJ1zTT=mm zR3QNY2TE{h)q4Q&U4`F(fQM$_Gx`7^^)Uhjlu7_407xY&K){hXfR6xRDDf8%&^H0! zumu3p7JdQ(QUXW-P)j5rG}kH#AOS!v%}t=zgl2epvbiV$BLMKn$jlcmR5Ik*-irkr zz&-$g*Kpy;jd@4|$CfP#EdT&etK1lyhBS5MR!3U_000_kZfY7$U6dwf+6n~#0BAH) Z%K!KgkpVaS25JBR002ovPDHLkV1oTdvk}@P)|`T3V$EmNFr7||JkRkPnWibT*^J3#g0&VAK}34@q9|Hf4}?H&0F&oAkB_gY>IDd>!q0ik z$qPuHyrvEhk?q^aUEt<}s=u3SU1rtG<2t zL>ZY!JBhL8J2`=G74Z2SCz+eQ3KGDap(4l-2=i z2eqJWjK+Werb~VY80vKyw^>~02Z0y!;v)TTqXUbq4{eijk^x08<4vF*xB#kU^V%A& z0sXBM6;Nvrye3q&2ks9c*#xSqQ^T}}d!EO9eCsvQWzp78Ti=U`xJ(6-i`D721El|O zVm<=SjVtzDr@O#t184>$KqjsnBjA>^W&?2P;OU1{H literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/textedit.png b/src/designer/components/formeditor/images/widgets/textedit.png new file mode 100644 index 0000000000000000000000000000000000000000..32e897d972759d27d3371b07b477c7ac701c474d GIT binary patch literal 823 zcmV-71IYY|P)k|>DHMF^e*Bh?U$NhR*iOb=zZyV*p0 zabTD?GjIO$-v7OMqba3$h!y}?US1xQQYM8EF)1ZdN`w$t@_loMoKs3IWHOn(0DzP- zIWjU5i$o%xoA2_daW9+A#x^!KlE5n;fDj@Ui9~2?YqNVW7{u@QwrfE{I z*SWd5p<1nSeSJ-(QlVTfQ!bYo7#KiG8MCR7Qo3$#ZLa9f2Q`{^oD#cZqa1jcJ-FrEI-gt>#L%roa{r>D@HL zFNf)S)@K7mpGOhj1*fG`dY<;!V6|G!^~{~#yMmY6N%7&!2lU6f4SHF*WcTMT+`#(R zbr;XoYPIRZ@o+pdJj3*xX_r#xlTMb#msm_JdH|eMG+OTBxcgl<_qC(vV`KF8_Oi0FV)O3-E`BOv7{=ve_){?d^1QbPx`Q(RCeN*Io5F5D4J+`_VMbmG_2WaC>{p;o%`dh#Y{X zlwxXXYS1*zq?9st$G7u8UsMiQI5;@i(;n(G{R2@4iLGj8ABX?|002ovPDHLkV1mzY Bd945d literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/timeedit.png b/src/designer/components/formeditor/images/widgets/timeedit.png new file mode 100644 index 0000000000000000000000000000000000000000..c66d91b2f78c740ff59a0490da286a05a916e877 GIT binary patch literal 1353 zcmV-P1-AN$P)ljZo6!Y-2&BGgce$YRIs)hAN&Ct z3=ckN)L4m8qyK;rqA}5+(HD(Bh>7~3G5$z|_#-BU2cwB#B1%l~M@w3468V_kufG`im9Fd@>y~>gYqS8&R5eG7 z!Y^Sf*I%}pa%9cqZ1niM4-P!zz%2^C{n>AJ^mOE&o1BdZqGN3B5(fMG$+xvp{JqHZ ziD~*b^;4RkXZXknAB|K=n!4!BH!(3?-uc19S3Um^@Xb#jTEC(-GhB|X-q0|{mQAbZ z@4uXeh717v_wVQMw};ueV*tLnU4>S@$R8BY$nMxD6KW`ZF%y5u7g2u z3j)=5v~Y?WSI6}A^&ujNSgcsAv!pg9F=^emViB=)uIyy{RV^GXoFWKRALwm$urV^| zoMUZnj!W0|paWg2Oa$iyCypdBRI3S2Tun|85x~*iwTkum5a%4mFz_eXx`9$GbmR;= zqY1-G9V+4wC!|S2T#ak7g}8G<6orUb)+}$J6br`ML0EVoiFE_B<&@>^31KB-DxRXF z<05<=;Cl+?`&7ymy1Q0mti_37MX=T|Jw1(cj_wr!v!f{@?$3%-Yl%}qDJWMeRLT|b zJlsN$(=_3ypT{}$%^`jm9>!RMbAsnVWnq1wHE}AWwl?nyK%DozIG9bAu(Gq0-gUhw zrBKR8DTVL*?09GgLwDZE3%g%pd}5-W?_JkRPj3%}84qzaRvm1u8A(#Z=_o@KMm5+u zsPgfZj}8L1Zr#c~cW>vFmtW5g9e&IDC2uTYj6Q{la#% zukn=P_xU1cqY}QaFxIf?%8f)4(w5Jol#dgkICF-t3JxO{Pr1>0#p7+IJxa_(Vb1c| zc)--;6rNHjrD)1z$z~d9X=)+R0lx2Z-8I+J+}wgviZF_J=iofQ&Ki`MiSf45o;uj= zx+6)_^nTCA$;T5u2SF?Ybj5@8!Yq*KYYTP2#tcYMEyaYFVgi}Dm{n)2J!NnYGn z;L&|&NvdViB!2t&Yqxy4__ygjq4Ib2zUd*`rJ>p1(9yyTJ({&E8d=kx!S^-46cc_b zCVVzlWn^*=m6WVQy*s;S{66QNkVV1gY{}4DH>ot;>pb1+{D2^nCDWJ%Ag+d_afoMA zJZI)42!<+q@BQ@e49_`-=Xn9J^gOlDuymO}@W2q+Wg7?@J3!?Ca8f1-PZAb?wBNn) z5lLwVm|xJ@1Q4ofonR_D%ZW6js`X{6t7j0TnOYv(_GzBXNidZ+rf1s=S8;XmHZuB>ZZW2;V z+LU%7bP;f4!A)BuscD*%+BUsZc7F0LFnoK7cWXm6a8o zbEpWSiU=GX9g)doz&REc7Fb+d#P_`5zaRo4?X$)h(&@zz{L=@p*5aJQ)a$6~S;pfD zlF4bZ*(~{do@8>0rgI>Is-U8XSVVB0Lnf`yFdrji&(7G zXtZjyT<3PGARefLZ(r?j#cieC4T8d8MTH=h0M_+CJUuT5s6*&*AePb8GWr)XJP;V5tQmIg@ z@3XgKI}q*lebl$G@$Z8{uq~BJynMv}idwknTfq(;4_036AE9NS_7`D3{1eH&-4Xx* N002ovPDHLkV1g}mVk7_n literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/toolbutton.png b/src/designer/components/formeditor/images/widgets/toolbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..0bff069a5df3ac4dccbd9cfcd2dc8e4b822ae7b5 GIT binary patch literal 1167 zcmV;A1aSL_P)0#0?im+f}R$5h6g;*?xWHNaoDS_5g^qw?{Rj8^028$xpQg~P~ z6Wh4_cs!1yqoWf^_xJZPF)@L@zCLtyeU6Tf4wRObLf3T&VWaf}n8=|pm{_JvBoZ=? z!}9VnrlzK_zP^sTcZV@MJA;9N0bIJ&f(sX#k(rqxg_+P;tVPT%VB%xdawXB|9$H%3 z;P+RexcC&FJ^KUq?~ll}#>Pe*9!kYfRnYB_sa&@jz}2{@g(xO(*p9y|zPetwRX(ZFgX#oXLH zwznf7aL(uXD7jYkLg<$G)16rvQyOKi1a!mH#f&CgzggQisJxM z5JDrB0jYLoW*SePJmO^1cBfBQke{#f0I9z<@Oa$RiX$5EOB5CsA}`PFCgNO`nfN|s z627(g_EHo`WtEiy+_=$+iV7dQgv#HhM86;u8s+^|&6<4bx`u1lu2WJU``Vyn4#C8H zNrytAkJzKU(YhYF3cdrp-U8W*f&wohk!?JG{yQD*T}pU{?u@5QDl0g9HgHHDPjSTx z0D~e(!DFw*`+<@b;PT}wWWldfRLz@6Wa|;NVup!Xbryxkj2!doQwhATnCG9YPg*pM ze%dqXxO=j*bMo`^J&cUv;v&s3jO|1s`4?I4DU&8B_9(*qG%K(%q4jSPi^UZ_|9n*G z$a4ZaEoIXjoQ7d{kwOO%s*0jS9S+AoA-P$EE+qf}002ovPDHLkV1hb~FP{Ja literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/vline.png b/src/designer/components/formeditor/images/widgets/vline.png new file mode 100644 index 0000000000000000000000000000000000000000..35a7300a580a121f815819409ed7a3a69b9d8c24 GIT binary patch literal 314 zcmV-A0mc4_P)eJoN>#n0|v8NirueRF<`-oIErrz$ui@-fa`u@6iY`{Ror|bOkLMf zaP?%ImvFr%9){sb>@v~!7yrdyEq-{?VqkvK3vtKULhOCg8pU}DiGg_ujpJz3H08{2 z*0!w_dzX;P^V}8PZ^V7ybMw45r)jcH({Pq?=Hy~$c-Q9J7X6Q(8|hxroEq~LdjJ3c M07*qoM6N<$f|gi?2mk;8 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/widgets/vscrollbar.png b/src/designer/components/formeditor/images/widgets/vscrollbar.png new file mode 100644 index 0000000000000000000000000000000000000000..28b7c40c6e2c596a7cfecc8be3ad14941e8899c8 GIT binary patch literal 415 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4foCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#Dl5R22v2|&P<#HS#}^oU2n zj`PU>jSrL^WLSg#|Nmdl<|M%qv9N6w^T)qWPf!29zvwBK#Q8av!spmJnV;r2ToKMT zP!~AOu+j8H>WQ|dA1B(JW6yc6Ie5om4bKrdgXYAxLk4FGH0IBaXs&FC5bdZlVKFbe@tfS=8dEv2wlh;c;|#&6a*EiTX@lTA)5=#pxR8ZO^HQFm{b^p z5|cv67@3$2GbO1&NQz1bD;S~%dS!KC{R>YQzJq0G3%%^(;l23I`Mu|RIOjb`Vlx4G zfF0On&b+PSf6M&~v=G<>BmeO0U=95r7; zha>1nD>~YSYGTOOf&3Rxtatt5>!a(|xm>O<8!OKCH~}n}w)z)+1i~9V&hCo+K&`Q* z!31{)gF$t6c4iuy5AK<;E5lPzBTg;g*&b_4V~-z*WFL6H{SwiVee- z%jA_ANJD#jyOPOd23(fYLF+g4Gk6*D&u4ZCkVPfO*ub#}o#kKwT1(T?}f@!NDX0AST0mw)zt+C5) z0WubCI|w9o15zNelb5UoGPHf79P-8X9`y_7mf{k{L5*L_|cCmX=~v6%t;&cyV<5 zykj>Wb#-+ezH-;OxX0JmchaOuR#`3Q?>%!&t*EI1y1&`CW1*0Os+pOYp=X$}v9Z0q zy{oHh^OEfuAw7*i*Xxx8`2{oZa&xfwO1dyA8HZbG*uS0s@4>F>Z;wjq{+9my@r}>+ zpWWP_=0Ed(#8}>OyZI-6RnByv&PSdujv*CsYbUr?H#>;9Cbl`mync7jdCxl_`1b$% zS#hq!Z?>m5@A$rVH7l=XiMV3#g0Q0(e2cbu*Bva_Vsd!R8oTN1-cMIgfA;M2rHe`_ zfjgq_-??|w*2Z@CiOyNyl=ld9x3j1iFHtjC8ZNo&@lzw-&36`9Prb_g*w;q1{*=QN z%L|HZ2Bxw8D$9N@V3b;zJ+Wia^jV6xoL)=Sq@h{ugELlH1d|Fwsq}u;uePjpWDc8S*u%#qVvtd0zkazkmPtGV6u?$bTR2 XI?rfBQh$sY(6WGajZqH6;Dcu>pHYr zEoB>z$0SLr@MC^9n{j)4TS*TgwVE1#e}9kf`xHe1 zl*JqlJ!-XDRoF1f*DT8jf{}8n*G)XnQ@oTC-}lL~3@H2A^9I!Gb=HrT<1&(fJYRLd za5&UDAPmREaiXx}>}$n*QaB99ESF1#2ZI9|jfTS0={M%{xx(FU7t69Vz9KxCOjs-y zim>fIySuvzFBS`~udfyEblTWR>Si=wzkw>A`C;4r0f7GB?kwtR?ILANz+vE>jR=xj*s6w&I(6IR>k(W{KiFJ yNLfCGL?S6uC;@>GAP^Wvx$K`>_~O62^L_ya)C>E9Py`780000T zl}k%iQ5462YoC*^UT7&oUm$7{1ql{7?F%#!B8cJ=O`=(YC`ScBj+&JpAjc7vp=lyY z54`3zjouJ+)#}`?uX`T5!P)yf(9}T-7Mr`yxxe*aYp;8kXsrtgKvKTwLs|R;!y5Jr3y39M7#Lk7p&z=lLh-2De zz&*|18CAoa`}zZ#JK|vI+X8l{icP7<&Ijuh-#(*~ckD1O%?{Jx0P};Ole@EE*M)pP zU*C5VrS=?n5upT>1L7EQ8vKM8=1-7Vv7}`ly^v81py19~l}?DH;Boe-7{`1z$pRgF9vG!y&+~|=<$egsWo9Np z1i}FPH3N<^inYJK6;o4F&zr|*y6T$%o%V6vDKT{BC7jq7Llb>gag=Gr^z`)I>1(tC zhuDU7w!o=&@N=yQAo3wxN>aI6>wXdW1nemk3eURvfL9#YGC`;zw34f{5UfIF5?~Gp zT=VD)u*>s2g2q~^WE1lvShAJBy=udAz$4&~1^))tipAo@2&LnVDto^h+iC!l6#(7? zH-Rd!40u3U%mxvl4mb;3B>p4Wj?Dp&frr36P%-djv75!Lr(Hms@!iC8{;yS~fxswT zFurVJVHPaF4xq_+9pG#M4uP5hCTmJ-ttGn)4cy^BEY`mcmO(at>WXLp0000(PGD8ik2PrecqGU!XnH2Vt64FI_ zDSFr*iXwz))1erewFE^meX&q;vF2qYBxG5`9%feaQqks?mzqv(?&6qEaq0fQ&VOz4 z*KO)-@MULb=Y0NW=V5?|;EIKYTZv>Cz?k!$3btSugt$MK%Q61BTyK8WYwY`=nPvly z9ZEw};W@PT-*k*6ipiQy>wy<_ykykgdyQ?ts{Q!;=+}OQO(LJbPeXI4)>Cr8A=rVo zM@qC;E75kb6?K^r>pFE01mWb%p*f9EW>HlwP&x^=^_j4>+laF7)|#5y<6Ki=mR9RwCCqOUHFw~X3UOsT`R^C1>8u44Rp9eEF22~)jLJ=+o0ZhoJ!fIZi6PLYLu?;c=|Na;lBHL!H2 zJrIm`xb$x`nX~Zrpzgc|hY#Grez?noZ`{sJ>HJ06sEInX$j)F(`)d?6J+U^_^3&XF zyxm_UPCxCW%q2P}tY&rNFkEqepPHH)FK&=dQxt_t^!}FoL!9S zux?aC*Pow|QoH-bc(nAKH=F>$kXc~lpd$2pJbPo6q}i7L_~xE06b2*Cd|B3 zp-|`-x+YAcMMXuX=H_O<&{bg?Ehs23H8nN)jrxRXG(SJz*x1;(A=E2Oqq(`cMu|kS zVblYrQGq~U6pO|Gg)W0>G&?)nAQFjuqU068%suB5rcpkhZ>X=Y_drR3q##l-DTK6{ z6h_+W8q@P0|KUK8WFC*_4L#-pwv(bryGhZm@ix~(e4ZM(TyFmg)I=7ZB^@LskPegf zl6JWswCWWs!Q@`QF;ENHz#*L=u}QI{$n|2&;H0FaQ*m)|s_^je0kZKjDbDqC{l=L6 Y1zDAa4!5w!`Tzg`07*qoM6N<$f?1iPWdHyG literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/adjustsize.png b/src/designer/components/formeditor/images/win/adjustsize.png new file mode 100644 index 0000000000000000000000000000000000000000..3cda3337136dc3446540da7ab9d818d100d8b18e GIT binary patch literal 1262 zcmVXGHQn6-j#1Pd4}$EaxI)l!mnYZMn0G9WpJH<^NHXE6e5UuUVGVcu~ll{$qV?DoQ?%k}{-{?V-1XJsZz(qCe)-6L-<(INLYu;jX4CLH2U*aTY^Ha~#yXGh#9Bbj^P%E@rh9}0Eu4$}o zPLVi~5(8*B1;E<-qZ&|h7B0o4UCE(13v2wF@tK)M*M<7ja%scNv1C4w(@ ztHx-{?zQb4`Q%*=zV|xYpIb+7NfQ=YxID80*QGf{q$z+jj{r%_<9VLcdA1Un`-`KX zD)$80dCT$+)-Ubg%_o*JG_`?q!(~Qi5~9D-W&rtT>g(%k)UfO#wCuu2G!Xz$ki?0w z!nGFUN+q}+l-xQc90va!1xo(_IJN%J`65x5<)K&?l*cXtD2Z}0;*~%r57#CHfdA9} Y26AiL{H)wuH2?qr07*qoM6N<$fJ~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/src/designer/components/formeditor/images/win/buddytool.png b/src/designer/components/formeditor/images/win/buddytool.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd968bbf5fb473cecf134e7c1dc0793e869445d GIT binary patch literal 997 zcmVM&= zaCqIkO%%LMHg(=I2^41ZLv&gA?K~%)&gM4jz?bvB&pGG&dCq&D!z&VrM8Qnb|5Pvl zku6b^8jVKu_V!|Ma1c734t;%n{C!|xfNNxA1pWQ}d~A6R^`xq*Dsqw91q=-hp{1n- zjg5^^sZ?ldYePdr16ONnD>^zl_;XWJ6Q4&hnoCMZNN@}o9v+6OUImRt!_CxcHSfuY zVx;cwZXPEPy*D>Evt;U#n3(7opx5iUnYK%d_V#wZn07%i%9x(Jy1IB=t=8~3#Ylo6 zI0vAt$gHfa3<`w;6%`fe>FMF7v>O`l?Cj({#mStKNlr>~44^}#tt*vEgocJfB9UOH z$8PusMME5!jv&&hOs0$_Wg8$5CEh4!K;8)YMd78QZpdA@__JsLlf# zl)(GPD|#j4keoZukdl((9H6eQj+f$ci5@wf3B1i`eHQQ{19)-{i%T*WpM~juBqUK+ zAr6Uf449ahfL5!u)ICWO#n!ZxvTPaPT_K>1!Q!(R{46;SNPS_Bae*n#6Yib|vDMwL z*)CvaW(HKMq~!|~5-kTB6+l_6wdB6Q1s6~=$1+H|7q)v#zKEmq3he+sfGSC^H}LEY z1_Of6$=D8_0_AZly6y+WZ^G(kj@txNmNyvSlPw4b%+Ah2H>3ld1(}oh4m2ge%Y;9= z;SUJb<`xKpnq)pUAr=6ckByCCa&i)*qoW8tp983F;#*}r7W4K1Nv^Qh%!v$=?fVym z1Lo%DU^I>`$;e@Miz^Wpn+wy6IDEZi-`oO8c7-X|&kDi;^YimO^O=tyK?goPJ&mcU zDcn=MVvrmxSpI4X$-;etN!TmDD3bN<8#4Y{7QEK$zbC} z0jSOmLqXhr4iP!?!=XB-QZyCrgE?!9ZSyNEUVH$xx5FWX?ONHvdKYA^XfiD_Jq&*2)<;3$TJbVi36-_g=il`qIOwd2kX!xaZ$hv=M+6gnf=5 z%-sAu5Vb{-5Keg6z6ToxtcEP8Gx3I*Up#5&CjSdyHKgEj;JW7j1y~JP&z$rdsg5DL TV66v=00000NkvXXu0mjfW+uQs literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/down.png b/src/designer/components/formeditor/images/win/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+u2DCF7oBg1O2Gjh4Lf=+d??SxhZHK2zLU9 zo&plu0F!>OQ!*70I2NWRs6=_tn$#>iEtK=#J_SyI${uHVQ^;9gd~N^D(@twOc~gsvu5zz^5`+wjwp|MH#zu$mrR*UxJ@zaX?gqfvRH|JG-zfEG!)1LbCD|LB>s z*d#B#-PP3BPnVM5egqe@LHivOHs`4zB}}%IZM*kFzEg#*%KgI|I3f4W zr-;y(EkkYG`^XH7KtfcsnVc2T_-s=uekj_5i>cyA5^pc9LZL_^2CQk5OcR=`sv6?G zvZTL0=B)?%4>uw&H0lA56JZpj<=xqJ;Mj0samC$NIZ=kMM9Jen?cI&rJ9j`Ev&tAT zYgS2hb#>&l$5f-y#7m{pkJCLpE_I6%QNQB#kpO4sDz#dj)zZ=;%g@g*WjQ)l)JE{` z^-4t0t&9J?Rghrl5ep&n^3(~1!o+FcfF8wFe96cmZm_9)eFD;agYMIns}5;zr*Gdk z9LR|@9O3cCy9Eh2$_pE3F9f5cxm(ORFg*rF{<36r4ERW0jK=V= zC++XXV4o}t*N292zDti?1%+r>u^gRi-ZrCn=;r3O&1_&G z!SugbGVlkmdQBWY4dy<%v@#0nEtzQT&?8q_h!bHvw7;<&b)==wx%q`CF021J(%Gr* zVssRmxPku~7)haBF;msKl*A`MPJ-1e4aPUTrRJ7CH(k)Z98OZiGaNh1XU{U~yxnoiefF5Zk*O7n#j*6d zc;*9b1^bDM%b%A2dZxX$a;B5<%a>fyI@=YreY#J_s3tZxRzT)>5}403U`*VK^y2cP z<-g3%t|ZmoPFL*cFj(^f^C5-y>3?)=V&ydWjHwTfjV3PiZlUE*E0h)yJ%$u|T4+M) fo=+@>IXU+q-@P0QWAg;!00000NkvXXu0mjfObmNZ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editcopy.png b/src/designer/components/formeditor/images/win/editcopy.png new file mode 100644 index 0000000000000000000000000000000000000000..1121b47d8b6b48f6cdf525851e7c0f502454ffe1 GIT binary patch literal 1325 zcmV+|1=9M7P)+{buk{F=nCJX#i^AQGtBfP^U9E=s!&EKn3+*9AyOC5y5_m4HQB2~pH` zRYX%2)m58>DgqW&sAx;EBU4=37{7w!SM2eOJs!`@eVlXtjqY3tTS5?(-{yaHb@l)B zec!pdSBVIB!=;~|e%P7xymRJ$5osgh_&4efy`JyCUMhx9Kk}ogMRI&{UihAR)H(VP z5`ixu00_b{5e5q-zVY>M|NPMBA38(Y-oZLchI61bL7|V$#mUE~-@NcRpbiK;|6O(N zfk%J%Y@t+fHjUDudrucpC_qYs_XMTNy?z?C^q*foM`|MO`|`smOlCq5Y$|AffYRZk z1m#2g^824X`-5laF9IMU{OT;%tWD%U5hqqQHn+rzNOfaXUit9}xpwhevNrpY)K=e+ z>e6d6H~pftTia5-{gJHQcvaqe>5RPb)EA!+5j21}j7vL!fJ7h$Fxhq7s2$=XbQ*apq!syTR7h7DJ;ak=_$L@YoY5qR0x4qnI>JyURTfv?kuG)9hFQ(Bzig%{x2T0Vw))rZsUuJTqc?}RB zayzmI$t>XKE_%9=#B-p^;FY>ji9xH7Anaju4o)p z9%uqB5oz!1paas%Pl^wu;12>?@7Wcg0d(Zx zhlqm$W(R^Xh!-Io1+@po6A*q0JdbvDCs~`i(%Gx6@WB#qEmxCq5xK#C!6ygR1K}{F z4Jh9Og<%N$=~Oqu`pG7hW&{7(U;2z<{$co^Ef>^0+f z`TE<9*>|q)G;S}O%acqlZ1T6YZ9d*=aWjvj1$6F8Kzppmtr|VSaptG4)aU>DhsN&h zS#$oPdv2-51h5X&^WrRbO)e0I{N~r^&-Nf3pIBgF>R(LU+~oac!W>Y~iu;rt_-#>2 jRe%!E1{&Go|C3Ju!Z7S4^>URZ00000NkvXXu0mjfFDrHZ literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editcut.png b/src/designer/components/formeditor/images/win/editcut.png new file mode 100644 index 0000000000000000000000000000000000000000..4b6c82c7a77a319c7b7e2cf0b307068fa2569d89 GIT binary patch literal 1384 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 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editform.png b/src/designer/components/formeditor/images/win/editform.png new file mode 100644 index 0000000000000000000000000000000000000000..452fcd8878b7c7999b5317aae0cb90c28df36a36 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAH3s;Exc>kD-yxy_$O8fhL*Fs( z!rTArAO26TYTokr|C4Y3r{DNL<@)~%ENYmvXLW(Yb#V6h5A=elF{r5}E)%Jgjs8 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editgrid.png b/src/designer/components/formeditor/images/win/editgrid.png new file mode 100644 index 0000000000000000000000000000000000000000..789bf7d96088434f71fc68dfe9d9e12308fb6172 GIT binary patch literal 349 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAH3s;Exc>kDAIO9NhlmD!$2cGv zQ{J3j)m&8DJmvcT={NqbfB1jPKuqGPcmK~md7EF;Tv*$D`PtjBxWsFp{@?uc|IXL{k!8(K zzW;yr?f;AK|KI-lZ@M$R7-&6vNswPK1Gln%#ga45C+v0tg<3pa978JN)}GtW#}p{w z9C+`{qq8&T6#x3q?^4wGgsDsP@|;Ivk1tN>W>5%{C|J8nv+0n*$xYHei8r2A@`p=2 zE3|3Wp8q^K_=oL`-KJT>Cn(ZZ9H^7Pb$F@H^@7&KsVdStK78M~e%)zopr0BilN6aWAK literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/edithlayout.png b/src/designer/components/formeditor/images/win/edithlayout.png new file mode 100644 index 0000000000000000000000000000000000000000..4dd3f0ce467d85487c16aa133cb536a630ea27f1 GIT binary patch literal 455 zcmV;&0XY7NP)Vo_CgIm}L12lDk~|M2VtIB4<7{q5;_Dg!`OQB{Vc@$7k} z`cPKS-BYQ<(Rc>*QkkkY3INIGkE>yw`!fH~|F6q?y;F--S5=j6i0Z`F}7s zz}g^ACVwoe?ID&6Xn=VNa??1qSs`(jN0_f5whf8QQ&9ND>crw(6ewg0a{^>Q5vf5c za}}5>6H$3PJpk)H#b++_x$a34h%XoJHX`V_`-S}u=toi x0?Q5zv2PV3(xdtl^=s1-cvsa`GTZgF7;iSorLl{}7Zgumy_y5no|DRXWeBs0YOCSDUe)jgt$N$$q z{lEG7|LxEJ?*P%)|98LqzyJ0Bqp$y;eEa|O+yA!7vkGgQ3+h^*ef$6X`~Mf;|A$)C ztpWy_UP+K&FaskCD=RlQpRlsBinhM7yRUy}d{IS3Mb*5;OP1|Bc<|tvv*#{fz53wI z`wu{{FgsTd7|k}GE{-7@!MziGy$=TnxSror=_xhQOWI4owLr0B5l7x%-G5QrcTIS; zZbx88NapIRv-+<;U*tb&X}M|p>A5=%KFH5Jmy@`KS7*6H$hIXD;_G?+yVsk3c{NSP z;Qum}RR=FkVs$al5V$yrwP%6Ho0yhW5{@@T3^!Q|T6b(b!hAJG?O;IV-V6T{ot%np zcqdu%wY@oAJgM3>u>QleuD{W#m+npbo|(4kivRl>sg0Q$(NAChbeZJdD^?;&`SaP-dr)yFjzEaWB#;0)2lVNpYQ53yz-g`6q5s-rT1N;NqTFO7L!^U4=tR%4n3kxd?qR|oxwWPzs#KHzmj3FjM zOpMWpBnEWN8g}R7&GVi+^M+jnws1DXFz@4>d(S=hjk$7dcC3C0QOT3NIVHchqii0FHQ`=*6>#=cXnErQ-;-;)p8nG%YxC zLwTBUHZhYGlw}#s%ygJ2;xVE@%E1^tRXZVwt*Tx)bV`352BigDgsV z_58`jPdjhtG$T1zvV4)$)|aKM!8n5dyz`n4`lETJR&Jewziq0*d5HwhsU(Z&%g1+f z_wU^3_4|E_<5&ptJQpKaYh_)l)neJMi)>|CE?`Yr2e7-lON)z(OF{s9Dvpn*c>$ah zgO)cBX)qWFDDF4C}e(fb91r=mR;m!VdV^DgiUZyyWM6< zv}^_mGQk{3XJ==H6zh^CsrKZVtRJg^XICiRNK)8>H!5kQNH&R8T_J!N!zDe#(m-DP z4;dj($Sg?J*fdXbYPBsP!^Bpuk?a5nH$g^K$vFhitIMSTojWT-*+W z!xdV2Qu3?7QOF8hHJ(q?v>6Z4B2&yGD+$Vv*;GlPsXl8kS4vX^2><|1Q48oFq6SvbUDGu1xUMxqgwg}G*4PMek%0riJ7lW%Rp4RIaglj*66-1}Pf}3W zHbmz5ghfY3k!_>`3Fb1+$nr5@v=P}LpQxhLF~MQ~7p*;dFro9#`!^CvC>Wv_Log_0 z3Uj*le*nce=En)PZr)z~r^mk^jOLMzG!Va|2LO(zF;;AhEFzZDnyRzQIy2O&k*NL> zZJZ>)*}|I_LUU1w2!-dL)|FdA=VBa1hMSz!XuJmJTmTO?MCx)}Gg)Y)AVtV97$TVD zTmtBc^9UG@3m>c}eNjsnTvZ=<0*+uNihR~3MPvXF1?S)sirT0^q*S9fGC+%FMLu~{ zoOSK*@6-DFdP5eP>f~1}5QYB0{NCQ)RD$hZueUU<{9 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editpaste.png b/src/designer/components/formeditor/images/win/editpaste.png new file mode 100644 index 0000000000000000000000000000000000000000..ffab15aaf8d57d13b63c788babec8eece7c2e979 GIT binary patch literal 1482 zcmV;*1vUDKP)2?}XaDXA(* zNUKVLR8hq%h0qqaDY#J*kX$P?&5KY{}Z4qXU$MGX5Ku`#v@^UG>sg(HFZe}N%=2g3-o2x-Y zKIQJ!JknBk+t8*5Cnw~8Am)iY{p~{e+aC3=BTp0E`T%7j!j))~f$>;bar?Zy;*;%k zb#-7n0X#2>pwP-9h2p1MHd7+~oarEUbdZE!vt{QB)urKDfn3{W|C)VrgbAR@Knzo{ zhrsKlv!k7+a5Ld>6U}LU7!LE^;VsO~4^uwtRzl_9^3A?uW2pME-4EUSWW%Oq&!4Sb zyFXELbJf}dJC>>M{?{k^`6D#R>2Jz&8v^p;i!aFP)!&iYhnLAc)ittk(Y^9ijgrLJ zkX(+qB$8v7vWA~6itc!njhVU=4va#jjA2e?CC84{^HIw&A{{3g=xrh3Z^P#bf(Il) z2Eg=#=_NikK>vkKyp#vP;*E_nT#MyODF>M7fiegv=2he|n$V1l*x0s(LQ!5G;==I` z6iHwVQWrr4lp+xuW}vSdbb!44sW^_0Q1Nv|aBdn9xg;POEL$<9$j9A_ONm`fknmDy zt;or-InnH7?52PiXbZ$cDMQkW)7 zo+Ry_Lt`&QyTRv&yFL#${PRzEzFEvEFQfHh0*qk9AZY@bm<U z*@aRuh6VC`@YTXNj}@QcrgxU}@oyhz;O!@fMH-p^kiV;aENVBt>Cp^J;T!K`6QDb1`WzZ z*+DFK3gBRAG@!DY9QRQ6lpsgmwzu_#^ zgXK&@+j&@Sh~#M8bho`cdg?r%0A7{@0Sm8j|F*8WttuyGHKb=SzE+V5MrcVYVC&&LU9bezOhC|=Khh6h#@nlhuCBSSTUqpC$Ex$~RbV=X&SpS2nP4?`rCwy`RJL=ddl{Pz_uMOuEw0SDB*! kH>AflK=n!07*qoM6N<$f|Xs>*8l(j literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editraise.png b/src/designer/components/formeditor/images/win/editraise.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8362c1f1e155338ae3ef1839199e19905fd03a GIT binary patch literal 1045 zcmV+w1nT>VP)1LNej4D|>9t8mb_`sv5mD!sDe05`YSn4B?h65k(c;VO=v3K~6%Xc_#M&nR~Bad;98x2M=zmD(()z!-o&K zb?erT3BW{9g23@H$fYpck+Me#*-qk}{sB8b5L z{yr0skhff=9btRxOazz&ih#hnrrFgU%wV(W)ROQKcsM2#83MIdB7&3>s!Ci`rvN6P z1W7?~!aGW!KsE?>N-~t7W}%o;l3+6Xpp?QG12bDb3xaGy_`tTkZ>B`(R)({~GfMAH z&dPAM!4e|poor^PDmmvx7_(3j)MD&#_0gw;LrO{nj6UXR1MtBrC)^zsK}1L?v0AM- zJ3U)?SP~rn{gi|6zClEYh{a*|95L0k+Y}L~OrDPZ{2h7Y#@vIDOQyAUDZ#x@KfC)( zkN3^Vm3OXRyXxUfP?7VyV6iuE@cw((4@)V8JMPYUy*@9|_9lAfvJjD*hlhu^pFVlQ zdT)=`+OoD&OGMCn$IM76ae8`6trZ_*UQ8E<-HU9!w}-n^Yo)bDN(m8J8fjOG-aDm~ zDbWa2jSKVh`Id9u&K(vB?t;!?I2B`Nmx9+?FO&Qt6A_XbInQb8?)V~tbK-aPE~OwM zv`t$OzBIw($B#KUIJl&$i|V>~kX_k!>qkdNbM61m7dCwWy!)E3GmriQuF|TSFjm!f P00000NkvXXu0mjfUcTOP literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editvlayout.png b/src/designer/components/formeditor/images/win/editvlayout.png new file mode 100644 index 0000000000000000000000000000000000000000..7ad28fdeabd8d5b44ee6c50bb5ec339e31cbafd0 GIT binary patch literal 340 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaAwFLNtxc>kDAIO9NhlmD!$2cJA z8I>3ilbBxBoKe-BU(;Mz+gw!JJmvcT={NqbyY+Vc!~dJ^yxsEn|DJnq!{QPn%bKIg znvXnud*bc?lkfhYdiOu2vN^4?Ii|e%?31@=-~7Mu_W$K)Z?Am(f9=!%o1gyQ`TGCK zxBpMR|9|%F|BLVc-~RevueU}O=mPeVAirP+Ze{(7C1;#Z*zE)gRd~8MhE&9@J!dX- z*nr0+P;knEIZx*l@B05YCpq9PPiVmxDUs+0>~>CWGN%rF7tOeFV#B%vb3EI8dBt*4 zUwl(YdtD(nF-3Ay!i``KDq$8|g3 j3u~Bsk7=42zV5R4*ZC3(f@d9ffda?= literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/editvlayoutsplit.png b/src/designer/components/formeditor/images/win/editvlayoutsplit.png new file mode 100644 index 0000000000000000000000000000000000000000..720e18bb32f1945fb20a32b3facc14fcff525a7d GIT binary patch literal 740 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|Pfzda>C&cyt|NlS{Ab>F7Tx3BA zTT)UIuB@l02cps;qM^CDdGh4R5H&zj-!Tp%wR-hxAQz~>HKxfkp$*8+s+|o{mY$xT zmX_8(ZNaRCtDk)PFCifzD=Vv{q-1MryJ`19Ha0eOb@lkF*3yR7stL1trp}snz{zE6<{y+8Z|G78+FTVYM?Zf|@AO4$}nmzsY|K*SWW@cvU z8tR3$%|*4%Iy$-)b&a|}N=J9f_5X&3hSPzj?Y>|N0ze%=Xu9s!TP`jxK0dxpciviB zTAq0OUszc9r=j~rHXwz_PwO+-J3Zx5C3s@DB(EZShI6ONOq{UOn06|Om1^b;rbcP zUsQ?;R=gGZ8T4~k%&EygbUqbWR38nR*|hF;s}I+6Ym11d9$s^VIXNF*=V5=F*Z2Kb z^4q`P9N)h%c=r91jr%D^>02+kH}V{P`jK+0!@eb@$aTTJ=EI90PY%BL z^JuE?R27cmB`+Wkx8a_@65j^u^siVQp>_ zPwn8;)Y0kK7)l|lBE%wFmC-v$eL~)Yfy# z^Ob>gz1owlwa4nuL@ZOXxYpY|_=&3meg}r2l-hGYxpQ6xt+akF&AwE-Rj{%5WonE%=bMnbMjE6fFxi8F4En`*+~J=7Qars`&=Io zhuwOigX}AaoB2WbpNI;Z{V^LaLk(JDtl#%qy z0f>Sm3j{{aCY8&CEXN`MM|ZE8%R+uhohP>-UnM8m)ey5NDv83-9qxc}B#Vq3KoHKA zr9$dh3?K|tQxUGnzF?FXfupNPBJcqpK@|`Kj_Uz9$Li`v=>Q=x&=v?xE!YNlY%Pp2 z`(c97k@x;0y2r zSkx79I6z~aP_V?vz+;97?mOd?a$#bTcUmw3OasdW&9Ju0FEPu=Z0000NkluMLp6h%0z3=<;A2g_#%st8T@8|R6`^WPY zN+~|cy!}H_Tu~a@KD%tm_m%7DBeBj`kG8vQU){vgw8h}2SJgOx_Wy$a@FNi_DPZfe z`nBJwsC#Z*QE@GJoQ`tuDa(UlIM}E5i50 zBA$rOU%pfGhYOm1CieM!Vph$nt*?~TY^loi`LXV`5}%pm#t@`))-rF!=crz=2waH* z;&%?&t(Tk+00|(JQV1c$vDa8QD>{33#j+hQ&7QY9D6ARciDBIrh-i^WiBC;mWoDv} z!6`vsx{B(RFHl;wgmA7OUDr{{iz~czzq@99`0t%p{lXW}qVfl#^_OC$#}{5(QTOzo zk_Fo%L2DS5x`{{AAP_~_I{M52cSceSW=aW^ZDx7>212EvwFb$?inmyRDnA3U(o1R+5=&d7U!}&E2ZTB0{=@8pPcNL+3uBftDi44tm{OIN(koEl8E)va^lqVyXUj5-2)s2 zuHBC#0hkYqo2M57N?UsXwh`2YX_07*qo IM6N<$f-Pt`I{*Lx literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/filesave.png b/src/designer/components/formeditor/images/win/filesave.png new file mode 100644 index 0000000000000000000000000000000000000000..8feec99bee8685290ec292e7baeb2e23ba4bdf0c GIT binary patch literal 1205 zcmV;m1WNmfP)NMq!WhGU zDb0?ir%%5E<_$1MjY0D7B#lW{;<)?El`Cg|1R~9@%$x6h^uLpO_>ug@xmmgJ*No_P znnEHG0)+hmDMa6YiFRG4o_LlgUp&G194;$ z#|Q7e%Hu~Tky2s+BT^*7)&c<-q*B=0DRAb}Cf_b=^dCQH;0Q1UG+z@p-S0s>z(fLJdaXofa#Y{a{XE!DHR|93_w4?hG<8kq+lom3=aj}y`daH}yY*TMGX*PU}a?k^Z$mMe=dBBKr3MP(B@#**TcTpzBa|A=h93v%QS-kjptJB{4u^z@&y0LCI4T6TSa45CS|$;yRAqEsIM__GKF%A16&yfb3{?Ev3O= zWp`NK(`0awX_vDHsHDgM8ZYP2344@ECC0|a7#$t8fc=zn6g_`~Gzu^#jRElxKudw6 z9$4;tmaREZLZad}%d_8@%6e3-*Khp}bb(|KKnidjfs}wT93TtVTG{6kY+YmJ^6l=8 z>lZ#>U;q0&umyDY?jo%iDv^z46gTJE4_y{w-(;l(uB)>!J7T{u)|M^Y%M$(Q0;0yWQjJ)lC|WuBldAVWm>@ zYqi>@@7LCS|K4h~T3%gTynM%)v;o`!%0SCx>I0b8{Dmvcx$`r{^6IAfuzWTu7V=>n zht*cAvEloba;37qRxV#(t5)yUfG*GhflRd$wsDcfR`M{f{>xbi>|uW%=st z#`gB=U1QP^==3?TwFS`Yb4+F%ysJLI!or#FfIoo|Kmay)=NJKTf0)V094!6;(N7PE TmU#bB00000NkvXXu0mjf&~!wG literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/forward.png b/src/designer/components/formeditor/images/win/forward.png new file mode 100644 index 0000000000000000000000000000000000000000..34b91f09fa3aa8e0b329edc97054c11defba8016 GIT binary patch literal 655 zcmV;A0&x9_P)cJAZUz`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/src/designer/components/formeditor/images/win/insertimage.png b/src/designer/components/formeditor/images/win/insertimage.png new file mode 100644 index 0000000000000000000000000000000000000000..cfab6375f7554b991d50a0c30bc4c431ed7c77ab GIT binary patch literal 885 zcmV-*1B(2KP)z2#D*<4OA{-$~GePHrD`W)^&HA}hI6wn3nP8)Jnunn=y2Qjh(SU1A z;A^lJl(3(uwES;`gXqJ9dkuJT5%L0B5iAX0Fa)DvCj}7@_SF>!HA)U%_&|bZI`ID6 z&Oiz5X7J5#@Y)He2k_%fICBAn2OF8~3WMWqc;XlcaAymaR^ZzkaB&GjAI7`zSX^zu zX2puuHsS2oZXIdi)|8bn6}llo-Ki2V9{3&v1^|)_@)D9!MS|D|t=*7NgBYm!Ah?GH zH@IDc{xm#!3?c(b?k@N1ZMc57!k|%uC75L?QT0x;LaGz(hU}Y;WljSg7zvBv>V^i z>3>GOxk>1a$!vitx)jNCBwMeNrZ16~fCZ5OLWh;10dYNmnyD}t?R-kPYB<^G@WbV+ z=&684cZDLo!d&kQlx^T?NQ#0e_9;|?PM$)x-b1HvqEx_ktzt#FohF{1V|jU%^qscie}rOO`Y7{LV~jd{yAzz7t9paK7aoN=dXXvX#C%y2%rSn z1`$C3QGil-BGiqd8w&HGVrjGFoxb4|7b||#UL9|-g@~X4s{{o1nm}MY=Cq&`gbK6> zp$G5IT0ZfwaYB8?pI5&GKD_^m;^_!~HpXI!#nMtjO~81Hzz9zVps{p9%L3P!kw2J| zWoFXLNWP1dDQn5_!3pMT8q*5!u{4#XAHckyaNN7gqWXni`Z*@M33-Z?|5Sz)PAOea zCF8>e*gX&t-fC_zGdNF6hJ@uU;`}GP{5IH(QJHhk=E$DPo>FGuzW*(3hT78!taj>B zeDw0`90i9Hl&g#mnLigAkX`tEGvJ3UkxkWXmhir@bR4|iz&)-}r300000 LNkvXXu0mjfgHV~P literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/minus.png b/src/designer/components/formeditor/images/win/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..c0dc274bb4fe04dab9220c9ce39fd3be89d08712 GIT binary patch literal 429 zcmV;e0aE^nP)<@ELK+5mf)&HQGufU<-T zBC6sBQo0Xen_HG(wsrovyXEZt)A|7JAZq=LG0fYAshlbGju?6^H$ z#q(M~k{(Y_p6_Q@bMy9cg{q}Civ;D|6cXpd;j(ejj!kV}hyJi>_p9gx{<*+6 XZ_4%z@%F;;00000NkvXXu0mjf6#2YG literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/plus.png b/src/designer/components/formeditor/images/win/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..ecf0589415464d3a432931d311c32550a53dae8d GIT binary patch literal 709 zcmV;$0y_PPP)g)r>qUh;|~v(@^U5(^ERwC$Wgg^wRYFo4c>EYN#})l#)s+lz`Uq(7pmE4yTs= z$A8!jT{g?FZ1_#K{1zSCp<_C9vJUOdc2h*;aEV;V_>!-aX@ZdyqxEuF$*_^dM)lR< zaO$~lW_jno^TX{!&cE(qMrKZWP-sCK4ltzHkYOalh{0hn7##h{V~?pkZ_97&>H!>g zPB`)`9v-Q*qS8Pmfqc}bBDSLd-Hfb=R)cuIN$5N!K8n4F7w^#3-ZgN*? z{A}?2(K8e|fmF(=;jifD5*f0llI(UO r(pvkt);u!M&-s-o&^cYW>~G6o+k}(Vi#=;y00000NkvXXu0mjfTZTZ= literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/redo.png b/src/designer/components/formeditor/images/win/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..686ad141c6e9fdff892db4098589ed9046bc1772 GIT binary patch literal 1212 zcmV;t1Vj6YP) zdrZ?;6s|7wF}KV)H=~j`iOvjlqWA&@T2WL~oM7n#PztgMB6J{BrPN=26v{v{TvRqH zG7y7R2na1IYPJwyFcFJsCk%xt!W65bBF}z1zolkd_j2(+catxt$?x9t-S3?5o^m)Z z&6>_Trw;x3) z>N+;Z7dCqY?7c(^eWtA3I{ZInpS-|(-0F~&`may6KawQ@OM`&90$@P^zzYS|N&$)7 zVu?=dcoNB+YhAWMRmT!>ILy%JtNkKBUa&0Nlvf0Z)R z!3+?ae7Bobw8VeUC7w9J>0b#Pmq<3r)Vdo-P62{=Qm~EUXFyyokbD|QDFGDuz~)1M zXdfU{0H09YmegqWmu4E>h04&s%FF$T8U5=I2N&Oft&FxQPY--di39cn@{_>+g68|m zjPlmJ@<#IoQ{O;-EubQmn=^=n2>eNzG`YJke3$+PSHx^zEN1MVC3h1hh}VRs){_fG z9Qzr(RU2Y8Mt!BlqR62V90O8I?sl&WOS>}DRhTeuWq2+>BHfU8&e-`)WB*`wB@nL# zHl~x(Jw%uY#3a`LykcWwotv2Pa`lt%;EI(s#b?b=$?m5(qu#Qc=_OHLKCWwdc=SBo zyo?C6#(Qq-cP7zg-XTn#_<>8HlV@mljQT|51;d>udK2wC3xpq`zNvu?L=>^R+^#!Yov9wptUZMxVnV9BomG-p~Ku8<_wakmhhp2 zix1-)o_6I}nc+@VE09@5x2OJy(+J6I*(?yr1}z~w0d+=KFIUW*W(i&+%$V&NqR|x4 z!5@Kh4c+~Lv4^j^_{2}LmX)>SD8hRa?B|LcJ%Y1Rvde#}xj8VH{XLLS3Iu6@Rn!}9 zG_abiz3U4i5GjkF9UePz+B`qDRK*pyx(sc=L2c!2k{D-Wj#mlqQdlsH7j!t`Kv8p5 zn+0Fik*mmU{+Yl^72rz-*nQ+Tf@cTq-k-lZn!hF8P*&ak$kcAJ1jHO{;0YBTHYBeQ z#t`N>dPu&CR2{!oNUGDm17td~FqtGW3;4Y7j|A*u>pgkeeD7Glr~`&Gwe62})i=AA z(ui>4%igph8M@G08pahWs+-IMgH;_s@pT}_0Ay4HSrve;_HiFITxa_}MiT6&yYQpE zLe!UY^~TQhyoxsV)!K{(n!NJTa?_ojZ=3G-oxf)8Ej8ZjExOv-Th!3md&c;nue847 zDZLNvp9Y^*iQSah%e&9cNwn@fU63E|6bz z7b*&9^b7uOg0MY#d&KDRlbqK`Mb5t4XWM)qX6?gx8%(gqG1fi*xY%Y6w{9Hr^8B^_ a|JE;Tm5W6WX4>We0000~;CpthKhb z<-mJ~0CeELlPSV?-KXC@#|E8~fBg1~yS#=MLTG?Sux;1`%d#P*V4VPEm%0wqG+|p7 zofh2*4pF*B%EWXy{oxnqLE;irq!Tg2&L-jYdck6;U{jsCE|JZeDzPplG+ReHl~i{u zOe!=9XetiUP6+@tU~vc#jYd&aRHPi@@i?TEP(-$>kO;D1k)$V+NkvZL`e{t!*n**m z1z>Igflk#KhJkcC&AR$kpU=nNiZmxDhm*3M$=H|ul#L~m$uODn%k}&5s0gPe4DK~M z^2v#~DrNI{JPao1Q`u9g6oe++RyuJ2!x6Vdj*%;4xMUp6f5L58HfUVAIvrv~cD;WL!?%aw(6Yb@3kWC)CuI^&%DOv`YkgxF86L%fRT80uL^vG8%;)H7tS&oWX}8G4$UW!rsLLu&2_)aC-8ICb z;v$61e9Tk3gbvucz~r2TD`Rn-?2Ry3=R0N7v<#-^v-oCY*(`vc+%@GEss8JdD_7s$ zzxU&}s{>eES%s2{GDx2Xvl$x$VGqWP7Rct;AXawa4(WWyHtCh9u3duqr>Z1?-PER6 z1srkq+kFc+2k)V){wi83BM`a<=@E!$HJBx<(f-0w96i*5uEU+!zOM_`qBY7U4IRRx zp>sn{-e-G8I{@s|0dV#1JK)`Co%eiVybpzbEgw&z?7NWAE0EUNKsYrR9IhOC(_L#pr1LrT? zC+|d~XCR87Z_FSVH&u1MKpB>o%;C=XG=j9kGqD85Nas6WQ5pLv-(M)EXE5r%16Q0! zrsnZX!&2P9o5-901Hv)~tg6n?Q2TLiNUH6tFE`blmarfViIQR;hOPzXM$EQz z0M5HDqU3zKL_0P$qix*^)KvLVUY-lxVB0)=c!1U=<9KCD97ndsmF}}2uuaV%x4@5s z|Fd!cOw&XAlB3+@G+cKUe5O(vOh@L?6N;d6aSoPMzvGjN5%)H?}AvgjWT zV`MTf1K_SR16wJDL#tsCq@l589^FDNVBpvDrv|u^29%qXAYF z+v4SI8E?zePk^PjFO1U{jMTkpVV35{<%8_%zw`17PWF8N(fc+DNc@CSLN%e0u$s_9 zXeG1}Hv9a6U9HQ(Nc7m75P-s9XQs j2LL$nQUd>)&mq9S+@^w>O-S(>00000NkvXXu0mjfcZ;=d literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/signalslottool.png b/src/designer/components/formeditor/images/win/signalslottool.png new file mode 100644 index 0000000000000000000000000000000000000000..e80fd1caa625d6fa6342bec58bc5619386b21b7c GIT binary patch literal 1128 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 Tg6EMl00000NkvXXu0mjf6WBk& literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/textanchor.png b/src/designer/components/formeditor/images/win/textanchor.png new file mode 100644 index 0000000000000000000000000000000000000000..1911ab0d5ab49b86b5d381f25c28a29c9368a78a GIT binary patch literal 1581 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 Hu0mjfNaS)2 literal 0 HcmV?d00001 diff --git a/src/designer/components/formeditor/images/win/textjustify.png b/src/designer/components/formeditor/images/win/textjustify.png new file mode 100644 index 0000000000000000000000000000000000000000..9de0c8808502f5c30ed02ff038e0325464ee0fa1 GIT binary patch literal 695 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 axbX+reAIe(fQ}9T0000z8wB<%qK}Q^REM+ z&I^UY*xK3}!Z3vEx~SD^sMqTVf9Ltvs&)jhAdBpkOz$F5?sA@59=$lEdbV3qlS4v zp-7Q);0MT$-^9ek1kMaS!k6U;u^6Yq02NUc1LIu=PjT?f~B}@ZgzlFAmWt@qaBAKITLZ_X+fPJ_A8~#t7P?#&K&>iu3S8W|JF52MVZ~N&)rhwPKG=tq{Egu`L51UR9A^|4gOY00I?wLDFZex_Lj4k@8kH9eWsN+9+UoVG+w4kSKB?LXT51x z(Qbo{tMq*i&)!bAiGfZyv!Gk|d8cEF*tGaj6 + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +struct Property { + Property() : m_sheet(0),m_id(-1) {} + Property(QDesignerPropertySheetExtension *sheet, int id) + : m_sheet(sheet), m_id(id) {} + + QDesignerPropertySheetExtension *m_sheet; + int m_id; +}; + +typedef QMap FakePropertyMap; + +struct ItemViewPropertySheetPrivate { + ItemViewPropertySheetPrivate(QDesignerFormEditorInterface *core, + QHeaderView *horizontalHeader, + QHeaderView *verticalHeader); + + inline QStringList realPropertyNames(); + inline QString fakePropertyName(const QString &prefix, const QString &realName); + + // Maps index of fake property to index of real property in respective sheet + FakePropertyMap m_propertyIdMap; + + // Maps name of fake property to name of real property + QHash m_propertyNameMap; + + QHash m_propertySheet; + QStringList m_realPropertyNames; +}; + +// Name of the fake group +static const char *headerGroup = "Header"; + +// Name of the real properties +static const char *visibleProperty = "visible"; +static const char *cascadingSectionResizesProperty = "cascadingSectionResizes"; +static const char *defaultSectionSizeProperty = "defaultSectionSize"; +static const char *highlightSectionsProperty = "highlightSections"; +static const char *minimumSectionSizeProperty = "minimumSectionSize"; +static const char *showSortIndicatorProperty = "showSortIndicator"; +static const char *stretchLastSectionProperty = "stretchLastSection"; +} // namespace qdesigner_internal + +using namespace qdesigner_internal; + + +/***************** ItemViewPropertySheetPrivate *********************/ + +ItemViewPropertySheetPrivate::ItemViewPropertySheetPrivate(QDesignerFormEditorInterface *core, + QHeaderView *horizontalHeader, + QHeaderView *verticalHeader) +{ + if (horizontalHeader) + m_propertySheet.insert(horizontalHeader, + qt_extension + (core->extensionManager(), horizontalHeader)); + if (verticalHeader) + m_propertySheet.insert(verticalHeader, + qt_extension + (core->extensionManager(), verticalHeader)); +} + +QStringList ItemViewPropertySheetPrivate::realPropertyNames() +{ + if (m_realPropertyNames.isEmpty()) + m_realPropertyNames + << QLatin1String(visibleProperty) + << QLatin1String(cascadingSectionResizesProperty) + << QLatin1String(defaultSectionSizeProperty) + << QLatin1String(highlightSectionsProperty) + << QLatin1String(minimumSectionSizeProperty) + << QLatin1String(showSortIndicatorProperty) + << QLatin1String(stretchLastSectionProperty); + return m_realPropertyNames; +} + +QString ItemViewPropertySheetPrivate::fakePropertyName(const QString &prefix, + const QString &realName) +{ + // prefix = "header", realPropertyName = "isVisible" returns "headerIsVisible" + QString fakeName = prefix + realName.at(0).toUpper() + realName.mid(1); + m_propertyNameMap.insert(fakeName, realName); + return fakeName; +} + +/***************** ItemViewPropertySheet *********************/ + +/*! + \class qdesigner_internal::ItemViewPropertySheet + + \brief + Adds header fake properties to QTreeView and QTableView objects + + QHeaderView objects are currently not shown in the object inspector. + This class adds some fake properties to the property sheet + of QTreeView and QTableView objects that nevertheless allow the manipulation + of the headers attached to the item view object. + + Currently the defaultAlignment property is not shown because the property sheet + would only show integers, instead of the Qt::Alignment enumeration. + + The fake properties here need special handling in QDesignerResource, uiloader and uic. + */ + +ItemViewPropertySheet::ItemViewPropertySheet(QTreeView *treeViewObject, QObject *parent) + : QDesignerPropertySheet(treeViewObject, parent), + d(new ItemViewPropertySheetPrivate(core(), treeViewObject->header(), 0)) +{ + initHeaderProperties(treeViewObject->header(), QLatin1String("header")); +} + +ItemViewPropertySheet::ItemViewPropertySheet(QTableView *tableViewObject, QObject *parent) + : QDesignerPropertySheet(tableViewObject, parent), + d(new ItemViewPropertySheetPrivate(core(), + tableViewObject->horizontalHeader(), + tableViewObject->verticalHeader())) +{ + initHeaderProperties(tableViewObject->horizontalHeader(), QLatin1String("horizontalHeader")); + initHeaderProperties(tableViewObject->verticalHeader(), QLatin1String("verticalHeader")); +} + +ItemViewPropertySheet::~ItemViewPropertySheet() +{ + delete d; +} + +void ItemViewPropertySheet::initHeaderProperties(QHeaderView *hv, const QString &prefix) +{ + QDesignerPropertySheetExtension *headerSheet = d->m_propertySheet.value(hv); + Q_ASSERT(headerSheet); + const QString headerGroupS = QLatin1String(headerGroup); + foreach (const QString &realPropertyName, d->realPropertyNames()) { + const int headerIndex = headerSheet->indexOf(realPropertyName); + Q_ASSERT(headerIndex != -1); + const QVariant defaultValue = realPropertyName == QLatin1String(visibleProperty) ? + QVariant(true) : headerSheet->property(headerIndex); + const QString fakePropertyName = d->fakePropertyName(prefix, realPropertyName); + const int fakeIndex = createFakeProperty(fakePropertyName, defaultValue); + d->m_propertyIdMap.insert(fakeIndex, Property(headerSheet, headerIndex)); + setAttribute(fakeIndex, true); + setPropertyGroup(fakeIndex, headerGroupS); + } +} + +/*! + Returns the mapping of fake property names to real property names + */ +QHash ItemViewPropertySheet::propertyNameMap() const +{ + return d->m_propertyNameMap; +} + +QVariant ItemViewPropertySheet::property(int index) const +{ + const FakePropertyMap::const_iterator it = d->m_propertyIdMap.constFind(index); + if (it != d->m_propertyIdMap.constEnd()) + return it.value().m_sheet->property(it.value().m_id); + return QDesignerPropertySheet::property(index); +} + +void ItemViewPropertySheet::setProperty(int index, const QVariant &value) +{ + const FakePropertyMap::iterator it = d->m_propertyIdMap.find(index); + if (it != d->m_propertyIdMap.end()) { + it.value().m_sheet->setProperty(it.value().m_id, value); + } else { + QDesignerPropertySheet::setProperty(index, value); + } +} + +void ItemViewPropertySheet::setChanged(int index, bool changed) +{ + const FakePropertyMap::iterator it = d->m_propertyIdMap.find(index); + if (it != d->m_propertyIdMap.end()) { + it.value().m_sheet->setChanged(it.value().m_id, changed); + } else { + QDesignerPropertySheet::setChanged(index, changed); + } +} + +bool ItemViewPropertySheet::isChanged(int index) const +{ + const FakePropertyMap::const_iterator it = d->m_propertyIdMap.constFind(index); + if (it != d->m_propertyIdMap.constEnd()) + return it.value().m_sheet->isChanged(it.value().m_id); + return QDesignerPropertySheet::isChanged(index); +} + +bool ItemViewPropertySheet::hasReset(int index) const +{ + const FakePropertyMap::const_iterator it = d->m_propertyIdMap.constFind(index); + if (it != d->m_propertyIdMap.constEnd()) + return it.value().m_sheet->hasReset(it.value().m_id); + return QDesignerPropertySheet::hasReset(index); +} + +bool ItemViewPropertySheet::reset(int index) +{ + const FakePropertyMap::iterator it = d->m_propertyIdMap.find(index); + if (it != d->m_propertyIdMap.end()) { + QDesignerPropertySheetExtension *headerSheet = it.value().m_sheet; + const int headerIndex = it.value().m_id; + const bool resetRC = headerSheet->reset(headerIndex); + // Resetting for "visible" might fail and the stored default + // of the Widget database is "false" due to the widget not being + // visible at the time it was determined. Reset to "true" manually. + if (!resetRC && headerSheet->propertyName(headerIndex) == QLatin1String(visibleProperty)) { + headerSheet->setProperty(headerIndex, QVariant(true)); + headerSheet->setChanged(headerIndex, false); + return true; + } + return resetRC; + } else { + return QDesignerPropertySheet::reset(index); + } +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/itemview_propertysheet.h b/src/designer/components/formeditor/itemview_propertysheet.h new file mode 100644 index 000000000..db6aa56dc --- /dev/null +++ b/src/designer/components/formeditor/itemview_propertysheet.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMVIEW_PROPERTYSHEET_H +#define ITEMVIEW_PROPERTYSHEET_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +struct ItemViewPropertySheetPrivate; + +class ItemViewPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit ItemViewPropertySheet(QTreeView *treeViewObject, QObject *parent = 0); + explicit ItemViewPropertySheet(QTableView *tableViewObject, QObject *parent = 0); + ~ItemViewPropertySheet(); + + QHash propertyNameMap() const; + + // QDesignerPropertySheet + QVariant property(int index) const; + void setProperty(int index, const QVariant &value); + + virtual void setChanged(int index, bool changed); + virtual bool isChanged(int index) const; + + virtual bool hasReset(int index) const; + virtual bool reset(int index); + +private: + void initHeaderProperties(QHeaderView *hv, const QString &prefix); + + ItemViewPropertySheetPrivate *d; +}; + +typedef QDesignerPropertySheetFactory + QTreeViewPropertySheetFactory; +typedef QDesignerPropertySheetFactory + QTableViewPropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ITEMVIEW_PROPERTYSHEET_H diff --git a/src/designer/components/formeditor/layout_propertysheet.cpp b/src/designer/components/formeditor/layout_propertysheet.cpp new file mode 100644 index 000000000..c1ff38218 --- /dev/null +++ b/src/designer/components/formeditor/layout_propertysheet.cpp @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layout_propertysheet.h" + +// sdk +#include +#include + +// shared +#include +#include +#include + +#include + +#include +#include +#include +#include +#include // Remove once there is an editor for lists + +QT_BEGIN_NAMESPACE + +#define USE_LAYOUT_SIZE_CONSTRAINT + +static const char *leftMargin = "leftMargin"; +static const char *topMargin = "topMargin"; +static const char *rightMargin = "rightMargin"; +static const char *bottomMargin = "bottomMargin"; +static const char *horizontalSpacing = "horizontalSpacing"; +static const char *verticalSpacing = "verticalSpacing"; +static const char *spacing = "spacing"; +static const char *margin = "margin"; +static const char *sizeConstraint = "sizeConstraint"; +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"; + +namespace { + enum LayoutPropertyType { + LayoutPropertyNone, + LayoutPropertyMargin, // Deprecated + LayoutPropertyLeftMargin, + LayoutPropertyTopMargin, + LayoutPropertyRightMargin, + LayoutPropertyBottomMargin, + LayoutPropertySpacing, + LayoutPropertyHorizontalSpacing, + LayoutPropertyVerticalSpacing, + LayoutPropertySizeConstraint, + LayoutPropertyBoxStretch, + LayoutPropertyGridRowStretch, + LayoutPropertyGridColumnStretch, + LayoutPropertyGridRowMinimumHeight, + LayoutPropertyGridColumnMinimumWidth + }; +} + +// Check for a comma-separated list of integers. Used for +// per-cell stretch properties and grid per row/column properties. +// As it works now, they are passed as QByteArray strings. The +// property sheet refuses all invalid values. This could be +// replaced by lists once the property editor can handle them. + +static bool isIntegerList(const QString &s) +{ + // Check for empty string or comma-separated list of integers + static const QRegExp re(QLatin1String("[0-9]+(,[0-9]+)+")); + Q_ASSERT(re.isValid()); + return s.isEmpty() || re.exactMatch(s); +} + +// Quick lookup by name +static LayoutPropertyType layoutPropertyType(const QString &name) +{ + static QHash namePropertyMap; + if (namePropertyMap.empty()) { + namePropertyMap.insert(QLatin1String(leftMargin), LayoutPropertyLeftMargin); + namePropertyMap.insert(QLatin1String(topMargin), LayoutPropertyTopMargin); + namePropertyMap.insert(QLatin1String(rightMargin), LayoutPropertyRightMargin); + namePropertyMap.insert(QLatin1String(bottomMargin), LayoutPropertyBottomMargin); + namePropertyMap.insert(QLatin1String(horizontalSpacing), LayoutPropertyHorizontalSpacing); + namePropertyMap.insert(QLatin1String(verticalSpacing), LayoutPropertyVerticalSpacing); + namePropertyMap.insert(QLatin1String(spacing), LayoutPropertySpacing); + namePropertyMap.insert(QLatin1String(margin), LayoutPropertyMargin); + namePropertyMap.insert(QLatin1String(sizeConstraint), LayoutPropertySizeConstraint); + namePropertyMap.insert(QLatin1String(boxStretchPropertyC ), LayoutPropertyBoxStretch); + namePropertyMap.insert(QLatin1String(gridRowStretchPropertyC), LayoutPropertyGridRowStretch); + namePropertyMap.insert(QLatin1String(gridColumnStretchPropertyC), LayoutPropertyGridColumnStretch); + namePropertyMap.insert(QLatin1String(gridRowMinimumHeightPropertyC), LayoutPropertyGridRowMinimumHeight); + namePropertyMap.insert(QLatin1String(gridColumnMinimumWidthPropertyC), LayoutPropertyGridColumnMinimumWidth); + } + return namePropertyMap.value(name, LayoutPropertyNone); +} + +// return the layout margin if it is margin +static int getLayoutMargin(const QLayout *l, LayoutPropertyType type) +{ + int left, top, right, bottom; + l->getContentsMargins(&left, &top, &right, &bottom); + switch (type) { + case LayoutPropertyLeftMargin: + return left; + case LayoutPropertyTopMargin: + return top; + case LayoutPropertyRightMargin: + return right; + case LayoutPropertyBottomMargin: + return bottom; + default: + Q_ASSERT(0); + break; + } + return 0; +} + +// return the layout margin if it is margin +static void setLayoutMargin(QLayout *l, LayoutPropertyType type, int margin) +{ + int left, top, right, bottom; + l->getContentsMargins(&left, &top, &right, &bottom); + switch (type) { + case LayoutPropertyLeftMargin: + left = margin; + break; + case LayoutPropertyTopMargin: + top = margin; + break; + case LayoutPropertyRightMargin: + right = margin; + break; + case LayoutPropertyBottomMargin: + bottom = margin; + break; + default: + Q_ASSERT(0); + break; + } + l->setContentsMargins(left, top, right, bottom); +} + +namespace qdesigner_internal { + +// ---------- LayoutPropertySheet: This sheet is never visible in +// the property editor. Rather, the sheet pulled for QLayoutWidget +// forwards all properties to it. Some properties (grid spacings) must be handled +// manually, as they are QDOC_PROPERTY only and not visible to introspection. Ditto +// for the 4 margins. + +LayoutPropertySheet::LayoutPropertySheet(QLayout *l, QObject *parent) + : QDesignerPropertySheet(l, parent), m_layout(l) +{ + const QString layoutGroup = QLatin1String("Layout"); + int pindex = createFakeProperty(QLatin1String(leftMargin), 0); + setPropertyGroup(pindex, layoutGroup); + + pindex = createFakeProperty(QLatin1String(topMargin), 0); + setPropertyGroup(pindex, layoutGroup); + + pindex = createFakeProperty(QLatin1String(rightMargin), 0); + setPropertyGroup(pindex, layoutGroup); + + pindex = createFakeProperty(QLatin1String(bottomMargin), 0); + setPropertyGroup(pindex, layoutGroup); + + const int visibleMask = LayoutProperties::visibleProperties(m_layout); + if (visibleMask & LayoutProperties::HorizSpacingProperty) { + pindex = createFakeProperty(QLatin1String(horizontalSpacing), 0); + setPropertyGroup(pindex, layoutGroup); + + pindex = createFakeProperty(QLatin1String(verticalSpacing), 0); + setPropertyGroup(pindex, layoutGroup); + + setAttribute(indexOf(QLatin1String(spacing)), true); + } + + setAttribute(indexOf(QLatin1String(margin)), true); + // Stretch + if (visibleMask & LayoutProperties::BoxStretchProperty) { + pindex = createFakeProperty(QLatin1String(boxStretchPropertyC), QByteArray()); + setPropertyGroup(pindex, layoutGroup); + setAttribute(pindex, true); + } else { + // Add the grid per-row/column stretch and size limits + if (visibleMask & LayoutProperties::GridColumnStretchProperty) { + const QByteArray empty; + pindex = createFakeProperty(QLatin1String(gridRowStretchPropertyC), empty); + setPropertyGroup(pindex, layoutGroup); + setAttribute(pindex, true); + pindex = createFakeProperty(QLatin1String(gridColumnStretchPropertyC), empty); + setPropertyGroup(pindex, layoutGroup); + setAttribute(pindex, true); + pindex = createFakeProperty(QLatin1String(gridRowMinimumHeightPropertyC), empty); + setPropertyGroup(pindex, layoutGroup); + setAttribute(pindex, true); + pindex = createFakeProperty(QLatin1String(gridColumnMinimumWidthPropertyC), empty); + setPropertyGroup(pindex, layoutGroup); + setAttribute(pindex, true); + } + } +#ifdef USE_LAYOUT_SIZE_CONSTRAINT + // SizeConstraint cannot possibly be handled as a real property + // as it affects the layout parent widget and thus + // conflicts with Designer's special layout widget. + // It will take effect on the preview only. + pindex = createFakeProperty(QLatin1String(sizeConstraint)); + setPropertyGroup(pindex, layoutGroup); +#endif +} + +LayoutPropertySheet::~LayoutPropertySheet() +{ +} + +void LayoutPropertySheet::setProperty(int index, const QVariant &value) +{ + const LayoutPropertyType type = layoutPropertyType(propertyName(index)); + if (QLayoutWidget *lw = qobject_cast(m_layout->parent())) { + switch (type) { + case LayoutPropertyLeftMargin: + lw->setLayoutLeftMargin(value.toInt()); + return; + case LayoutPropertyTopMargin: + lw->setLayoutTopMargin(value.toInt()); + return; + case LayoutPropertyRightMargin: + lw->setLayoutRightMargin(value.toInt()); + return; + case LayoutPropertyBottomMargin: + lw->setLayoutBottomMargin(value.toInt()); + return; + case LayoutPropertyMargin: { + const int v = value.toInt(); + lw->setLayoutLeftMargin(v); + lw->setLayoutTopMargin(v); + lw->setLayoutRightMargin(v); + lw->setLayoutBottomMargin(v); + } + return; + default: + break; + } + } + switch (type) { + case LayoutPropertyLeftMargin: + case LayoutPropertyTopMargin: + case LayoutPropertyRightMargin: + case LayoutPropertyBottomMargin: + setLayoutMargin(m_layout, type, value.toInt()); + return; + case LayoutPropertyHorizontalSpacing: + if (QGridLayout *grid = qobject_cast(m_layout)) { + grid->setHorizontalSpacing(value.toInt()); + return; + } + if (QFormLayout *form = qobject_cast(m_layout)) { + form->setHorizontalSpacing(value.toInt()); + return; + } + break; + case LayoutPropertyVerticalSpacing: + if (QGridLayout *grid = qobject_cast(m_layout)) { + grid->setVerticalSpacing(value.toInt()); + return; + } + if (QFormLayout *form = qobject_cast(m_layout)) { + form->setVerticalSpacing(value.toInt()); + return; + } + break; + case LayoutPropertyBoxStretch: + // TODO: Remove the regexp check once a proper editor for integer + // lists is in place? + if (QBoxLayout *box = qobject_cast(m_layout)) { + const QString stretch = value.toString(); + if (isIntegerList(stretch)) + QFormBuilderExtra::setBoxLayoutStretch(value.toString(), box); + } + break; + case LayoutPropertyGridRowStretch: + if (QGridLayout *grid = qobject_cast(m_layout)) { + const QString stretch = value.toString(); + if (isIntegerList(stretch)) + QFormBuilderExtra::setGridLayoutRowStretch(stretch, grid); + } + break; + case LayoutPropertyGridColumnStretch: + if (QGridLayout *grid = qobject_cast(m_layout)) { + const QString stretch = value.toString(); + if (isIntegerList(stretch)) + QFormBuilderExtra::setGridLayoutColumnStretch(value.toString(), grid); + } + break; + case LayoutPropertyGridRowMinimumHeight: + if (QGridLayout *grid = qobject_cast(m_layout)) { + const QString minSize = value.toString(); + if (isIntegerList(minSize)) + QFormBuilderExtra::setGridLayoutRowMinimumHeight(minSize, grid); + } + break; + case LayoutPropertyGridColumnMinimumWidth: + if (QGridLayout *grid = qobject_cast(m_layout)) { + const QString minSize = value.toString(); + if (isIntegerList(minSize)) + QFormBuilderExtra::setGridLayoutColumnMinimumWidth(minSize, grid); + } + break; + default: + break; + } + QDesignerPropertySheet::setProperty(index, value); +} + +QVariant LayoutPropertySheet::property(int index) const +{ + const LayoutPropertyType type = layoutPropertyType(propertyName(index)); + if (const QLayoutWidget *lw = qobject_cast(m_layout->parent())) { + switch (type) { + case LayoutPropertyLeftMargin: + return lw->layoutLeftMargin(); + case LayoutPropertyTopMargin: + return lw->layoutTopMargin(); + case LayoutPropertyRightMargin: + return lw->layoutRightMargin(); + case LayoutPropertyBottomMargin: + return lw->layoutBottomMargin(); + default: + break; + } + } + switch (type) { + case LayoutPropertyLeftMargin: + case LayoutPropertyTopMargin: + case LayoutPropertyRightMargin: + case LayoutPropertyBottomMargin: + return getLayoutMargin(m_layout, type); + case LayoutPropertyHorizontalSpacing: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return grid->horizontalSpacing(); + if (const QFormLayout *form = qobject_cast(m_layout)) + return form->horizontalSpacing(); + break; + case LayoutPropertyVerticalSpacing: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return grid->verticalSpacing(); + if (const QFormLayout *form = qobject_cast(m_layout)) + return form->verticalSpacing(); + case LayoutPropertyBoxStretch: + if (const QBoxLayout *box = qobject_cast(m_layout)) + return QVariant(QByteArray(QFormBuilderExtra::boxLayoutStretch(box).toUtf8())); + break; + case LayoutPropertyGridRowStretch: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowStretch(grid).toUtf8())); + break; + case LayoutPropertyGridColumnStretch: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnStretch(grid).toUtf8())); + break; + case LayoutPropertyGridRowMinimumHeight: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowMinimumHeight(grid).toUtf8())); + break; + case LayoutPropertyGridColumnMinimumWidth: + if (const QGridLayout *grid = qobject_cast(m_layout)) + return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnMinimumWidth(grid).toUtf8())); + break; + default: + break; + } + return QDesignerPropertySheet::property(index); +} + +bool LayoutPropertySheet::reset(int index) +{ + int left, top, right, bottom; + m_layout->getContentsMargins(&left, &top, &right, &bottom); + const LayoutPropertyType type = layoutPropertyType(propertyName(index)); + bool rc = true; + switch (type) { + case LayoutPropertyLeftMargin: + m_layout->setContentsMargins(-1, top, right, bottom); + break; + case LayoutPropertyTopMargin: + m_layout->setContentsMargins(left, -1, right, bottom); + break; + case LayoutPropertyRightMargin: + m_layout->setContentsMargins(left, top, -1, bottom); + break; + case LayoutPropertyBottomMargin: + m_layout->setContentsMargins(left, top, right, -1); + break; + case LayoutPropertyBoxStretch: + if (QBoxLayout *box = qobject_cast(m_layout)) + QFormBuilderExtra::clearBoxLayoutStretch(box); + break; + case LayoutPropertyGridRowStretch: + if (QGridLayout *grid = qobject_cast(m_layout)) + QFormBuilderExtra::clearGridLayoutRowStretch(grid); + break; + case LayoutPropertyGridColumnStretch: + if (QGridLayout *grid = qobject_cast(m_layout)) + QFormBuilderExtra::clearGridLayoutColumnStretch(grid); + break; + case LayoutPropertyGridRowMinimumHeight: + if (QGridLayout *grid = qobject_cast(m_layout)) + QFormBuilderExtra::clearGridLayoutRowMinimumHeight(grid); + break; + case LayoutPropertyGridColumnMinimumWidth: + if (QGridLayout *grid = qobject_cast(m_layout)) + QFormBuilderExtra::clearGridLayoutColumnMinimumWidth(grid); + break; + default: + rc = QDesignerPropertySheet::reset(index); + break; + } + return rc; +} + +void LayoutPropertySheet::setChanged(int index, bool changed) +{ + const LayoutPropertyType type = layoutPropertyType(propertyName(index)); + switch (type) { + case LayoutPropertySpacing: + if (LayoutProperties::visibleProperties(m_layout) & LayoutProperties::HorizSpacingProperty) { + setChanged(indexOf(QLatin1String(horizontalSpacing)), changed); + setChanged(indexOf(QLatin1String(verticalSpacing)), changed); + } + break; + case LayoutPropertyMargin: + setChanged(indexOf(QLatin1String(leftMargin)), changed); + setChanged(indexOf(QLatin1String(topMargin)), changed); + setChanged(indexOf(QLatin1String(rightMargin)), changed); + setChanged(indexOf(QLatin1String(bottomMargin)), changed); + break; + default: + break; + } + QDesignerPropertySheet::setChanged(index, changed); +} + +void LayoutPropertySheet::stretchAttributesToDom(QDesignerFormEditorInterface *core, QLayout *lt, DomLayout *domLayout) +{ + // Check if the respective stretch properties of the layout are changed. + // If so, set them to the DOM + const int visibleMask = LayoutProperties::visibleProperties(lt); + if (!(visibleMask & (LayoutProperties::BoxStretchProperty|LayoutProperties::GridColumnStretchProperty|LayoutProperties::GridRowStretchProperty))) + return; + const QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), lt); + Q_ASSERT(sheet); + + // Stretch + if (visibleMask & LayoutProperties::BoxStretchProperty) { + const int index = sheet->indexOf(QLatin1String(boxStretchPropertyC)); + Q_ASSERT(index != -1); + if (sheet->isChanged(index)) + domLayout->setAttributeStretch(sheet->property(index).toString()); + } + if (visibleMask & LayoutProperties::GridColumnStretchProperty) { + const int index = sheet->indexOf(QLatin1String(gridColumnStretchPropertyC)); + Q_ASSERT(index != -1); + if (sheet->isChanged(index)) + domLayout->setAttributeColumnStretch(sheet->property(index).toString()); + } + if (visibleMask & LayoutProperties::GridRowStretchProperty) { + const int index = sheet->indexOf(QLatin1String(gridRowStretchPropertyC)); + Q_ASSERT(index != -1); + if (sheet->isChanged(index)) + domLayout->setAttributeRowStretch(sheet->property(index).toString()); + } + if (visibleMask & LayoutProperties::GridRowMinimumHeightProperty) { + const int index = sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC)); + Q_ASSERT(index != -1); + if (sheet->isChanged(index)) + domLayout->setAttributeRowMinimumHeight(sheet->property(index).toString()); + } + if (visibleMask & LayoutProperties::GridColumnMinimumWidthProperty) { + const int index = sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC)); + Q_ASSERT(index != -1); + if (sheet->isChanged(index)) + domLayout->setAttributeColumnMinimumWidth(sheet->property(index).toString()); + } +} + +void LayoutPropertySheet::markChangedStretchProperties(QDesignerFormEditorInterface *core, QLayout *lt, const DomLayout *domLayout) +{ + // While the actual values are applied by the form builder, we still need + // to mark them as 'changed'. + QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), lt); + Q_ASSERT(sheet); + if (!domLayout->attributeStretch().isEmpty()) + sheet->setChanged(sheet->indexOf(QLatin1String(boxStretchPropertyC)), true); + if (!domLayout->attributeRowStretch().isEmpty()) + sheet->setChanged(sheet->indexOf(QLatin1String(gridRowStretchPropertyC)), true); + if (!domLayout->attributeColumnStretch().isEmpty()) + sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnStretchPropertyC)), true); + if (!domLayout->attributeColumnMinimumWidth().isEmpty()) + sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC)), true); + if (!domLayout->attributeRowMinimumHeight().isEmpty()) + sheet->setChanged(sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC)), true); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/layout_propertysheet.h b/src/designer/components/formeditor/layout_propertysheet.h new file mode 100644 index 000000000..eab66fbd6 --- /dev/null +++ b/src/designer/components/formeditor/layout_propertysheet.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LAYOUT_PROPERTYSHEET_H +#define LAYOUT_PROPERTYSHEET_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class DomLayout; + +namespace qdesigner_internal { + +class LayoutPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit LayoutPropertySheet(QLayout *object, QObject *parent = 0); + virtual ~LayoutPropertySheet(); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + void setChanged(int index, bool changed); + + static void stretchAttributesToDom(QDesignerFormEditorInterface *core, QLayout *lt, DomLayout *domLayout); + static void markChangedStretchProperties(QDesignerFormEditorInterface *core, QLayout *lt, const DomLayout *domLayout); + +private: + QLayout *m_layout; +}; + +typedef QDesignerPropertySheetFactory LayoutPropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LAYOUT_PROPERTYSHEET_H diff --git a/src/designer/components/formeditor/line_propertysheet.cpp b/src/designer/components/formeditor/line_propertysheet.cpp new file mode 100644 index 000000000..4d1d4f01b --- /dev/null +++ b/src/designer/components/formeditor/line_propertysheet.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "line_propertysheet.h" +#include "formwindow.h" + +// sdk +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +LinePropertySheet::LinePropertySheet(Line *object, QObject *parent) + : QDesignerPropertySheet(object, parent) +{ + clearFakeProperties(); +} + +LinePropertySheet::~LinePropertySheet() +{ +} + +bool LinePropertySheet::isVisible(int index) const +{ + const QString name = propertyName(index); + + if (name == QLatin1String("frameShape")) + return false; + return QDesignerPropertySheet::isVisible(index); +} + +void LinePropertySheet::setProperty(int index, const QVariant &value) +{ + QDesignerPropertySheet::setProperty(index, value); +} + +QString LinePropertySheet::propertyGroup(int index) const +{ + return QDesignerPropertySheet::propertyGroup(index); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/line_propertysheet.h b/src/designer/components/formeditor/line_propertysheet.h new file mode 100644 index 000000000..cf2226589 --- /dev/null +++ b/src/designer/components/formeditor/line_propertysheet.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINE_PROPERTYSHEET_H +#define LINE_PROPERTYSHEET_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class LinePropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit LinePropertySheet(Line *object, QObject *parent = 0); + virtual ~LinePropertySheet(); + + virtual void setProperty(int index, const QVariant &value); + virtual bool isVisible(int index) const; + virtual QString propertyGroup(int index) const; +}; + +typedef QDesignerPropertySheetFactory LinePropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LINE_PROPERTYSHEET_H diff --git a/src/designer/components/formeditor/previewactiongroup.cpp b/src/designer/components/formeditor/previewactiongroup.cpp new file mode 100644 index 000000000..6c35813e1 --- /dev/null +++ b/src/designer/components/formeditor/previewactiongroup.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "previewactiongroup.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +enum { MaxDeviceActions = 20 }; + +namespace qdesigner_internal { + +PreviewActionGroup::PreviewActionGroup(QDesignerFormEditorInterface *core, QObject *parent) : + QActionGroup(parent), + m_core(core) +{ + /* Create a list of up to MaxDeviceActions invisible actions to be + * populated with device profiles (actiondata: index) followed by the + * standard style actions (actiondata: style name). */ + connect(this, SIGNAL(triggered(QAction*)), this, SLOT(slotTriggered(QAction*))); + setExclusive(true); + + const QString objNamePostfix = QLatin1String("_action"); + // Create invisible actions for devices. Set index as action data. + QString objNamePrefix = QLatin1String("__qt_designer_device_"); + for (int i = 0; i < MaxDeviceActions; i++) { + QAction *a = new QAction(this); + QString objName = objNamePrefix; + objName += QString::number(i); + objName += objNamePostfix; + a->setObjectName(objName); + a->setVisible(false); + a->setData(i); + addAction(a); + } + // Create separator at index MaxDeviceActions + QAction *sep = new QAction(this); + sep->setObjectName(QLatin1String("__qt_designer_deviceseparator")); + sep->setSeparator(true); + sep->setVisible(false); + addAction(sep); + // Populate devices + updateDeviceProfiles(); + + // Add style actions + const QStringList styles = QStyleFactory::keys(); + const QStringList::const_iterator cend = styles.constEnd(); + // Make sure ObjectName is unique in case toolbar solution is used. + objNamePrefix = QLatin1String("__qt_designer_style_"); + // Create styles. Set style name string as action data. + for (QStringList::const_iterator it = styles.constBegin(); it != cend ;++it) { + QAction *a = new QAction(tr("%1 Style").arg(*it), this); + QString objName = objNamePrefix; + objName += *it; + objName += objNamePostfix; + a->setObjectName(objName); + a->setData(*it); + addAction(a); + } +} + +void PreviewActionGroup::updateDeviceProfiles() +{ + typedef QList DeviceProfileList; + typedef QList ActionList; + + const QDesignerSharedSettings settings(m_core); + const DeviceProfileList profiles = settings.deviceProfiles(); + const ActionList al = actions(); + // Separator? + const bool hasProfiles = !profiles.empty(); + al.at(MaxDeviceActions)->setVisible(hasProfiles); + int index = 0; + if (hasProfiles) { + // Make actions visible + const int maxIndex = qMin(static_cast(MaxDeviceActions), profiles.size()); + for (; index < maxIndex; index++) { + const QString name = profiles.at(index).name(); + al.at(index)->setText(name); + al.at(index)->setVisible(true); + } + } + // Hide rest + for ( ; index < MaxDeviceActions; index++) + al.at(index)->setVisible(false); +} + +void PreviewActionGroup::slotTriggered(QAction *a) +{ + // Device or style according to data. + const QVariant data = a->data(); + switch (data.type()) { + case QVariant::String: + emit preview(data.toString(), -1); + break; + case QVariant::Int: + emit preview(QString(), data.toInt()); + break; + default: + break; + } +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/previewactiongroup.h b/src/designer/components/formeditor/previewactiongroup.h new file mode 100644 index 000000000..0718f8b05 --- /dev/null +++ b/src/designer/components/formeditor/previewactiongroup.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 PREVIEWACTIONGROUP_H +#define PREVIEWACTIONGROUP_H + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +/* PreviewActionGroup: To be used as a submenu for 'Preview in...' + * Offers a menu of styles and device profiles. */ + +class PreviewActionGroup : public QActionGroup +{ + Q_DISABLE_COPY(PreviewActionGroup) + Q_OBJECT +public: + explicit PreviewActionGroup(QDesignerFormEditorInterface *core, QObject *parent = 0); + +signals: + void preview(const QString &style, int deviceProfileIndex); + +public slots: + void updateDeviceProfiles(); + +private slots: + void slotTriggered(QAction *); + +private: + QDesignerFormEditorInterface *m_core; +}; +} + +QT_END_NAMESPACE + +#endif // PREVIEWACTIONGROUP_H diff --git a/src/designer/components/formeditor/qdesigner_resource.cpp b/src/designer/components/formeditor/qdesigner_resource.cpp new file mode 100644 index 000000000..338157451 --- /dev/null +++ b/src/designer/components/formeditor/qdesigner_resource.cpp @@ -0,0 +1,2475 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_resource.h" +#include "formwindow.h" +#include "dynamicpropertysheet.h" +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_toolbox_p.h" +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_dockwidget_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_membersheet_p.h" +#include "qtresourcemodel_p.h" +#include "qmdiarea_container.h" +#include "qwizard_container.h" +#include "layout_propertysheet.h" + +#include +#include +#include +#include +#include + +// shared +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// 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 + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList DomPropertyList; +} + +static const char *currentUiVersion = "4.0"; +static const char *clipboardObjectName = "__qt_fake_top_level"; + +#define OLD_RESOURCE_FORMAT // Support pre 4.4 format. + +namespace qdesigner_internal { + +// -------------------- QDesignerResourceBuilder: A resource builder that works on the property sheet icon types. +class QDesignerResourceBuilder : public QResourceBuilder +{ +public: + QDesignerResourceBuilder(QDesignerFormEditorInterface *core, DesignerPixmapCache *pixmapCache, DesignerIconCache *iconCache); + + void setPixmapCache(DesignerPixmapCache *pixmapCache) { m_pixmapCache = pixmapCache; } + void setIconCache(DesignerIconCache *iconCache) { m_iconCache = iconCache; } + bool isSaveRelative() const { return m_saveRelative; } + void setSaveRelative(bool relative) { m_saveRelative = relative; } + QStringList usedQrcFiles() const { return m_usedQrcFiles.keys(); } +#ifdef OLD_RESOURCE_FORMAT + QStringList loadedQrcFiles() const { return m_loadedQrcFiles.keys(); } // needed only for loading old resource attribute of tag. +#endif + + virtual QVariant loadResource(const QDir &workingDirectory, const DomProperty *icon) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveResource(const QDir &workingDirectory, const QVariant &value) const; + + virtual bool isResourceType(const QVariant &value) const; +private: + + QDesignerFormEditorInterface *m_core; + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + const QDesignerLanguageExtension *m_lang; + bool m_saveRelative; + mutable QMap m_usedQrcFiles; + mutable QMap m_loadedQrcFiles; +}; + +QDesignerResourceBuilder::QDesignerResourceBuilder(QDesignerFormEditorInterface *core, DesignerPixmapCache *pixmapCache, DesignerIconCache *iconCache) : + m_core(core), + m_pixmapCache(pixmapCache), + m_iconCache(iconCache), + m_lang(qt_extension(core->extensionManager(), core)), + m_saveRelative(true) +{ +} + +static inline void setIconPixmap(QIcon::Mode m, QIcon::State s, const QDir &workingDirectory, + QString path, PropertySheetIconValue &icon, + const QDesignerLanguageExtension *lang = 0) +{ + if (lang == 0 || !lang->isLanguageResource(path)) + path = QFileInfo(workingDirectory, path).absoluteFilePath(); + icon.setPixmap(m, s, PropertySheetPixmapValue(path)); +} + +QVariant QDesignerResourceBuilder::loadResource(const QDir &workingDirectory, const DomProperty *property) const +{ + switch (property->kind()) { + case DomProperty::Pixmap: { + PropertySheetPixmapValue pixmap; + DomResourcePixmap *dp = property->elementPixmap(); + if (!dp->text().isEmpty()) { + if (m_lang != 0 && m_lang->isLanguageResource(dp->text())) { + pixmap.setPath(dp->text()); + } else { + pixmap.setPath(QFileInfo(workingDirectory, dp->text()).absoluteFilePath()); + } +#ifdef OLD_RESOURCE_FORMAT + if (dp->hasAttributeResource()) + m_loadedQrcFiles.insert(QFileInfo(workingDirectory, dp->attributeResource()).absoluteFilePath(), false); +#endif + } + return QVariant::fromValue(pixmap); + } + + case DomProperty::IconSet: { + PropertySheetIconValue icon; + DomResourceIcon *di = property->elementIconSet(); + icon.setTheme(di->attributeTheme()); + if (const int flags = iconStateFlags(di)) { // new, post 4.4 format + if (flags & NormalOff) + setIconPixmap(QIcon::Normal, QIcon::Off, workingDirectory, di->elementNormalOff()->text(), icon, m_lang); + if (flags & NormalOn) + setIconPixmap(QIcon::Normal, QIcon::On, workingDirectory, di->elementNormalOn()->text(), icon, m_lang); + if (flags & DisabledOff) + setIconPixmap(QIcon::Disabled, QIcon::Off, workingDirectory, di->elementDisabledOff()->text(), icon, m_lang); + if (flags & DisabledOn) + setIconPixmap(QIcon::Disabled, QIcon::On, workingDirectory, di->elementDisabledOn()->text(), icon, m_lang); + if (flags & ActiveOff) + setIconPixmap(QIcon::Active, QIcon::Off, workingDirectory, di->elementActiveOff()->text(), icon, m_lang); + if (flags & ActiveOn) + setIconPixmap(QIcon::Active, QIcon::On, workingDirectory, di->elementActiveOn()->text(), icon, m_lang); + if (flags & SelectedOff) + setIconPixmap(QIcon::Selected, QIcon::Off, workingDirectory, di->elementSelectedOff()->text(), icon, m_lang); + if (flags & SelectedOn) + setIconPixmap(QIcon::Selected, QIcon::On, workingDirectory, di->elementSelectedOn()->text(), icon, m_lang); + } else { +#ifdef OLD_RESOURCE_FORMAT + setIconPixmap(QIcon::Normal, QIcon::Off, workingDirectory, di->text(), icon, m_lang); + if (di->hasAttributeResource()) + m_loadedQrcFiles.insert(QFileInfo(workingDirectory, di->attributeResource()).absoluteFilePath(), false); +#endif + } + return QVariant::fromValue(icon); + } + default: + break; + } + return QVariant(); +} + +QVariant QDesignerResourceBuilder::toNativeValue(const QVariant &value) const +{ + if (value.canConvert()) { + if (m_pixmapCache) + return m_pixmapCache->pixmap(qvariant_cast(value)); + } else if (value.canConvert()) { + if (m_iconCache) + return m_iconCache->icon(qvariant_cast(value)); + } + return value; +} + +DomProperty *QDesignerResourceBuilder::saveResource(const QDir &workingDirectory, const QVariant &value) const +{ + DomProperty *p = new DomProperty; + if (value.canConvert()) { + const PropertySheetPixmapValue pix = qvariant_cast(value); + DomResourcePixmap *rp = new DomResourcePixmap; + const QString pixPath = pix.path(); + switch (pix.pixmapSource(m_core)) { + case PropertySheetPixmapValue::LanguageResourcePixmap: + rp->setText(pixPath); + break; + case PropertySheetPixmapValue::ResourcePixmap: { + rp->setText(pixPath); + const QString qrcFile = m_core->resourceModel()->qrcPath(pixPath); + if (!qrcFile.isEmpty()) { + m_usedQrcFiles.insert(qrcFile, false); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Add qrc path + rp->setAttributeResource(workingDirectory.relativeFilePath(qrcFile)); +#endif + } + } + break; + case PropertySheetPixmapValue::FilePixmap: + rp->setText(m_saveRelative ? workingDirectory.relativeFilePath(pixPath) : pixPath); + break; + } + p->setElementPixmap(rp); + return p; + } else if (value.canConvert()) { + const PropertySheetIconValue icon = qvariant_cast(value); + const QMap, PropertySheetPixmapValue> pixmaps = icon.paths(); + const QString theme = icon.theme(); + if (!pixmaps.isEmpty() || !theme.isEmpty()) { + DomResourceIcon *ri = new DomResourceIcon; + if (!theme.isEmpty()) + ri->setAttributeTheme(theme); + QMapIterator, PropertySheetPixmapValue> itPix(pixmaps); + while (itPix.hasNext()) { + const QIcon::Mode mode = itPix.next().key().first; + const QIcon::State state = itPix.key().second; + DomResourcePixmap *rp = new DomResourcePixmap; + const PropertySheetPixmapValue pix = itPix.value(); + const PropertySheetPixmapValue::PixmapSource ps = pix.pixmapSource(m_core); + const QString pixPath = pix.path(); + rp->setText(ps == PropertySheetPixmapValue::FilePixmap && m_saveRelative ? workingDirectory.relativeFilePath(pixPath) : pixPath); + if (state == QIcon::Off) { + switch (mode) { + case QIcon::Normal: + ri->setElementNormalOff(rp); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Set Normal off as text/path in old format. + ri->setText(rp->text()); +#endif + if (ps == PropertySheetPixmapValue::ResourcePixmap) { + // Be sure that ri->text() file comes from active resourceSet (i.e. make appropriate + // resourceSet active before calling this method). + const QString qrcFile = m_core->resourceModel()->qrcPath(ri->text()); + if (!qrcFile.isEmpty()) { + m_usedQrcFiles.insert(qrcFile, false); +#ifdef OLD_RESOURCE_FORMAT // Legacy: Set Normal off as text/path in old format. + ri->setAttributeResource(workingDirectory.relativeFilePath(qrcFile)); +#endif + } + } + break; + case QIcon::Disabled: ri->setElementDisabledOff(rp); break; + case QIcon::Active: ri->setElementActiveOff(rp); break; + case QIcon::Selected: ri->setElementSelectedOff(rp); break; + } + } else { + switch (mode) { + case QIcon::Normal: ri->setElementNormalOn(rp); break; + case QIcon::Disabled: ri->setElementDisabledOn(rp); break; + case QIcon::Active: ri->setElementActiveOn(rp); break; + case QIcon::Selected: ri->setElementSelectedOn(rp); break; + } + } + } + p->setElementIconSet(ri); + return p; + } + } + delete p; + return 0; +} + +bool QDesignerResourceBuilder::isResourceType(const QVariant &value) const +{ + if (value.canConvert() || value.canConvert()) + return true; + return false; +} +// ------------------------- QDesignerTextBuilder +class QDesignerTextBuilder : public QTextBuilder +{ +public: + QDesignerTextBuilder() {} + + virtual QVariant loadText(const DomProperty *icon) const; + + virtual QVariant toNativeValue(const QVariant &value) const; + + virtual DomProperty *saveText(const QVariant &value) const; +}; + +QVariant QDesignerTextBuilder::loadText(const DomProperty *text) const +{ + const DomString *str = text->elementString(); + PropertySheetStringValue strVal(str->text()); + if (str->hasAttributeComment()) { + strVal.setDisambiguation(str->attributeComment()); + } + if (str->hasAttributeExtraComment()) { + strVal.setComment(str->attributeExtraComment()); + } + if (str->hasAttributeNotr()) { + const QString notr = str->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + strVal.setTranslatable(translatable); + } + return QVariant::fromValue(strVal); +} + +QVariant QDesignerTextBuilder::toNativeValue(const QVariant &value) const +{ + if (value.canConvert()) + return QVariant::fromValue(qvariant_cast(value).value()); + return value; +} + +DomProperty *QDesignerTextBuilder::saveText(const QVariant &value) const +{ + if (!value.canConvert() && !value.canConvert()) + return 0; + + DomProperty *property = new DomProperty(); + DomString *domStr = new DomString(); + + if (value.canConvert()) { + PropertySheetStringValue str = qvariant_cast(value); + + domStr->setText(str.value()); + + const QString property_comment = str.disambiguation(); + if (!property_comment.isEmpty()) + domStr->setAttributeComment(property_comment); + const QString property_extraComment = str.comment(); + if (!property_extraComment.isEmpty()) + domStr->setAttributeExtraComment(property_extraComment); + const bool property_translatable = str.translatable(); + if (!property_translatable) + domStr->setAttributeNotr(QLatin1String("true")); + } else { + domStr->setText(value.toString()); + } + + property->setElementString(domStr); + return property; +} + +QDesignerResource::QDesignerResource(FormWindow *formWindow) : + QEditorFormBuilder(formWindow->core()), + m_formWindow(formWindow), + m_topLevelSpacerCount(0), + m_copyWidget(false), + m_selected(0), + m_resourceBuilder(new QDesignerResourceBuilder(m_formWindow->core(), m_formWindow->pixmapCache(), m_formWindow->iconCache())) +{ + setWorkingDirectory(formWindow->absoluteDir()); + setResourceBuilder(m_resourceBuilder); + setTextBuilder(new QDesignerTextBuilder()); + + // ### generalise + const QString designerWidget = QLatin1String("QDesignerWidget"); + const QString layoutWidget = QLatin1String("QLayoutWidget"); + const QString widget = QLatin1String("QWidget"); + m_internal_to_qt.insert(layoutWidget, widget); + m_internal_to_qt.insert(designerWidget, widget); + m_internal_to_qt.insert(QLatin1String("QDesignerDialog"), QLatin1String("QDialog")); + m_internal_to_qt.insert(QLatin1String("QDesignerMenuBar"), QLatin1String("QMenuBar")); + m_internal_to_qt.insert(QLatin1String("QDesignerMenu"), QLatin1String("QMenu")); + m_internal_to_qt.insert(QLatin1String("QDesignerDockWidget"), QLatin1String("QDockWidget")); + m_internal_to_qt.insert(QLatin1String("QDesignerQ3WidgetStack"), QLatin1String("Q3WidgetStack")); + + // invert + QHash::const_iterator cend = m_internal_to_qt.constEnd(); + for (QHash::const_iterator it = m_internal_to_qt.constBegin();it != cend; ++it ) { + if (it.value() != designerWidget && it.value() != layoutWidget) + m_qt_to_internal.insert(it.value(), it.key()); + + } +} + +QDesignerResource::~QDesignerResource() +{ +} + +static inline QString messageBoxTitle() +{ + return QApplication::translate("Designer", "Qt Designer"); +} + +void QDesignerResource::save(QIODevice *dev, QWidget *widget) +{ + m_topLevelSpacerCount = 0; + + QAbstractFormBuilder::save(dev, widget); + + if (QSimpleResource::warningsEnabled() && m_topLevelSpacerCount != 0) { + const QString message = QApplication::translate("Designer", "This file contains top level spacers.
" + "They have NOT been saved into the form."); + const QString infoMessage = QApplication::translate("Designer", "Perhaps you forgot to create a layout?"); + + core()->dialogGui()->message(widget->window(), QDesignerDialogGuiInterface::TopLevelSpacerMessage, + QMessageBox::Warning, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + } +} + +void QDesignerResource::saveDom(DomUI *ui, QWidget *widget) +{ + QAbstractFormBuilder::saveDom(ui, widget); + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), widget); + Q_ASSERT(sheet != 0); + + const QVariant classVar = sheet->property(sheet->indexOf(QLatin1String("objectName"))); + QString classStr; + if (classVar.canConvert(QVariant::String)) + classStr = classVar.toString(); + else + classStr = qvariant_cast(classVar).value(); + ui->setElementClass(classStr); + + for (int index = 0; index < m_formWindow->toolCount(); ++index) { + QDesignerFormWindowToolInterface *tool = m_formWindow->tool(index); + Q_ASSERT(tool != 0); + tool->saveToDom(ui, widget); + } + + const QString author = m_formWindow->author(); + if (!author.isEmpty()) { + ui->setElementAuthor(author); + } + + const QString comment = m_formWindow->comment(); + if (!comment.isEmpty()) { + ui->setElementComment(comment); + } + + const QString exportMacro = m_formWindow->exportMacro(); + if (!exportMacro.isEmpty()) { + ui->setElementExportMacro(exportMacro); + } + + const QVariantMap designerFormData = m_formWindow->formData(); + if (!designerFormData.empty()) { + DomPropertyList domPropertyList; + const QVariantMap::const_iterator cend = designerFormData.constEnd(); + for (QVariantMap::const_iterator it = designerFormData.constBegin(); it != cend; ++it) { + if (DomProperty *prop = variantToDomProperty(this, widget->metaObject(), it.key(), it.value())) + domPropertyList += prop; + } + if (!domPropertyList.empty()) { + DomDesignerData* domDesignerFormData = new DomDesignerData; + domDesignerFormData->setElementProperty(domPropertyList); + ui->setElementDesignerdata(domDesignerFormData); + } + } + + if (!m_formWindow->includeHints().isEmpty()) { + const QString local = QLatin1String("local"); + const QString global = QLatin1String("global"); + QList ui_includes; + foreach (QString includeHint, m_formWindow->includeHints()) { + if (includeHint.isEmpty()) + continue; + DomInclude *incl = new DomInclude; + const QString location = includeHint.at(0) == QLatin1Char('<') ? global : local; + includeHint.remove(QLatin1Char('"')); + includeHint.remove(QLatin1Char('<')); + includeHint.remove(QLatin1Char('>')); + incl->setAttributeLocation(location); + incl->setText(includeHint); + ui_includes.append(incl); + } + + DomIncludes *includes = new DomIncludes; + includes->setElementInclude(ui_includes); + ui->setElementIncludes(includes); + } + + int defaultMargin = INT_MIN, defaultSpacing = INT_MIN; + m_formWindow->layoutDefault(&defaultMargin, &defaultSpacing); + + if (defaultMargin != INT_MIN || defaultSpacing != INT_MIN) { + DomLayoutDefault *def = new DomLayoutDefault; + if (defaultMargin != INT_MIN) + def->setAttributeMargin(defaultMargin); + if (defaultSpacing != INT_MIN) + def->setAttributeSpacing(defaultSpacing); + ui->setElementLayoutDefault(def); + } + + QString marginFunction, spacingFunction; + m_formWindow->layoutFunction(&marginFunction, &spacingFunction); + if (!marginFunction.isEmpty() || !spacingFunction.isEmpty()) { + DomLayoutFunction *def = new DomLayoutFunction; + + if (!marginFunction.isEmpty()) + def->setAttributeMargin(marginFunction); + if (!spacingFunction.isEmpty()) + def->setAttributeSpacing(spacingFunction); + ui->setElementLayoutFunction(def); + } + + QString pixFunction = m_formWindow->pixmapFunction(); + if (!pixFunction.isEmpty()) { + ui->setElementPixmapFunction(pixFunction); + } + + if (QDesignerExtraInfoExtension *extra = qt_extension(core()->extensionManager(), core())) + extra->saveUiExtraInfo(ui); + + if (MetaDataBase *metaDataBase = qobject_cast(core()->metaDataBase())) { + const MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(m_formWindow->mainContainer()); + const QStringList fakeSlots = item->fakeSlots(); + const QStringList fakeSignals =item->fakeSignals(); + if (!fakeSlots.empty() || !fakeSignals.empty()) { + DomSlots *domSlots = new DomSlots(); + domSlots->setElementSlot(fakeSlots); + domSlots->setElementSignal(fakeSignals); + ui->setElementSlots(domSlots); + } + } +} + +namespace { + enum LoadPreCheck { LoadPreCheckFailed, LoadPreCheckVersion3, LoadPreCheckVersionMismatch, LoadPreCheckOk }; + // Pair of major, minor + typedef QPair UiVersion; +} + +static UiVersion uiVersion(const QString &attr) +{ + const QStringList versions = attr.split(QLatin1Char('.')); + if (versions.empty()) + return UiVersion(-1, -1); + + bool ok = false; + UiVersion rc(versions.at(0).toInt(&ok), 0); + + if (!ok) + return UiVersion(-1, -1); + + if (versions.size() > 1) { + const int minorVersion = versions.at(1).toInt(&ok); + if (ok) + rc.second = minorVersion; + } + return rc; +} + +// Read version and language attributes of an element. +static bool readUiAttributes(QIODevice *dev, QString *errorMessage, + QString *version, + QString *language) +{ + const QString uiElement = QLatin1String("ui"); + const QString versionAttribute = QLatin1String("version"); + const QString languageAttribute = QLatin1String("language"); + QXmlStreamReader reader(dev); + // Read up to first element + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + const QStringRef tag = reader.name(); + if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0) { + const QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(versionAttribute)) + *version = attributes.value(versionAttribute).toString(); + if (attributes.hasAttribute(languageAttribute)) + *language = attributes.value(languageAttribute).toString(); + return true; + } else { + *errorMessage = QCoreApplication::translate("Designer", "Invalid UI file: The root element is missing."); + return false; + + } + } + } + *errorMessage = QCoreApplication::translate("Designer", "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 false; +} + +// While loading a file, check language, version and extra extension +static LoadPreCheck loadPrecheck(QDesignerFormEditorInterface *core, + QIODevice *dev, + QString *errorMessage, QString *versionString) +{ + QString language; + // Read attributes of and rewind + if (!readUiAttributes(dev, errorMessage, versionString, &language)) { + // XML error: Mimick the behaviour occurring if an XML error is + // detected later on, report to warning log and have embedding + // application display a dialog. + designerWarning(*errorMessage); + errorMessage->clear(); + return LoadPreCheckFailed; + } + dev->seek(0); + + // Check language unless extension present (Jambi) + if (!language.isEmpty() && !qt_extension(core->extensionManager(), core)) { + if (language.toLower() != QLatin1String("c++")) { + // Jambi?! + *errorMessage = QApplication::translate("Designer", "This file cannot be read because it was created using %1.").arg(language); + return LoadPreCheckFailed; + } + } + + // Version + if (!versionString->isEmpty()) { + const UiVersion version = uiVersion(*versionString); + switch (version.first) { + case 3: + return LoadPreCheckVersion3; + case 4: + break; + default: + *errorMessage = QApplication::translate("Designer", "This file was created using Designer from Qt-%1 and cannot be read.").arg(*versionString); + return LoadPreCheckVersionMismatch; + } + } + return LoadPreCheckOk; +} + +QWidget *QDesignerResource::load(QIODevice *dev, QWidget *parentWidget) +{ + // Run loadPreCheck for version and language + QString errorMessage; + QString version; + switch (loadPrecheck(core(), dev, &errorMessage, &version)) { + case LoadPreCheckFailed: + case LoadPreCheckVersionMismatch: + if (!errorMessage.isEmpty()) + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), errorMessage, QMessageBox::Ok); + return 0; + case LoadPreCheckVersion3: { + QWidget *w = 0; + QByteArray ba; + if (runUIC( m_formWindow->fileName(), ba, errorMessage)) { + QBuffer buffer(&ba); + buffer.open(QIODevice::ReadOnly); + w = load(&buffer, parentWidget); + if (w) { + // Force the form to pop up a save file dialog + m_formWindow->setFileName(QString()); + } else { + errorMessage = QApplication::translate("Designer", "The converted file could not be read."); + } + } + if (w) { + const QString message = QApplication::translate("Designer", + "This file was created using Designer from Qt-%1 and" + " will be converted to a new form by Qt Designer.").arg(version); + const QString infoMessage = QApplication::translate("Designer", + "The old form has not been touched, but you will have to save the form" + " under a new name."); + + core()->dialogGui()->message(parentWidget->window(), + QDesignerDialogGuiInterface::UiVersionMismatchMessage, + QMessageBox::Information, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + return w; + } + + const QString message = QApplication::translate("Designer", + "This file was created using Designer from Qt-%1 and " + "could not be read:\n%2").arg(version).arg(errorMessage); + const QString infoMessage = QApplication::translate("Designer", + "Please run it through uic3 -convert to convert " + "it to Qt-4's ui format."); + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), message, infoMessage, + QMessageBox::Ok); + return 0; + } + + case LoadPreCheckOk: + break; + } + QWidget *w = QEditorFormBuilder::load(dev, parentWidget); + if (w) // Store the class name as 'reset' value for the main container's object name. + w->setProperty("_q_classname", w->objectName()); + return w; +} + +bool QDesignerResource::saveRelative() const +{ + return m_resourceBuilder->isSaveRelative(); +} + +void QDesignerResource::setSaveRelative(bool relative) +{ + m_resourceBuilder->setSaveRelative(relative); +} + +QWidget *QDesignerResource::create(DomUI *ui, QWidget *parentWidget) +{ + // Load extra info extension. This is used by Jambi for preventing + // C++ UI files from being loaded + if (QDesignerExtraInfoExtension *extra = qt_extension(core()->extensionManager(), core())) { + if (!extra->loadUiExtraInfo(ui)) { + const QString errorMessage = QApplication::translate("Designer", "This file cannot be read because the extra info extension failed to load."); + core()->dialogGui()->message(parentWidget->window(), QDesignerDialogGuiInterface::FormLoadFailureMessage, + QMessageBox::Warning, messageBoxTitle(), errorMessage, QMessageBox::Ok); + return 0; + } + } + + qdesigner_internal::WidgetFactory *factory = qobject_cast(core()->widgetFactory()); + Q_ASSERT(factory != 0); + + QDesignerFormWindowInterface *previousFormWindow = factory->currentFormWindow(m_formWindow); + + m_isMainWidget = true; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QWidget *mainWidget = QAbstractFormBuilder::create(ui, parentWidget); + + if (mainWidget && m_formWindow) { + m_formWindow->setAuthor(ui->elementAuthor()); + m_formWindow->setComment(ui->elementComment()); + m_formWindow->setExportMacro(ui->elementExportMacro()); + + // Designer data + QVariantMap designerFormData; + if (ui->hasElementDesignerdata()) { + const DomPropertyList domPropertyList = ui->elementDesignerdata()->elementProperty(); + const DomPropertyList::const_iterator cend = domPropertyList.constEnd(); + for (DomPropertyList::const_iterator it = domPropertyList.constBegin(); it != cend; ++it) { + const QVariant vprop = domPropertyToVariant(this, mainWidget->metaObject(), *it); + if (vprop.type() != QVariant::Invalid) + designerFormData.insert((*it)->attributeName(), vprop); + } + } + m_formWindow->setFormData(designerFormData); + + m_formWindow->setPixmapFunction(ui->elementPixmapFunction()); + + if (DomLayoutDefault *def = ui->elementLayoutDefault()) { + m_formWindow->setLayoutDefault(def->attributeMargin(), def->attributeSpacing()); + } + + if (DomLayoutFunction *fun = ui->elementLayoutFunction()) { + m_formWindow->setLayoutFunction(fun->attributeMargin(), fun->attributeSpacing()); + } + + if (DomIncludes *includes = ui->elementIncludes()) { + const QString global = QLatin1String("global"); + QStringList includeHints; + foreach (DomInclude *incl, includes->elementInclude()) { + QString text = incl->text(); + + if (text.isEmpty()) + continue; + + if (incl->hasAttributeLocation() && incl->attributeLocation() == global ) { + text = text.prepend(QLatin1Char('<')).append(QLatin1Char('>')); + } else { + text = text.prepend(QLatin1Char('"')).append(QLatin1Char('"')); + } + + includeHints.append(text); + } + + m_formWindow->setIncludeHints(includeHints); + } + + // Register all button groups the form builder adds as children of the main container for them to be found + // in the signal slot editor + const QObjectList mchildren = mainWidget->children(); + if (!mchildren.empty()) { + QDesignerMetaDataBaseInterface *mdb = core()->metaDataBase(); + const QObjectList::const_iterator cend = mchildren.constEnd(); + for (QObjectList::const_iterator it = mchildren.constBegin(); it != cend; ++it) + if (QButtonGroup *bg = qobject_cast(*it)) + mdb->add(bg); + } + // Load tools + for (int index = 0; index < m_formWindow->toolCount(); ++index) { + QDesignerFormWindowToolInterface *tool = m_formWindow->tool(index); + Q_ASSERT(tool != 0); + tool->loadFromDom(ui, mainWidget); + } + } + + factory->currentFormWindow(previousFormWindow); + + if (const DomSlots *domSlots = ui->elementSlots()) { + if (MetaDataBase *metaDataBase = qobject_cast(core()->metaDataBase())) { + QStringList fakeSlots; + QStringList fakeSignals; + if (addFakeMethods(domSlots, fakeSlots, fakeSignals)) { + MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(mainWidget); + item->setFakeSlots(fakeSlots); + item->setFakeSignals(fakeSignals); + } + } + } + if (mainWidget) { + // Initialize the mainwindow geometry. Has it been explicitly specified? + bool hasExplicitGeometry = false; + const QList properties = ui->elementWidget()->elementProperty(); + if (!properties.empty()) { + const QString geometry = QLatin1String("geometry"); + foreach (const DomProperty *p, properties) + if (p->attributeName() == geometry) { + hasExplicitGeometry = true; + break; + } + } + if (hasExplicitGeometry) { + // Geometry was specified explicitly: Verify that smartMinSize is respected + // (changed fonts, label wrapping policies, etc). This does not happen automatically in docked mode. + const QSize size = mainWidget->size(); + const QSize minSize = size.expandedTo(qSmartMinSize(mainWidget)); + if (minSize != size) + mainWidget->resize(minSize); + } else { + // No explicit Geometry: perform an adjustSize() to resize the form correctly before embedding it into a container + // (which might otherwise squeeze the form) + mainWidget->adjustSize(); + } + // Some integration wizards create forms with main containers + // based on derived classes of QWidget and load them into Designer + // without the plugin existing. This will trigger the auto-promotion + // mechanism of Designer, which will set container=false for + // QWidgets. For the main container, force container=true and warn. + const QDesignerWidgetDataBaseInterface *wdb = core()->widgetDataBase(); + const int wdbIndex = wdb->indexOfObject(mainWidget); + if (wdbIndex != -1) { + QDesignerWidgetDataBaseItemInterface *item = wdb->item(wdbIndex); + // Promoted main container that is not of container type + if (item->isPromoted() && !item->isContainer()) { + item->setContainer(true); + qWarning("** WARNING The form's main container is an unknown custom widget '%s'." + " Defaulting to a promoted instance of '%s', assuming container.", + item->name().toUtf8().constData(), item->extends().toUtf8().constData()); + } + } + } + return mainWidget; +} + +QWidget *QDesignerResource::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + const QString className = ui_widget->attributeClass(); + if (!m_isMainWidget && className == QLatin1String("QWidget") && ui_widget->elementLayout().size() && + !ui_widget->hasAttributeNative()) { + // ### check if elementLayout.size() == 1 + + QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), parentWidget); + + if (container == 0) { + // generate a QLayoutWidget iff the parent is not an QDesignerContainerExtension. + ui_widget->setAttributeClass(QLatin1String("QLayoutWidget")); + } + } + + // save the actions + const QList actionRefs = ui_widget->elementAddAction(); + ui_widget->setElementAddAction(QList()); + + QWidget *w = QAbstractFormBuilder::create(ui_widget, parentWidget); + + // restore the actions + ui_widget->setElementAddAction(actionRefs); + + if (w == 0) + return 0; + + // ### generalize using the extension manager + QDesignerMenu *menu = qobject_cast(w); + QDesignerMenuBar *menuBar = qobject_cast(w); + + if (menu) { + menu->interactive(false); + menu->hide(); + } else if (menuBar) { + menuBar->interactive(false); + } + + foreach (DomActionRef *ui_action_ref, actionRefs) { + const QString name = ui_action_ref->attributeName(); + if (name == QLatin1String("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 = w->findChild(name)) { + w->addAction(menu->menuAction()); + addMenuAction(menu->menuAction()); + } + } + + if (menu) { + menu->interactive(true); + menu->adjustSpecialActions(); + } else if (menuBar) { + menuBar->interactive(true); + menuBar->adjustSpecialActions(); + } + + ui_widget->setAttributeClass(className); // fix the class name + applyExtensionDataFromDOM(this, core(), ui_widget, w, true); + + // store user-defined scripts + if (MetaDataBase *metaDataBase = qobject_cast(core()->metaDataBase())) { + const QString designerSource = QLatin1String("designer"); + const DomScripts domScripts = ui_widget->elementScript(); + if (!domScripts.empty()) { + foreach (const DomScript *script, domScripts) { + if (script->hasAttributeSource() && script->attributeSource() == designerSource) { + metaDataBase->metaDataBaseItem(w)->setScript(script->text()); + } + } + } + } + + return w; +} + +QLayout *QDesignerResource::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + QLayout *l = QAbstractFormBuilder::create(ui_layout, layout, parentWidget); + + if (QGridLayout *gridLayout = qobject_cast(l)) { + QLayoutSupport::createEmptyCells(gridLayout); + } else { + if (QFormLayout *formLayout = qobject_cast(l)) + QLayoutSupport::createEmptyCells(formLayout); + } + // While the actual values are applied by the form builder, we still need + // to mark them as 'changed'. + LayoutPropertySheet::markChangedStretchProperties(core(), l, ui_layout); + return l; +} + +QLayoutItem *QDesignerResource::create(DomLayoutItem *ui_layoutItem, QLayout *layout, QWidget *parentWidget) +{ + if (ui_layoutItem->kind() == DomLayoutItem::Spacer) { + const DomSpacer *domSpacer = ui_layoutItem->elementSpacer(); + const QHash properties = propertyMap(domSpacer->elementProperty()); + Spacer *spacer = static_cast(core()->widgetFactory()->createWidget(QLatin1String("Spacer"), parentWidget)); + if (domSpacer->hasAttributeName()) + changeObjectName(spacer, domSpacer->attributeName()); + core()->metaDataBase()->add(spacer); + + spacer->setInteractiveMode(false); + applyProperties(spacer, ui_layoutItem->elementSpacer()->elementProperty()); + spacer->setInteractiveMode(true); + + if (m_formWindow) { + m_formWindow->manageWidget(spacer); + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), spacer)) + sheet->setChanged(sheet->indexOf(QLatin1String("orientation")), true); + } + + return new QWidgetItem(spacer); + } else if (ui_layoutItem->kind() == DomLayoutItem::Layout && parentWidget) { + DomLayout *ui_layout = ui_layoutItem->elementLayout(); + QLayoutWidget *layoutWidget = new QLayoutWidget(m_formWindow, parentWidget); + core()->metaDataBase()->add(layoutWidget); + if (m_formWindow) + m_formWindow->manageWidget(layoutWidget); + (void) create(ui_layout, 0, layoutWidget); + return new QWidgetItem(layoutWidget); + } + return QAbstractFormBuilder::create(ui_layoutItem, layout, parentWidget); +} + +void QDesignerResource::changeObjectName(QObject *o, QString objName) +{ + m_formWindow->unify(o, objName, true); + o->setObjectName(objName); + +} + +/* If the property is a enum or flag value, retrieve + * the existing enum/flag via property sheet and use it to convert */ + +static bool readDomEnumerationValue(const DomProperty *p, + const QDesignerPropertySheetExtension* sheet, int index, + QVariant &v) +{ + switch (p->kind()) { + case DomProperty::Set: { + const QVariant sheetValue = sheet->property(index); + if (sheetValue.canConvert()) { + 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 QVariant sheetValue = sheet->property(index); + if (sheetValue.canConvert()) { + 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 QDesignerResource::applyProperties(QObject *o, const QList &properties) +{ + if (properties.empty()) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), o); + if (!sheet) + return; + + QFormBuilderExtra *formBuilderExtra = QFormBuilderExtra::instance(this); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core()->extensionManager(), o); + const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed(); + + const QString objectNameProperty = QLatin1String("objectName"); + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const DomProperty *p = *it; + const QString propertyName = p->attributeName(); + const int index = sheet->indexOf(propertyName); + QVariant v; + if (!readDomEnumerationValue(p, sheet, index, v)) + v = toVariant(o->metaObject(), *it); + + if (p->kind() == DomProperty::String) { + if (index != -1 && sheet->property(index).userType() == qMetaTypeId()) { + const DomString *key = p->elementString(); + PropertySheetKeySequenceValue keyVal(QKeySequence(key->text())); + if (key->hasAttributeComment()) + keyVal.setDisambiguation(key->attributeComment()); + if (key->hasAttributeExtraComment()) + keyVal.setComment(key->attributeExtraComment()); + if (key->hasAttributeNotr()) { + const QString notr = key->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + keyVal.setTranslatable(translatable); + } + v = QVariant::fromValue(keyVal); + } else { + const DomString *str = p->elementString(); + PropertySheetStringValue strVal(v.toString()); + if (str->hasAttributeComment()) + strVal.setDisambiguation(str->attributeComment()); + if (str->hasAttributeExtraComment()) + strVal.setComment(str->attributeExtraComment()); + if (str->hasAttributeNotr()) { + const QString notr = str->attributeNotr(); + const bool translatable = !(notr == QLatin1String("true") || notr == QLatin1String("yes")); + if (!translatable) + strVal.setTranslatable(translatable); + } + v = QVariant::fromValue(strVal); + } + } + + formBuilderExtra->applyPropertyInternally(o, propertyName, v); + if (index != -1) { + sheet->setProperty(index, v); + sheet->setChanged(index, true); + } else if (dynamicPropertiesAllowed) { + QVariant defaultValue = QVariant(v.type()); + bool isDefault = (v == defaultValue); + if (v.canConvert()) { + defaultValue = QVariant(QVariant::Icon); + isDefault = (qvariant_cast(v) == PropertySheetIconValue()); + } else if (v.canConvert()) { + defaultValue = QVariant(QVariant::Pixmap); + isDefault = (qvariant_cast(v) == PropertySheetPixmapValue()); + } else if (v.canConvert()) { + defaultValue = QVariant(QVariant::String); + isDefault = (qvariant_cast(v) == PropertySheetStringValue()); + } else if (v.canConvert()) { + defaultValue = QVariant(QVariant::KeySequence); + isDefault = (qvariant_cast(v) == PropertySheetKeySequenceValue()); + } + if (defaultValue.type() != QVariant::UserType) { + const int idx = dynamicSheet->addDynamicProperty(p->attributeName(), defaultValue); + if (idx != -1) { + sheet->setProperty(idx, v); + sheet->setChanged(idx, !isDefault); + } + } + } + + if (propertyName == objectNameProperty) + changeObjectName(o, o->objectName()); + } +} + +QWidget *QDesignerResource::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &_name) +{ + QString name = _name; + QString className = widgetName; + if (m_isMainWidget) + m_isMainWidget = false; + + QWidget *w = core()->widgetFactory()->createWidget(className, parentWidget); + if (!w) + return 0; + + if (name.isEmpty()) { + QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + if (QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(w))) + name = qtify(item->name()); + } + + changeObjectName(w, name); + + QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), parentWidget); + if (!qobject_cast(w) && (!parentWidget || !container)) { + m_formWindow->manageWidget(w); + if (parentWidget) { + QList list = qvariant_cast(parentWidget->property("_q_widgetOrder")); + list.append(w); + parentWidget->setProperty("_q_widgetOrder", QVariant::fromValue(list)); + QList zOrder = qvariant_cast(parentWidget->property("_q_zOrder")); + zOrder.append(w); + parentWidget->setProperty("_q_zOrder", QVariant::fromValue(zOrder)); + } + } else { + core()->metaDataBase()->add(w); + } + + w->setWindowFlags(w->windowFlags() & ~Qt::Window); + // Make sure it is non-modal (for example, KDialog calls setModal(true) in the constructor). + w->setWindowModality(Qt::NonModal); + + return w; +} + +QLayout *QDesignerResource::createLayout(const QString &layoutName, QObject *parent, const QString &name) +{ + QWidget *layoutBase = 0; + QLayout *layout = qobject_cast(parent); + + if (parent->isWidgetType()) + layoutBase = static_cast(parent); + else { + Q_ASSERT( layout != 0 ); + layoutBase = layout->parentWidget(); + } + + LayoutInfo::Type layoutType = LayoutInfo::layoutType(layoutName); + if (layoutType == LayoutInfo::NoLayout) { + designerWarning(QCoreApplication::translate("QDesignerResource", "The layout type '%1' is not supported, defaulting to grid.").arg(layoutName)); + layoutType = LayoutInfo::Grid; + } + QLayout *lay = core()->widgetFactory()->createLayout(layoutBase, layout, layoutType); + if (lay != 0) + changeObjectName(lay, name); + + return lay; +} + +// save +DomWidget *QDesignerResource::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(widget); + if (!item) + return 0; + + if (qobject_cast(widget) && m_copyWidget == false) { + ++m_topLevelSpacerCount; + return 0; + } + + const QDesignerWidgetDataBaseInterface *wdb = core()->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *widgetInfo = 0; + const int widgetInfoIndex = wdb->indexOfObject(widget, false); + if (widgetInfoIndex != -1) { + widgetInfo = wdb->item(widgetInfoIndex); + // Recursively add all dependent custom widgets + QDesignerWidgetDataBaseItemInterface *customInfo = widgetInfo; + while (customInfo && customInfo->isCustom()) { + m_usedCustomWidgets.insert(customInfo, true); + const QString extends = customInfo->extends(); + if (extends == customInfo->name()) { + break; // There are faulty files around that have name==extends + } else { + const int extendsIndex = wdb->indexOfClassName(customInfo->extends()); + customInfo = extendsIndex != -1 ? wdb->item(extendsIndex) : static_cast(0); + } + } + } + + DomWidget *w = 0; + + if (QTabWidget *tabWidget = qobject_cast(widget)) + w = saveWidget(tabWidget, ui_parentWidget); + else if (QStackedWidget *stackedWidget = qobject_cast(widget)) + w = saveWidget(stackedWidget, ui_parentWidget); + else if (QToolBox *toolBox = qobject_cast(widget)) + w = saveWidget(toolBox, ui_parentWidget); + else if (QToolBar *toolBar = qobject_cast(widget)) + w = saveWidget(toolBar, ui_parentWidget); + else if (QDesignerDockWidget *dockWidget = qobject_cast(widget)) + w = saveWidget(dockWidget, ui_parentWidget); + else if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), widget)) + w = saveWidget(widget, container, ui_parentWidget); + else if (QWizardPage *wizardPage = qobject_cast(widget)) + w = saveWidget(wizardPage, ui_parentWidget); + else + w = QAbstractFormBuilder::createDom(widget, ui_parentWidget, recursive); + + Q_ASSERT( w != 0 ); + + if (!qobject_cast(widget) && w->attributeClass() == QLatin1String("QWidget")) { + w->setAttributeNative(true); + } + + const QString className = w->attributeClass(); + if (m_internal_to_qt.contains(className)) + w->setAttributeClass(m_internal_to_qt.value(className)); + + w->setAttributeName(widget->objectName()); + + if (isPromoted( core(), widget)) { // is promoted? + Q_ASSERT(widgetInfo != 0); + + w->setAttributeName(widget->objectName()); + w->setAttributeClass(widgetInfo->name()); + + QList prop_list = w->elementProperty(); + foreach (DomProperty *prop, prop_list) { + if (prop->attributeName() == QLatin1String("geometry")) { + if (DomRect *rect = prop->elementRect()) { + rect->setElementX(widget->x()); + rect->setElementY(widget->y()); + } + break; + } + } + } else if (widgetInfo != 0 && m_usedCustomWidgets.contains(widgetInfo)) { + if (widgetInfo->name() != w->attributeClass()) + w->setAttributeClass(widgetInfo->name()); + } + addExtensionDataToDOM(this, core(), w, widget); + + addUserDefinedScripts(widget, w); + return w; +} + +DomLayout *QDesignerResource::createDom(QLayout *layout, DomLayout *ui_parentLayout, DomWidget *ui_parentWidget) +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(layout); + + if (item == 0) { + layout = layout->findChild(); + // refresh the meta database item + item = core()->metaDataBase()->item(layout); + } + + if (item == 0) { + // nothing to do. + return 0; + } + + if (qobject_cast(layout->parentWidget()) != 0) { + // nothing to do. + return 0; + } + + m_chain.push(layout); + + DomLayout *l = QAbstractFormBuilder::createDom(layout, ui_parentLayout, ui_parentWidget); + Q_ASSERT(l != 0); + LayoutPropertySheet::stretchAttributesToDom(core(), layout, l); + + m_chain.pop(); + + return l; +} + +DomLayoutItem *QDesignerResource::createDom(QLayoutItem *item, DomLayout *ui_layout, DomWidget *ui_parentWidget) +{ + DomLayoutItem *ui_item = 0; + + if (Spacer *s = qobject_cast(item->widget())) { + if (!core()->metaDataBase()->item(s)) + return 0; + + DomSpacer *spacer = new DomSpacer(); + const QString objectName = s->objectName(); + if (!objectName.isEmpty()) + spacer->setAttributeName(objectName); + const QList properties = computeProperties(item->widget()); + // ### filter the properties + spacer->setElementProperty(properties); + + ui_item = new DomLayoutItem(); + ui_item->setElementSpacer(spacer); + m_laidout.insert(item->widget(), true); + } else if (QLayoutWidget *layoutWidget = qobject_cast(item->widget())) { + // Do not save a QLayoutWidget if it is within a layout (else it is saved as "QWidget" + Q_ASSERT(layoutWidget->layout()); + DomLayout *l = createDom(layoutWidget->layout(), ui_layout, ui_parentWidget); + ui_item = new DomLayoutItem(); + ui_item->setElementLayout(l); + m_laidout.insert(item->widget(), true); + } else if (!item->spacerItem()) { // we use spacer as fake item in the Designer + ui_item = QAbstractFormBuilder::createDom(item, ui_layout, ui_parentWidget); + } else { + return 0; + } + return ui_item; +} + +void QDesignerResource::createCustomWidgets(DomCustomWidgets *dom_custom_widgets) +{ + QSimpleResource::handleDomCustomWidgets(core(), dom_custom_widgets); +} + +DomTabStops *QDesignerResource::saveTabStops() +{ + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(m_formWindow); + Q_ASSERT(item); + + QStringList tabStops; + foreach (QWidget *widget, item->tabOrder()) { + if (m_formWindow->mainContainer()->isAncestorOf(widget)) + tabStops.append(widget->objectName()); + } + + if (tabStops.count()) { + DomTabStops *dom = new DomTabStops; + dom->setElementTabStop(tabStops); + return dom; + } + + return 0; +} + +void QDesignerResource::applyTabStops(QWidget *widget, DomTabStops *tabStops) +{ + if (!tabStops) + return; + + QList tabOrder; + foreach (const QString &widgetName, tabStops->elementTabStop()) { + if (QWidget *w = widget->findChild(widgetName)) { + tabOrder.append(w); + } + } + + QDesignerMetaDataBaseItemInterface *item = core()->metaDataBase()->item(m_formWindow); + Q_ASSERT(item); + item->setTabOrder(tabOrder); +} + +/* Unmanaged container pages occur when someone adds a page in a custom widget + * constructor. They don't have a meta DB entry which causes createDom + * to return 0. */ +inline QString msgUnmanagedPage(QDesignerFormEditorInterface *core, + QWidget *container, int index, QWidget *page) +{ + return QCoreApplication::translate("QDesignerResource", +"The container extension of the widget '%1' (%2) returned a widget not managed by Designer '%3' (%4) when queried for page #%5.\n" +"Container pages should only be added by specifying them in XML returned by the domXml() method of the custom widget."). + arg(container->objectName(), WidgetFactory::classNameOf(core, container), + page->objectName(), WidgetFactory::classNameOf(core, page)). + arg(index); +} + +DomWidget *QDesignerResource::saveWidget(QWidget *widget, QDesignerContainerExtension *container, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList ui_widget_list; + + for (int i=0; icount(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + if (DomWidget *ui_page = createDom(page, ui_widget)) { + ui_widget_list.append(ui_page); + } else { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + } + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QStackedWidget *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList ui_widget_list; + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), widget)) { + for (int i=0; icount(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + if (DomWidget *ui_page = createDom(page, ui_widget)) { + ui_widget_list.append(ui_page); + } else { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + } + } + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QToolBar *toolBar, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(toolBar, ui_parentWidget, false); + if (const QMainWindow *mainWindow = qobject_cast(toolBar->parentWidget())) { + const bool toolBarBreak = mainWindow->toolBarBreak(toolBar); + const Qt::ToolBarArea area = mainWindow->toolBarArea(toolBar); + + QList attributes = ui_widget->elementAttribute(); + + DomProperty *attr = new DomProperty(); + attr->setAttributeName(QLatin1String("toolBarArea")); + attr->setElementEnum(QLatin1String(toolBarAreaMetaEnum().valueToKey(area))); + attributes << attr; + + attr = new DomProperty(); + attr->setAttributeName(QLatin1String("toolBarBreak")); + attr->setElementBool(toolBarBreak ? QLatin1String("true") : QLatin1String("false")); + attributes << attr; + ui_widget->setElementAttribute(attributes); + } + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QDesignerDockWidget *dockWidget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(dockWidget, ui_parentWidget, true); + if (QMainWindow *mainWindow = qobject_cast(dockWidget->parentWidget())) { + const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(dockWidget); + DomProperty *attr = new DomProperty(); + attr->setAttributeName(QLatin1String("dockWidgetArea")); + attr->setElementNumber(int(area)); + ui_widget->setElementAttribute(ui_widget->elementAttribute() << attr); + } + + return ui_widget; +} + +static void saveStringProperty(DomProperty *property, const PropertySheetStringValue &value) +{ + DomString *str = new DomString(); + str->setText(value.value()); + + const QString property_comment = value.disambiguation(); + if (!property_comment.isEmpty()) + str->setAttributeComment(property_comment); + const QString property_extraComment = value.comment(); + if (!property_extraComment.isEmpty()) + str->setAttributeExtraComment(property_extraComment); + const bool property_translatable = value.translatable(); + if (!property_translatable) + str->setAttributeNotr(QLatin1String("true")); + + property->setElementString(str); +} + +static void saveKeySequenceProperty(DomProperty *property, const PropertySheetKeySequenceValue &value) +{ + DomString *str = new DomString(); + str->setText(value.value().toString()); + + const QString property_comment = value.disambiguation(); + if (!property_comment.isEmpty()) + str->setAttributeComment(property_comment); + const QString property_extraComment = value.comment(); + if (!property_extraComment.isEmpty()) + str->setAttributeExtraComment(property_extraComment); + const bool property_translatable = value.translatable(); + if (!property_translatable) + str->setAttributeNotr(QLatin1String("true")); + + property->setElementString(str); +} + +DomWidget *QDesignerResource::saveWidget(QTabWidget *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList ui_widget_list; + + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), widget)) { + const int current = widget->currentIndex(); + for (int i=0; icount(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + DomWidget *ui_page = createDom(page, ui_widget); + if (!ui_page) { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + continue; + } + QList ui_attribute_list; + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + // attribute `icon' + widget->setCurrentIndex(i); + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), widget); + PropertySheetIconValue icon = qvariant_cast(sheet->property(sheet->indexOf(QLatin1String("currentTabIcon")))); + DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), QVariant::fromValue(icon)); + if (p) { + p->setAttributeName(strings.iconAttribute); + ui_attribute_list.append(p); + } + // attribute `title' + p = textBuilder()->saveText(sheet->property(sheet->indexOf(QLatin1String("currentTabText")))); + if (p) { + p->setAttributeName(strings.titleAttribute); + ui_attribute_list.append(p); + } + + // attribute `toolTip' + QVariant v = sheet->property(sheet->indexOf(QLatin1String("currentTabToolTip"))); + if (!qvariant_cast(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.toolTipAttribute); + ui_attribute_list.append(p); + } + } + + // attribute `whatsThis' + v = sheet->property(sheet->indexOf(QLatin1String("currentTabWhatsThis"))); + if (!qvariant_cast(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.whatsThisAttribute); + ui_attribute_list.append(p); + } + } + + ui_page->setElementAttribute(ui_attribute_list); + + ui_widget_list.append(ui_page); + } + widget->setCurrentIndex(current); + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QToolBox *widget, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(widget, ui_parentWidget, false); + QList ui_widget_list; + + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), widget)) { + const int current = widget->currentIndex(); + for (int i=0; icount(); ++i) { + QWidget *page = container->widget(i); + Q_ASSERT(page); + + DomWidget *ui_page = createDom(page, ui_widget); + if (!ui_page) { + if (QSimpleResource::warningsEnabled()) + designerWarning(msgUnmanagedPage(core(), widget, i, page)); + continue; + } + + // attribute `label' + QList ui_attribute_list; + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + // attribute `icon' + widget->setCurrentIndex(i); + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), widget); + PropertySheetIconValue icon = qvariant_cast(sheet->property(sheet->indexOf(QLatin1String("currentItemIcon")))); + DomProperty *p = resourceBuilder()->saveResource(workingDirectory(), QVariant::fromValue(icon)); + if (p) { + p->setAttributeName(strings.iconAttribute); + ui_attribute_list.append(p); + } + p = textBuilder()->saveText(sheet->property(sheet->indexOf(QLatin1String("currentItemText")))); + if (p) { + p->setAttributeName(strings.labelAttribute); + ui_attribute_list.append(p); + } + + // attribute `toolTip' + QVariant v = sheet->property(sheet->indexOf(QLatin1String("currentItemToolTip"))); + if (!qvariant_cast(v).value().isEmpty()) { + p = textBuilder()->saveText(v); + if (p) { + p->setAttributeName(strings.toolTipAttribute); + ui_attribute_list.append(p); + } + } + + ui_page->setElementAttribute(ui_attribute_list); + + ui_widget_list.append(ui_page); + } + widget->setCurrentIndex(current); + } + + ui_widget->setElementWidget(ui_widget_list); + + return ui_widget; +} + +DomWidget *QDesignerResource::saveWidget(QWizardPage *wizardPage, DomWidget *ui_parentWidget) +{ + DomWidget *ui_widget = QAbstractFormBuilder::createDom(wizardPage, ui_parentWidget, true); + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), wizardPage); + // Save the page id (string) attribute, append to existing attributes + const QString pageIdPropertyName = QLatin1String(QWizardPagePropertySheet::pageIdProperty); + const int pageIdIndex = sheet->indexOf(pageIdPropertyName); + if (pageIdIndex != -1 && sheet->isChanged(pageIdIndex)) { + DomProperty *property = variantToDomProperty(this, wizardPage->metaObject(), pageIdPropertyName, sheet->property(pageIdIndex)); + Q_ASSERT(property); + property->elementString()->setAttributeNotr(QLatin1String("true")); + DomPropertyList attributes = ui_widget->elementAttribute(); + attributes.push_back(property); + ui_widget->setElementAttribute(attributes); + } + return ui_widget; +} + +// Do not save the 'currentTabName' properties of containers +static inline bool checkContainerProperty(const QWidget *w, const QString &propertyName) +{ + if (qobject_cast(w)) + return QToolBoxWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast(w)) + return QTabWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast(w)) + return QStackedWidgetPropertySheet::checkProperty(propertyName); + if (qobject_cast(w) || qobject_cast(w)) + return QMdiAreaPropertySheet::checkProperty(propertyName); + return true; +} + +bool QDesignerResource::checkProperty(QObject *obj, const QString &prop) const +{ + const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(obj); + + const int pindex = meta->indexOfProperty(prop); + if (pindex != -1 && !(meta->property(pindex)->attributes(obj) & QDesignerMetaPropertyInterface::StoredAttribute)) + return false; + + if (prop == QLatin1String("objectName") || prop == QLatin1String("spacerName")) // ### don't store the property objectName + return false; + + QWidget *check_widget = 0; + if (obj->isWidgetType()) + check_widget = static_cast(obj); + + if (check_widget && prop == QLatin1String("geometry")) { + if (check_widget == m_formWindow->mainContainer()) + return true; // Save although maincontainer is technically laid-out by embedding container + if (m_selected && m_selected == check_widget) + return true; + + return !LayoutInfo::isWidgetLaidout(core(), check_widget); + } + + if (check_widget && !checkContainerProperty(check_widget, prop)) + return false; + + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), obj)) { + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core()->extensionManager(), obj); + const int pindex = sheet->indexOf(prop); + if (sheet->isAttribute(pindex)) + return false; + + if (!dynamicSheet || !dynamicSheet->isDynamicProperty(pindex)) + return sheet->isChanged(pindex); + if (!sheet->isVisible(pindex)) + return false; + return true; + } + + return false; +} + +bool QDesignerResource::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + if (item->widget() == 0) { + return false; + } + + QGridLayout *grid = qobject_cast(layout); + QBoxLayout *box = qobject_cast(layout); + + if (grid != 0) { + const int rowSpan = ui_item->hasAttributeRowSpan() ? ui_item->attributeRowSpan() : 1; + const int colSpan = ui_item->hasAttributeColSpan() ? ui_item->attributeColSpan() : 1; + grid->addWidget(item->widget(), ui_item->attributeRow(), ui_item->attributeColumn(), rowSpan, colSpan, item->alignment()); + return true; + } else if (box != 0) { + box->addItem(item); + return true; + } + + return QAbstractFormBuilder::addItem(ui_item, item, layout); +} + +bool QDesignerResource::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + core()->metaDataBase()->add(widget); // ensure the widget is in the meta database + + if (! QAbstractFormBuilder::addItem(ui_widget, widget, parentWidget) || qobject_cast (parentWidget)) { + if (QDesignerContainerExtension *container = qt_extension(core()->extensionManager(), parentWidget)) + container->addWidget(widget); + } + + if (QTabWidget *tabWidget = qobject_cast(parentWidget)) { + const int tabIndex = tabWidget->count() - 1; + const int current = tabWidget->currentIndex(); + + tabWidget->setCurrentIndex(tabIndex); + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute()); + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), parentWidget); + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabIcon")), v); + } + if (DomProperty *ptext = attributes.value(strings.titleAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabText")), v); + } + if (DomProperty *ptext = attributes.value(strings.toolTipAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabToolTip")), v); + } + if (DomProperty *ptext = attributes.value(strings.whatsThisAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabWhatsThis")), v); + } + tabWidget->setCurrentIndex(current); + } else if (QToolBox *toolBox = qobject_cast(parentWidget)) { + const int itemIndex = toolBox->count() - 1; + const int current = toolBox->currentIndex(); + + toolBox->setCurrentIndex(itemIndex); + + const QFormBuilderStrings &strings = QFormBuilderStrings::instance(); + + const DomPropertyHash attributes = propertyMap(ui_widget->elementAttribute()); + QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), parentWidget); + if (DomProperty *picon = attributes.value(strings.iconAttribute)) { + QVariant v = resourceBuilder()->loadResource(workingDirectory(), picon); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemIcon")), v); + } + if (DomProperty *ptext = attributes.value(strings.labelAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemText")), v); + } + if (DomProperty *ptext = attributes.value(strings.toolTipAttribute)) { + QVariant v = textBuilder()->loadText(ptext); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemToolTip")), v); + } + toolBox->setCurrentIndex(current); + } + + return true; +} + +bool QDesignerResource::copy(QIODevice *dev, const FormBuilderClipboard &selection) +{ + m_copyWidget = true; + + DomUI *ui = copy(selection); + + m_laidout.clear(); + m_copyWidget = false; + + if (!ui) + return false; + + QXmlStreamWriter writer(dev); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + ui->write(writer); + writer.writeEndDocument(); + delete ui; + return true; +} + +DomUI *QDesignerResource::copy(const FormBuilderClipboard &selection) +{ + if (selection.empty()) + return 0; + + m_copyWidget = true; + + DomWidget *ui_widget = new DomWidget(); + ui_widget->setAttributeName(QLatin1String(clipboardObjectName)); + bool hasItems = false; + // Widgets + if (!selection.m_widgets.empty()) { + QList ui_widget_list; + const int size = selection.m_widgets.size(); + for (int i=0; i< size; ++i) { + QWidget *w = selection.m_widgets.at(i); + m_selected = w; + DomWidget *ui_child = createDom(w, ui_widget); + m_selected = 0; + if (ui_child) + ui_widget_list.append(ui_child); + } + if (!ui_widget_list.empty()) { + ui_widget->setElementWidget(ui_widget_list); + hasItems = true; + } + } + // actions + if (!selection.m_actions.empty()) { + QList domActions; + foreach(QAction* action, selection.m_actions) + if (DomAction *domAction = createDom(action)) + domActions += domAction; + if (!domActions.empty()) { + ui_widget-> setElementAction(domActions); + hasItems = true; + } + } + + m_laidout.clear(); + m_copyWidget = false; + + if (!hasItems) { + delete ui_widget; + return 0; + } + // UI + DomUI *ui = new DomUI(); + ui->setAttributeVersion(QLatin1String(currentUiVersion)); + ui->setElementWidget(ui_widget); + ui->setElementResources(saveResources(m_resourceBuilder->usedQrcFiles())); + if (DomCustomWidgets *cws = saveCustomWidgets()) + ui->setElementCustomWidgets(cws); + return ui; +} + +FormBuilderClipboard QDesignerResource::paste(DomUI *ui, QWidget *widgetParent, QObject *actionParent) +{ + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + const int saved = m_isMainWidget; + m_isMainWidget = false; + + FormBuilderClipboard rc; + + // Widgets + const DomWidget *topLevel = ui->elementWidget(); + initialize(ui); + const QList domWidgets = topLevel->elementWidget(); + if (!domWidgets.empty()) { + const QPoint offset = m_formWindow->grid(); + foreach (DomWidget* domWidget, domWidgets) { + if (QWidget *w = create(domWidget, widgetParent)) { + w->move(w->pos() + offset); + // ### change the init properties of w + rc.m_widgets.append(w); + } + } + } + const QList domActions = topLevel->elementAction(); + if (!domActions.empty()) + foreach (DomAction *domAction, domActions) + if (QAction *a = create(domAction, actionParent)) + rc.m_actions .append(a); + + m_isMainWidget = saved; + + if (QDesignerExtraInfoExtension *extra = qt_extension(core()->extensionManager(), core())) + extra->loadUiExtraInfo(ui); + + createResources(ui->elementResources()); + + return rc; +} + +FormBuilderClipboard QDesignerResource::paste(QIODevice *dev, QWidget *widgetParent, QObject *actionParent) +{ + DomUI ui; + QXmlStreamReader reader(dev); + bool uiInitialized = false; + + const QString uiElement = QLatin1String("ui"); + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + if (reader.name().compare(uiElement, Qt::CaseInsensitive)) { + ui.read(reader); + uiInitialized = true; + } else { + //: Parsing clipboard contents + reader.raiseError(QCoreApplication::translate("QDesignerResource", "Unexpected element <%1>").arg(reader.name().toString())); + } + } + } + if (reader.hasError()) { + //: Parsing clipboard contents + designerWarning(QCoreApplication::translate("QDesignerResource", "Error while pasting clipboard contents at line %1, column %2: %3") + .arg(reader.lineNumber()).arg(reader.columnNumber()) + .arg(reader.errorString())); + uiInitialized = false; + } else if (uiInitialized == false) { + //: Parsing clipboard contents + designerWarning(QCoreApplication::translate("QDesignerResource", "Error while pasting clipboard contents: The root element is missing.")); + } + + if (!uiInitialized) + return FormBuilderClipboard(); + + FormBuilderClipboard clipBoard = paste(&ui, widgetParent, actionParent); + + return clipBoard; +} + +void QDesignerResource::layoutInfo(DomLayout *layout, QObject *parent, int *margin, int *spacing) +{ + QAbstractFormBuilder::layoutInfo(layout, parent, margin, spacing); +} + +DomCustomWidgets *QDesignerResource::saveCustomWidgets() +{ + if (m_usedCustomWidgets.isEmpty()) + return 0; + + // We would like the list to be in order of the widget database indexes + // to ensure that base classes come first (nice optics) + QDesignerFormEditorInterface *core = m_formWindow->core(); + QDesignerWidgetDataBaseInterface *db = core->widgetDataBase(); + const bool isInternalWidgetDataBase = qobject_cast(db); + typedef QMap OrderedDBIndexDomCustomWidgetMap; + OrderedDBIndexDomCustomWidgetMap orderedMap; + + const QString global = QLatin1String("global"); + foreach (QDesignerWidgetDataBaseItemInterface *item, m_usedCustomWidgets.keys()) { + const QString name = item->name(); + DomCustomWidget *custom_widget = new DomCustomWidget; + + custom_widget->setElementClass(name); + if (item->isContainer()) + custom_widget->setElementContainer(item->isContainer()); + + if (!item->includeFile().isEmpty()) { + DomHeader *header = new DomHeader; + const IncludeSpecification spec = includeSpecification(item->includeFile()); + header->setText(spec.first); + if (spec.second == IncludeGlobal) { + header->setAttributeLocation(global); + } + custom_widget->setElementHeader(header); + custom_widget->setElementExtends(item->extends()); + } + + if (isInternalWidgetDataBase) { + WidgetDataBaseItem *internalItem = static_cast(item); + const QStringList fakeSlots = internalItem->fakeSlots(); + const QStringList fakeSignals = internalItem->fakeSignals(); + if (!fakeSlots.empty() || !fakeSignals.empty()) { + DomSlots *domSlots = new DomSlots(); + domSlots->setElementSlot(fakeSlots); + domSlots->setElementSignal(fakeSignals); + custom_widget->setElementSlots(domSlots); + } + const QString addPageMethod = internalItem->addPageMethod(); + if (!addPageMethod.isEmpty()) + custom_widget->setElementAddPageMethod(addPageMethod); + } + + // Look up static per-class scripts of designer + if (DomScript *domScript = createScript(customWidgetScript(core, name), ScriptCustomWidgetPlugin)) + custom_widget->setElementScript(domScript); + + orderedMap.insert(db->indexOfClassName(name), custom_widget); + } + + DomCustomWidgets *customWidgets = new DomCustomWidgets; + customWidgets->setElementCustomWidget(orderedMap.values()); + return customWidgets; +} + +bool QDesignerResource::canCompressMargins(QObject *object) const +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), object)) { + if (qobject_cast(object)) { + const int l = sheet->property(sheet->indexOf(QLatin1String("leftMargin"))).toInt(); + const int t = sheet->property(sheet->indexOf(QLatin1String("topMargin"))).toInt(); + const int r = sheet->property(sheet->indexOf(QLatin1String("rightMargin"))).toInt(); + const int b = sheet->property(sheet->indexOf(QLatin1String("bottomMargin"))).toInt(); + if (l == t && l == r && l == b) + return true; + } + } + return false; +} + +bool QDesignerResource::canCompressSpacings(QObject *object) const +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), object)) { + if (qobject_cast(object)) { + const int h = sheet->property(sheet->indexOf(QLatin1String("horizontalSpacing"))).toInt(); + const int v = sheet->property(sheet->indexOf(QLatin1String("verticalSpacing"))).toInt(); + if (h == v) + return true; + } + } + return false; +} + +QList QDesignerResource::computeProperties(QObject *object) +{ + QList properties; + if (QDesignerPropertySheetExtension *sheet = qt_extension(core()->extensionManager(), object)) { + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(core()->extensionManager(), object); + const int count = sheet->count(); + QList marginProperties; + QList spacingProperties; + const bool compressMargins = canCompressMargins(object); + const bool compressSpacings = canCompressSpacings(object); + for (int index = 0; index < count; ++index) { + if (!sheet->isChanged(index) && (!dynamicSheet || !dynamicSheet->isDynamicProperty(index))) + continue; + + const QString propertyName = sheet->propertyName(index); + // Suppress windowModality in legacy forms that have it set on child widgets + if (propertyName == QLatin1String("windowModality") && !sheet->isVisible(index)) + continue; + + const QVariant value = sheet->property(index); + if (DomProperty *p = createProperty(object, propertyName, value)) { + if (compressMargins && (propertyName == QLatin1String("leftMargin") + || propertyName == QLatin1String("rightMargin") + || propertyName == QLatin1String("topMargin") + || propertyName == QLatin1String("bottomMargin"))) { + marginProperties.append(p); + } else if (compressSpacings && (propertyName == QLatin1String("horizontalSpacing") + || propertyName == QLatin1String("verticalSpacing"))) { + spacingProperties.append(p); + } else { + properties.append(p); + } + } + } + if (compressMargins) { + if (marginProperties.count() == 4) { // if we have 3 it means one is reset so we can't compress + DomProperty *marginProperty = marginProperties.at(0); + marginProperty->setAttributeName(QLatin1String("margin")); + properties.append(marginProperty); + delete marginProperties.at(1); + delete marginProperties.at(2); + delete marginProperties.at(3); + } else { + properties += marginProperties; + } + } + if (compressSpacings) { + if (spacingProperties.count() == 2) { + DomProperty *spacingProperty = spacingProperties.at(0); + spacingProperty->setAttributeName(QLatin1String("spacing")); + properties.append(spacingProperty); + delete spacingProperties.at(1); + } else { + properties += spacingProperties; + } + } + } + return properties; +} + +DomProperty *QDesignerResource::applyProperStdSetAttribute(QObject *object, const QString &propertyName, DomProperty *property) +{ + if (!property) + return 0; + + QExtensionManager *mgr = core()->extensionManager(); + if (const QDesignerPropertySheetExtension *sheet = qt_extension(mgr, object)) { + const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension(mgr, object); + const QDesignerPropertySheet *designerSheet = qobject_cast(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))); + const int index = sheet->indexOf(propertyName); + if ((dynamicSheet && dynamicSheet->isDynamicProperty(index)) || (designerSheet && designerSheet->isDefaultDynamicProperty(index))) + property->setAttributeStdset(0); + } + return property; +} + +// Optimistic check for a standard setter function +static inline bool hasSetter(QDesignerFormEditorInterface *core, QObject *object, const QString &propertyName) +{ + const QDesignerMetaObjectInterface *meta = core->introspection()->metaObject(object); + const int pindex = meta->indexOfProperty(propertyName); + if (pindex == -1) + return true; + return meta->property(pindex)->hasSetter(); +} + +DomProperty *QDesignerResource::createProperty(QObject *object, const QString &propertyName, const QVariant &value) +{ + if (!checkProperty(object, propertyName)) { + return 0; + } + + if (value.canConvert()) { + const PropertySheetFlagValue f = qvariant_cast(value); + const QString flagString = f.metaFlags.toString(f.value, DesignerMetaFlags::FullyQualified); + if (flagString.isEmpty()) + return 0; + + DomProperty *p = new DomProperty; + // check if we have a standard cpp set function + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + p->setAttributeName(propertyName); + p->setElementSet(flagString); + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert()) { + const PropertySheetEnumValue e = qvariant_cast(value); + bool ok; + const QString id = e.metaEnum.toString(e.value, DesignerMetaEnum::FullyQualified, &ok); + if (!ok) + designerWarning(e.metaEnum.messageToStringFailed(e.value)); + if (id.isEmpty()) + return 0; + + DomProperty *p = new DomProperty; + // check if we have a standard cpp set function + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + p->setAttributeName(propertyName); + p->setElementEnum(id); + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert()) { + const PropertySheetStringValue strVal = qvariant_cast(value); + DomProperty *p = new DomProperty; + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + + p->setAttributeName(propertyName); + + saveStringProperty(p, strVal); + + return applyProperStdSetAttribute(object, propertyName, p); + } else if (value.canConvert()) { + const PropertySheetKeySequenceValue keyVal = qvariant_cast(value); + DomProperty *p = new DomProperty; + if (!hasSetter(core(), object, propertyName)) + p->setAttributeStdset(0); + + p->setAttributeName(propertyName); + + saveKeySequenceProperty(p, keyVal); + + return applyProperStdSetAttribute(object, propertyName, p); + } + + return applyProperStdSetAttribute(object, propertyName, QAbstractFormBuilder::createProperty(object, propertyName, value)); +} + +QStringList QDesignerResource::mergeWithLoadedPaths(const QStringList &paths) const +{ + QStringList newPaths = paths; +#ifdef OLD_RESOURCE_FORMAT + QStringList loadedPaths = m_resourceBuilder->loadedQrcFiles(); + QStringListIterator it(loadedPaths); + while (it.hasNext()) { + const QString path = it.next(); + if (!newPaths.contains(path)) + newPaths << path; + } +#endif + return newPaths; +} + + +void QDesignerResource::createResources(DomResources *resources) +{ + QStringList paths; + if (resources != 0) { + const QList dom_include = resources->elementInclude(); + foreach (DomResource *res, dom_include) { + QString path = QDir::cleanPath(m_formWindow->absoluteDir().absoluteFilePath(res->attributeLocation())); + while (!QFile::exists(path)) { + QWidget *dialogParent = m_formWindow->core()->topLevel(); + const QString promptTitle = QApplication::translate("qdesigner_internal::QDesignerResource", "Loading qrc file", 0, QApplication::UnicodeUTF8); + const QString prompt = QApplication::translate("qdesigner_internal::QDesignerResource", "The specified qrc file

%1

could not be found. Do you want to update the file location?

", 0, QApplication::UnicodeUTF8).arg(path); + + const QMessageBox::StandardButton answer = core()->dialogGui()->message(dialogParent, QDesignerDialogGuiInterface::ResourceLoadFailureMessage, + QMessageBox::Warning, promptTitle, prompt, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); + if (answer == QMessageBox::Yes) { + const QFileInfo fi(path); + const QString fileDialogTitle = QApplication::translate("qdesigner_internal::QDesignerResource", "New location for %1", 0, QApplication::UnicodeUTF8).arg(fi.fileName()); + const QString fileDialogPattern = QApplication::translate("qdesigner_internal::QDesignerResource", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8); + path = core()->dialogGui()->getOpenFileName(dialogParent, fileDialogTitle, fi.absolutePath(), fileDialogPattern); + if (path.isEmpty()) + break; + m_formWindow->setProperty("_q_resourcepathchanged", QVariant(true)); + } else { + break; + } + } + if (!path.isEmpty()) { + paths << path; + m_formWindow->addResourceFile(path); + } + } + } + +#ifdef OLD_RESOURCE_FORMAT + paths = mergeWithLoadedPaths(paths); +#endif + + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + if (resourceSet) { + QStringList oldPaths = resourceSet->activeQrcPaths(); + QStringList newPaths = oldPaths; + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + if (!newPaths.contains(path)) + newPaths << path; + } + resourceSet->activateQrcPaths(newPaths); + } else { + resourceSet = m_formWindow->core()->resourceModel()->addResourceSet(paths); + m_formWindow->setResourceSet(resourceSet); + QObject::connect(m_formWindow->core()->resourceModel(), SIGNAL(resourceSetActivated(QtResourceSet*,bool)), + m_formWindow, SLOT(resourceSetActivated(QtResourceSet*,bool))); + } +} + +DomResources *QDesignerResource::saveResources() +{ + QStringList paths; + if (m_formWindow->saveResourcesBehaviour() == FormWindowBase::SaveAll) { + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + QList dom_include; + if (resourceSet) + paths = resourceSet->activeQrcPaths(); + } else if (m_formWindow->saveResourcesBehaviour() == FormWindowBase::SaveOnlyUsedQrcFiles) { + paths = m_resourceBuilder->usedQrcFiles(); + } + + return saveResources(paths); +} + +DomResources *QDesignerResource::saveResources(const QStringList &qrcPaths) +{ + QtResourceSet *resourceSet = m_formWindow->resourceSet(); + QList dom_include; + if (resourceSet) { + const QStringList activePaths = resourceSet->activeQrcPaths(); + foreach (const QString &path, activePaths) { + if (qrcPaths.contains(path)) { + DomResource *dom_res = new DomResource; + QString conv_path = path; + if (m_resourceBuilder->isSaveRelative()) + conv_path = m_formWindow->absoluteDir().relativeFilePath(path); + dom_res->setAttributeLocation(conv_path.replace(QDir::separator(), QLatin1Char('/'))); + dom_include.append(dom_res); + } + } + } + + DomResources *dom_resources = new DomResources; + dom_resources->setElementInclude(dom_include); + + return dom_resources; +} + +DomAction *QDesignerResource::createDom(QAction *action) +{ + if (!core()->metaDataBase()->item(action) || action->menu()) + return 0; + + return QAbstractFormBuilder::createDom(action); +} + +DomActionGroup *QDesignerResource::createDom(QActionGroup *actionGroup) +{ + if (core()->metaDataBase()->item(actionGroup) != 0) { + return QAbstractFormBuilder::createDom(actionGroup); + } + + return 0; +} + +QAction *QDesignerResource::create(DomAction *ui_action, QObject *parent) +{ + if (QAction *action = QAbstractFormBuilder::create(ui_action, parent)) { + core()->metaDataBase()->add(action); + return action; + } + + return 0; +} + +QActionGroup *QDesignerResource::create(DomActionGroup *ui_action_group, QObject *parent) +{ + if (QActionGroup *actionGroup = QAbstractFormBuilder::create(ui_action_group, parent)) { + core()->metaDataBase()->add(actionGroup); + return actionGroup; + } + + return 0; +} + +DomActionRef *QDesignerResource::createActionRefDom(QAction *action) +{ + if (!core()->metaDataBase()->item(action) + || (!action->isSeparator() && !action->menu() && action->objectName().isEmpty())) + return 0; + + return QAbstractFormBuilder::createActionRefDom(action); +} + +void QDesignerResource::addMenuAction(QAction *action) +{ + core()->metaDataBase()->add(action); +} + +QAction *QDesignerResource::createAction(QObject *parent, const QString &name) +{ + if (QAction *action = QAbstractFormBuilder::createAction(parent, name)) { + core()->metaDataBase()->add(action); + return action; + } + + return 0; +} + +QActionGroup *QDesignerResource::createActionGroup(QObject *parent, const QString &name) +{ + if (QActionGroup *actionGroup = QAbstractFormBuilder::createActionGroup(parent, name)) { + core()->metaDataBase()->add(actionGroup); + return actionGroup; + } + + return 0; +} + +/* Apply the attributes to a widget via property sheet where appropriate, + * that is, the sheet handles attributive fake properties */ +void QDesignerResource::applyAttributesToPropertySheet(const DomWidget *ui_widget, QWidget *widget) +{ + const DomPropertyList attributes = ui_widget->elementAttribute(); + if (attributes.empty()) + return; + QDesignerPropertySheetExtension *sheet = qt_extension(m_formWindow->core()->extensionManager(), widget); + const DomPropertyList::const_iterator acend = attributes.constEnd(); + for (DomPropertyList::const_iterator it = attributes.constBegin(); it != acend; ++it) { + const QString name = (*it)->attributeName(); + const int index = sheet->indexOf(name); + if (index == -1) { + const QString msg = QString::fromUtf8("Unable to apply attributive property '%1' to '%2'. It does not exist.").arg(name, widget->objectName()); + designerWarning(msg); + } else { + sheet->setProperty(index, domPropertyToVariant(this, widget->metaObject(), *it)); + sheet->setChanged(index, true); + } + } +} + +void QDesignerResource::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + QAbstractFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget); + // Apply the page id attribute of a QWizardPage (which is an attributive fake property) + if (qobject_cast(widget)) + applyAttributesToPropertySheet(ui_widget, widget); +} + +// Add user defined scripts (dialog box) belonging to QWidget to DomWidget. +void QDesignerResource::addUserDefinedScripts(QWidget *w, DomWidget *ui_widget) +{ + QDesignerFormEditorInterface *core = m_formWindow->core(); + DomScripts domScripts = ui_widget->elementScript(); + // Look up user-defined scripts of designer + if (const qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast(core->metaDataBase())) { + if (const qdesigner_internal::MetaDataBaseItem *metaItem = metaDataBase->metaDataBaseItem(w)) { + addScript(metaItem->script(), ScriptDesigner, domScripts); + } + } + if (!domScripts.empty()) + ui_widget->setElementScript(domScripts); +} +} + +QT_END_NAMESPACE diff --git a/src/designer/components/formeditor/qdesigner_resource.h b/src/designer/components/formeditor/qdesigner_resource.h new file mode 100644 index 000000000..ddcd9bb1f --- /dev/null +++ b/src/designer/components/formeditor/qdesigner_resource.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_RESOURCE_H +#define QDESIGNER_RESOURCE_H + +#include "formeditor_global.h" +#include "qsimpleresource_p.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class DomCustomWidget; +class DomCustomWidgets; +class DomResource; + +class QDesignerContainerExtension; +class QDesignerFormEditorInterface; +class QDesignerCustomWidgetInterface; +class QDesignerWidgetDataBaseItemInterface; + +class QTabWidget; +class QStackedWidget; +class QToolBox; +class QToolBar; +class QDesignerDockWidget; +class QLayoutWidget; +class QWizardPage; + +namespace qdesigner_internal { + +class FormWindow; + +class QT_FORMEDITOR_EXPORT QDesignerResource : public QEditorFormBuilder +{ +public: + explicit QDesignerResource(FormWindow *fw); + virtual ~QDesignerResource(); + + virtual void save(QIODevice *dev, QWidget *widget); + + virtual bool copy(QIODevice *dev, const FormBuilderClipboard &selection); + virtual DomUI *copy(const FormBuilderClipboard &selection); + + virtual FormBuilderClipboard paste(DomUI *ui, QWidget *widgetParent, QObject *actionParent = 0); + virtual FormBuilderClipboard paste(QIODevice *dev, QWidget *widgetParent, QObject *actionParent = 0); + + bool saveRelative() const; + void setSaveRelative(bool relative); + + virtual QWidget *load(QIODevice *dev, QWidget *parentWidget = 0); + +protected: + using QEditorFormBuilder::create; + using QEditorFormBuilder::createDom; + + virtual void saveDom(DomUI *ui, QWidget *widget); + 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 void applyProperties(QObject *o, const QList &properties); + virtual QList computeProperties(QObject *obj); + virtual DomProperty *createProperty(QObject *object, const QString &propertyName, const QVariant &value); + + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual QLayout *createLayout(const QString &layoutName, QObject *parent, const QString &name); + virtual void createCustomWidgets(DomCustomWidgets *); + virtual void createResources(DomResources*); + virtual void applyTabStops(QWidget *widget, DomTabStops *tabStops); + + virtual bool addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout); + virtual bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + 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_layout, DomWidget *ui_parentWidget); + + virtual QAction *create(DomAction *ui_action, QObject *parent); + virtual QActionGroup *create(DomActionGroup *ui_action_group, QObject *parent); + virtual void addMenuAction(QAction *action); + + virtual DomAction *createDom(QAction *action); + virtual DomActionGroup *createDom(QActionGroup *actionGroup); + virtual DomActionRef *createActionRefDom(QAction *action); + + virtual QAction *createAction(QObject *parent, const QString &name); + virtual QActionGroup *createActionGroup(QObject *parent, const QString &name); + + virtual bool checkProperty(QObject *obj, const QString &prop) const; + + DomWidget *saveWidget(QTabWidget *widget, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QStackedWidget *widget, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QToolBox *widget, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QWidget *widget, QDesignerContainerExtension *container, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QToolBar *toolBar, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QDesignerDockWidget *dockWidget, DomWidget *ui_parentWidget); + DomWidget *saveWidget(QWizardPage *wizardPage, DomWidget *ui_parentWidget); + + virtual DomCustomWidgets *saveCustomWidgets(); + virtual DomTabStops *saveTabStops(); + virtual DomResources *saveResources(); + + virtual void layoutInfo(DomLayout *layout, QObject *parent, int *margin, int *spacing); + + virtual void loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + void changeObjectName(QObject *o, QString name); + DomProperty *applyProperStdSetAttribute(QObject *object, const QString &propertyName, DomProperty *property); + +private: + void addUserDefinedScripts(QWidget *w, DomWidget *ui_widget); + DomResources *saveResources(const QStringList &qrcPaths); + bool canCompressMargins(QObject *object) const; + bool canCompressSpacings(QObject *object) const; + QStringList mergeWithLoadedPaths(const QStringList &paths) const; + void applyAttributesToPropertySheet(const DomWidget *ui_widget, QWidget *widget); + + typedef QList DomCustomWidgetList; + void addCustomWidgetsToWidgetDatabase(DomCustomWidgetList& list); + FormWindow *m_formWindow; + bool m_isMainWidget; + QHash m_internal_to_qt; + QHash m_qt_to_internal; + QStack m_chain; + QHash m_usedCustomWidgets; + int m_topLevelSpacerCount; + bool m_copyWidget; + QWidget *m_selected; + class QDesignerResourceBuilder *m_resourceBuilder; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_RESOURCE_H diff --git a/src/designer/components/formeditor/qdesignerundostack.cpp b/src/designer/components/formeditor/qdesignerundostack.cpp new file mode 100644 index 000000000..508995f1d --- /dev/null +++ b/src/designer/components/formeditor/qdesignerundostack.cpp @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesignerundostack.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerUndoStack::QDesignerUndoStack(QObject *parent) : + QObject(parent), + m_undoStack(new QUndoStack), + m_fakeDirty(false) +{ + connect(m_undoStack, SIGNAL(indexChanged(int)), this, SIGNAL(changed())); +} + +QDesignerUndoStack::~QDesignerUndoStack() +{ // QUndoStack is managed by the QUndoGroup +} + +void QDesignerUndoStack::push(QUndoCommand * cmd) +{ + m_undoStack->push(cmd); +} + +void QDesignerUndoStack::beginMacro(const QString &text) +{ + m_undoStack->beginMacro(text); +} + +void QDesignerUndoStack::endMacro() +{ + m_undoStack->endMacro(); +} + +int QDesignerUndoStack::index() const +{ + return m_undoStack->index(); +} + +const QUndoStack *QDesignerUndoStack::qundoStack() const +{ + return m_undoStack; +} +QUndoStack *QDesignerUndoStack::qundoStack() +{ + return m_undoStack; +} + +bool QDesignerUndoStack::isDirty() const +{ + return m_fakeDirty || !m_undoStack->isClean(); +} + +void QDesignerUndoStack::setDirty(bool v) +{ + if (isDirty() == v) + return; + if (v) { + m_fakeDirty = true; + emit changed(); + } else { + m_fakeDirty = false; + m_undoStack->setClean(); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qdesignerundostack.h b/src/designer/components/formeditor/qdesignerundostack.h new file mode 100644 index 000000000..c6a0b35f1 --- /dev/null +++ b/src/designer/components/formeditor/qdesignerundostack.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNERUNDOSTACK_H +#define QDESIGNERUNDOSTACK_H + +#include + +QT_BEGIN_NAMESPACE +class QUndoStack; +class QUndoCommand; + +namespace qdesigner_internal { + +/* QDesignerUndoStack: A QUndoStack extended by a way of setting it to + * "dirty" indepently of commands (by modifications without commands + * such as resizing). Accomplished via bool m_fakeDirty flag. The + * lifecycle of the QUndoStack is managed by the QUndoGroup. */ +class QDesignerUndoStack : public QObject +{ + Q_DISABLE_COPY(QDesignerUndoStack) + Q_OBJECT +public: + explicit QDesignerUndoStack(QObject *parent = 0); + virtual ~QDesignerUndoStack(); + + void push(QUndoCommand * cmd); + void beginMacro(const QString &text); + void endMacro(); + int index() const; + + const QUndoStack *qundoStack() const; + QUndoStack *qundoStack(); + + bool isDirty() const; + +signals: + void changed(); + +public slots: + void setDirty(bool); + +private: + QUndoStack *m_undoStack; + bool m_fakeDirty; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNERUNDOSTACK_H diff --git a/src/designer/components/formeditor/qlayoutwidget_propertysheet.cpp b/src/designer/components/formeditor/qlayoutwidget_propertysheet.cpp new file mode 100644 index 000000000..858bdec61 --- /dev/null +++ b/src/designer/components/formeditor/qlayoutwidget_propertysheet.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlayoutwidget_propertysheet.h" +#include "qlayout_widget_p.h" +#include "formwindow.h" +#include "formeditor.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +QLayoutWidgetPropertySheet::QLayoutWidgetPropertySheet(QLayoutWidget *object, QObject *parent) + : QDesignerPropertySheet(object, parent) +{ + clearFakeProperties(); +} + +QLayoutWidgetPropertySheet::~QLayoutWidgetPropertySheet() +{ +} + +bool QLayoutWidgetPropertySheet::isVisible(int index) const +{ + static const QString layoutPropertyGroup = QLatin1String("Layout"); + if (propertyGroup(index) == layoutPropertyGroup) + return QDesignerPropertySheet::isVisible(index); + return false; +} + +void QLayoutWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + QDesignerPropertySheet::setProperty(index, value); +} + +bool QLayoutWidgetPropertySheet::dynamicPropertiesAllowed() const +{ + return false; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qlayoutwidget_propertysheet.h b/src/designer/components/formeditor/qlayoutwidget_propertysheet.h new file mode 100644 index 000000000..8c074700d --- /dev/null +++ b/src/designer/components/formeditor/qlayoutwidget_propertysheet.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLAYOUTWIDGET_PROPERTYSHEET_H +#define QLAYOUTWIDGET_PROPERTYSHEET_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QLayoutWidgetPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit QLayoutWidgetPropertySheet(QLayoutWidget *object, QObject *parent = 0); + virtual ~QLayoutWidgetPropertySheet(); + + virtual void setProperty(int index, const QVariant &value); + virtual bool isVisible(int index) const; + + virtual bool dynamicPropertiesAllowed() const; +}; + +typedef QDesignerPropertySheetFactory QLayoutWidgetPropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QLAYOUTWIDGET_PROPERTYSHEET_H diff --git a/src/designer/components/formeditor/qmainwindow_container.cpp b/src/designer/components/formeditor/qmainwindow_container.cpp new file mode 100644 index 000000000..03669e5d7 --- /dev/null +++ b/src/designer/components/formeditor/qmainwindow_container.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmainwindow_container.h" +#include "qdesigner_toolbar_p.h" +#include "formwindow.h" + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +QMainWindowContainer::QMainWindowContainer(QMainWindow *widget, QObject *parent) + : QObject(parent), + m_mainWindow(widget) +{ +} + +int QMainWindowContainer::count() const +{ + return m_widgets.count(); +} + +QWidget *QMainWindowContainer::widget(int index) const +{ + if (index == -1) + return 0; + + return m_widgets.at(index); +} + +int QMainWindowContainer::currentIndex() const +{ + return m_mainWindow->centralWidget() ? 0 : -1; +} + +void QMainWindowContainer::setCurrentIndex(int index) +{ + Q_UNUSED(index); +} + + +namespace { + // Pair of + typedef QPair ToolBarData; + + ToolBarData toolBarData(QToolBar *me) { + const QMainWindow *mw = qobject_cast(me->parentWidget()); + if (!mw || !mw->layout() || mw->layout()->indexOf(me) == -1) + return ToolBarData(Qt::TopToolBarArea,false); + return ToolBarData(mw->toolBarArea(me), mw->toolBarBreak(me)); + } + +Qt::DockWidgetArea dockWidgetArea(QDockWidget *me) +{ + if (const QMainWindow *mw = qobject_cast(me->parentWidget())) { + // Make sure that me is actually managed by mw, otherwise + // QMainWindow::dockWidgetArea() will be VERY upset + QList candidates; + if (mw->layout()) { + candidates.append(mw->layout()); + candidates += mw->layout()->findChildren(); + } + foreach (QLayout *l, candidates) { + if (l->indexOf(me) != -1) { + return mw->dockWidgetArea(me); + } + } + } + return Qt::LeftDockWidgetArea; +} +} + +void QMainWindowContainer::addWidget(QWidget *widget) +{ + // remove all the occurrences of widget + m_widgets.removeAll(widget); + + // the + if (QToolBar *toolBar = qobject_cast(widget)) { + m_widgets.append(widget); + const ToolBarData data = toolBarData(toolBar); + m_mainWindow->addToolBar(data.first, toolBar); + if (data.second) m_mainWindow->insertToolBarBreak(toolBar); + toolBar->show(); + } + + else if (QMenuBar *menuBar = qobject_cast(widget)) { + if (menuBar != m_mainWindow->menuBar()) + m_mainWindow->setMenuBar(menuBar); + + m_widgets.append(widget); + menuBar->show(); + } + + else if (QStatusBar *statusBar = qobject_cast(widget)) { + if (statusBar != m_mainWindow->statusBar()) + m_mainWindow->setStatusBar(statusBar); + + m_widgets.append(widget); + statusBar->show(); + } + + else if (QDockWidget *dockWidget = qobject_cast(widget)) { + m_widgets.append(widget); + m_mainWindow->addDockWidget(dockWidgetArea(dockWidget), dockWidget); + dockWidget->show(); + + if (FormWindow *fw = FormWindow::findFormWindow(m_mainWindow)) { + fw->manageWidget(widget); + } + } + + else if (widget) { + m_widgets.prepend(widget); + + if (widget != m_mainWindow->centralWidget()) { + // note that qmainwindow will delete the current central widget if you + // call setCentralWidget(), we end up with dangeling pointers in m_widgets list + m_widgets.removeAll(m_mainWindow->centralWidget()); + + widget->setParent(m_mainWindow); + m_mainWindow->setCentralWidget(widget); + } + } +} + +void QMainWindowContainer::insertWidget(int index, QWidget *widget) +{ + Q_UNUSED(index); + + addWidget(widget); +} + +void QMainWindowContainer::remove(int index) +{ + QWidget *widget = m_widgets.at(index); + if (QToolBar *toolBar = qobject_cast(widget)) { + m_mainWindow->removeToolBar(toolBar); + } else if (QMenuBar *menuBar = qobject_cast(widget)) { + menuBar->hide(); + menuBar->setParent(0); + m_mainWindow->setMenuBar(0); + } else if (QStatusBar *statusBar = qobject_cast(widget)) { + statusBar->hide(); + statusBar->setParent(0); + m_mainWindow->setStatusBar(0); + } else if (QDockWidget *dockWidget = qobject_cast(widget)) { + m_mainWindow->removeDockWidget(dockWidget); + } + m_widgets.removeAt(index); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qmainwindow_container.h b/src/designer/components/formeditor/qmainwindow_container.h new file mode 100644 index 000000000..40e25c37a --- /dev/null +++ b/src/designer/components/formeditor/qmainwindow_container.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAINWINDOW_CONTAINER_H +#define QMAINWINDOW_CONTAINER_H + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QMainWindowContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QMainWindowContainer(QMainWindow *widget, QObject *parent = 0); + + virtual int count() const; + virtual QWidget *widget(int index) const; + virtual int currentIndex() const; + virtual void setCurrentIndex(int index); + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QMainWindow *m_mainWindow; + QList m_widgets; +}; + +typedef ExtensionFactory QMainWindowContainerFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QMAINWINDOW_CONTAINER_H diff --git a/src/designer/components/formeditor/qmdiarea_container.cpp b/src/designer/components/formeditor/qmdiarea_container.cpp new file mode 100644 index 000000000..77166c79b --- /dev/null +++ b/src/designer/components/formeditor/qmdiarea_container.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmdiarea_container.h" + +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QMdiAreaContainer::QMdiAreaContainer(QMdiArea *widget, QObject *parent) + : QObject(parent), + m_mdiArea(widget) +{ +} + +int QMdiAreaContainer::count() const +{ + return m_mdiArea->subWindowList(QMdiArea::CreationOrder).count(); +} + +QWidget *QMdiAreaContainer::widget(int index) const +{ + if (index < 0) + return 0; + return m_mdiArea->subWindowList(QMdiArea::CreationOrder).at(index)->widget(); +} + +int QMdiAreaContainer::currentIndex() const +{ + if (QMdiSubWindow *sub = m_mdiArea->activeSubWindow()) + return m_mdiArea->subWindowList(QMdiArea::CreationOrder).indexOf(sub); + return -1; +} + +void QMdiAreaContainer::setCurrentIndex(int index) +{ + if (index < 0) { + qDebug() << "** WARNING Attempt to QMdiAreaContainer::setCurrentIndex(-1)"; + return; + } + QMdiSubWindow *frame = m_mdiArea->subWindowList(QMdiArea::CreationOrder).at(index); + m_mdiArea->setActiveSubWindow(frame); +} + +void QMdiAreaContainer::addWidget(QWidget *widget) +{ + QMdiSubWindow *frame = m_mdiArea->addSubWindow(widget, Qt::Window); + frame->show(); + m_mdiArea->cascadeSubWindows(); + positionNewMdiChild(m_mdiArea, frame); +} + +// Semi-smart positioning of new windows: Make child fill the whole MDI window below +// cascaded other windows +void QMdiAreaContainer::positionNewMdiChild(const QWidget *area, QWidget *mdiChild) +{ + enum { MinSize = 20 }; + const QPoint pos = mdiChild->pos(); + const QSize areaSize = area->size(); + switch (QApplication::layoutDirection()) { + case Qt::LayoutDirectionAuto: + case Qt::LeftToRight: { + const QSize fullSize = QSize(areaSize.width() - pos.x(), areaSize.height() - pos.y()); + if (fullSize.width() > MinSize && fullSize.height() > MinSize) + mdiChild->resize(fullSize); + } + break; + case Qt::RightToLeft: { + const QSize fullSize = QSize(pos.x() + mdiChild->width(), areaSize.height() - pos.y()); + if (fullSize.width() > MinSize && fullSize.height() > MinSize) { + mdiChild->move(0, pos.y()); + mdiChild->resize(fullSize); + } + } + break; + } +} + +void QMdiAreaContainer::insertWidget(int, QWidget *widget) +{ + addWidget(widget); +} + +void QMdiAreaContainer::remove(int index) +{ + QList subWins = m_mdiArea->subWindowList(QMdiArea::CreationOrder); + if (index >= 0 && index < subWins.size()) { + QMdiSubWindow *f = subWins.at(index); + m_mdiArea->removeSubWindow(f->widget()); + delete f; + } +} + +// ---------- MdiAreaPropertySheet, creates fake properties: +// 1) window name (object name of child) +// 2) title (windowTitle of child). + +static const char *subWindowTitleC = "activeSubWindowTitle"; +static const char *subWindowNameC = "activeSubWindowName"; + +QMdiAreaPropertySheet::QMdiAreaPropertySheet(QWidget *mdiArea, QObject *parent) : + QDesignerPropertySheet(mdiArea, parent), + m_windowTitleProperty(QLatin1String("windowTitle")) +{ + createFakeProperty(QLatin1String(subWindowNameC), QString()); + createFakeProperty(QLatin1String(subWindowTitleC), QString()); +} + +QMdiAreaPropertySheet::MdiAreaProperty QMdiAreaPropertySheet::mdiAreaProperty(const QString &name) +{ + typedef QHash MdiAreaPropertyHash; + static MdiAreaPropertyHash mdiAreaPropertyHash; + if (mdiAreaPropertyHash.empty()) { + mdiAreaPropertyHash.insert(QLatin1String(subWindowNameC), MdiAreaSubWindowName); + mdiAreaPropertyHash.insert(QLatin1String(subWindowTitleC), MdiAreaSubWindowTitle); + } + return mdiAreaPropertyHash.value(name,MdiAreaNone); +} + +void QMdiAreaPropertySheet::setProperty(int index, const QVariant &value) +{ + switch (mdiAreaProperty(propertyName(index))) { + case MdiAreaSubWindowName: + if (QWidget *w = currentWindow()) + w->setObjectName(value.toString()); + break; + case MdiAreaSubWindowTitle: // Forward to window title of subwindow + if (QDesignerPropertySheetExtension *cws = currentWindowSheet()) { + const int index = cws->indexOf(m_windowTitleProperty); + cws->setProperty(index, value); + cws->setChanged(index, true); + } + break; + default: + QDesignerPropertySheet::setProperty(index, value); + break; + } +} + +bool QMdiAreaPropertySheet::reset(int index) +{ + bool rc = true; + switch (mdiAreaProperty(propertyName(index))) { + case MdiAreaSubWindowName: + setProperty(index, QVariant(QString())); + setChanged(index, false); + break; + case MdiAreaSubWindowTitle: // Forward to window title of subwindow + if (QDesignerPropertySheetExtension *cws = currentWindowSheet()) { + const int index = cws->indexOf(m_windowTitleProperty); + rc = cws->reset(index); + } + break; + default: + rc = QDesignerPropertySheet::reset(index); + break; + } + return rc; +} + +QVariant QMdiAreaPropertySheet::property(int index) const +{ + switch (mdiAreaProperty(propertyName(index))) { + case MdiAreaSubWindowName: + if (QWidget *w = currentWindow()) + return w->objectName(); + return QVariant(QString()); + case MdiAreaSubWindowTitle: + if (QWidget *w = currentWindow()) + return w->windowTitle(); + return QVariant(QString()); + case MdiAreaNone: + break; + } + return QDesignerPropertySheet::property(index); +} + +bool QMdiAreaPropertySheet::isEnabled(int index) const +{ + switch (mdiAreaProperty(propertyName(index))) { + case MdiAreaSubWindowName: + case MdiAreaSubWindowTitle: + return currentWindow() != 0; + case MdiAreaNone: + break; + } + return QDesignerPropertySheet::isEnabled(index); +} + +bool QMdiAreaPropertySheet::isChanged(int index) const +{ + bool rc = false; + switch (mdiAreaProperty(propertyName(index))) { + case MdiAreaSubWindowName: + rc = currentWindow() != 0; + break; + case MdiAreaSubWindowTitle: + if (QDesignerPropertySheetExtension *cws = currentWindowSheet()) { + const int index = cws->indexOf(m_windowTitleProperty); + rc = cws->isChanged(index); + } + break; + default: + rc = QDesignerPropertySheet::isChanged(index); + break; + } + return rc; +} + +QWidget *QMdiAreaPropertySheet::currentWindow() const +{ + if (const QDesignerContainerExtension *c = qt_extension(core()->extensionManager(), object())) { + const int ci = c->currentIndex(); + if (ci < 0) + return 0; + return c->widget(ci); + } + return 0; +} + +QDesignerPropertySheetExtension *QMdiAreaPropertySheet::currentWindowSheet() const +{ + QWidget *cw = currentWindow(); + if (cw == 0) + return 0; + return qt_extension(core()->extensionManager(), cw); +} + +bool QMdiAreaPropertySheet::checkProperty(const QString &propertyName) +{ + return mdiAreaProperty(propertyName) == MdiAreaNone; +} +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qmdiarea_container.h b/src/designer/components/formeditor/qmdiarea_container.h new file mode 100644 index 000000000..ec44dbff6 --- /dev/null +++ b/src/designer/components/formeditor/qmdiarea_container.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMDIAREA_CONTAINER_H +#define QMDIAREA_CONTAINER_H + +#include + + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Container for QMdiArea +class QMdiAreaContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QMdiAreaContainer(QMdiArea *widget, QObject *parent = 0); + + virtual int count() const; + virtual QWidget *widget(int index) const; + virtual int currentIndex() const; + virtual void setCurrentIndex(int index); + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + + // Semismart positioning of a new MDI child after cascading + static void positionNewMdiChild(const QWidget *area, QWidget *mdiChild); + +private: + QMdiArea *m_mdiArea; +}; + +// PropertySheet for QMdiArea: Fakes window title and name. +// Also works for a QWorkspace as it relies on the container extension. + +class QMdiAreaPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit QMdiAreaPropertySheet(QWidget *mdiArea, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + virtual bool isChanged(int index) const; + virtual QVariant property(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: + const QString m_windowTitleProperty; + QWidget *currentWindow() const; + QDesignerPropertySheetExtension *currentWindowSheet() const; + + enum MdiAreaProperty { MdiAreaSubWindowName, MdiAreaSubWindowTitle, MdiAreaNone }; + static MdiAreaProperty mdiAreaProperty(const QString &name); +}; + +// Factories + +typedef ExtensionFactory QMdiAreaContainerFactory; +typedef QDesignerPropertySheetFactory QMdiAreaPropertySheetFactory; +typedef QDesignerPropertySheetFactory QWorkspacePropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QMDIAREA_CONTAINER_H diff --git a/src/designer/components/formeditor/qtbrushmanager.cpp b/src/designer/components/formeditor/qtbrushmanager.cpp new file mode 100644 index 000000000..de06f12c2 --- /dev/null +++ b/src/designer/components/formeditor/qtbrushmanager.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtbrushmanager.h" +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QtBrushManagerPrivate +{ + QtBrushManager *q_ptr; + Q_DECLARE_PUBLIC(QtBrushManager) +public: + QMap theBrushMap; + QString theCurrentBrush; +}; + +QtBrushManager::QtBrushManager(QObject *parent) + : QDesignerBrushManagerInterface(parent), d_ptr(new QtBrushManagerPrivate) +{ + d_ptr->q_ptr = this; +} + +QtBrushManager::~QtBrushManager() +{ +} + +QBrush QtBrushManager::brush(const QString &name) const +{ + if (d_ptr->theBrushMap.contains(name)) + return d_ptr->theBrushMap[name]; + return QBrush(); +} + +QMap QtBrushManager::brushes() const +{ + return d_ptr->theBrushMap; +} + +QString QtBrushManager::currentBrush() const +{ + return d_ptr->theCurrentBrush; +} + +QString QtBrushManager::addBrush(const QString &name, const QBrush &brush) +{ + if (name.isNull()) + return QString(); + + QString newName = name; + QString nameBase = newName; + int i = 0; + while (d_ptr->theBrushMap.contains(newName)) { + newName = nameBase + QString::number(++i); + } + d_ptr->theBrushMap[newName] = brush; + emit brushAdded(newName, brush); + + return newName; +} + +void QtBrushManager::removeBrush(const QString &name) +{ + if (!d_ptr->theBrushMap.contains(name)) + return; + if (currentBrush() == name) + setCurrentBrush(QString()); + emit brushRemoved(name); + d_ptr->theBrushMap.remove(name); +} + +void QtBrushManager::setCurrentBrush(const QString &name) +{ + QBrush newBrush; + if (!name.isNull()) { + if (d_ptr->theBrushMap.contains(name)) + newBrush = d_ptr->theBrushMap[name]; + else + return; + } + d_ptr->theCurrentBrush = name; + emit currentBrushChanged(name, newBrush); +} + +QPixmap QtBrushManager::brushPixmap(const QBrush &brush) const +{ + int w = 64; + int h = 64; + + QImage img(w, h, QImage::Format_ARGB32_Premultiplied); + QPainter p(&img); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.fillRect(QRect(0, 0, w, h), brush); + return QPixmap::fromImage(img); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qtbrushmanager.h b/src/designer/components/formeditor/qtbrushmanager.h new file mode 100644 index 000000000..025927a66 --- /dev/null +++ b/src/designer/components/formeditor/qtbrushmanager.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTBRUSHMANAGER_H +#define QTBRUSHMANAGER_H + +#include +#include "formeditor_global.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QtBrushManagerPrivate; + +class QT_FORMEDITOR_EXPORT QtBrushManager : public QDesignerBrushManagerInterface +{ + Q_OBJECT +public: + QtBrushManager(QObject *parent = 0); + ~QtBrushManager(); + + QBrush brush(const QString &name) const; + QMap brushes() const; + QString currentBrush() const; + + QString addBrush(const QString &name, const QBrush &brush); + void removeBrush(const QString &name); + void setCurrentBrush(const QString &name); + + QPixmap brushPixmap(const QBrush &brush) const; + +private: + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QtBrushManager) + Q_DISABLE_COPY(QtBrushManager) +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/src/designer/components/formeditor/qwizard_container.cpp b/src/designer/components/formeditor/qwizard_container.cpp new file mode 100644 index 000000000..9d7fb18f4 --- /dev/null +++ b/src/designer/components/formeditor/qwizard_container.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwizard_container.h" + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QList IdList; +typedef QList WizardPageList; + +namespace qdesigner_internal { + +QWizardContainer::QWizardContainer(QWizard *widget, QObject *parent) : + QObject(parent), + m_wizard(widget) +{ +} + +int QWizardContainer::count() const +{ + return m_wizard->pageIds().size(); +} + +QWidget *QWizardContainer::widget(int index) const +{ + QWidget *rc = 0; + if (index >= 0) { + const IdList idList = m_wizard->pageIds(); + if (index < idList.size()) + rc = m_wizard->page(idList.at(index)); + } + return rc; +} + +int QWizardContainer::currentIndex() const +{ + const IdList idList = m_wizard->pageIds(); + const int currentId = m_wizard->currentId(); + const int rc = idList.empty() ? -1 : idList.indexOf(currentId); + return rc; +} + +void QWizardContainer::setCurrentIndex(int index) +{ + if (index < 0 || m_wizard->pageIds().empty()) + return; + + int currentIdx = currentIndex(); + + if (currentIdx == -1) { + m_wizard->restart(); + currentIdx = currentIndex(); + } + + if (currentIdx == index) + return; + + const int d = qAbs(index - currentIdx); + if (index > currentIdx) { + for (int i = 0; i < d; i++) + m_wizard->next(); + } else { + for (int i = 0; i < d; i++) + m_wizard->back(); + } +} + +static const char *msgWrongType = "** WARNING Attempt to add oject that is not of class WizardPage to a QWizard"; + +void QWizardContainer::addWidget(QWidget *widget) +{ + QWizardPage *page = qobject_cast(widget); + if (!page) { + qWarning("%s", msgWrongType); + return; + } + m_wizard->addPage(page); + // Might be -1 after adding the first page + setCurrentIndex(m_wizard->pageIds().size() - 1); +} + +void QWizardContainer::insertWidget(int index, QWidget *widget) +{ + enum { delta = 5 }; + + QWizardPage *newPage = qobject_cast(widget); + if (!newPage) { + qWarning("%s", msgWrongType); + return; + } + + const IdList idList = m_wizard->pageIds(); + const int pageCount = idList.size(); + if (index >= pageCount) { + addWidget(widget); + return; + } + + // Insert before, reshuffle ids if required + const int idBefore = idList.at(index); + const int newId = idBefore - 1; + const bool needsShuffle = + (index == 0 && newId < 0) // At start: QWizard refuses to insert id -1 + || (index > 0 && idList.at(index - 1) == newId); // In-between + if (needsShuffle) { + // Create a gap by shuffling pages + WizardPageList pageList; + pageList.push_back(newPage); + for (int i = index; i < pageCount; i++) { + pageList.push_back(m_wizard->page(idList.at(i))); + m_wizard->removePage(idList.at(i)); + } + int newId = idBefore + delta; + const WizardPageList::const_iterator wcend = pageList.constEnd(); + for (WizardPageList::const_iterator it = pageList.constBegin(); it != wcend; ++it) { + m_wizard->setPage(newId, *it); + newId += delta; + } + } else { + // Gap found, just insert + m_wizard->setPage(newId, newPage); + } + // Might be at -1 after adding the first page + setCurrentIndex(index); +} + +void QWizardContainer::remove(int index) +{ + if (index < 0) + return; + + const IdList idList = m_wizard->pageIds(); + if (index >= idList.size()) + return; + + m_wizard->removePage(idList.at(index)); + // goto next page, preferably + const int newSize = idList.size() - 1; + if (index < newSize) { + setCurrentIndex(index); + } else { + if (newSize > 0) + setCurrentIndex(newSize - 1); + } +} + +// ---------------- QWizardPagePropertySheet +const char *QWizardPagePropertySheet::pageIdProperty = "pageId"; + +QWizardPagePropertySheet::QWizardPagePropertySheet(QWizardPage *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_pageIdIndex(createFakeProperty(QLatin1String(pageIdProperty), QString())) +{ + setAttribute(m_pageIdIndex, true); +} + +bool QWizardPagePropertySheet::reset(int index) +{ + if (index == m_pageIdIndex) { + setProperty(index, QString()); + return true; + } + return QDesignerPropertySheet::reset(index); +} + +// ---------------- QWizardPropertySheet +QWizardPropertySheet::QWizardPropertySheet(QWizard *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_startId(QLatin1String("startId")) +{ +} + +bool QWizardPropertySheet::isVisible(int index) const +{ + if (propertyName(index) == m_startId) + return false; + return QDesignerPropertySheet::isVisible(index); +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qwizard_container.h b/src/designer/components/formeditor/qwizard_container.h new file mode 100644 index 000000000..12397a6d8 --- /dev/null +++ b/src/designer/components/formeditor/qwizard_container.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWIZARD_CONTAINER_H +#define QWIZARD_CONTAINER_H + +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QWizardPage; + +namespace qdesigner_internal { + +// Container for QWizard. Care must be taken to position +// the QWizard at some valid page after removal/insertion +// as it is not used to having its pages ripped out. +class QWizardContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QWizardContainer(QWizard *widget, QObject *parent = 0); + + virtual int count() const; + virtual QWidget *widget(int index) const; + virtual int currentIndex() const; + virtual void setCurrentIndex(int index); + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QWizard *m_wizard; +}; + +// QWizardPagePropertySheet: Introduces a attribute string fake property +// "pageId" that allows for specifying enumeration values (uic only). +// This breaks the pattern of having a "currentSth" property for the +// container, but was deemed to make sense here since the Page has +// its own "title" properties. +class QWizardPagePropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT +public: + explicit QWizardPagePropertySheet(QWizardPage *object, QObject *parent = 0); + + virtual bool reset(int index); + + static const char *pageIdProperty; + +private: + const int m_pageIdIndex; +}; + +// QWizardPropertySheet: Hides the "startId" property. It cannot be used +// as QWizard cannot handle setting it as a property before the actual +// page is added. + +class QWizardPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT +public: + explicit QWizardPropertySheet(QWizard *object, QObject *parent = 0); + virtual bool isVisible(int index) const; + +private: + const QString m_startId; +}; + +// Factories +typedef QDesignerPropertySheetFactory QWizardPropertySheetFactory; +typedef QDesignerPropertySheetFactory QWizardPagePropertySheetFactory; +typedef ExtensionFactory QWizardContainerFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QWIZARD_CONTAINER_H diff --git a/src/designer/components/formeditor/qworkspace_container.cpp b/src/designer/components/formeditor/qworkspace_container.cpp new file mode 100644 index 000000000..e299ba492 --- /dev/null +++ b/src/designer/components/formeditor/qworkspace_container.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qworkspace_container.h" +#include "qmdiarea_container.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QWorkspaceContainer::QWorkspaceContainer(QWorkspace *widget, QObject *parent) + : QObject(parent), + m_workspace(widget) +{ +} + +int QWorkspaceContainer::count() const +{ + return m_workspace->windowList(QWorkspace::CreationOrder).count(); +} + +QWidget *QWorkspaceContainer::widget(int index) const +{ + if (index < 0) + return 0; + return m_workspace->windowList(QWorkspace::CreationOrder).at(index); +} + +int QWorkspaceContainer::currentIndex() const +{ + if (QWidget *aw = m_workspace->activeWindow()) + return m_workspace->windowList(QWorkspace::CreationOrder).indexOf(aw); + return -1; +} + +void QWorkspaceContainer::setCurrentIndex(int index) +{ + m_workspace->setActiveWindow(m_workspace->windowList(QWorkspace::CreationOrder).at(index)); +} + +void QWorkspaceContainer::addWidget(QWidget *widget) +{ + QWidget *frame = m_workspace->addWindow(widget, Qt::Window); + frame->show(); + m_workspace->cascade(); + QMdiAreaContainer::positionNewMdiChild(m_workspace, frame); +} + +void QWorkspaceContainer::insertWidget(int, QWidget *widget) +{ + addWidget(widget); +} + +void QWorkspaceContainer::remove(int /* index */) +{ + // nothing to do here, reparenting to formwindow is apparently sufficient +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/qworkspace_container.h b/src/designer/components/formeditor/qworkspace_container.h new file mode 100644 index 000000000..a1e534a7d --- /dev/null +++ b/src/designer/components/formeditor/qworkspace_container.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWORKSPACE_CONTAINER_H +#define QWORKSPACE_CONTAINER_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QWorkspaceContainer: public QObject, public QDesignerContainerExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerContainerExtension) +public: + explicit QWorkspaceContainer(QWorkspace *widget, QObject *parent = 0); + + virtual int count() const; + virtual QWidget *widget(int index) const; + virtual int currentIndex() const; + virtual void setCurrentIndex(int index); + virtual void addWidget(QWidget *widget); + virtual void insertWidget(int index, QWidget *widget); + virtual void remove(int index); + +private: + QWorkspace *m_workspace; +}; + +typedef ExtensionFactory QWorkspaceContainerFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QWORKSPACE_CONTAINER_H diff --git a/src/designer/components/formeditor/spacer_propertysheet.cpp b/src/designer/components/formeditor/spacer_propertysheet.cpp new file mode 100644 index 000000000..981e7a604 --- /dev/null +++ b/src/designer/components/formeditor/spacer_propertysheet.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "spacer_propertysheet.h" +#include "qdesigner_widget_p.h" +#include "formwindow.h" +#include "spacer_widget_p.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal +{ +SpacerPropertySheet::SpacerPropertySheet(Spacer *object, QObject *parent) + : QDesignerPropertySheet(object, parent) +{ + clearFakeProperties(); +} + +SpacerPropertySheet::~SpacerPropertySheet() +{ +} + +bool SpacerPropertySheet::isVisible(int index) const +{ + static const QString spacerGroup = QLatin1String("Spacer"); + return propertyGroup(index) == spacerGroup; +} + +void SpacerPropertySheet::setProperty(int index, const QVariant &value) +{ + QDesignerPropertySheet::setProperty(index, value); +} + +bool SpacerPropertySheet::dynamicPropertiesAllowed() const +{ + return false; +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/spacer_propertysheet.h b/src/designer/components/formeditor/spacer_propertysheet.h new file mode 100644 index 000000000..eebac63d4 --- /dev/null +++ b/src/designer/components/formeditor/spacer_propertysheet.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SPACER_PROPERTYSHEET_H +#define SPACER_PROPERTYSHEET_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class SpacerPropertySheet: public QDesignerPropertySheet +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension) +public: + explicit SpacerPropertySheet(Spacer *object, QObject *parent = 0); + virtual ~SpacerPropertySheet(); + + virtual void setProperty(int index, const QVariant &value); + virtual bool isVisible(int index) const; + + virtual bool dynamicPropertiesAllowed() const; +}; + +typedef QDesignerPropertySheetFactory SpacerPropertySheetFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SPACER_PROPERTYSHEET_H diff --git a/src/designer/components/formeditor/templateoptionspage.cpp b/src/designer/components/formeditor/templateoptionspage.cpp new file mode 100644 index 000000000..a89ab12c2 --- /dev/null +++ b/src/designer/components/formeditor/templateoptionspage.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "templateoptionspage.h" +#include "ui_templateoptionspage.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ----------------- TemplateOptionsWidget +TemplateOptionsWidget::TemplateOptionsWidget(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + m_core(core), + m_ui(new Ui::TemplateOptionsWidget) +{ + m_ui->setupUi(this); + + m_ui->m_addTemplatePathButton->setIcon( + qdesigner_internal::createIconSet(QString::fromUtf8("plus.png"))); + m_ui->m_removeTemplatePathButton->setIcon( + qdesigner_internal::createIconSet(QString::fromUtf8("minus.png"))); + + connect(m_ui->m_templatePathListWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(templatePathSelectionChanged())); + connect(m_ui->m_addTemplatePathButton, SIGNAL(clicked()), this, SLOT(addTemplatePath())); + connect(m_ui->m_removeTemplatePathButton, SIGNAL(clicked()), this, SLOT(removeTemplatePath())); +} + +TemplateOptionsWidget::~TemplateOptionsWidget() +{ + delete m_ui; +} + +QStringList TemplateOptionsWidget::templatePaths() const +{ + QStringList rc; + const int count = m_ui->m_templatePathListWidget->count(); + for (int i = 0; i < count; i++) { + rc += m_ui->m_templatePathListWidget->item(i)->text(); + } + return rc; +} + +void TemplateOptionsWidget::setTemplatePaths(const QStringList &l) +{ + // add paths and select 0 + m_ui->m_templatePathListWidget->clear(); + if (l.empty()) { + // disable button + templatePathSelectionChanged(); + } else { + const QStringList::const_iterator cend = l.constEnd(); + for (QStringList::const_iterator it = l.constBegin(); it != cend; ++it) + m_ui->m_templatePathListWidget->addItem(*it); + m_ui->m_templatePathListWidget->setCurrentItem(m_ui->m_templatePathListWidget->item(0)); + } +} + +void TemplateOptionsWidget::addTemplatePath() +{ + const QString templatePath = chooseTemplatePath(m_core, this); + if (templatePath.isEmpty()) + return; + + const QList existing + = m_ui->m_templatePathListWidget->findItems(templatePath, Qt::MatchExactly); + if (!existing.empty()) + return; + + QListWidgetItem *newItem = new QListWidgetItem(templatePath); + m_ui->m_templatePathListWidget->addItem(newItem); + m_ui->m_templatePathListWidget->setCurrentItem(newItem); +} + +void TemplateOptionsWidget::removeTemplatePath() +{ + const QList selectedPaths + = m_ui->m_templatePathListWidget->selectedItems(); + if (selectedPaths.empty()) + return; + delete selectedPaths.front(); +} + +void TemplateOptionsWidget::templatePathSelectionChanged() +{ + const QList selectedPaths = m_ui->m_templatePathListWidget->selectedItems(); + m_ui->m_removeTemplatePathButton->setEnabled(!selectedPaths.empty()); +} + +QString TemplateOptionsWidget::chooseTemplatePath(QDesignerFormEditorInterface *core, QWidget *parent) +{ + QString rc = core->dialogGui()->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; +} + +// ----------------- TemplateOptionsPage +TemplateOptionsPage::TemplateOptionsPage(QDesignerFormEditorInterface *core) : + m_core(core) +{ +} + +QString TemplateOptionsPage::name() const +{ + //: Tab in preferences dialog + return QCoreApplication::translate("TemplateOptionsPage", "Template Paths"); +} + +QWidget *TemplateOptionsPage::createPage(QWidget *parent) +{ + m_widget = new TemplateOptionsWidget(m_core, parent); + m_initialTemplatePaths = QDesignerSharedSettings(m_core).additionalFormTemplatePaths(); + m_widget->setTemplatePaths(m_initialTemplatePaths); + return m_widget; +} + +void TemplateOptionsPage::apply() +{ + if (m_widget) { + const QStringList newTemplatePaths = m_widget->templatePaths(); + if (newTemplatePaths != m_initialTemplatePaths) { + QDesignerSharedSettings settings(m_core); + settings.setAdditionalFormTemplatePaths(newTemplatePaths); + m_initialTemplatePaths = newTemplatePaths; + } + } +} + +void TemplateOptionsPage::finish() +{ +} +} +QT_END_NAMESPACE + +#include "moc_templateoptionspage.h" diff --git a/src/designer/components/formeditor/templateoptionspage.h b/src/designer/components/formeditor/templateoptionspage.h new file mode 100644 index 000000000..41f2cdc7b --- /dev/null +++ b/src/designer/components/formeditor/templateoptionspage.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDESIGNER_TEMPLATEOPTIONS_H +#define QDESIGNER_TEMPLATEOPTIONS_H + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +namespace Ui { + class TemplateOptionsWidget; +} + +/* Present the user with a list of form template paths to save + * form templates. */ +class TemplateOptionsWidget : public QWidget +{ + Q_OBJECT + Q_DISABLE_COPY(TemplateOptionsWidget) +public: + explicit TemplateOptionsWidget(QDesignerFormEditorInterface *core, + QWidget *parent = 0); + ~TemplateOptionsWidget(); + + + QStringList templatePaths() const; + void setTemplatePaths(const QStringList &l); + + static QString chooseTemplatePath(QDesignerFormEditorInterface *core, QWidget *parent); + +private slots: + void addTemplatePath(); + void removeTemplatePath(); + void templatePathSelectionChanged(); + +private: + QDesignerFormEditorInterface *m_core; + Ui::TemplateOptionsWidget *m_ui; +}; + +class TemplateOptionsPage : public QDesignerOptionsPageInterface +{ + Q_DISABLE_COPY(TemplateOptionsPage) +public: + explicit TemplateOptionsPage(QDesignerFormEditorInterface *core); + + virtual QString name() const; + virtual QWidget *createPage(QWidget *parent); + virtual void apply(); + virtual void finish(); + +private: + QDesignerFormEditorInterface *m_core; + QStringList m_initialTemplatePaths; + QPointer m_widget; +}; + +} + +QT_END_NAMESPACE + +#endif // QDESIGNER_TEMPLATEOPTIONS_H diff --git a/src/designer/components/formeditor/templateoptionspage.ui b/src/designer/components/formeditor/templateoptionspage.ui new file mode 100644 index 000000000..3427ffeb8 --- /dev/null +++ b/src/designer/components/formeditor/templateoptionspage.ui @@ -0,0 +1,59 @@ + + qdesigner_internal::TemplateOptionsWidget + + + + 0 + 0 + 376 + 387 + + + + Form + + + + + + Additional Template Paths + + + + + + + + + ... + + + + + + + ... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + diff --git a/src/designer/components/formeditor/tool_widgeteditor.cpp b/src/designer/components/formeditor/tool_widgeteditor.cpp new file mode 100644 index 000000000..6fcd17abd --- /dev/null +++ b/src/designer/components/formeditor/tool_widgeteditor.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tool_widgeteditor.h" +#include "formwindow.h" + +// sdk +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +WidgetEditorTool::WidgetEditorTool(FormWindow *formWindow) + : QDesignerFormWindowToolInterface(formWindow), + m_formWindow(formWindow), + m_action(new QAction(tr("Edit Widgets"), this)), + m_specialDockDrag(false) +{ +} + +QAction *WidgetEditorTool::action() const +{ + return m_action; +} + +WidgetEditorTool::~WidgetEditorTool() +{ +} + +QDesignerFormEditorInterface *WidgetEditorTool::core() const +{ + return m_formWindow->core(); +} + +QDesignerFormWindowInterface *WidgetEditorTool::formWindow() const +{ + return m_formWindow; +} + +bool WidgetEditorTool::mainWindowSeparatorEvent(QWidget *widget, QEvent *event) +{ + QMainWindow *mw = qobject_cast(widget); + if (mw == 0) + return false; + + if (event->type() != QEvent::MouseButtonPress + && event->type() != QEvent::MouseMove + && event->type() != QEvent::MouseButtonRelease) + return false; + + QMouseEvent *e = static_cast(event); + + if (event->type() == QEvent::MouseButtonPress) { + if (mw->isSeparator(e->pos())) { + m_separator_drag_mw = mw; + return true; + } + return false; + } + + if (event->type() == QEvent::MouseMove) + return m_separator_drag_mw == mw; + + if (event->type() == QEvent::MouseButtonRelease) { + if (m_separator_drag_mw != mw) + return false; + m_separator_drag_mw = 0; + return true; + } + + return false; +} + +bool WidgetEditorTool::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) +{ + const bool passive = core()->widgetFactory()->isPassiveInteractor(widget) != 0 + || mainWindowSeparatorEvent(widget, event); // separators in QMainWindow + // are no longer widgets + switch (event->type()) { + case QEvent::Resize: + case QEvent::Move: + m_formWindow->updateSelection(widget); + break; + + case QEvent::FocusOut: + case QEvent::FocusIn: // Popup cancelled over a form widget: Reset its focus frame + return !(passive || widget == m_formWindow || widget == m_formWindow->mainContainer()); + + case QEvent::Wheel: // Prevent spinboxes and combos from reacting + return !passive; + + case QEvent::KeyPress: + return !passive && handleKeyPressEvent(widget, managedWidget, static_cast(event)); + + case QEvent::KeyRelease: + return !passive && handleKeyReleaseEvent(widget, managedWidget, static_cast(event)); + + case QEvent::MouseMove: + return !passive && handleMouseMoveEvent(widget, managedWidget, static_cast(event)); + + case QEvent::MouseButtonPress: + return !passive && handleMousePressEvent(widget, managedWidget, static_cast(event)); + + case QEvent::MouseButtonRelease: + return !passive && handleMouseReleaseEvent(widget, managedWidget, static_cast(event)); + + case QEvent::MouseButtonDblClick: + return !passive && handleMouseButtonDblClickEvent(widget, managedWidget, static_cast(event)); + + case QEvent::ContextMenu: + return !passive && handleContextMenu(widget, managedWidget, static_cast(event)); + + case QEvent::DragEnter: + return handleDragEnterMoveEvent(widget, managedWidget, static_cast(event), true); + case QEvent::DragMove: + return handleDragEnterMoveEvent(widget, managedWidget, static_cast(event), false); + case QEvent::DragLeave: + return handleDragLeaveEvent(widget, managedWidget, static_cast(event)); + case QEvent::Drop: + return handleDropEvent(widget, managedWidget, static_cast(event)); + default: + break; + + } // end switch + + return false; +} + +// ### remove me + +bool WidgetEditorTool::handleContextMenu(QWidget *widget, QWidget *managedWidget, QContextMenuEvent *e) +{ + return m_formWindow->handleContextMenu(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleMouseButtonDblClickEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e) +{ + return m_formWindow->handleMouseButtonDblClickEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleMousePressEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e) +{ + return m_formWindow->handleMousePressEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleMouseMoveEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e) +{ + return m_formWindow->handleMouseMoveEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleMouseReleaseEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e) +{ + return m_formWindow->handleMouseReleaseEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleKeyPressEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e) +{ + return m_formWindow->handleKeyPressEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handleKeyReleaseEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e) +{ + return m_formWindow->handleKeyReleaseEvent(widget, managedWidget, e); +} + +bool WidgetEditorTool::handlePaintEvent(QWidget *widget, QWidget *managedWidget, QPaintEvent *e) +{ + Q_UNUSED(widget); + Q_UNUSED(managedWidget); + Q_UNUSED(e); + + return false; +} + +void WidgetEditorTool::detectDockDrag(const QDesignerMimeData *mimeData) +{ + m_specialDockDrag = false; + if (!mimeData) + return; + + QMainWindow *mw = qobject_cast(m_formWindow->mainContainer()); + if (!mw) + return; + + const QList item_list = mimeData->items(); + + foreach (QDesignerDnDItemInterface *item, item_list) { + if (item->decoration() && item->decoration()->property("_q_dockDrag").toBool()) + m_specialDockDrag = true; + + } +} + +bool WidgetEditorTool::handleDragEnterMoveEvent(QWidget *widget, QWidget * /*managedWidget*/, QDragMoveEvent *e, bool isEnter) +{ + const QDesignerMimeData *mimeData = qobject_cast(e->mimeData()); + if (!mimeData) + return false; + + if (!m_formWindow->hasFeature(QDesignerFormWindowInterface::EditFeature)) { + e->ignore(); + return true; + } + + if (isEnter) + detectDockDrag(mimeData); + + + QPoint globalPos = QPoint(0, 0); + if (m_specialDockDrag) { + m_lastDropTarget = 0; + QMainWindow *mw = qobject_cast(m_formWindow->mainContainer()); + if (mw) + m_lastDropTarget = mw->centralWidget(); + } else { + // If custom widgets have acceptDrops=true, the event occurs for them + const QPoint formPos = widget != m_formWindow ? widget->mapTo(m_formWindow, e->pos()) : e->pos(); + globalPos = m_formWindow->mapToGlobal(formPos); + const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget; + QWidget *dropTarget = m_formWindow->widgetUnderMouse(formPos, wum); + if (m_lastDropTarget && dropTarget != m_lastDropTarget) + m_formWindow->highlightWidget(m_lastDropTarget, m_lastDropTarget->mapFromGlobal(globalPos), FormWindow::Restore); + m_lastDropTarget = dropTarget; + } + + if (m_lastDropTarget) + m_formWindow->highlightWidget(m_lastDropTarget, m_lastDropTarget->mapFromGlobal(globalPos), FormWindow::Highlight); + + if (isEnter || m_lastDropTarget) + mimeData->acceptEvent(e); + else + e->ignore(); + return true; +} + +bool WidgetEditorTool::handleDropEvent(QWidget *widget, QWidget *, QDropEvent *e) +{ + const QDesignerMimeData *mimeData = qobject_cast(e->mimeData()); + if (!mimeData) + return false; + + if (!m_lastDropTarget || + !m_formWindow->hasFeature(QDesignerFormWindowInterface::EditFeature)) { + e->ignore(); + return true; + } + // FormWindow determines the position from the decoration. + const QPoint globalPos = widget->mapToGlobal(e->pos()); + mimeData->moveDecoration(globalPos); + if (m_specialDockDrag) { + if (!m_formWindow->dropDockWidget(mimeData->items().at(0), globalPos)) { + e->ignore(); + return true; + } + } else if (!m_formWindow->dropWidgets(mimeData->items(), m_lastDropTarget, globalPos)) { + e->ignore(); + return true; + } + mimeData->acceptEvent(e); + return true; +} + +bool WidgetEditorTool::restoreDropHighlighting() +{ + if (!m_lastDropTarget) + return false; + + m_formWindow->highlightWidget(m_lastDropTarget, m_lastDropTarget->mapFromGlobal(QCursor::pos()), FormWindow::Restore); + m_lastDropTarget = 0; + return true; +} + +bool WidgetEditorTool::handleDragLeaveEvent(QWidget *, QWidget *, QDragLeaveEvent *event) +{ + if (restoreDropHighlighting()) { + event->accept(); + return true; + } + return false; +} + +QWidget *WidgetEditorTool::editor() const +{ + Q_ASSERT(formWindow() != 0); + return formWindow()->mainContainer(); +} + +void WidgetEditorTool::activated() +{ + if (core()->widgetBox()) + core()->widgetBox()->setEnabled(true); + + if (m_formWindow == 0) + return; + + QList sel = m_formWindow->selectedWidgets(); + foreach (QWidget *w, sel) + m_formWindow->raiseSelection(w); +} + +void WidgetEditorTool::deactivated() +{ + if (core()->widgetBox()) + core()->widgetBox()->setEnabled(false); + + if (m_formWindow == 0) + return; + + m_formWindow->clearSelection(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/tool_widgeteditor.h b/src/designer/components/formeditor/tool_widgeteditor.h new file mode 100644 index 000000000..bc7e9a612 --- /dev/null +++ b/src/designer/components/formeditor/tool_widgeteditor.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOOL_WIDGETEDITOR_H +#define TOOL_WIDGETEDITOR_H + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QAction; +class QMainWindow; + +namespace qdesigner_internal { + +class FormWindow; +class QDesignerMimeData; + +class WidgetEditorTool: public QDesignerFormWindowToolInterface +{ + Q_OBJECT +public: + explicit WidgetEditorTool(FormWindow *formWindow); + virtual ~WidgetEditorTool(); + + virtual QDesignerFormEditorInterface *core() const; + virtual QDesignerFormWindowInterface *formWindow() const; + virtual QWidget *editor() const; + virtual QAction *action() const; + + virtual void activated(); + virtual void deactivated(); + + virtual bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event); + + bool handleContextMenu(QWidget *widget, QWidget *managedWidget, QContextMenuEvent *e); + bool handleMouseButtonDblClickEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMousePressEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMouseMoveEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleMouseReleaseEvent(QWidget *widget, QWidget *managedWidget, QMouseEvent *e); + bool handleKeyPressEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e); + bool handleKeyReleaseEvent(QWidget *widget, QWidget *managedWidget, QKeyEvent *e); + bool handlePaintEvent(QWidget *widget, QWidget *managedWidget, QPaintEvent *e); + + bool handleDragEnterMoveEvent(QWidget *widget, QWidget *managedWidget, QDragMoveEvent *e, bool isEnter); + bool handleDragLeaveEvent(QWidget *widget, QWidget *managedWidget, QDragLeaveEvent *e); + bool handleDropEvent(QWidget *widget, QWidget *managedWidget, QDropEvent *e); + +private: + bool restoreDropHighlighting(); + void detectDockDrag(const QDesignerMimeData *mimeData); + + FormWindow *m_formWindow; + QAction *m_action; + + bool mainWindowSeparatorEvent(QWidget *widget, QEvent *event); + QPointer m_separator_drag_mw; + QPointer m_lastDropTarget; + bool m_specialDockDrag; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TOOL_WIDGETEDITOR_H diff --git a/src/designer/components/formeditor/widgetselection.cpp b/src/designer/components/formeditor/widgetselection.cpp new file mode 100644 index 000000000..81f5ce738 --- /dev/null +++ b/src/designer/components/formeditor/widgetselection.cpp @@ -0,0 +1,745 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetselection.h" +#include "formwindow.h" +#include "formwindowmanager.h" + +// sdk +#include +#include + +// shared +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +enum { debugWidgetSelection = 0 }; + +// Return the layout the widget is in +template +static inline Layout *managedLayoutOf(const QDesignerFormEditorInterface *core, + QWidget *w, + const Layout * /* vs6dummy */ = 0) +{ + if (QWidget *p = w->parentWidget()) + if (QLayout *l = LayoutInfo::managedLayout(core, p)) + return qobject_cast(l); + return 0; +} + +// ----------- WidgetHandle +WidgetHandle::WidgetHandle(FormWindow *parent, WidgetHandle::Type t, WidgetSelection *s) : + InvisibleWidget(parent->formContainer()), + m_widget(0), + m_type(t), + m_formWindow( parent), + m_sel(s), + m_active(true) +{ + setMouseTracking(false); + setAutoFillBackground(true); + + setBackgroundRole(m_active ? QPalette::Text : QPalette::Dark); + setFixedSize(6, 6); + + updateCursor(); +} + +void WidgetHandle::updateCursor() +{ +#ifndef QT_NO_CURSOR + if (!m_active) { + setCursor(Qt::ArrowCursor); + return; + } + + switch (m_type) { + case LeftTop: + setCursor(Qt::SizeFDiagCursor); + break; + case Top: + setCursor(Qt::SizeVerCursor); + break; + case RightTop: + setCursor(Qt::SizeBDiagCursor); + break; + case Right: + setCursor(Qt::SizeHorCursor); + break; + case RightBottom: + setCursor(Qt::SizeFDiagCursor); + break; + case Bottom: + setCursor(Qt::SizeVerCursor); + break; + case LeftBottom: + setCursor(Qt::SizeBDiagCursor); + break; + case Left: + setCursor(Qt::SizeHorCursor); + break; + default: + Q_ASSERT(0); + } +#endif +} + +QDesignerFormEditorInterface *WidgetHandle::core() const +{ + if (m_formWindow) + return m_formWindow->core(); + + return 0; +} + +void WidgetHandle::setActive(bool a) +{ + m_active = a; + setBackgroundRole(m_active ? QPalette::Text : QPalette::Dark); + updateCursor(); +} + +void WidgetHandle::setWidget(QWidget *w) +{ + m_widget = w; +} + +void WidgetHandle::paintEvent(QPaintEvent *) +{ + QDesignerFormWindowManagerInterface *m = m_formWindow->core()->formWindowManager(); + + QStylePainter p(this); + if (m_formWindow->currentWidget() == m_widget) { + p.setPen(m->activeFormWindow() == m_formWindow ? Qt::blue : Qt::red); + p.drawRect(0, 0, width() - 1, height() - 1); + } +} + +void WidgetHandle::mousePressEvent(QMouseEvent *e) +{ + e->accept(); + + if (!m_formWindow->hasFeature(FormWindow::EditFeature)) + return; + + if (!(m_widget && e->button() == Qt::LeftButton)) + return; + + if (!(m_active)) + return; + + QWidget *container = m_widget->parentWidget(); + + m_origPressPos = container->mapFromGlobal(e->globalPos()); + m_geom = m_origGeom = m_widget->geometry(); +} + +void WidgetHandle::mouseMoveEvent(QMouseEvent *e) +{ + if (!(m_widget && m_active && e->buttons() & Qt::LeftButton)) + return; + + e->accept(); + + QWidget *container = m_widget->parentWidget(); + + const QPoint rp = container->mapFromGlobal(e->globalPos()); + const QPoint d = rp - m_origPressPos; + + const QRect pr = container->rect(); + + qdesigner_internal::Grid grid; + if (const qdesigner_internal::FormWindowBase *fwb = qobject_cast(m_formWindow)) + grid = fwb->designerGrid(); + + switch (m_type) { + + case LeftTop: { + if (rp.x() > pr.width() - 2 * width() || rp.y() > pr.height() - 2 * height()) + return; + + int w = m_origGeom.width() - d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + int h = m_origGeom.height() - d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + const int dx = m_widget->width() - w; + const int dy = m_widget->height() - h; + + trySetGeometry(m_widget, m_widget->x() + dx, m_widget->y() + dy, w, h); + } break; + + case Top: { + if (rp.y() > pr.height() - 2 * height()) + return; + + int h = m_origGeom.height() - d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + const int dy = m_widget->height() - h; + trySetGeometry(m_widget, m_widget->x(), m_widget->y() + dy, m_widget->width(), h); + } break; + + case RightTop: { + if (rp.x() < 2 * width() || rp.y() > pr.height() - 2 * height()) + return; + + int h = m_origGeom.height() - d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + const int dy = m_widget->height() - h; + + int w = m_origGeom.width() + d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + trySetGeometry(m_widget, m_widget->x(), m_widget->y() + dy, w, h); + } break; + + case Right: { + if (rp.x() < 2 * width()) + return; + + int w = m_origGeom.width() + d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + tryResize(m_widget, w, m_widget->height()); + } break; + + case RightBottom: { + if (rp.x() < 2 * width() || rp.y() < 2 * height()) + return; + + int w = m_origGeom.width() + d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + int h = m_origGeom.height() + d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + tryResize(m_widget, w, h); + } break; + + case Bottom: { + if (rp.y() < 2 * height()) + return; + + int h = m_origGeom.height() + d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + tryResize(m_widget, m_widget->width(), h); + } break; + + case LeftBottom: { + if (rp.x() > pr.width() - 2 * width() || rp.y() < 2 * height()) + return; + + int w = m_origGeom.width() - d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + int h = m_origGeom.height() + d.y(); + m_geom.setHeight(h); + h = grid.widgetHandleAdjustY(h); + + int dx = m_widget->width() - w; + + trySetGeometry(m_widget, m_widget->x() + dx, m_widget->y(), w, h); + } break; + + case Left: { + if (rp.x() > pr.width() - 2 * width()) + return; + + int w = m_origGeom.width() - d.x(); + m_geom.setWidth(w); + w = grid.widgetHandleAdjustX(w); + + const int dx = m_widget->width() - w; + + trySetGeometry(m_widget, m_widget->x() + dx, m_widget->y(), w, m_widget->height()); + } break; + + default: break; + + } // end switch + + m_sel->updateGeometry(); + + if (LayoutInfo::layoutType(m_formWindow->core(), m_widget) != LayoutInfo::NoLayout) + m_formWindow->updateChildSelections(m_widget); +} + +void WidgetHandle::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton || !m_active) + return; + + e->accept(); + + if (!m_formWindow->hasFeature(FormWindow::EditFeature)) + return; + + switch (WidgetSelection::widgetState(m_formWindow->core(), m_widget)) { + case WidgetSelection::UnlaidOut: + if (m_geom != m_widget->geometry()) { + SetPropertyCommand *cmd = new SetPropertyCommand(m_formWindow); + cmd->init(m_widget, QLatin1String("geometry"), m_widget->geometry()); + cmd->setOldValue(m_origGeom); + m_formWindow->commandHistory()->push(cmd); + m_formWindow->emitSelectionChanged(); + } + break; + case WidgetSelection::LaidOut: + break; + case WidgetSelection::ManagedGridLayout: + changeGridLayoutItemSpan(); + break; + case WidgetSelection::ManagedFormLayout: + changeFormLayoutItemSpan(); + break; + } +} + +// Match the left/right widget handle mouse movements to form layout span-changing operations +static inline int formLayoutLeftHandleOperation(int dx, unsigned possibleOperations) +{ + if (dx < 0) { + if (possibleOperations & ChangeFormLayoutItemRoleCommand::FieldToSpanning) + return ChangeFormLayoutItemRoleCommand::FieldToSpanning; + return 0; + } + if (possibleOperations & ChangeFormLayoutItemRoleCommand::SpanningToField) + return ChangeFormLayoutItemRoleCommand::SpanningToField; + return 0; +} + +static inline int formLayoutRightHandleOperation(int dx, unsigned possibleOperations) +{ + if (dx < 0) { + if (possibleOperations & ChangeFormLayoutItemRoleCommand::SpanningToLabel) + return ChangeFormLayoutItemRoleCommand::SpanningToLabel; + return 0; + } + if (possibleOperations & ChangeFormLayoutItemRoleCommand::LabelToSpanning) + return ChangeFormLayoutItemRoleCommand::LabelToSpanning; + return 0; +} + +// Change form layout item horizontal span +void WidgetHandle::changeFormLayoutItemSpan() +{ + QUndoCommand *cmd = 0; + // Figure out command according to the movement + const int dx = m_widget->geometry().center().x() - m_origGeom.center().x(); + if (qAbs(dx) >= QApplication::startDragDistance()) { + int operation = 0; + if (const unsigned possibleOperations = ChangeFormLayoutItemRoleCommand::possibleOperations(m_formWindow->core(), m_widget)) { + switch (m_type) { + case WidgetHandle::Left: + operation = formLayoutLeftHandleOperation(dx, possibleOperations); + break; + case WidgetHandle::Right: + operation = formLayoutRightHandleOperation(dx, possibleOperations); + break; + default: + break; + } + if (operation) { + ChangeFormLayoutItemRoleCommand *fcmd = new ChangeFormLayoutItemRoleCommand(m_formWindow); + fcmd->init(m_widget, static_cast(operation)); + cmd = fcmd; + } + } + } + if (cmd) { + m_formWindow->commandHistory()->push(cmd); + } else { + // Cancelled/Invalid. Restore the size of the widget. + if (QFormLayout *form = managedLayoutOf(m_formWindow->core(), m_widget)) { + form->invalidate(); + form->activate(); + m_formWindow->clearSelection(false); + m_formWindow->selectWidget(m_widget); + } + } +} + +void WidgetHandle::changeGridLayoutItemSpan() +{ + QDesignerLayoutDecorationExtension *deco = qt_extension(core()->extensionManager(), m_widget->parentWidget()); + if (!deco) + return; + QGridLayout *grid = managedLayoutOf(m_formWindow->core(), m_widget); + if (!grid) + return; + + const int index = deco->indexOf(m_widget); + const QRect info = deco->itemInfo(index); + const int top = deco->findItemAt(info.top() - 1, info.left()); + const int left = deco->findItemAt(info.top(), info.left() - 1); + const int bottom = deco->findItemAt(info.bottom() + 1, info.left()); + const int right = deco->findItemAt(info.top(), info.right() + 1); + + const QPoint pt = m_origGeom.center() - m_widget->geometry().center(); + + ChangeLayoutItemGeometry *cmd = 0; + + switch (m_type) { + default: + break; + + case WidgetHandle::Top: { + if (pt.y() < 0 && info.height() > 1) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y() + 1, info.x(), info.height() - 1, info.width()); + } else if (pt.y() > 0 && top != -1 && grid->itemAt(top)->spacerItem()) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y() - 1, info.x(), info.height() + 1, info.width()); + } + } + break; + + case WidgetHandle::Left: { + if (pt.x() < 0 && info.width() > 1) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x() + 1, info.height(), info.width() - 1); + } else if (pt.x() > 0 && left != -1 && grid->itemAt(left)->spacerItem()) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x() - 1, info.height(), info.width() + 1); + } + } + break; + + case WidgetHandle::Right: { + if (pt.x() > 0 && info.width() > 1) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x(), info.height(), info.width() - 1); + } else if (pt.x() < 0 && right != -1 && grid->itemAt(right)->spacerItem()) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x(), info.height(), info.width() + 1); + } + } + break; + + case WidgetHandle::Bottom: { + if (pt.y() > 0 && info.width() > 1) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x(), info.height() - 1, info.width()); + } else if (pt.y() < 0 && bottom != -1 && grid->itemAt(bottom)->spacerItem()) { + cmd = new ChangeLayoutItemGeometry(m_formWindow); + cmd->init(m_widget, info.y(), info.x(), info.height() + 1, info.width()); + } + } + break; + } + + if (cmd != 0) { + m_formWindow->commandHistory()->push(cmd); + } else { + grid->invalidate(); + grid->activate(); + m_formWindow->clearSelection(false); + m_formWindow->selectWidget(m_widget); + } +} + +void WidgetHandle::trySetGeometry(QWidget *w, int x, int y, int width, int height) +{ + if (!m_formWindow->hasFeature(FormWindow::EditFeature)) + return; + + int minw = w->minimumSize().width(); + minw = qMax(minw, 2 * m_formWindow->grid().x()); + + int minh = w->minimumSize().height(); + minh = qMax(minh, 2 * m_formWindow->grid().y()); + + if (qMax(minw, width) > w->maximumWidth() || + qMax(minh, height) > w->maximumHeight()) + return; + + if (width < minw && x != w->x()) + x -= minw - width; + + if (height < minh && y != w->y()) + y -= minh - height; + + w->setGeometry(x, y, qMax(minw, width), qMax(minh, height)); +} + +void WidgetHandle::tryResize(QWidget *w, int width, int height) +{ + int minw = w->minimumSize().width(); + minw = qMax(minw, 16); + + int minh = w->minimumSize().height(); + minh = qMax(minh, 16); + + w->resize(qMax(minw, width), qMax(minh, height)); +} + +// ------------------ WidgetSelection + +WidgetSelection::WidgetState WidgetSelection::widgetState(const QDesignerFormEditorInterface *core, QWidget *w) +{ + bool isManaged; + const LayoutInfo::Type lt = LayoutInfo::laidoutWidgetType(core, w, &isManaged); + if (lt == LayoutInfo::NoLayout) + return UnlaidOut; + if (!isManaged) + return LaidOut; + switch (lt) { + case LayoutInfo::Grid: + return ManagedGridLayout; + case LayoutInfo::Form: + return ManagedFormLayout; + default: + break; + } + return LaidOut; +} + +WidgetSelection::WidgetSelection(FormWindow *parent) : + m_widget(0), + m_formWindow(parent) +{ + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) + m_handles[i] = new WidgetHandle(m_formWindow, static_cast(i), this); + hide(); +} + +void WidgetSelection::setWidget(QWidget *w) +{ + if (m_widget != 0) + m_widget->removeEventFilter(this); + + if (w == 0) { + hide(); + m_widget = 0; + return; + } + + m_widget = w; + + m_widget->installEventFilter(this); + + updateActive(); + + updateGeometry(); + show(); +} + +void WidgetSelection::updateActive() +{ + const WidgetState ws = widgetState(m_formWindow->core(), m_widget); + bool active[WidgetHandle::TypeCount]; + qFill(active, active + WidgetHandle::TypeCount, false); + // Determine active handles + switch (ws) { + case UnlaidOut: + qFill(active, active + WidgetHandle::TypeCount, true); + break; + case ManagedGridLayout: // Grid: Allow changing span + active[WidgetHandle::Left] = active[WidgetHandle::Top] = active[WidgetHandle::Right] = active[WidgetHandle::Bottom] = true; + break; + case ManagedFormLayout: // Form: Allow changing column span + if (const unsigned operation = ChangeFormLayoutItemRoleCommand::possibleOperations(m_formWindow->core(), m_widget)) { + active[WidgetHandle::Left] = operation & (ChangeFormLayoutItemRoleCommand::SpanningToField|ChangeFormLayoutItemRoleCommand::FieldToSpanning); + active[WidgetHandle::Right] = operation & (ChangeFormLayoutItemRoleCommand::SpanningToLabel|ChangeFormLayoutItemRoleCommand::LabelToSpanning); + } + break; + default: + break; + } + + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) + if (WidgetHandle *h = m_handles[i]) { + h->setWidget(m_widget); + h->setActive(active[i]); + } +} + +bool WidgetSelection::isUsed() const +{ + return m_widget != 0; +} + +void WidgetSelection::updateGeometry() +{ + if (!m_widget || !m_widget->parentWidget()) + return; + + QPoint p = m_widget->parentWidget()->mapToGlobal(m_widget->pos()); + p = m_formWindow->formContainer()->mapFromGlobal(p); + const QRect r(p, m_widget->size()); + + const int w = 6; + const int h = 6; + + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) { + WidgetHandle *hndl = m_handles[ i ]; + if (!hndl) + continue; + switch (i) { + case WidgetHandle::LeftTop: + hndl->move(r.x() - w / 2, r.y() - h / 2); + break; + case WidgetHandle::Top: + hndl->move(r.x() + r.width() / 2 - w / 2, r.y() - h / 2); + break; + case WidgetHandle::RightTop: + hndl->move(r.x() + r.width() - w / 2, r.y() - h / 2); + break; + case WidgetHandle::Right: + hndl->move(r.x() + r.width() - w / 2, r.y() + r.height() / 2 - h / 2); + break; + case WidgetHandle::RightBottom: + hndl->move(r.x() + r.width() - w / 2, r.y() + r.height() - h / 2); + break; + case WidgetHandle::Bottom: + hndl->move(r.x() + r.width() / 2 - w / 2, r.y() + r.height() - h / 2); + break; + case WidgetHandle::LeftBottom: + hndl->move(r.x() - w / 2, r.y() + r.height() - h / 2); + break; + case WidgetHandle::Left: + hndl->move(r.x() - w / 2, r.y() + r.height() / 2 - h / 2); + break; + default: + break; + } + } +} + +void WidgetSelection::hide() +{ + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) { + WidgetHandle *h = m_handles[ i ]; + if (h) + h->hide(); + } +} + +void WidgetSelection::show() +{ + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) { + WidgetHandle *h = m_handles[ i ]; + if (h) { + h->show(); + h->raise(); + } + } +} + +void WidgetSelection::update() +{ + for (int i = WidgetHandle::LeftTop; i < WidgetHandle::TypeCount; ++i) { + WidgetHandle *h = m_handles[ i ]; + if (h) + h->update(); + } +} + +QWidget *WidgetSelection::widget() const +{ + return m_widget; +} + +QDesignerFormEditorInterface *WidgetSelection::core() const +{ + if (m_formWindow) + return m_formWindow->core(); + + return 0; +} + +bool WidgetSelection::eventFilter(QObject *object, QEvent *event) +{ + if (object != widget()) + return false; + + switch (event->type()) { + default: break; + + case QEvent::Move: + case QEvent::Resize: + updateGeometry(); + break; + case QEvent::ZOrderChange: + show(); + break; + } // end switch + + return false; +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/formeditor/widgetselection.h b/src/designer/components/formeditor/widgetselection.h new file mode 100644 index 000000000..3d90b3a55 --- /dev/null +++ b/src/designer/components/formeditor/widgetselection.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETSELECTION_H +#define WIDGETSELECTION_H + +#include "formeditor_global.h" +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QMouseEvent; +class QPaintEvent; + +namespace qdesigner_internal { + +class FormWindow; +class WidgetSelection; + +class QT_FORMEDITOR_EXPORT WidgetHandle: public InvisibleWidget +{ + Q_OBJECT +public: + enum Type + { + LeftTop, + Top, + RightTop, + Right, + RightBottom, + Bottom, + LeftBottom, + Left, + + TypeCount + }; + + WidgetHandle(FormWindow *parent, Type t, WidgetSelection *s); + void setWidget(QWidget *w); + void setActive(bool a); + void updateCursor(); + + void setEnabled(bool) {} + + QDesignerFormEditorInterface *core() const; + +protected: + void paintEvent(QPaintEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + +private: + void changeGridLayoutItemSpan(); + void changeFormLayoutItemSpan(); + void trySetGeometry(QWidget *w, int x, int y, int width, int height); + void tryResize(QWidget *w, int width, int height); + +private: + QWidget *m_widget; + const Type m_type; + QPoint m_origPressPos; + FormWindow *m_formWindow; + WidgetSelection *m_sel; + QRect m_geom, m_origGeom; + bool m_active; +}; + +class QT_FORMEDITOR_EXPORT WidgetSelection: public QObject +{ + Q_OBJECT +public: + WidgetSelection(FormWindow *parent); + + void setWidget(QWidget *w); + bool isUsed() const; + + void updateActive(); + void updateGeometry(); + void hide(); + void show(); + void update(); + + QWidget *widget() const; + + QDesignerFormEditorInterface *core() const; + + virtual bool eventFilter(QObject *object, QEvent *event); + + enum WidgetState { UnlaidOut, LaidOut, ManagedGridLayout, ManagedFormLayout }; + static WidgetState widgetState(const QDesignerFormEditorInterface *core, QWidget *w); + +private: + WidgetHandle *m_handles[WidgetHandle::TypeCount]; + QPointer m_widget; + FormWindow *m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETSELECTION_H diff --git a/src/designer/components/objectinspector/objectinspector.cmake b/src/designer/components/objectinspector/objectinspector.cmake new file mode 100644 index 000000000..0eb56ba43 --- /dev/null +++ b/src/designer/components/objectinspector/objectinspector.cmake @@ -0,0 +1,13 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector/objectinspector.h + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector/objectinspectormodel_p.h + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector/objectinspector_global.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector/objectinspector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/objectinspector/objectinspectormodel.cpp +) + diff --git a/src/designer/components/objectinspector/objectinspector.cpp b/src/designer/components/objectinspector/objectinspector.cpp new file mode 100644 index 000000000..435743366 --- /dev/null +++ b/src/designer/components/objectinspector/objectinspector.cpp @@ -0,0 +1,836 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectinspector.h" +#include "objectinspectormodel_p.h" +#include "formwindow.h" + +// sdk +#include +#include +#include +#include +#include +#include +#include +#include + +// shared +#include +#include +#include +#include +#include +#include +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + // Selections: Basically, ObjectInspector has to ensure a consistent + // selection, that is, either form-managed widgets (represented + // by the cursor interface selection), or unmanaged widgets/objects, + // for example actions, container pages, menu bars, tool bars + // and the like. The selection state of the latter is managed only in the object inspector. + // As soon as a managed widget is selected, unmanaged objects + // have to be unselected + // Normally, an empty selection is not allowed, the main container + // should be selected in this case (applyCursorSelection()). + // An exception is when clearSelection is called directly for example + // by the action editor that puts an unassociated action into the property + // editor. A hack exists to avoid the update in this case. + + enum SelectionType { + NoSelection, + // A QObject that has a meta database entry + QObjectSelection, + // Unmanaged widget, menu bar or the like + UnmanagedWidgetSelection, + // A widget managed by the form window cursor + ManagedWidgetSelection }; + + typedef QVector QObjectVector; +} + +static inline SelectionType selectionType(const QDesignerFormWindowInterface *fw, QObject *o) +{ + if (!o->isWidgetType()) + return fw->core()->metaDataBase()->item(o) ? QObjectSelection : NoSelection; + return fw->isManaged(qobject_cast(o)) ? ManagedWidgetSelection : UnmanagedWidgetSelection; +} + +// Return an offset for dropping (when dropping widgets on the object +// inspector, we fake a position on the form based on the widget dropped on). +// Position the dropped widget with form grid offset to avoid overlapping unless we +// drop on a layout. Position doesn't matter in the layout case +// and this enables us to drop on a squeezed layout widget of size zero + +static inline QPoint dropPointOffset(const qdesigner_internal::FormWindowBase *fw, const QWidget *dropTarget) +{ + if (!dropTarget || dropTarget->layout()) + return QPoint(0, 0); + return QPoint(fw->designerGrid().deltaX(), fw->designerGrid().deltaY()); +} + +namespace qdesigner_internal { +// Delegate with object name validator for the object name column +class ObjectInspectorDelegate : public QItemDelegate { +public: + explicit ObjectInspectorDelegate(QObject *parent = 0); + + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; + +ObjectInspectorDelegate::ObjectInspectorDelegate(QObject *parent) : + QItemDelegate(parent) +{ +} + +QWidget *ObjectInspectorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option, const QModelIndex &index) const +{ + if (index.column() != ObjectInspectorModel::ObjectNameColumn) + return QItemDelegate::createEditor(parent, option, index); + // Object name editor + const bool isMainContainer = !index.parent().isValid(); + return new TextPropertyEditor(parent, TextPropertyEditor::EmbeddingTreeView, + isMainContainer ? ValidationObjectNameScope : ValidationObjectName); +} + +// ------------ ObjectInspectorTreeView: +// - Makes the Space key start editing +// - Suppresses a range selection by dragging or Shift-up/down, which does not really work due +// to the need to maintain a consistent selection. + +class ObjectInspectorTreeView : public QTreeView { +public: + ObjectInspectorTreeView(QWidget *parent = 0) : QTreeView(parent) {} + +protected: + virtual void mouseMoveEvent (QMouseEvent * event); + virtual void keyPressEvent(QKeyEvent *event); + +}; + +void ObjectInspectorTreeView::mouseMoveEvent(QMouseEvent *event) +{ + event->ignore(); // suppress a range selection by dragging +} + +void ObjectInspectorTreeView::keyPressEvent(QKeyEvent *event) +{ + bool handled = false; + switch (event->key()) { + case Qt::Key_Up: + case Qt::Key_Down: // suppress shift-up/down range selection + if (event->modifiers() & Qt::ShiftModifier) { + event->ignore(); + handled = true; + } + break; + case Qt::Key_Space: { // Space pressed: Start editing + const QModelIndex index = currentIndex(); + if (index.isValid() && index.column() == 0 && !model()->hasChildren(index) && model()->flags(index) & Qt::ItemIsEditable) { + event->accept(); + handled = true; + edit(index); + } + } + break; + default: + break; + } + if (!handled) + QTreeView::keyPressEvent(event); +} + +// ------------ ObjectInspectorPrivate + +class ObjectInspector::ObjectInspectorPrivate { +public: + ObjectInspectorPrivate(QDesignerFormEditorInterface *core); + ~ObjectInspectorPrivate(); + + QTreeView *treeView() const { return m_treeView; } + ItemViewFindWidget *findWidget() const { return m_findWidget; } + QDesignerFormEditorInterface *core() const { return m_core; } + const QPointer &formWindow() const { return m_formWindow; } + + void clear(); + void setFormWindow(QDesignerFormWindowInterface *fwi); + + QWidget *managedWidgetAt(const QPoint &global_mouse_pos); + + void restoreDropHighlighting(); + void handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter); + void dropEvent (QDropEvent * event); + + void clearSelection(); + bool selectObject(QObject *o); + void slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected); + void getSelection(Selection &s) const; + + void slotHeaderDoubleClicked(int column) { m_treeView->resizeColumnToContents(column); } + void slotPopupContextMenu(QWidget *parent, const QPoint &pos); + +private: + void setFormWindowBlocked(QDesignerFormWindowInterface *fwi); + void applyCursorSelection(); + void synchronizeSelection(const QItemSelection & selected, const QItemSelection &deselected); + bool checkManagedWidgetSelection(const QModelIndexList &selection); + void showContainersCurrentPage(QWidget *widget); + + enum SelectionFlags { AddToSelection = 1, MakeCurrent = 2}; + void selectIndexRange(const QModelIndexList &indexes, unsigned flags); + + QDesignerFormEditorInterface *m_core; + QTreeView *m_treeView; + ObjectInspectorModel *m_model; + ItemViewFindWidget *m_findWidget; + QPointer m_formWindow; + QPointer m_formFakeDropTarget; + bool m_withinClearSelection; +}; + +ObjectInspector::ObjectInspectorPrivate::ObjectInspectorPrivate(QDesignerFormEditorInterface *core) : + m_core(core), + m_treeView(new ObjectInspectorTreeView), + m_model(new ObjectInspectorModel(m_treeView)), + m_findWidget(new ItemViewFindWidget( + ItemViewFindWidget::NarrowLayout | ItemViewFindWidget::NoWholeWords)), + m_withinClearSelection(false) +{ + m_treeView->setModel(m_model); + m_treeView->setItemDelegate(new ObjectInspectorDelegate); + m_treeView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_treeView->header()->setResizeMode(1, QHeaderView::Stretch); + m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_treeView->setAlternatingRowColors(true); + m_treeView->setTextElideMode (Qt::ElideMiddle); + + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); +} + +ObjectInspector::ObjectInspectorPrivate::~ObjectInspectorPrivate() +{ + delete m_treeView->itemDelegate(); +} + +void ObjectInspector::ObjectInspectorPrivate::clearSelection() +{ + m_withinClearSelection = true; + m_treeView->clearSelection(); + m_withinClearSelection = false; +} + +QWidget *ObjectInspector::ObjectInspectorPrivate::managedWidgetAt(const QPoint &global_mouse_pos) +{ + if (!m_formWindow) + return 0; + + const QPoint pos = m_treeView->viewport()->mapFromGlobal(global_mouse_pos); + QObject *o = m_model->objectAt(m_treeView->indexAt(pos)); + + if (!o || !o->isWidgetType()) + return 0; + + QWidget *rc = qobject_cast(o); + if (!m_formWindow->isManaged(rc)) + return 0; + return rc; +} + +void ObjectInspector::ObjectInspectorPrivate::showContainersCurrentPage(QWidget *widget) +{ + if (!widget) + return; + + FormWindow *fw = FormWindow::findFormWindow(widget); + if (!fw) + return; + + QWidget *w = widget->parentWidget(); + bool macroStarted = false; + // Find a multipage container (tab widgets, etc.) in the hierarchy and set the right page. + while (w != 0) { + if (fw->isManaged(w) && !qobject_cast(w)) { // Rule out unmanaged internal scroll areas, for example, on QToolBoxes. + if (QDesignerContainerExtension *c = qt_extension(m_core->extensionManager(), w)) { + const int count = c->count(); + if (count > 1 && !c->widget(c->currentIndex())->isAncestorOf(widget)) { + for (int i = 0; i < count; i++) + if (c->widget(i)->isAncestorOf(widget)) { + if (macroStarted == false) { + macroStarted = true; + fw->beginCommand(tr("Change Current Page")); + } + ChangeCurrentPageCommand *cmd = new ChangeCurrentPageCommand(fw); + cmd->init(w, i); + fw->commandHistory()->push(cmd); + break; + } + } + } + } + w = w->parentWidget(); + } + if (macroStarted == true) + fw->endCommand(); +} + +void ObjectInspector::ObjectInspectorPrivate::restoreDropHighlighting() +{ + if (m_formFakeDropTarget) { + if (m_formWindow) { + m_formWindow->highlightWidget(m_formFakeDropTarget, QPoint(5, 5), FormWindow::Restore); + } + m_formFakeDropTarget = 0; + } +} + +void ObjectInspector::ObjectInspectorPrivate::handleDragEnterMoveEvent(const QWidget *objectInspectorWidget, QDragMoveEvent * event, bool isDragEnter) +{ + if (!m_formWindow) { + event->ignore(); + return; + } + + const QDesignerMimeData *mimeData = qobject_cast(event->mimeData()); + if (!mimeData) { + event->ignore(); + return; + } + + QWidget *dropTarget = 0; + QPoint fakeDropTargetOffset = QPoint(0, 0); + if (QWidget *managedWidget = managedWidgetAt(objectInspectorWidget->mapToGlobal(event->pos()))) { + fakeDropTargetOffset = dropPointOffset(m_formWindow, managedWidget); + // pretend we drag over the managed widget on the form + const QPoint fakeFormPos = m_formWindow->mapFromGlobal(managedWidget->mapToGlobal(fakeDropTargetOffset)); + const FormWindowBase::WidgetUnderMouseMode wum = mimeData->items().size() == 1 ? FormWindowBase::FindSingleSelectionDropTarget : FormWindowBase::FindMultiSelectionDropTarget; + dropTarget = m_formWindow->widgetUnderMouse(fakeFormPos, wum); + } + + if (m_formFakeDropTarget && dropTarget != m_formFakeDropTarget) + m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Restore); + + m_formFakeDropTarget = dropTarget; + if (m_formFakeDropTarget) + m_formWindow->highlightWidget(m_formFakeDropTarget, fakeDropTargetOffset, FormWindow::Highlight); + + // Do not refuse drag enter even if the area is not droppable + if (isDragEnter || m_formFakeDropTarget) + mimeData->acceptEvent(event); + else + event->ignore(); +} +void ObjectInspector::ObjectInspectorPrivate::dropEvent (QDropEvent * event) +{ + if (!m_formWindow || !m_formFakeDropTarget) { + event->ignore(); + return; + } + + const QDesignerMimeData *mimeData = qobject_cast(event->mimeData()); + if (!mimeData) { + event->ignore(); + return; + } + const QPoint fakeGlobalDropFormPos = m_formFakeDropTarget->mapToGlobal(dropPointOffset(m_formWindow , m_formFakeDropTarget)); + mimeData->moveDecoration(fakeGlobalDropFormPos + mimeData->hotSpot()); + if (!m_formWindow->dropWidgets(mimeData->items(), m_formFakeDropTarget, fakeGlobalDropFormPos)) { + event->ignore(); + return; + } + mimeData->acceptEvent(event); +} + +bool ObjectInspector::ObjectInspectorPrivate::selectObject(QObject *o) +{ + if (!m_core->metaDataBase()->item(o)) + return false; + + typedef QSet ModelIndexSet; + + const QModelIndexList objectIndexes = m_model->indexesOf(o); + if (objectIndexes.empty()) + return false; + + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const ModelIndexSet currentSelectedItems = selectionModel->selectedRows(0).toSet(); + + // Change in selection? + if (!currentSelectedItems.empty() && currentSelectedItems == objectIndexes.toSet()) + return true; + + // do select and update + selectIndexRange(objectIndexes, MakeCurrent); + return true; +} + +void ObjectInspector::ObjectInspectorPrivate::selectIndexRange(const QModelIndexList &indexes, unsigned flags) +{ + if (indexes.empty()) + return; + + QItemSelectionModel::SelectionFlags selectFlags = QItemSelectionModel::Select|QItemSelectionModel::Rows; + if (!(flags & AddToSelection)) + selectFlags |= QItemSelectionModel::Clear; + if (flags & MakeCurrent) + selectFlags |= QItemSelectionModel::Current; + + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const QModelIndexList::const_iterator cend = indexes.constEnd(); + for (QModelIndexList::const_iterator it = indexes.constBegin(); it != cend; ++it) + if (it->column() == 0) { + selectionModel->select(*it, selectFlags); + selectFlags &= ~(QItemSelectionModel::Clear|QItemSelectionModel::Current); + } + if (flags & MakeCurrent) + m_treeView->scrollTo(indexes.front(), QAbstractItemView::EnsureVisible); +} + +void ObjectInspector::ObjectInspectorPrivate::clear() +{ + m_formFakeDropTarget = 0; + m_formWindow = 0; +} + +// Form window cursor is in state 'main container only' +static inline bool mainContainerIsCurrent(const QDesignerFormWindowInterface *fw) +{ + const QDesignerFormWindowCursorInterface *cursor = fw->cursor(); + if (cursor->selectedWidgetCount() > 1) + return false; + const QWidget *current = cursor->current(); + return current == fw || current == fw->mainContainer(); +} + +void ObjectInspector::ObjectInspectorPrivate::setFormWindow(QDesignerFormWindowInterface *fwi) +{ + const bool blocked = m_treeView->selectionModel()->blockSignals(true); + { + UpdateBlocker ub(m_treeView); + setFormWindowBlocked(fwi); + } + + m_treeView->update(); + m_treeView->selectionModel()->blockSignals(blocked); +} + +void ObjectInspector::ObjectInspectorPrivate::setFormWindowBlocked(QDesignerFormWindowInterface *fwi) +{ + FormWindowBase *fw = qobject_cast(fwi); + const bool formWindowChanged = m_formWindow != fw; + + m_formWindow = fw; + + const int oldWidth = m_treeView->columnWidth(0); + const int xoffset = m_treeView->horizontalScrollBar()->value(); + const int yoffset = m_treeView->verticalScrollBar()->value(); + + if (formWindowChanged) + m_formFakeDropTarget = 0; + + switch (m_model->update(m_formWindow)) { + case ObjectInspectorModel::NoForm: + clear(); + return; + case ObjectInspectorModel::Rebuilt: // Complete rebuild: Just apply cursor selection + applyCursorSelection(); + m_treeView->expandAll(); + if (formWindowChanged) { + m_treeView->resizeColumnToContents(0); + } else { + m_treeView->setColumnWidth(0, oldWidth); + m_treeView->horizontalScrollBar()->setValue(xoffset); + m_treeView->verticalScrollBar()->setValue(yoffset); + } + break; + case ObjectInspectorModel::Updated: { + // Same structure (property changed or click on the form) + // We maintain a selection of unmanaged objects + // only if the cursor is in state "mainContainer() == current". + // and we have a non-managed selection. + // Else we take over the cursor selection. + bool applySelection = !mainContainerIsCurrent(m_formWindow); + if (!applySelection) { + const QModelIndexList currentIndexes = m_treeView->selectionModel()->selectedRows(0); + if (currentIndexes.empty()) { + applySelection = true; + } else { + applySelection = selectionType(m_formWindow, m_model->objectAt(currentIndexes.front())) == ManagedWidgetSelection; + } + } + if (applySelection) + applyCursorSelection(); + } + break; + } +} + +// Apply selection of form window cursor to object inspector, set current +void ObjectInspector::ObjectInspectorPrivate::applyCursorSelection() +{ + const QDesignerFormWindowCursorInterface *cursor = m_formWindow->cursor(); + const int count = cursor->selectedWidgetCount(); + if (!count) + return; + + // Set the current widget first which also clears the selection + QWidget *currentWidget = cursor->current(); + if (currentWidget) + selectIndexRange(m_model->indexesOf(currentWidget), MakeCurrent); + else + m_treeView->selectionModel()->clearSelection(); + + for (int i = 0;i < count; i++) { + QWidget *widget = cursor->selectedWidget(i); + if (widget != currentWidget) + selectIndexRange(m_model->indexesOf(widget), AddToSelection); + } +} + +// Synchronize managed widget in the form (select in cursor). Block updates +static int selectInCursor(FormWindowBase *fw, const QObjectVector &objects, bool value) +{ + int rc = 0; + const bool blocked = fw->blockSelectionChanged(true); + const QObjectVector::const_iterator ocend = objects.constEnd(); + for (QObjectVector::const_iterator it = objects.constBegin(); it != ocend; ++it) + if (selectionType(fw, *it) == ManagedWidgetSelection) { + fw->selectWidget(static_cast(*it), value); + rc++; + } + fw->blockSelectionChanged(blocked); + return rc; +} + +void ObjectInspector::ObjectInspectorPrivate::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + if (m_formWindow) { + synchronizeSelection(selected, deselected); + QMetaObject::invokeMethod(m_core->formWindowManager(), "slotUpdateActions"); + } +} + +// Convert indexes to object vectors taking into account that +// some index lists are multicolumn ranges +static inline QObjectVector indexesToObjects(const ObjectInspectorModel *model, const QModelIndexList &indexes) +{ + if (indexes.empty()) + return QObjectVector(); + QObjectVector rc; + rc.reserve(indexes.size()); + const QModelIndexList::const_iterator icend = indexes.constEnd(); + for (QModelIndexList::const_iterator it = indexes.constBegin(); it != icend; ++it) + if (it->column() == 0) + rc.push_back(model->objectAt(*it)); + return rc; +} + +// Check if any managed widgets are selected. If so, iterate over +// selection and deselect all unmanaged objects +bool ObjectInspector::ObjectInspectorPrivate::checkManagedWidgetSelection(const QModelIndexList &rowSelection) +{ + bool isManagedWidgetSelection = false; + QItemSelectionModel *selectionModel = m_treeView->selectionModel(); + const QModelIndexList::const_iterator cscend = rowSelection.constEnd(); + for (QModelIndexList::const_iterator it = rowSelection.constBegin(); it != cscend; ++it) { + QObject *object = m_model->objectAt(*it); + if (selectionType(m_formWindow, object) == ManagedWidgetSelection) { + isManagedWidgetSelection = true; + break; + } + } + + if (!isManagedWidgetSelection) + return false; + // Need to unselect unmanaged ones + const bool blocked = selectionModel->blockSignals(true); + for (QModelIndexList::const_iterator it = rowSelection.constBegin(); it != cscend; ++it) { + QObject *object = m_model->objectAt(*it); + if (selectionType(m_formWindow, object) != ManagedWidgetSelection) + selectionModel->select(*it, QItemSelectionModel::Deselect|QItemSelectionModel::Rows); + } + selectionModel->blockSignals(blocked); + return true; +} + +void ObjectInspector::ObjectInspectorPrivate::synchronizeSelection(const QItemSelection & selectedSelection, const QItemSelection &deselectedSelection) +{ + // Synchronize form window cursor. + const QObjectVector deselected = indexesToObjects(m_model, deselectedSelection.indexes()); + const QObjectVector newlySelected = indexesToObjects(m_model, selectedSelection.indexes()); + + const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0); + + int deselectedManagedWidgetCount = 0; + if (!deselected.empty()) + deselectedManagedWidgetCount = selectInCursor(m_formWindow, deselected, false); + + if (newlySelected.empty()) { // Nothing selected + if (currentSelectedIndexes.empty()) // Do not allow a null-selection, reset to main container + m_formWindow->clearSelection(!m_withinClearSelection); + return; + } + + const int selectManagedWidgetCount = selectInCursor(m_formWindow, newlySelected, true); + // Check consistency: Make sure either managed widgets or unmanaged objects are selected. + // No newly-selected managed widgets: Unless there are ones in the (old) current selection, + // select the unmanaged object + if (selectManagedWidgetCount == 0) { + if (checkManagedWidgetSelection(currentSelectedIndexes)) { + // Managed selection exists, refuse and update if necessary + if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0) + m_formWindow->emitSelectionChanged(); + return; + } + // And now for the unmanaged selection + m_formWindow->clearSelection(false); + QObject *unmanagedObject = newlySelected.front(); + m_core->propertyEditor()->setObject(unmanagedObject); + m_core->propertyEditor()->setEnabled(true); + // open container page if it is a single widget + if (newlySelected.size() == 1 && unmanagedObject->isWidgetType()) + showContainersCurrentPage(static_cast(unmanagedObject)); + return; + } + // Open container page if it is a single widget + if (newlySelected.size() == 1) { + QObject *object = newlySelected.back(); + if (object->isWidgetType()) + showContainersCurrentPage(static_cast(object)); + } + + // A managed widget was newly selected. Make sure there are no unmanaged objects + // in the whole unless just single selection + if (currentSelectedIndexes.size() > selectManagedWidgetCount) + checkManagedWidgetSelection(currentSelectedIndexes); + // Update form + if (deselectedManagedWidgetCount != 0 || selectManagedWidgetCount != 0) + m_formWindow->emitSelectionChanged(); +} + + +void ObjectInspector::ObjectInspectorPrivate::getSelection(Selection &s) const +{ + s.clear(); + + if (!m_formWindow) + return; + + const QModelIndexList currentSelectedIndexes = m_treeView->selectionModel()->selectedRows(0); + if (currentSelectedIndexes.empty()) + return; + + // sort objects + foreach (const QModelIndex &index, currentSelectedIndexes) + if (QObject *object = m_model->objectAt(index)) + switch (selectionType(m_formWindow, object)) { + case NoSelection: + break; + case QObjectSelection: + // It is actually possible to select an action twice if it is in a menu bar + // and in a tool bar. + if (!s.objects.contains(object)) + s.objects.push_back(object); + break; + case UnmanagedWidgetSelection: + s.unmanaged.push_back(qobject_cast(object)); + break; + case ManagedWidgetSelection: + s.managed.push_back(qobject_cast(object)); + break; + } +} + +// Utility to create a task menu +static inline QMenu *createTaskMenu(QObject *object, QDesignerFormWindowInterface *fw) +{ + // 1) Objects + if (!object->isWidgetType()) + return FormWindowBase::createExtensionTaskMenu(fw, object, false); + // 2) Unmanaged widgets + QWidget *w = static_cast(object); + if (!fw->isManaged(w)) + return FormWindowBase::createExtensionTaskMenu(fw, w, false); + // 3) Mananaged widgets + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(fw)) + return fwb->initializePopupMenu(w); + return 0; +} + +void ObjectInspector::ObjectInspectorPrivate::slotPopupContextMenu(QWidget * /*parent*/, const QPoint &pos) +{ + if (m_formWindow == 0 || m_formWindow->currentTool() != 0) + return; + + const QModelIndex index = m_treeView->indexAt (pos); + if (QObject *object = m_model->objectAt(m_treeView->indexAt(pos))) + if (QMenu *menu = createTaskMenu(object, m_formWindow)) { + menu->exec(m_treeView->viewport()->mapToGlobal(pos)); + delete menu; + } +} + +// ------------ ObjectInspector +ObjectInspector::ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) : + QDesignerObjectInspector(parent), + m_impl(new ObjectInspectorPrivate(core)) +{ + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->setMargin(0); + + QTreeView *treeView = m_impl->treeView(); + vbox->addWidget(treeView); + + connect(treeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotPopupContextMenu(QPoint))); + + connect(treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + + connect(treeView->header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(slotHeaderDoubleClicked(int))); + setAcceptDrops(true); + + ItemViewFindWidget *findWidget = m_impl->findWidget(); + vbox->addWidget(findWidget); + + findWidget->setItemView(treeView); + QAction *findAction = new QAction( + ItemViewFindWidget::findIconSet(), + tr("&Find in Text..."), + this); + findAction->setShortcut(QKeySequence::Find); + findAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + addAction(findAction); + connect(findAction, SIGNAL(triggered(bool)), findWidget, SLOT(activate())); +} + +ObjectInspector::~ObjectInspector() +{ + delete m_impl; +} + +QDesignerFormEditorInterface *ObjectInspector::core() const +{ + return m_impl->core(); +} + +void ObjectInspector::slotPopupContextMenu(const QPoint &pos) +{ + m_impl->slotPopupContextMenu(this, pos); +} + +void ObjectInspector::setFormWindow(QDesignerFormWindowInterface *fwi) +{ + m_impl->setFormWindow(fwi); +} + +void ObjectInspector::slotSelectionChanged(const QItemSelection & selected, const QItemSelection &deselected) +{ + m_impl->slotSelectionChanged(selected, deselected); +} + +void ObjectInspector::getSelection(Selection &s) const +{ + m_impl->getSelection(s); +} + +bool ObjectInspector::selectObject(QObject *o) +{ + return m_impl->selectObject(o); +} + +void ObjectInspector::clearSelection() +{ + m_impl->clearSelection(); +} + +void ObjectInspector::slotHeaderDoubleClicked(int column) +{ + m_impl->slotHeaderDoubleClicked(column); +} + +void ObjectInspector::mainContainerChanged() +{ + // Invalidate references to objects kept in items + if (sender() == m_impl->formWindow()) + setFormWindow(0); +} + +void ObjectInspector::dragEnterEvent (QDragEnterEvent * event) +{ + m_impl->handleDragEnterMoveEvent(this, event, true); +} + +void ObjectInspector::dragMoveEvent(QDragMoveEvent * event) +{ + m_impl->handleDragEnterMoveEvent(this, event, false); +} + +void ObjectInspector::dragLeaveEvent(QDragLeaveEvent * /* event*/) +{ + m_impl->restoreDropHighlighting(); +} + +void ObjectInspector::dropEvent (QDropEvent * event) +{ + m_impl->dropEvent(event); + +QT_END_NAMESPACE +} +} +#include diff --git a/src/designer/components/objectinspector/objectinspector.h b/src/designer/components/objectinspector/objectinspector.h new file mode 100644 index 000000000..194108dd4 --- /dev/null +++ b/src/designer/components/objectinspector/objectinspector.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECTINSPECTOR_H +#define OBJECTINSPECTOR_H + +#include "objectinspector_global.h" +#include "qdesigner_objectinspector_p.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QItemSelection; + +namespace qdesigner_internal { + +class QT_OBJECTINSPECTOR_EXPORT ObjectInspector: public QDesignerObjectInspector +{ + Q_OBJECT +public: + explicit ObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent = 0); + virtual ~ObjectInspector(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual void getSelection(Selection &s) const; + virtual bool selectObject(QObject *o); + virtual void clearSelection(); + + void setFormWindow(QDesignerFormWindowInterface *formWindow); + +public slots: + virtual void mainContainerChanged(); + +private slots: + void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void slotPopupContextMenu(const QPoint &pos); + void slotHeaderDoubleClicked(int column); + +protected: + virtual void dragEnterEvent (QDragEnterEvent * event); + virtual void dragMoveEvent(QDragMoveEvent * event); + virtual void dragLeaveEvent(QDragLeaveEvent * event); + virtual void dropEvent (QDropEvent * event); + +private: + class ObjectInspectorPrivate; + ObjectInspectorPrivate *m_impl; +}; + +} // namespace qdesigner_internal + +#endif // OBJECTINSPECTOR_H + +QT_END_NAMESPACE diff --git a/src/designer/components/objectinspector/objectinspector_global.h b/src/designer/components/objectinspector/objectinspector_global.h new file mode 100644 index 000000000..e9b1abb9f --- /dev/null +++ b/src/designer/components/objectinspector/objectinspector_global.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECTINSPECTOR_GLOBAL_H +#define OBJECTINSPECTOR_GLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +#ifdef QT_OBJECTINSPECTOR_LIBRARY +# define QT_OBJECTINSPECTOR_EXPORT +#else +# define QT_OBJECTINSPECTOR_EXPORT +#endif +#else +#define QT_OBJECTINSPECTOR_EXPORT +#endif + +#endif // OBJECTINSPECTOR_GLOBAL_H + +QT_END_NAMESPACE diff --git a/src/designer/components/objectinspector/objectinspectormodel.cpp b/src/designer/components/objectinspector/objectinspectormodel.cpp new file mode 100644 index 000000000..80dc048f8 --- /dev/null +++ b/src/designer/components/objectinspector/objectinspectormodel.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "objectinspectormodel_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 { DataRole = 1000 }; +} + +static inline QObject *objectOfItem(const QStandardItem *item) { + return qvariant_cast(item->data(DataRole)); +} + +static bool sortEntry(const QObject *a, const QObject *b) +{ + return a->objectName() < b->objectName(); +} + +static bool sameIcon(const QIcon &i1, const QIcon &i2) +{ + if (i1.isNull() && i2.isNull()) + return true; + if (i1.isNull() != i2.isNull()) + return false; + return i1.cacheKey() == i2.cacheKey(); +} + +static inline bool isNameColumnEditable(const QObject *) +{ + return true; +} + +static qdesigner_internal::ObjectData::StandardItemList createModelRow(const QObject *o) +{ + qdesigner_internal::ObjectData::StandardItemList rc; + const Qt::ItemFlags baseFlags = Qt::ItemIsSelectable|Qt::ItemIsDropEnabled|Qt::ItemIsEnabled; + for (int i = 0; i < qdesigner_internal::ObjectInspectorModel::NumColumns; i++) { + QStandardItem *item = new QStandardItem; + Qt::ItemFlags flags = baseFlags; + if (i == qdesigner_internal::ObjectInspectorModel::ObjectNameColumn && isNameColumnEditable(o)) + flags |= Qt::ItemIsEditable; + item->setFlags(flags); + rc += item; + } + return rc; +} + +static inline bool isQLayoutWidget(const QObject *o) +{ + return o->metaObject() == &QLayoutWidget::staticMetaObject; +} + +namespace qdesigner_internal { + + // context kept while building a model, just there to reduce string allocations + struct ModelRecursionContext { + explicit ModelRecursionContext(QDesignerFormEditorInterface *core, const QString &sepName); + + const QString designerPrefix; + const QString separator; + + QDesignerFormEditorInterface *core; + const QDesignerWidgetDataBaseInterface *db; + const QDesignerMetaDataBaseInterface *mdb; + }; + + ModelRecursionContext::ModelRecursionContext(QDesignerFormEditorInterface *c, const QString &sepName) : + designerPrefix(QLatin1String("QDesigner")), + separator(sepName), + core(c), + db(c->widgetDataBase()), + mdb(c->metaDataBase()) + { + } + + // ------------ ObjectData/ ObjectModel: + // Whenever the selection changes, ObjectInspector::setFormWindow is + // called. To avoid rebuilding the tree every time (loosing expanded state) + // a model is first built from the object tree by recursion. + // As a tree is difficult to represent, a flat list of entries (ObjectData) + // containing object and parent object is used. + // ObjectData has an overloaded operator== that compares the object pointers. + // Structural changes which cause a rebuild can be detected by + // comparing the lists of ObjectData. If it is the same, only the item data (class name [changed by promotion], + // object name and icon) are checked and the existing items are updated. + + ObjectData::ObjectData() : + m_parent(0), + m_object(0), + m_type(Object), + m_managedLayoutType(LayoutInfo::NoLayout) + { + } + + ObjectData::ObjectData(QObject *parent, QObject *object, const ModelRecursionContext &ctx) : + m_parent(parent), + m_object(object), + m_type(Object), + m_className(QLatin1String(object->metaObject()->className())), + m_objectName(object->objectName()), + m_managedLayoutType(LayoutInfo::NoLayout) + { + + // 1) set entry + if (object->isWidgetType()) { + initWidget(static_cast(object), ctx); + } else { + initObject(ctx); + } + if (m_className.startsWith(ctx.designerPrefix)) + m_className.remove(1, ctx.designerPrefix.size() - 1); + } + + void ObjectData::initObject(const ModelRecursionContext &ctx) + { + // Check objects: Action? + if (const QAction *act = qobject_cast(m_object)) { + if (act->isSeparator()) { // separator is reserved + m_objectName = ctx.separator; + m_type = SeparatorAction; + } else { + m_type = Action; + } + m_classIcon = act->icon(); + } else { + m_type = Object; + } + } + + void ObjectData::initWidget(QWidget *w, const ModelRecursionContext &ctx) + { + // Check for extension container, QLayoutwidget, or normal container + bool isContainer = false; + if (const QDesignerWidgetDataBaseItemInterface *widgetItem = ctx.db->item(ctx.db->indexOfObject(w, true))) { + m_classIcon = widgetItem->icon(); + m_className = widgetItem->name(); + isContainer = widgetItem->isContainer(); + } + + // We might encounter temporary states with no layouts when re-layouting. + // Just default to Widget handling for the moment. + if (isQLayoutWidget(w)) { + if (const QLayout *layout = w->layout()) { + m_type = LayoutWidget; + m_managedLayoutType = LayoutInfo::layoutType(ctx.core, layout); + m_className = QLatin1String(layout->metaObject()->className()); + m_objectName = layout->objectName(); + } + return; + } + + if (qt_extension(ctx.core->extensionManager(), w)) { + m_type = ExtensionContainer; + return; + } + if (isContainer) { + m_type = LayoutableContainer; + m_managedLayoutType = LayoutInfo::managedLayoutType(ctx.core, w); + return; + } + m_type = ChildWidget; + } + + bool ObjectData::equals(const ObjectData & me) const + { + return m_parent == me.m_parent && m_object == me.m_object; + } + + unsigned ObjectData::compare(const ObjectData & rhs) const + { + unsigned rc = 0; + if (m_className != rhs.m_className) + rc |= ClassNameChanged; + if (m_objectName != rhs.m_objectName) + rc |= ObjectNameChanged; + if (!sameIcon(m_classIcon, rhs.m_classIcon)) + rc |= ClassIconChanged; + if (m_type != rhs.m_type) + rc |= TypeChanged; + if (m_managedLayoutType != rhs.m_managedLayoutType) + rc |= LayoutTypeChanged; + return rc; + } + + void ObjectData::setItemsDisplayData(const StandardItemList &row, const ObjectInspectorIcons &icons, unsigned mask) const + { + if (mask & ObjectNameChanged) + row[ObjectInspectorModel::ObjectNameColumn]->setText(m_objectName); + if (mask & ClassNameChanged) { + row[ObjectInspectorModel::ClassNameColumn]->setText(m_className); + row[ObjectInspectorModel::ClassNameColumn]->setToolTip(m_className); + } + // Set a layout icon only for containers. Note that QLayoutWidget don't have + // real class icons + if (mask & (ClassIconChanged|TypeChanged|LayoutTypeChanged)) { + switch (m_type) { + case LayoutWidget: + row[ObjectInspectorModel::ObjectNameColumn]->setIcon(icons.layoutIcons[m_managedLayoutType]); + row[ObjectInspectorModel::ClassNameColumn]->setIcon(icons.layoutIcons[m_managedLayoutType]); + break; + case LayoutableContainer: + row[ObjectInspectorModel::ObjectNameColumn]->setIcon(icons.layoutIcons[m_managedLayoutType]); + row[ObjectInspectorModel::ClassNameColumn]->setIcon(m_classIcon); + break; + default: + row[ObjectInspectorModel::ObjectNameColumn]->setIcon(QIcon()); + row[ObjectInspectorModel::ClassNameColumn]->setIcon(m_classIcon); + break; + } + } + } + + void ObjectData::setItems(const StandardItemList &row, const ObjectInspectorIcons &icons) const + { + const QVariant object = QVariant::fromValue(m_object); + row[ObjectInspectorModel::ObjectNameColumn]->setData(object, DataRole); + row[ObjectInspectorModel::ClassNameColumn]->setData(object, DataRole); + setItemsDisplayData(row, icons, ClassNameChanged|ObjectNameChanged|ClassIconChanged|TypeChanged|LayoutTypeChanged); + } + + typedef QList ObjectModel; + + // Recursive routine that creates the model by traversing the form window object tree. + void createModelRecursion(const QDesignerFormWindowInterface *fwi, + QObject *parent, + QObject *object, + ObjectModel &model, + const ModelRecursionContext &ctx) + { + typedef QList ButtonGroupList; + typedef QList ActionList; + + // 1) Create entry + const ObjectData entry(parent, object, ctx); + model.push_back(entry); + + // 2) recurse over widget children via container extension or children list + const QDesignerContainerExtension *containerExtension = 0; + if (entry.type() == ObjectData::ExtensionContainer) { + containerExtension = qt_extension(fwi->core()->extensionManager(), object); + Q_ASSERT(containerExtension); + const int count = containerExtension->count(); + for (int i=0; i < count; ++i) { + QObject *page = containerExtension->widget(i); + Q_ASSERT(page != 0); + createModelRecursion(fwi, object, page, model, ctx); + } + } + + QObjectList children = object->children(); + if (!children.empty()) { + ButtonGroupList buttonGroups; + qSort(children.begin(), children.end(), sortEntry); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + // Managed child widgets unless we had a container extension + if ((*it)->isWidgetType()) { + if (!containerExtension) { + QWidget *widget = qobject_cast(*it); + if (fwi->isManaged(widget)) + createModelRecursion(fwi, object, widget, model, ctx); + } + } else { + if (ctx.mdb->item(*it)) { + if (QButtonGroup *bg = qobject_cast(*it)) + buttonGroups.push_back(bg); + } // Has MetaDataBase entry + } + } + // Add button groups + if (!buttonGroups.empty()) { + const ButtonGroupList::const_iterator bgcend = buttonGroups.constEnd(); + for (ButtonGroupList::const_iterator bgit = buttonGroups.constBegin(); bgit != bgcend; ++bgit) + createModelRecursion(fwi, object, *bgit, model, ctx); + } + } // has children + if (object->isWidgetType()) { + // Add actions + const ActionList actions = static_cast(object)->actions(); + if (!actions.empty()) { + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) + if (ctx.mdb->item(*it)) { + QAction *action = *it; + QObject *obj = action; + if (action->menu()) + obj = action->menu(); + createModelRecursion(fwi, object, obj, model, ctx); + } + } + } + } + + // ------------ ObjectInspectorModel + ObjectInspectorModel::ObjectInspectorModel(QObject *parent) : + QStandardItemModel(0, NumColumns, parent) + { + QStringList headers; + headers += QCoreApplication::translate("ObjectInspectorModel", "Object"); + headers += QCoreApplication::translate("ObjectInspectorModel", "Class"); + Q_ASSERT(headers.size() == NumColumns); + setColumnCount(NumColumns); + setHorizontalHeaderLabels(headers); + // Icons + m_icons.layoutIcons[LayoutInfo::NoLayout] = createIconSet(QLatin1String("editbreaklayout.png")); + m_icons.layoutIcons[LayoutInfo::HSplitter] = createIconSet(QLatin1String("edithlayoutsplit.png")); + m_icons.layoutIcons[LayoutInfo::VSplitter] = createIconSet(QLatin1String("editvlayoutsplit.png")); + m_icons.layoutIcons[LayoutInfo::HBox] = createIconSet(QLatin1String("edithlayout.png")); + m_icons.layoutIcons[LayoutInfo::VBox] = createIconSet(QLatin1String("editvlayout.png")); + m_icons.layoutIcons[LayoutInfo::Grid] = createIconSet(QLatin1String("editgrid.png")); + m_icons.layoutIcons[LayoutInfo::Form] = createIconSet(QLatin1String("editform.png")); + } + + void ObjectInspectorModel::clearItems() + { + m_objectIndexMultiMap.clear(); + m_model.clear(); + reset(); // force editors to be closed in views + removeRow(0); + } + + ObjectInspectorModel::UpdateResult ObjectInspectorModel::update(QDesignerFormWindowInterface *fw) + { + QWidget *mainContainer = fw ? fw->mainContainer() : static_cast(0); + if (!mainContainer) { + clearItems(); + m_formWindow = 0; + return NoForm; + } + m_formWindow = fw; + // Build new model and compare to previous one. If the structure is + // identical, just update, else rebuild + ObjectModel newModel; + + static const QString separator = QCoreApplication::translate("ObjectInspectorModel", "separator"); + const ModelRecursionContext ctx(fw->core(), separator); + createModelRecursion(fw, 0, mainContainer, newModel, ctx); + + if (newModel == m_model) { + updateItemContents(m_model, newModel); + return Updated; + } + + rebuild(newModel); + m_model = newModel; + return Rebuilt; + } + + QObject *ObjectInspectorModel::objectAt(const QModelIndex &index) const + { + if (index.isValid()) + if (const QStandardItem *item = itemFromIndex(index)) + return objectOfItem(item); + return 0; + } + + // Missing Qt API: get a row + ObjectInspectorModel::StandardItemList ObjectInspectorModel::rowAt(QModelIndex index) const + { + StandardItemList rc; + while (true) { + rc += itemFromIndex(index); + const int nextColumn = index.column() + 1; + if (nextColumn >= NumColumns) + break; + index = index.sibling(index.row(), nextColumn); + } + return rc; + } + + // Rebuild the tree in case the model has completely changed. + void ObjectInspectorModel::rebuild(const ObjectModel &newModel) + { + clearItems(); + if (newModel.empty()) + return; + + const ObjectModel::const_iterator mcend = newModel.constEnd(); + ObjectModel::const_iterator it = newModel.constBegin(); + // Set up root element + StandardItemList rootRow = createModelRow(it->object()); + it->setItems(rootRow, m_icons); + appendRow(rootRow); + m_objectIndexMultiMap.insert(it->object(), indexFromItem(rootRow.front())); + for (++it; it != mcend; ++it) { + // Add to parent item, found via map + const QModelIndex parentIndex = m_objectIndexMultiMap.value(it->parent(), QModelIndex()); + Q_ASSERT(parentIndex.isValid()); + QStandardItem *parentItem = itemFromIndex(parentIndex); + StandardItemList row = createModelRow(it->object()); + it->setItems(row, m_icons); + parentItem->appendRow(row); + m_objectIndexMultiMap.insert(it->object(), indexFromItem(row.front())); + } + } + + // Update item data in case the model has the same structure + void ObjectInspectorModel::updateItemContents(ObjectModel &oldModel, const ObjectModel &newModel) + { + // Change text and icon. Keep a set of changed object + // as for example actions might occur several times in the tree. + typedef QSet QObjectSet; + + QObjectSet changedObjects; + + const int size = newModel.size(); + Q_ASSERT(oldModel.size() == size); + for (int i = 0; i < size; i++) { + const ObjectData &newEntry = newModel[i]; + ObjectData &entry = oldModel[i]; + // Has some data changed? + if (const unsigned changedMask = entry.compare(newEntry)) { + entry = newEntry; + QObject * o = entry.object(); + if (!changedObjects.contains(o)) { + changedObjects.insert(o); + const QModelIndexList indexes = m_objectIndexMultiMap.values(o); + foreach (const QModelIndex &index, indexes) + entry.setItemsDisplayData(rowAt(index), m_icons, changedMask); + } + } + } + } + + QVariant ObjectInspectorModel::data(const QModelIndex &index, int role) const + { + const QVariant rc = QStandardItemModel::data(index, role); + // Return if the string is empty for the display role + // only (else, editing starts with ). + if (role == Qt::DisplayRole && rc.type() == QVariant::String) { + const QString s = rc.toString(); + if (s.isEmpty()) { + static const QString noName = QCoreApplication::translate("ObjectInspectorModel", ""); + return QVariant(noName); + } + } + return rc; + } + + bool ObjectInspectorModel::setData(const QModelIndex &index, const QVariant &value, int role) + { + if (role != Qt::EditRole || !m_formWindow) + return false; + + QObject *object = objectAt(index); + if (!object) + return false; + // Is this a layout widget? + const QString nameProperty = isQLayoutWidget(object) ? QLatin1String("layoutName") : QLatin1String("objectName"); + m_formWindow->commandHistory()->push(createTextPropertyCommand(nameProperty, value.toString(), object, m_formWindow)); + return true; + } +} + +QT_END_NAMESPACE diff --git a/src/designer/components/objectinspector/objectinspectormodel_p.h b/src/designer/components/objectinspector/objectinspectormodel_p.h new file mode 100644 index 000000000..9e2be1cc8 --- /dev/null +++ b/src/designer/components/objectinspector/objectinspectormodel_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 OBJECTINSPECTORMODEL_H +#define OBJECTINSPECTORMODEL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + + // Data structure containing the fixed item type icons + struct ObjectInspectorIcons { + QIcon layoutIcons[LayoutInfo::UnknownLayout + 1]; + }; + + struct ModelRecursionContext; + + // Data structure representing one item of the object inspector. + class ObjectData { + public: + enum Type { + Object, + Action, + SeparatorAction, + ChildWidget, // A child widget + LayoutableContainer, // A container that can be laid out + LayoutWidget, // A QLayoutWidget + ExtensionContainer // QTabWidget and the like, container extension + }; + + typedef QList StandardItemList; + + explicit ObjectData(QObject *parent, QObject *object, const ModelRecursionContext &ctx); + ObjectData(); + + inline Type type() const { return m_type; } + inline QObject *object() const { return m_object; } + inline QObject *parent() const { return m_parent; } + inline QString objectName() const { return m_objectName; } + + bool equals(const ObjectData & me) const; + + enum ChangedMask { ClassNameChanged = 1, ObjectNameChanged = 2, + ClassIconChanged = 4, TypeChanged = 8, + LayoutTypeChanged = 16}; + + unsigned compare(const ObjectData & me) const; + + // Initially set up a row + void setItems(const StandardItemList &row, const ObjectInspectorIcons &icons) const; + // Update row data according to change mask + void setItemsDisplayData(const StandardItemList &row, const ObjectInspectorIcons &icons, unsigned mask) const; + + private: + void initObject(const ModelRecursionContext &ctx); + void initWidget(QWidget *w, const ModelRecursionContext &ctx); + + QObject *m_parent; + QObject *m_object; + Type m_type; + QString m_className; + QString m_objectName; + QIcon m_classIcon; + LayoutInfo::Type m_managedLayoutType; + }; + + inline bool operator==(const ObjectData &e1, const ObjectData &e2) { return e1.equals(e2); } + inline bool operator!=(const ObjectData &e1, const ObjectData &e2) { return !e1.equals(e2); } + + typedef QList ObjectModel; + + // QStandardItemModel for ObjectInspector. Uses ObjectData/ObjectModel + // internally for its updates. + class ObjectInspectorModel : public QStandardItemModel { + public: + typedef QList StandardItemList; + enum { ObjectNameColumn, ClassNameColumn, NumColumns }; + + explicit ObjectInspectorModel(QObject *parent); + + enum UpdateResult { NoForm, Rebuilt, Updated }; + UpdateResult update(QDesignerFormWindowInterface *fw); + + const QModelIndexList indexesOf(QObject *o) const { return m_objectIndexMultiMap.values(o); } + QObject *objectAt(const QModelIndex &index) const; + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + private: + typedef QMultiMap ObjectIndexMultiMap; + + void rebuild(const ObjectModel &newModel); + void updateItemContents(ObjectModel &oldModel, const ObjectModel &newModel); + void clearItems(); + StandardItemList rowAt(QModelIndex index) const; + + ObjectInspectorIcons m_icons; + ObjectIndexMultiMap m_objectIndexMultiMap; + ObjectModel m_model; + QPointer m_formWindow; + }; +} // namespace qdesigner_internal + +#endif // OBJECTINSPECTORMODEL_H + +QT_END_NAMESPACE diff --git a/src/designer/components/propertyeditor/brushpropertymanager.cpp b/src/designer/components/propertyeditor/brushpropertymanager.cpp new file mode 100644 index 000000000..8abd9aa24 --- /dev/null +++ b/src/designer/components/propertyeditor/brushpropertymanager.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "brushpropertymanager.h" +#include "qtpropertymanager.h" +#include "qtvariantproperty.h" +#include "qtpropertybrowserutils_p.h" + +#include +#include +#include + +static const char *brushStyles[] = { +QT_TRANSLATE_NOOP("BrushPropertyManager", "No brush"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Solid"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 1"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 2"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 3"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 4"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 5"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 6"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Dense 7"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Horizontal"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Vertical"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Cross"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Backward diagonal"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Forward diagonal"), +QT_TRANSLATE_NOOP("BrushPropertyManager", "Crossing diagonal"), +}; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +BrushPropertyManager::BrushPropertyManager() +{ +} + +int BrushPropertyManager::brushStyleToIndex(Qt::BrushStyle st) +{ + switch (st) { + case Qt::NoBrush: return 0; + case Qt::SolidPattern: return 1; + case Qt::Dense1Pattern: return 2; + case Qt::Dense2Pattern: return 3; + case Qt::Dense3Pattern: return 4; + case Qt::Dense4Pattern: return 5; + case Qt::Dense5Pattern: return 6; + case Qt::Dense6Pattern: return 7; + case Qt::Dense7Pattern: return 8; + case Qt::HorPattern: return 9; + case Qt::VerPattern: return 10; + case Qt::CrossPattern: return 11; + case Qt::BDiagPattern: return 12; + case Qt::FDiagPattern: return 13; + case Qt::DiagCrossPattern: return 14; + default: break; + } + return 0; +} + +Qt::BrushStyle BrushPropertyManager::brushStyleIndexToStyle(int brushStyleIndex) +{ + switch (brushStyleIndex) { + case 0: return Qt::NoBrush; + case 1: return Qt::SolidPattern; + case 2: return Qt::Dense1Pattern; + case 3: return Qt::Dense2Pattern; + case 4: return Qt::Dense3Pattern; + case 5: return Qt::Dense4Pattern; + case 6: return Qt::Dense5Pattern; + case 7: return Qt::Dense6Pattern; + case 8: return Qt::Dense7Pattern; + case 9: return Qt::HorPattern; + case 10: return Qt::VerPattern; + case 11: return Qt::CrossPattern; + case 12: return Qt::BDiagPattern; + case 13: return Qt::FDiagPattern; + case 14: return Qt::DiagCrossPattern; + } + return Qt::NoBrush; +} + + +typedef QMap EnumIndexIconMap; + +static void clearBrushIcons(); +Q_GLOBAL_STATIC_WITH_INITIALIZER(EnumIndexIconMap, brushIcons, qAddPostRoutine(clearBrushIcons)) + +static void clearBrushIcons() +{ + brushIcons()->clear(); +} + +const BrushPropertyManager::EnumIndexIconMap &BrushPropertyManager::brushStyleIcons() +{ + // Create a map of icons for the brush style editor + if (brushIcons()->empty()) { + const int brushStyleCount = sizeof(brushStyles)/sizeof(const char *); + QBrush brush(Qt::black); + const QIcon solidIcon = QtPropertyBrowserUtils::brushValueIcon(brush); + for (int i = 0; i < brushStyleCount; i++) { + const Qt::BrushStyle style = brushStyleIndexToStyle(i); + brush.setStyle(style); + brushIcons()->insert(i, QtPropertyBrowserUtils::brushValueIcon(brush)); + } + } + return *(brushIcons()); +} + +QString BrushPropertyManager::brushStyleIndexToString(int brushStyleIndex) +{ + const int brushStyleCount = sizeof(brushStyles)/sizeof(const char *); + return brushStyleIndex < brushStyleCount ? QCoreApplication::translate("BrushPropertyManager", brushStyles[brushStyleIndex]) : QString(); +} + +void BrushPropertyManager::initializeProperty(QtVariantPropertyManager *vm, QtProperty *property, int enumTypeId) +{ + m_brushValues.insert(property, QBrush()); + // style + QtVariantProperty *styleSubProperty = vm->addProperty(enumTypeId, QCoreApplication::translate("BrushPropertyManager", "Style")); + property->addSubProperty(styleSubProperty); + QStringList styles; + const int brushStyleCount = sizeof(brushStyles)/sizeof(const char *); + for (int i = 0; i < brushStyleCount; i++) + styles.push_back(QCoreApplication::translate("BrushPropertyManager", brushStyles[i])); + styleSubProperty->setAttribute(QLatin1String("enumNames"), styles); + styleSubProperty->setAttribute(QLatin1String("enumIcons"), QVariant::fromValue(brushStyleIcons())); + m_brushPropertyToStyleSubProperty.insert(property, styleSubProperty); + m_brushStyleSubPropertyToProperty.insert(styleSubProperty, property); + // color + QtVariantProperty *colorSubProperty = vm->addProperty(QVariant::Color, QCoreApplication::translate("BrushPropertyManager", "Color")); + property->addSubProperty(colorSubProperty); + m_brushPropertyToColorSubProperty.insert(property, colorSubProperty); + m_brushColorSubPropertyToProperty.insert(colorSubProperty, property); +} + +bool BrushPropertyManager::uninitializeProperty(QtProperty *property) +{ + const PropertyBrushMap::iterator brit = m_brushValues.find(property); // Brushes + if (brit == m_brushValues.end()) + return false; + m_brushValues.erase(brit); + // style + PropertyToPropertyMap::iterator subit = m_brushPropertyToStyleSubProperty.find(property); + if (subit != m_brushPropertyToStyleSubProperty.end()) { + QtProperty *styleProp = subit.value(); + m_brushStyleSubPropertyToProperty.remove(styleProp); + m_brushPropertyToStyleSubProperty.erase(subit); + delete styleProp; + } + // color + subit = m_brushPropertyToColorSubProperty.find(property); + if (subit != m_brushPropertyToColorSubProperty.end()) { + QtProperty *colorProp = subit.value(); + m_brushColorSubPropertyToProperty.remove(colorProp); + m_brushPropertyToColorSubProperty.erase(subit); + delete colorProp; + } + return true; +} + +void BrushPropertyManager::slotPropertyDestroyed(QtProperty *property) +{ + PropertyToPropertyMap::iterator subit = m_brushStyleSubPropertyToProperty.find(property); + if (subit != m_brushStyleSubPropertyToProperty.end()) { + m_brushPropertyToStyleSubProperty[subit.value()] = 0; + m_brushStyleSubPropertyToProperty.erase(subit); + } + subit = m_brushColorSubPropertyToProperty.find(property); + if (subit != m_brushColorSubPropertyToProperty.end()) { + m_brushPropertyToColorSubProperty[subit.value()] = 0; + m_brushColorSubPropertyToProperty.erase(subit); + } +} + + +BrushPropertyManager::ValueChangedResult BrushPropertyManager::valueChanged(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value) +{ + switch (value.type()) { + case QVariant::Int: // Style subproperty? + if (QtProperty *brushProperty = m_brushStyleSubPropertyToProperty.value(property, 0)) { + const QBrush oldValue = m_brushValues.value(brushProperty); + QBrush newBrush = oldValue; + const int index = value.toInt(); + newBrush.setStyle(brushStyleIndexToStyle(index)); + if (newBrush == oldValue) + return Unchanged; + vm->variantProperty(brushProperty)->setValue(newBrush); + return Changed; + } + break; + case QVariant::Color: // Color subproperty? + if (QtProperty *brushProperty = m_brushColorSubPropertyToProperty.value(property, 0)) { + const QBrush oldValue = m_brushValues.value(brushProperty); + QBrush newBrush = oldValue; + newBrush.setColor(qvariant_cast(value)); + if (newBrush == oldValue) + return Unchanged; + vm->variantProperty(brushProperty)->setValue(newBrush); + return Changed; + } + break; + default: + break; + } + return NoMatch; +} + +BrushPropertyManager::ValueChangedResult BrushPropertyManager::setValue(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value) +{ + if (value.type() != QVariant::Brush) + return NoMatch; + const PropertyBrushMap::iterator brit = m_brushValues.find(property); + if (brit == m_brushValues.end()) + return NoMatch; + + const QBrush newBrush = qvariant_cast(value); + if (newBrush == brit.value()) + return Unchanged; + brit.value() = newBrush; + if (QtProperty *styleProperty = m_brushPropertyToStyleSubProperty.value(property)) + vm->variantProperty(styleProperty)->setValue(brushStyleToIndex(newBrush.style())); + if (QtProperty *colorProperty = m_brushPropertyToColorSubProperty.value(property)) + vm->variantProperty(colorProperty)->setValue(newBrush.color()); + + return Changed; +} + +bool BrushPropertyManager::valueText(const QtProperty *property, QString *text) const +{ + const PropertyBrushMap::const_iterator brit = m_brushValues.constFind(const_cast(property)); + if (brit == m_brushValues.constEnd()) + return false; + const QBrush &brush = brit.value(); + const QString styleName = brushStyleIndexToString(brushStyleToIndex(brush.style())); + *text = QCoreApplication::translate("BrushPropertyManager", "[%1, %2]").arg(styleName).arg(QtPropertyBrowserUtils::colorValueText(brush.color())); + return true; +} + +bool BrushPropertyManager::valueIcon(const QtProperty *property, QIcon *icon) const +{ + const PropertyBrushMap::const_iterator brit = m_brushValues.constFind(const_cast(property)); + if (brit == m_brushValues.constEnd()) + return false; + *icon = QtPropertyBrowserUtils::brushValueIcon(brit.value()); + return true; +} + +bool BrushPropertyManager::value(const QtProperty *property, QVariant *v) const +{ + const PropertyBrushMap::const_iterator brit = m_brushValues.constFind(const_cast(property)); + if (brit == m_brushValues.constEnd()) + return false; + qVariantSetValue(*v, brit.value()); + return true; +} +} + +QT_END_NAMESPACE diff --git a/src/designer/components/propertyeditor/brushpropertymanager.h b/src/designer/components/propertyeditor/brushpropertymanager.h new file mode 100644 index 000000000..b8980f584 --- /dev/null +++ b/src/designer/components/propertyeditor/brushpropertymanager.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BRUSHPROPERTYMANAGER_H +#define BRUSHPROPERTYMANAGER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QtProperty; +class QtVariantPropertyManager; + +class QString; +class QVariant; + +namespace qdesigner_internal { + +// BrushPropertyManager: A mixin for DesignerPropertyManager that manages brush properties. + +class BrushPropertyManager { + BrushPropertyManager(const BrushPropertyManager&); + BrushPropertyManager &operator=(const BrushPropertyManager&); + +public: + BrushPropertyManager(); + + void initializeProperty(QtVariantPropertyManager *vm, QtProperty *property, int enumTypeId); + bool uninitializeProperty(QtProperty *property); + + // Call from slotValueChanged(). + enum ValueChangedResult { NoMatch, Unchanged, Changed }; + ValueChangedResult valueChanged(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value); + ValueChangedResult setValue(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value); + + bool valueText(const QtProperty *property, QString *text) const; + bool valueIcon(const QtProperty *property, QIcon *icon) const; + bool value(const QtProperty *property, QVariant *v) const; + + // Call from QtPropertyManager's propertyDestroyed signal + void slotPropertyDestroyed(QtProperty *property); + +private: + static int brushStyleToIndex(Qt::BrushStyle st); + static Qt::BrushStyle brushStyleIndexToStyle(int brushStyleIndex); + static QString brushStyleIndexToString(int brushStyleIndex); + + typedef QMap EnumIndexIconMap; + static const EnumIndexIconMap &brushStyleIcons(); + + typedef QMap PropertyToPropertyMap; + PropertyToPropertyMap m_brushPropertyToStyleSubProperty; + PropertyToPropertyMap m_brushPropertyToColorSubProperty; + PropertyToPropertyMap m_brushStyleSubPropertyToProperty; + PropertyToPropertyMap m_brushColorSubPropertyToProperty; + + typedef QMap PropertyBrushMap; + PropertyBrushMap m_brushValues; +}; + +} + +QT_END_NAMESPACE + +#endif // BRUSHPROPERTYMANAGER_H diff --git a/src/designer/components/propertyeditor/designerpropertymanager.cpp b/src/designer/components/propertyeditor/designerpropertymanager.cpp new file mode 100644 index 000000000..48bb1dbd4 --- /dev/null +++ b/src/designer/components/propertyeditor/designerpropertymanager.cpp @@ -0,0 +1,2837 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "designerpropertymanager.h" +#include "qtpropertymanager.h" +#include "paletteeditorbutton.h" +#include "qlonglongvalidator.h" +#include "stringlisteditorbutton.h" +#include "qtresourceview_p.h" +#include "qtpropertybrowserutils_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 + +QT_BEGIN_NAMESPACE + +static const char *resettableAttributeC = "resettable"; +static const char *flagsAttributeC = "flags"; +static const char *validationModesAttributeC = "validationMode"; +static const char *superPaletteAttributeC = "superPalette"; +static const char *defaultResourceAttributeC = "defaultResource"; +static const char *fontAttributeC = "font"; +static const char *themeAttributeC = "theme"; + +class DesignerFlagPropertyType +{ +}; + + +class DesignerAlignmentPropertyType +{ +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(DesignerFlagPropertyType) +Q_DECLARE_METATYPE(DesignerAlignmentPropertyType) + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ------------ TextEditor +class TextEditor : public QWidget +{ + Q_OBJECT +public: + TextEditor(QDesignerFormEditorInterface *core, QWidget *parent); + + TextPropertyValidationMode textPropertyValidationMode() const; + void setTextPropertyValidationMode(TextPropertyValidationMode vm); + + void setRichTextDefaultFont(const QFont &font) { m_richTextDefaultFont = font; } + QFont richTextDefaultFont() const { return m_richTextDefaultFont; } + + void setSpacing(int spacing); + + TextPropertyEditor::UpdateMode updateMode() const { return m_editor->updateMode(); } + void setUpdateMode(TextPropertyEditor::UpdateMode um) { m_editor->setUpdateMode(um); } + + void setIconThemeModeEnabled(bool enable); + +public slots: + void setText(const QString &text); + +signals: + void textChanged(const QString &text); + +private slots: + void buttonClicked(); + void resourceActionActivated(); + void fileActionActivated(); +private: + TextPropertyEditor *m_editor; + IconThemeEditor *m_themeEditor; + bool m_iconThemeModeEnabled; + QFont m_richTextDefaultFont; + QToolButton *m_button; + QMenu *m_menu; + QAction *m_resourceAction; + QAction *m_fileAction; + QHBoxLayout *m_layout; + QDesignerFormEditorInterface *m_core; +}; + +TextEditor::TextEditor(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + m_editor(new TextPropertyEditor(this)), + m_themeEditor(new IconThemeEditor(this, false)), + m_iconThemeModeEnabled(false), + m_richTextDefaultFont(QApplication::font()), + m_button(new QToolButton(this)), + m_menu(new QMenu(this)), + m_resourceAction(new QAction(tr("Choose Resource..."), this)), + m_fileAction(new QAction(tr("Choose File..."), this)), + m_layout(new QHBoxLayout(this)), + m_core(core) +{ + m_themeEditor->setVisible(false); + m_button->setVisible(false); + + m_layout->addWidget(m_editor); + m_layout->addWidget(m_themeEditor); + m_button->setText(tr("...")); + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(20); + m_layout->addWidget(m_button); + m_layout->setMargin(0); + m_layout->setSpacing(0); + + connect(m_resourceAction, SIGNAL(triggered()), this, SLOT(resourceActionActivated())); + connect(m_fileAction, SIGNAL(triggered()), this, SLOT(fileActionActivated())); + connect(m_editor, SIGNAL(textChanged(QString)), this, SIGNAL(textChanged(QString))); + connect(m_themeEditor, SIGNAL(edited(QString)), this, SIGNAL(textChanged(QString))); + connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); + + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + setFocusProxy(m_editor); + + m_menu->addAction(m_resourceAction); + m_menu->addAction(m_fileAction); +} + +void TextEditor::setSpacing(int spacing) +{ + m_layout->setSpacing(spacing); +} + +void TextEditor::setIconThemeModeEnabled(bool enable) +{ + if (m_iconThemeModeEnabled == enable) + return; // nothing changes + m_iconThemeModeEnabled = enable; + m_editor->setVisible(!enable); + m_themeEditor->setVisible(enable); + if (enable) { + m_themeEditor->setTheme(m_editor->text()); + setFocusProxy(m_themeEditor); + } else { + m_editor->setText(m_themeEditor->theme()); + setFocusProxy(m_editor); + } +} + +TextPropertyValidationMode TextEditor::textPropertyValidationMode() const +{ + return m_editor->textPropertyValidationMode(); +} + +void TextEditor::setTextPropertyValidationMode(TextPropertyValidationMode vm) +{ + m_editor->setTextPropertyValidationMode(vm); + if (vm == ValidationURL) { + m_button->setMenu(m_menu); + m_button->setFixedWidth(30); + m_button->setPopupMode(QToolButton::MenuButtonPopup); + } else { + m_button->setMenu(0); + m_button->setFixedWidth(20); + m_button->setPopupMode(QToolButton::DelayedPopup); + } + m_button->setVisible(vm == ValidationStyleSheet || vm == ValidationRichText || vm == ValidationMultiLine || vm == ValidationURL); +} + +void TextEditor::setText(const QString &text) +{ + if (m_iconThemeModeEnabled) + m_themeEditor->setTheme(text); + else + m_editor->setText(text); +} + +void TextEditor::buttonClicked() +{ + const QString oldText = m_editor->text(); + QString newText; + switch (textPropertyValidationMode()) { + case ValidationStyleSheet: { + StyleSheetEditorDialog dlg(m_core, this); + dlg.setText(oldText); + if (dlg.exec() != QDialog::Accepted) + return; + newText = dlg.text(); + } + break; + case ValidationRichText: { + RichTextEditorDialog dlg(m_core, this); + dlg.setDefaultFont(m_richTextDefaultFont); + dlg.setText(oldText); + if (dlg.showDialog() != QDialog::Accepted) + return; + newText = dlg.text(Qt::AutoText); + } + break; + case ValidationMultiLine: { + PlainTextEditorDialog dlg(m_core, this); + dlg.setDefaultFont(m_richTextDefaultFont); + dlg.setText(oldText); + if (dlg.showDialog() != QDialog::Accepted) + return; + newText = dlg.text(); + } + break; + case ValidationURL: { + QString oldPath = oldText; + if (oldPath.isEmpty() || oldPath.startsWith(QLatin1String("qrc:"))) + resourceActionActivated(); + else + fileActionActivated(); + } + return; + default: + return; + } + if (newText != oldText) { + m_editor->setText(newText); + emit textChanged(newText); + } +} + +void TextEditor::resourceActionActivated() +{ + QString oldPath = m_editor->text(); + if (oldPath.startsWith(QLatin1String("qrc:"))) + oldPath.remove(0, 4); + // returns ':/file' + QString newPath = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), oldPath, this); + if (newPath.startsWith(QLatin1Char(':'))) + newPath.remove(0, 1); + if (newPath.isEmpty() || newPath == oldPath) + return; + const QString newText = QLatin1String("qrc:") + newPath; + m_editor->setText(newText); + emit textChanged(newText); +} + +void TextEditor::fileActionActivated() +{ + QString oldPath = m_editor->text(); + if (oldPath.startsWith(QLatin1String("file:"))) + oldPath = oldPath.mid(5); + const QString newPath = m_core->dialogGui()->getOpenFileName(this, tr("Choose a File"), oldPath); + if (newPath.isEmpty() || newPath == oldPath) + return; + const QString newText = QUrl::fromLocalFile(newPath).toString(); + m_editor->setText(newText); + emit textChanged(newText); +} + +// ------------ ThemeInputDialog + +class IconThemeDialog : public QDialog +{ + Q_OBJECT +public: + static QString getTheme(QWidget *parent, const QString &theme, bool *ok); +private: + IconThemeDialog(QWidget *parent); + IconThemeEditor *m_editor; +}; + +IconThemeDialog::IconThemeDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(tr("Set Icon From Theme")); + + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *label = new QLabel(tr("Input icon name from the current theme:"), this); + m_editor = new IconThemeEditor(this); + QDialogButtonBox *buttons = new QDialogButtonBox(this); + buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + layout->addWidget(label); + layout->addWidget(m_editor); + layout->addWidget(buttons); + + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); +} + +QString IconThemeDialog::getTheme(QWidget *parent, const QString &theme, bool *ok) +{ + IconThemeDialog dlg(parent); + dlg.m_editor->setTheme(theme); + if (dlg.exec() == QDialog::Accepted) { + *ok = true; + return dlg.m_editor->theme(); + } + *ok = false; + return QString(); +} + +// ------------ PixmapEditor +class PixmapEditor : public QWidget +{ + Q_OBJECT +public: + PixmapEditor(QDesignerFormEditorInterface *core, QWidget *parent); + + void setSpacing(int spacing); + void setPixmapCache(DesignerPixmapCache *cache); + void setIconThemeModeEnabled(bool enabled); +public slots: + void setPath(const QString &path); + void setTheme(const QString &theme); + void setDefaultPixmap(const QPixmap &pixmap); + +signals: + void pathChanged(const QString &path); + void themeChanged(const QString &theme); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + +private slots: + void defaultActionActivated(); + void resourceActionActivated(); + void fileActionActivated(); + void themeActionActivated(); + void copyActionActivated(); + void pasteActionActivated(); + void clipboardDataChanged(); +private: + void updateLabels(); + bool m_iconThemeModeEnabled; + QDesignerFormEditorInterface *m_core; + QLabel *m_pixmapLabel; + QLabel *m_pathLabel; + QToolButton *m_button; + QAction *m_resourceAction; + QAction *m_fileAction; + QAction *m_themeAction; + QAction *m_copyAction; + QAction *m_pasteAction; + QHBoxLayout *m_layout; + QPixmap m_defaultPixmap; + QString m_path; + QString m_theme; + DesignerPixmapCache *m_pixmapCache; +}; + +PixmapEditor::PixmapEditor(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + m_iconThemeModeEnabled(false), + m_core(core), + m_pixmapLabel(new QLabel(this)), + m_pathLabel(new QLabel(this)), + m_button(new QToolButton(this)), + m_resourceAction(new QAction(tr("Choose Resource..."), this)), + m_fileAction(new QAction(tr("Choose File..."), this)), + m_themeAction(new QAction(tr("Set Icon From Theme..."), this)), + m_copyAction(new QAction(createIconSet(QLatin1String("editcopy.png")), tr("Copy Path"), this)), + m_pasteAction(new QAction(createIconSet(QLatin1String("editpaste.png")), tr("Paste Path"), this)), + m_layout(new QHBoxLayout(this)), + m_pixmapCache(0) +{ + m_layout->addWidget(m_pixmapLabel); + m_layout->addWidget(m_pathLabel); + m_button->setText(tr("...")); + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(30); + m_button->setPopupMode(QToolButton::MenuButtonPopup); + m_layout->addWidget(m_button); + m_layout->setMargin(0); + m_layout->setSpacing(0); + m_pixmapLabel->setFixedWidth(16); + m_pixmapLabel->setAlignment(Qt::AlignCenter); + m_pathLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + m_themeAction->setVisible(false); + + QMenu *menu = new QMenu(this); + menu->addAction(m_resourceAction); + menu->addAction(m_fileAction); + menu->addAction(m_themeAction); + + m_button->setMenu(menu); + m_button->setText(tr("...")); + + connect(m_button, SIGNAL(clicked()), this, SLOT(defaultActionActivated())); + connect(m_resourceAction, SIGNAL(triggered()), this, SLOT(resourceActionActivated())); + connect(m_fileAction, SIGNAL(triggered()), this, SLOT(fileActionActivated())); + connect(m_themeAction, SIGNAL(triggered()), this, SLOT(themeActionActivated())); + connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copyActionActivated())); + connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(pasteActionActivated())); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored)); + setFocusProxy(m_button); + + connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardDataChanged())); + clipboardDataChanged(); +} + +void PixmapEditor::setPixmapCache(DesignerPixmapCache *cache) +{ + m_pixmapCache = cache; +} + +void PixmapEditor::setIconThemeModeEnabled(bool enabled) +{ + if (m_iconThemeModeEnabled == enabled) + return; + m_iconThemeModeEnabled = enabled; + m_themeAction->setVisible(enabled); +} + +void PixmapEditor::setSpacing(int spacing) +{ + m_layout->setSpacing(spacing); +} + +void PixmapEditor::setPath(const QString &path) +{ + m_path = path; + updateLabels(); +} + +void PixmapEditor::setTheme(const QString &theme) +{ + m_theme = theme; + updateLabels(); +} + +void PixmapEditor::updateLabels() +{ + if (m_iconThemeModeEnabled && QIcon::hasThemeIcon(m_theme)) { + m_pixmapLabel->setPixmap(QIcon::fromTheme(m_theme).pixmap(16, 16)); + m_pathLabel->setText(tr("[Theme] %1").arg(m_theme)); + m_copyAction->setEnabled(true); + } else { + if (m_path.isEmpty()) { + m_pathLabel->setText(m_path); + m_pixmapLabel->setPixmap(m_defaultPixmap); + m_copyAction->setEnabled(false); + } else { + m_pathLabel->setText(QFileInfo(m_path).fileName()); + if (m_pixmapCache) + m_pixmapLabel->setPixmap(QIcon(m_pixmapCache->pixmap(PropertySheetPixmapValue(m_path))).pixmap(16, 16)); + m_copyAction->setEnabled(true); + } + } +} + +void PixmapEditor::setDefaultPixmap(const QPixmap &pixmap) +{ + m_defaultPixmap = QIcon(pixmap).pixmap(16, 16); + const bool hasThemeIcon = m_iconThemeModeEnabled && QIcon::hasThemeIcon(m_theme); + if (!hasThemeIcon && m_path.isEmpty()) + m_pixmapLabel->setPixmap(m_defaultPixmap); +} + +void PixmapEditor::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu menu(this); + menu.addAction(m_copyAction); + menu.addAction(m_pasteAction); + menu.exec(event->globalPos()); + event->accept(); +} + +void PixmapEditor::defaultActionActivated() +{ + if (m_iconThemeModeEnabled && QIcon::hasThemeIcon(m_theme)) { + themeActionActivated(); + return; + } + // Default to resource + const PropertySheetPixmapValue::PixmapSource ps = m_path.isEmpty() ? PropertySheetPixmapValue::ResourcePixmap : PropertySheetPixmapValue::getPixmapSource(m_core, m_path); + switch (ps) { + case PropertySheetPixmapValue::LanguageResourcePixmap: + case PropertySheetPixmapValue::ResourcePixmap: + resourceActionActivated(); + break; + case PropertySheetPixmapValue::FilePixmap: + fileActionActivated(); + break; + } +} + +void PixmapEditor::resourceActionActivated() +{ + const QString oldPath = m_path; + const QString newPath = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), oldPath, this); + if (!newPath.isEmpty() && newPath != oldPath) { + setTheme(QString()); + setPath(newPath); + emit pathChanged(newPath); + } +} + +void PixmapEditor::fileActionActivated() +{ + const QString newPath = IconSelector::choosePixmapFile(m_path, m_core->dialogGui(), this); + if (!newPath.isEmpty() && newPath != m_path) { + setTheme(QString()); + setPath(newPath); + emit pathChanged(newPath); + } +} + +void PixmapEditor::themeActionActivated() +{ + bool ok; + const QString newTheme = IconThemeDialog::getTheme(this, m_theme, &ok); + if (ok && newTheme != m_theme) { + setTheme(newTheme); + setPath(QString()); + emit themeChanged(newTheme); + } +} + +void PixmapEditor::copyActionActivated() +{ + QClipboard *clipboard = QApplication::clipboard(); + if (m_iconThemeModeEnabled && QIcon::hasThemeIcon(m_theme)) + clipboard->setText(m_theme); + else + clipboard->setText(m_path); +} + +void PixmapEditor::pasteActionActivated() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString subtype = QLatin1String("plain"); + QString text = clipboard->text(subtype); + if (!text.isNull()) { + QStringList list = text.split(QLatin1Char('\n')); + if (list.size() > 0) { + text = list.at(0); + if (m_iconThemeModeEnabled && QIcon::hasThemeIcon(text)) { + setTheme(text); + setPath(QString()); + emit themeChanged(text); + } else { + setPath(text); + setTheme(QString()); + emit pathChanged(text); + } + } + } +} + +void PixmapEditor::clipboardDataChanged() +{ + QClipboard *clipboard = QApplication::clipboard(); + QString subtype = QLatin1String("plain"); + const QString text = clipboard->text(subtype); + m_pasteAction->setEnabled(!text.isNull()); +} + +// --------------- ResetWidget +class ResetWidget : public QWidget +{ + Q_OBJECT +public: + ResetWidget(QtProperty *property, QWidget *parent = 0); + + void setWidget(QWidget *widget); + void setResetEnabled(bool enabled); + void setValueText(const QString &text); + void setValueIcon(const QIcon &icon); + void setSpacing(int spacing); +signals: + void resetProperty(QtProperty *property); +private slots: + void slotClicked(); +private: + QtProperty *m_property; + QLabel *m_textLabel; + QLabel *m_iconLabel; + QToolButton *m_button; + int m_spacing; +}; + +ResetWidget::ResetWidget(QtProperty *property, QWidget *parent) : + QWidget(parent), + m_property(property), + m_textLabel(new QLabel(this)), + m_iconLabel(new QLabel(this)), + m_button(new QToolButton(this)), + m_spacing(-1) +{ + m_textLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + m_iconLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + m_button->setToolButtonStyle(Qt::ToolButtonIconOnly); + m_button->setIcon(createIconSet(QLatin1String("resetproperty.png"))); + m_button->setIconSize(QSize(8,8)); + m_button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); + connect(m_button, SIGNAL(clicked()), this, SLOT(slotClicked())); + QLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(m_spacing); + layout->addWidget(m_iconLabel); + layout->addWidget(m_textLabel); + layout->addWidget(m_button); + setFocusProxy(m_textLabel); + setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); +} + +void ResetWidget::setSpacing(int spacing) +{ + m_spacing = spacing; + layout()->setSpacing(m_spacing); +} + +void ResetWidget::setWidget(QWidget *widget) +{ + if (m_textLabel) { + delete m_textLabel; + m_textLabel = 0; + } + if (m_iconLabel) { + delete m_iconLabel; + m_iconLabel = 0; + } + delete layout(); + QLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(m_spacing); + layout->addWidget(widget); + layout->addWidget(m_button); + setFocusProxy(widget); +} + +void ResetWidget::setResetEnabled(bool enabled) +{ + m_button->setEnabled(enabled); +} + +void ResetWidget::setValueText(const QString &text) +{ + if (m_textLabel) + m_textLabel->setText(text); +} + +void ResetWidget::setValueIcon(const QIcon &icon) +{ + QPixmap pix = icon.pixmap(QSize(16, 16)); + if (m_iconLabel) { + m_iconLabel->setVisible(!pix.isNull()); + m_iconLabel->setPixmap(pix); + } +} + +void ResetWidget::slotClicked() +{ + emit resetProperty(m_property); +} + + +// ------------ DesignerPropertyManager: + +DesignerPropertyManager::DesignerPropertyManager(QDesignerFormEditorInterface *core, QObject *parent) : + QtVariantPropertyManager(parent), + m_changingSubValue(false), + m_core(core), + m_sourceOfChange(0) +{ + connect(this, SIGNAL(valueChanged(QtProperty*,QVariant)), this, SLOT(slotValueChanged(QtProperty*,QVariant))); + connect(this, SIGNAL(propertyDestroyed(QtProperty*)), this, SLOT(slotPropertyDestroyed(QtProperty*))); +} + +DesignerPropertyManager::~DesignerPropertyManager() +{ + clear(); +} + +int DesignerPropertyManager::bitCount(int mask) const +{ + int count = 0; + for (; mask; count++) + mask &= mask - 1; // clear the least significant bit set + return count; +} + +int DesignerPropertyManager::alignToIndexH(uint align) const +{ + if (align & Qt::AlignLeft) + return 0; + if (align & Qt::AlignHCenter) + return 1; + if (align & Qt::AlignRight) + return 2; + if (align & Qt::AlignJustify) + return 3; + return 0; +} + +int DesignerPropertyManager::alignToIndexV(uint align) const +{ + if (align & Qt::AlignTop) + return 0; + if (align & Qt::AlignVCenter) + return 1; + if (align & Qt::AlignBottom) + return 2; + return 1; +} + +uint DesignerPropertyManager::indexHToAlign(int idx) const +{ + switch (idx) { + case 0: return Qt::AlignLeft; + case 1: return Qt::AlignHCenter; + case 2: return Qt::AlignRight; + case 3: return Qt::AlignJustify; + default: break; + } + return Qt::AlignLeft; +} + +uint DesignerPropertyManager::indexVToAlign(int idx) const +{ + switch (idx) { + case 0: return Qt::AlignTop; + case 1: return Qt::AlignVCenter; + case 2: return Qt::AlignBottom; + default: break; + } + return Qt::AlignVCenter; +} + +QString DesignerPropertyManager::indexHToString(int idx) const +{ + switch (idx) { + case 0: return tr("AlignLeft"); + case 1: return tr("AlignHCenter"); + case 2: return tr("AlignRight"); + case 3: return tr("AlignJustify"); + default: break; + } + return tr("AlignLeft"); +} + +QString DesignerPropertyManager::indexVToString(int idx) const +{ + switch (idx) { + case 0: return tr("AlignTop"); + case 1: return tr("AlignVCenter"); + case 2: return tr("AlignBottom"); + default: break; + } + return tr("AlignVCenter"); +} + +void DesignerPropertyManager::slotValueChanged(QtProperty *property, const QVariant &value) +{ + if (m_changingSubValue) + return; + bool enableSubPropertyHandling = true; + + if (QtProperty *flagProperty = m_flagToProperty.value(property, 0)) { + const QList subFlags = m_propertyToFlags.value(flagProperty); + const int subFlagCount = subFlags.count(); + // flag changed + const bool subValue = variantProperty(property)->value().toBool(); + const int subIndex = subFlags.indexOf(property); + if (subIndex < 0) + return; + + uint newValue = 0; + + m_changingSubValue = true; + + FlagData data = m_flagValues.value(flagProperty); + const QList values = data.values; + // Compute new value, without including (additional) supermasks + if (values.at(subIndex) == 0) { + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + subFlag->setValue(i == subIndex); + } + } else { + if (subValue) + newValue = values.at(subIndex); // value mask of subValue + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + if (subFlag->value().toBool() && bitCount(values.at(i)) == 1) + newValue |= values.at(i); + } + if (newValue == 0) { + // Uncheck all items except 0-mask + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + subFlag->setValue(values.at(i) == 0); + } + } else if (newValue == data.val) { + if (!subValue && bitCount(values.at(subIndex)) > 1) { + // We unchecked something, but the original value still holds + variantProperty(property)->setValue(true); + } + } else { + // Make sure 0-mask is not selected + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + if (values.at(i) == 0) + subFlag->setValue(false); + } + // Check/uncheck proper masks + if (subValue) { + // Make sure submasks and supermasks are selected + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + const uint vi = values.at(i); + if ((vi != 0) && ((vi & newValue) == vi) && !subFlag->value().toBool()) + subFlag->setValue(true); + } + } else { + // Make sure supermasks are not selected if they're no longer valid + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + const uint vi = values.at(i); + if (subFlag->value().toBool() && ((vi & newValue) != vi)) + subFlag->setValue(false); + } + } + } + } + m_changingSubValue = false; + data.val = newValue; + QVariant v; + v.setValue(data.val); + variantProperty(flagProperty)->setValue(v); + } else if (QtProperty *alignProperty = m_alignHToProperty.value(property, 0)) { + const uint v = m_alignValues.value(alignProperty); + const uint newValue = indexHToAlign(value.toInt()) | indexVToAlign(alignToIndexV(v)); + if (v == newValue) + return; + + variantProperty(alignProperty)->setValue(newValue); + } else if (QtProperty *alignProperty = m_alignVToProperty.value(property, 0)) { + const uint v = m_alignValues.value(alignProperty); + const uint newValue = indexVToAlign(value.toInt()) | indexHToAlign(alignToIndexH(v)); + if (v == newValue) + return; + + variantProperty(alignProperty)->setValue(newValue); + } else if (QtProperty *stringProperty = m_commentToString.value(property, 0)) { + const PropertySheetStringValue v = m_stringValues.value(stringProperty); + PropertySheetStringValue newValue = v; + newValue.setComment(value.toString()); + if (v == newValue) + return; + + variantProperty(stringProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *stringProperty = m_translatableToString.value(property, 0)) { + const PropertySheetStringValue v = m_stringValues.value(stringProperty); + PropertySheetStringValue newValue = v; + newValue.setTranslatable(value.toBool()); + if (v == newValue) + return; + + variantProperty(stringProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *stringProperty = m_disambiguationToString.value(property, 0)) { + const PropertySheetStringValue v = m_stringValues.value(stringProperty); + PropertySheetStringValue newValue = v; + newValue.setDisambiguation(value.toString()); + if (v == newValue) + return; + + variantProperty(stringProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *keySequenceProperty = m_commentToKeySequence.value(property, 0)) { + const PropertySheetKeySequenceValue v = m_keySequenceValues.value(keySequenceProperty); + PropertySheetKeySequenceValue newValue = v; + newValue.setComment(value.toString()); + if (v == newValue) + return; + + variantProperty(keySequenceProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *keySequenceProperty = m_translatableToKeySequence.value(property, 0)) { + const PropertySheetKeySequenceValue v = m_keySequenceValues.value(keySequenceProperty); + PropertySheetKeySequenceValue newValue = v; + newValue.setTranslatable(value.toBool()); + if (v == newValue) + return; + + variantProperty(keySequenceProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *keySequenceProperty = m_disambiguationToKeySequence.value(property, 0)) { + const PropertySheetKeySequenceValue v = m_keySequenceValues.value(keySequenceProperty); + PropertySheetKeySequenceValue newValue = v; + newValue.setDisambiguation(value.toString()); + if (v == newValue) + return; + + variantProperty(keySequenceProperty)->setValue(QVariant::fromValue(newValue)); + } else if (QtProperty *iProperty = m_iconSubPropertyToProperty.value(property, 0)) { + QtVariantProperty *iconProperty = variantProperty(iProperty); + PropertySheetIconValue icon = qvariant_cast(iconProperty->value()); + QMap >::ConstIterator itState = m_iconSubPropertyToState.constFind(property); + if (itState != m_iconSubPropertyToState.constEnd()) { + QPair pair = m_iconSubPropertyToState.value(property); + icon.setPixmap(pair.first, pair.second, qvariant_cast(value)); + } else { // must be theme property + icon.setTheme(value.toString()); + } + QtProperty *origSourceOfChange = m_sourceOfChange; + if (!origSourceOfChange) + m_sourceOfChange = property; + iconProperty->setValue(QVariant::fromValue(icon)); + if (!origSourceOfChange) + m_sourceOfChange = origSourceOfChange; + } else if (m_iconValues.contains(property)) { + enableSubPropertyHandling = m_sourceOfChange; + } else { + if (m_brushManager.valueChanged(this, property, value) == BrushPropertyManager::Unchanged) + return; + if (m_fontManager.valueChanged(this, property, value) == FontPropertyManager::Unchanged) + return; + } + + emit valueChanged(property, value, enableSubPropertyHandling); +} + +void DesignerPropertyManager::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *flagProperty = m_flagToProperty.value(property, 0)) { + PropertyToPropertyListMap::iterator it = m_propertyToFlags.find(flagProperty); + QList &propertyList = it.value(); + propertyList.replace(propertyList.indexOf(property), 0); + m_flagToProperty.remove(property); + } else if (QtProperty *alignProperty = m_alignHToProperty.value(property, 0)) { + m_propertyToAlignH.remove(alignProperty); + m_alignHToProperty.remove(property); + } else if (QtProperty *alignProperty = m_alignVToProperty.value(property, 0)) { + m_propertyToAlignV.remove(alignProperty); + m_alignVToProperty.remove(property); + } else if (QtProperty *stringCommentProperty = m_commentToString.value(property, 0)) { + m_stringToComment.remove(stringCommentProperty); + m_commentToString.remove(property); + } else if (QtProperty *stringTranslatableProperty = m_translatableToString.value(property, 0)) { + m_stringToTranslatable.remove(stringTranslatableProperty); + m_translatableToString.remove(property); + } else if (QtProperty *stringDisambiguationProperty = m_disambiguationToString.value(property, 0)) { + m_stringToDisambiguation.remove(stringDisambiguationProperty); + m_disambiguationToString.remove(property); + } else if (QtProperty *keySequenceCommentProperty = m_commentToKeySequence.value(property, 0)) { + m_keySequenceToComment.remove(keySequenceCommentProperty); + m_commentToKeySequence.remove(property); + } else if (QtProperty *keySequenceTranslatableProperty = m_translatableToKeySequence.value(property, 0)) { + m_keySequenceToTranslatable.remove(keySequenceTranslatableProperty); + m_translatableToKeySequence.remove(property); + } else if (QtProperty *keySequenceDisambiguationProperty = m_disambiguationToKeySequence.value(property, 0)) { + m_keySequenceToDisambiguation.remove(keySequenceDisambiguationProperty); + m_disambiguationToKeySequence.remove(property); + } else if (QtProperty *iconProperty = m_iconSubPropertyToProperty.value(property, 0)) { + if (m_propertyToTheme.value(iconProperty) == property) { + m_propertyToTheme.remove(iconProperty); + } else { + QMap, QtProperty *> >::iterator it = + m_propertyToIconSubProperties.find(iconProperty); + QPair state = m_iconSubPropertyToState.value(property); + QMap, QtProperty *> &propertyList = it.value(); + propertyList.remove(state); + m_iconSubPropertyToState.remove(property); + } + m_iconSubPropertyToProperty.remove(property); + } else { + m_fontManager.slotPropertyDestroyed(property); + m_brushManager.slotPropertyDestroyed(property); + } +} + +QStringList DesignerPropertyManager::attributes(int propertyType) const +{ + if (!isPropertyTypeSupported(propertyType)) + return QStringList(); + + QStringList list = QtVariantPropertyManager::attributes(propertyType); + if (propertyType == designerFlagTypeId()) { + list.append(QLatin1String(flagsAttributeC)); + } else if (propertyType == designerPixmapTypeId()) { + list.append(QLatin1String(defaultResourceAttributeC)); + } else if (propertyType == designerIconTypeId()) { + list.append(QLatin1String(defaultResourceAttributeC)); + } else if (propertyType == designerStringTypeId() || propertyType == QVariant::String) { + list.append(QLatin1String(validationModesAttributeC)); + list.append(QLatin1String(fontAttributeC)); + list.append(QLatin1String(themeAttributeC)); + } else if (propertyType == QVariant::Palette) { + list.append(QLatin1String(superPaletteAttributeC)); + } + list.append(QLatin1String(resettableAttributeC)); + return list; +} + +int DesignerPropertyManager::attributeType(int propertyType, const QString &attribute) const +{ + if (!isPropertyTypeSupported(propertyType)) + return 0; + + if (propertyType == designerFlagTypeId() && attribute == QLatin1String(flagsAttributeC)) + return designerFlagListTypeId(); + if (propertyType == designerPixmapTypeId() && attribute == QLatin1String(defaultResourceAttributeC)) + return QVariant::Pixmap; + if (propertyType == designerIconTypeId() && attribute == QLatin1String(defaultResourceAttributeC)) + return QVariant::Icon; + if (attribute == QLatin1String(resettableAttributeC)) + return QVariant::Bool; + if (propertyType == designerStringTypeId() || propertyType == QVariant::String) { + if (attribute == QLatin1String(validationModesAttributeC)) + return QVariant::Int; + if (attribute == QLatin1String(fontAttributeC)) + return QVariant::Font; + if (attribute == QLatin1String(themeAttributeC)) + return QVariant::Bool; + } + if (propertyType == QVariant::Palette && attribute == QLatin1String(superPaletteAttributeC)) + return QVariant::Palette; + + return QtVariantPropertyManager::attributeType(propertyType, attribute); +} + +QVariant DesignerPropertyManager::attributeValue(const QtProperty *property, const QString &attribute) const +{ + QtProperty *prop = const_cast(property); + + if (attribute == QLatin1String(resettableAttributeC)) { + const PropertyBoolMap::const_iterator it = m_resetMap.constFind(prop); + if (it != m_resetMap.constEnd()) + return it.value(); + } + + if (attribute == QLatin1String(flagsAttributeC)) { + PropertyFlagDataMap::const_iterator it = m_flagValues.constFind(prop); + if (it != m_flagValues.constEnd()) { + QVariant v; + v.setValue(it.value().flags); + return v; + } + } + if (attribute == QLatin1String(validationModesAttributeC)) { + const PropertyIntMap::const_iterator it = m_stringAttributes.constFind(prop); + if (it != m_stringAttributes.constEnd()) + return it.value(); + } + + if (attribute == QLatin1String(fontAttributeC)) { + const PropertyFontMap::const_iterator it = m_stringFontAttributes.constFind(prop); + if (it != m_stringFontAttributes.constEnd()) + return it.value(); + } + + if (attribute == QLatin1String(themeAttributeC)) { + const PropertyBoolMap::const_iterator it = m_stringThemeAttributes.constFind(prop); + if (it != m_stringThemeAttributes.constEnd()) + return it.value(); + } + + if (attribute == QLatin1String(superPaletteAttributeC)) { + PropertyPaletteDataMap::const_iterator it = m_paletteValues.constFind(prop); + if (it != m_paletteValues.constEnd()) + return it.value().superPalette; + } + + if (attribute == QLatin1String(defaultResourceAttributeC)) { + QMap::const_iterator itPix = m_defaultPixmaps.constFind(prop); + if (itPix != m_defaultPixmaps.constEnd()) + return itPix.value(); + + QMap::const_iterator itIcon = m_defaultIcons.constFind(prop); + if (itIcon != m_defaultIcons.constEnd()) + return itIcon.value(); + } + + return QtVariantPropertyManager::attributeValue(property, attribute); +} + +void DesignerPropertyManager::setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value) +{ + if (attribute == QLatin1String(resettableAttributeC) && m_resetMap.contains(property)) { + if (value.userType() != QVariant::Bool) + return; + const bool val = value.toBool(); + const PropertyBoolMap::iterator it = m_resetMap.find(property); + if (it.value() == val) + return; + it.value() = val; + emit attributeChanged(variantProperty(property), attribute, value); + return; + } else if (attribute == QLatin1String(flagsAttributeC) && m_flagValues.contains(property)) { + if (value.userType() != designerFlagListTypeId()) + return; + + const DesignerFlagList flags = qvariant_cast(value); + PropertyFlagDataMap::iterator fit = m_flagValues.find(property); + FlagData data = fit.value(); + if (data.flags == flags) + return; + + PropertyToPropertyListMap::iterator pfit = m_propertyToFlags.find(property); + QListIterator itProp(pfit.value()); + while (itProp.hasNext()) { + if (QtProperty *prop = itProp.next()) { + delete prop; + m_flagToProperty.remove(prop); + } + } + pfit.value().clear(); + + QList values; + + QListIterator > itFlag(flags); + while (itFlag.hasNext()) { + const QPair pair = itFlag.next(); + const QString flagName = pair.first; + QtProperty *prop = addProperty(QVariant::Bool); + prop->setPropertyName(flagName); + property->addSubProperty(prop); + m_propertyToFlags[property].append(prop); + m_flagToProperty[prop] = property; + values.append(pair.second); + } + + data.val = 0; + data.flags = flags; + data.values = values; + + fit.value() = data; + + QVariant v; + v.setValue(flags); + emit attributeChanged(property, attribute, v); + + emit propertyChanged(property); + emit QtVariantPropertyManager::valueChanged(property, data.val); + } else if (attribute == QLatin1String(validationModesAttributeC) && m_stringAttributes.contains(property)) { + if (value.userType() != QVariant::Int) + return; + + const PropertyIntMap::iterator it = m_stringAttributes.find(property); + const int oldValue = it.value(); + + const int newValue = value.toInt(); + + if (oldValue == newValue) + return; + + it.value() = newValue; + + emit attributeChanged(property, attribute, newValue); + } else if (attribute == QLatin1String(fontAttributeC) && m_stringFontAttributes.contains(property)) { + if (value.userType() != QVariant::Font) + return; + + const PropertyFontMap::iterator it = m_stringFontAttributes.find(property); + const QFont oldValue = it.value(); + + const QFont newValue = qvariant_cast(value); + + if (oldValue == newValue) + return; + + it.value() = newValue; + + emit attributeChanged(property, attribute, newValue); + } else if (attribute == QLatin1String(themeAttributeC) && m_stringThemeAttributes.contains(property)) { + if (value.userType() != QVariant::Bool) + return; + + const PropertyBoolMap::iterator it = m_stringThemeAttributes.find(property); + const bool oldValue = it.value(); + + const bool newValue = value.toBool(); + + if (oldValue == newValue) + return; + + it.value() = newValue; + + emit attributeChanged(property, attribute, newValue); + } else if (attribute == QLatin1String(superPaletteAttributeC) && m_paletteValues.contains(property)) { + if (value.userType() != QVariant::Palette) + return; + + QPalette superPalette = qvariant_cast(value); + + const PropertyPaletteDataMap::iterator it = m_paletteValues.find(property); + PaletteData data = it.value(); + if (data.superPalette == superPalette) + return; + + data.superPalette = superPalette; + // resolve here + const uint mask = data.val.resolve(); + data.val = data.val.resolve(superPalette); + data.val.resolve(mask); + + it.value() = data; + + QVariant v; + v.setValue(superPalette); + emit attributeChanged(property, attribute, v); + + emit propertyChanged(property); + emit QtVariantPropertyManager::valueChanged(property, data.val); // if resolve was done, this is also for consistency + } else if (attribute == QLatin1String(defaultResourceAttributeC) && m_defaultPixmaps.contains(property)) { + if (value.userType() != QVariant::Pixmap) + return; + + QPixmap defaultPixmap = qvariant_cast(value); + + const QMap::iterator it = m_defaultPixmaps.find(property); + QPixmap oldDefaultPixmap = it.value(); + if (defaultPixmap.cacheKey() == oldDefaultPixmap.cacheKey()) + return; + + it.value() = defaultPixmap; + + QVariant v = QVariant::fromValue(defaultPixmap); + emit attributeChanged(property, attribute, v); + + emit propertyChanged(property); + } else if (attribute == QLatin1String(defaultResourceAttributeC) && m_defaultIcons.contains(property)) { + if (value.userType() != QVariant::Icon) + return; + + QIcon defaultIcon = qvariant_cast(value); + + const QMap::iterator it = m_defaultIcons.find(property); + QIcon oldDefaultIcon = it.value(); + if (defaultIcon.cacheKey() == oldDefaultIcon.cacheKey()) + return; + + it.value() = defaultIcon; + + qdesigner_internal::PropertySheetIconValue icon = m_iconValues.value(property); + if (icon.paths().isEmpty()) { + QMap, QtProperty *> subIconProperties = m_propertyToIconSubProperties.value(property); + QMapIterator, QtProperty *> itSub(subIconProperties); + while (itSub.hasNext()) { + QPair pair = itSub.next().key(); + QtProperty *subProp = itSub.value(); + setAttribute(subProp, QLatin1String(defaultResourceAttributeC), + defaultIcon.pixmap(16, 16, pair.first, pair.second)); + } + } + + QVariant v = QVariant::fromValue(defaultIcon); + emit attributeChanged(property, attribute, v); + + emit propertyChanged(property); + } + QtVariantPropertyManager::setAttribute(property, attribute, value); +} + +int DesignerPropertyManager::designerFlagTypeId() +{ + static const int rc = qMetaTypeId(); + return rc; +} + +int DesignerPropertyManager::designerFlagListTypeId() +{ + static const int rc = qMetaTypeId(); + return rc; +} + +int DesignerPropertyManager::designerAlignmentTypeId() +{ + static const int rc = qMetaTypeId(); + return rc; +} + +int DesignerPropertyManager::designerPixmapTypeId() +{ + return qMetaTypeId(); +} + +int DesignerPropertyManager::designerIconTypeId() +{ + return qMetaTypeId(); +} + +int DesignerPropertyManager::designerStringTypeId() +{ + return qMetaTypeId(); +} + +int DesignerPropertyManager::designerKeySequenceTypeId() +{ + return qMetaTypeId(); +} + +bool DesignerPropertyManager::isPropertyTypeSupported(int propertyType) const +{ + switch (propertyType) { + case QVariant::Palette: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Url: + case QVariant::ByteArray: + case QVariant::StringList: + case QVariant::Brush: + return true; + default: + break; + } + + if (propertyType == designerFlagTypeId()) + return true; + if (propertyType == designerAlignmentTypeId()) + return true; + if (propertyType == designerPixmapTypeId()) + return true; + if (propertyType == designerIconTypeId()) + return true; + if (propertyType == designerStringTypeId()) + return true; + if (propertyType == designerKeySequenceTypeId()) + return true; + return QtVariantPropertyManager::isPropertyTypeSupported(propertyType); +} + +QString DesignerPropertyManager::valueText(const QtProperty *property) const +{ + if (m_flagValues.contains(const_cast(property))) { + const FlagData data = m_flagValues.value(const_cast(property)); + const uint v = data.val; + const QChar bar = QLatin1Char('|'); + QString valueStr; + const QList > flags = data.flags; + const QList >::const_iterator fcend = flags.constEnd(); + for (QList >::const_iterator it = flags.constBegin(); it != fcend; ++it) { + const uint val = it->second; + const bool checked = (val == 0) ? (v == 0) : ((val & v) == val); + if (checked) { + if (!valueStr.isEmpty()) + valueStr += bar; + valueStr += it->first; + } + } + return valueStr; + } + if (m_alignValues.contains(const_cast(property))) { + const uint v = m_alignValues.value(const_cast(property)); + return tr("%1, %2").arg(indexHToString(alignToIndexH(v))).arg(indexVToString(alignToIndexV(v))); + } + if (m_paletteValues.contains(const_cast(property))) { + const PaletteData data = m_paletteValues.value(const_cast(property)); + const uint mask = data.val.resolve(); + if (mask) + return tr("Customized (%n roles)", 0, bitCount(mask)); + static const QString inherited = tr("Inherited"); + return inherited; + } + if (m_iconValues.contains(const_cast(property))) { + const PropertySheetIconValue icon = m_iconValues.value(const_cast(property)); + const QString theme = icon.theme(); + if (!theme.isEmpty() && QIcon::hasThemeIcon(theme)) + return tr("[Theme] %1").arg(theme); + const PropertySheetIconValue::ModeStateToPixmapMap paths = icon.paths(); + const PropertySheetIconValue::ModeStateToPixmapMap::const_iterator it = paths.constFind(qMakePair(QIcon::Normal, QIcon::Off)); + if (it == paths.constEnd()) + return QString(); + return QFileInfo(it.value().path()).fileName(); + } + if (m_pixmapValues.contains(const_cast(property))) { + const QString path = m_pixmapValues.value(const_cast(property)).path(); + if (path.isEmpty()) + return QString(); + return QFileInfo(path).fileName(); + } + if (m_uintValues.contains(const_cast(property))) { + return QString::number(m_uintValues.value(const_cast(property))); + } + if (m_longLongValues.contains(const_cast(property))) { + return QString::number(m_longLongValues.value(const_cast(property))); + } + if (m_uLongLongValues.contains(const_cast(property))) { + return QString::number(m_uLongLongValues.value(const_cast(property))); + } + if (m_urlValues.contains(const_cast(property))) { + return m_urlValues.value(const_cast(property)).toString(); + } + if (m_byteArrayValues.contains(const_cast(property))) { + return QString::fromUtf8(m_byteArrayValues.value(const_cast(property))); + } + if (m_stringListValues.contains(const_cast(property))) { + return m_stringListValues.value(const_cast(property)).join(QLatin1String("; ")); + } + if (QtVariantPropertyManager::valueType(property) == QVariant::String || QtVariantPropertyManager::valueType(property) == designerStringTypeId()) { + const QString str = (QtVariantPropertyManager::valueType(property) == QVariant::String) ? value(property).toString() : qvariant_cast(value(property)).value(); + const int validationMode = attributeValue(property, QLatin1String(validationModesAttributeC)).toInt(); + return TextPropertyEditor::stringToEditorString(str, static_cast(validationMode)); + } + if (QtVariantPropertyManager::valueType(property) == designerKeySequenceTypeId()) { + return qvariant_cast(value(property)).value(); + } + if (QtVariantPropertyManager::valueType(property) == QVariant::Bool) { + return QString(); + } + + QString rc; + if (m_brushManager.valueText(property, &rc)) + return rc; + return QtVariantPropertyManager::valueText(property); +} + +void DesignerPropertyManager::reloadResourceProperties() +{ + DesignerIconCache *iconCache = 0; + QMapIterator itIcon(m_iconValues); + while (itIcon.hasNext()) { + QtProperty *property = itIcon.next().key(); + PropertySheetIconValue icon = itIcon.value(); + + QIcon defaultIcon = m_defaultIcons.value(property); + if (!icon.paths().isEmpty()) { + if (!iconCache) { + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + qdesigner_internal::FormWindowBase *fwb = qobject_cast(formWindow); + iconCache = fwb->iconCache(); + } + if (iconCache) + defaultIcon = iconCache->icon(icon); + } + + QMap, PropertySheetPixmapValue> iconPaths = icon.paths(); + + QMap, QtProperty *> subProperties = m_propertyToIconSubProperties.value(property); + QMapIterator, QtProperty *> itSub(subProperties); + while (itSub.hasNext()) { + const QPair pair = itSub.next().key(); + QtVariantProperty *subProperty = variantProperty(itSub.value()); + subProperty->setAttribute(QLatin1String(defaultResourceAttributeC), + defaultIcon.pixmap(16, 16, pair.first, pair.second)); + } + + emit propertyChanged(property); + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(itIcon.value())); + } + QMapIterator itPix(m_pixmapValues); + while (itPix.hasNext()) { + QtProperty *property = itPix.next().key(); + emit propertyChanged(property); + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(itPix.value())); + } +} + +QIcon DesignerPropertyManager::valueIcon(const QtProperty *property) const +{ + if (m_iconValues.contains(const_cast(property))) { + if (!property->isModified()) + return m_defaultIcons.value(const_cast(property)).pixmap(16, 16); + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + qdesigner_internal::FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + return fwb->iconCache()->icon(m_iconValues.value(const_cast(property))).pixmap(16, 16); + } else if (m_pixmapValues.contains(const_cast(property))) { + if (!property->isModified()) + return m_defaultPixmaps.value(const_cast(property)); + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + qdesigner_internal::FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + return fwb->pixmapCache()->pixmap(m_pixmapValues.value(const_cast(property))); + } else if (m_stringThemeAttributes.value(const_cast(property), false)) { + return QIcon::fromTheme(value(property).toString()); + } else { + QIcon rc; + if (m_brushManager.valueIcon(property, &rc)) + return rc; + } + + return QtVariantPropertyManager::valueIcon(property); +} + +QVariant DesignerPropertyManager::value(const QtProperty *property) const +{ + if (m_flagValues.contains(const_cast(property))) + return m_flagValues.value(const_cast(property)).val; + if (m_alignValues.contains(const_cast(property))) + return m_alignValues.value(const_cast(property)); + if (m_paletteValues.contains(const_cast(property))) + return m_paletteValues.value(const_cast(property)).val; + if (m_iconValues.contains(const_cast(property))) + return QVariant::fromValue(m_iconValues.value(const_cast(property))); + if (m_pixmapValues.contains(const_cast(property))) + return QVariant::fromValue(m_pixmapValues.value(const_cast(property))); + if (m_stringValues.contains(const_cast(property))) + return QVariant::fromValue(m_stringValues.value(const_cast(property))); + if (m_keySequenceValues.contains(const_cast(property))) + return QVariant::fromValue(m_keySequenceValues.value(const_cast(property))); + if (m_uintValues.contains(const_cast(property))) + return m_uintValues.value(const_cast(property)); + if (m_longLongValues.contains(const_cast(property))) + return m_longLongValues.value(const_cast(property)); + if (m_uLongLongValues.contains(const_cast(property))) + return m_uLongLongValues.value(const_cast(property)); + if (m_urlValues.contains(const_cast(property))) + return m_urlValues.value(const_cast(property)); + if (m_byteArrayValues.contains(const_cast(property))) + return m_byteArrayValues.value(const_cast(property)); + if (m_stringListValues.contains(const_cast(property))) + return m_stringListValues.value(const_cast(property)); + + QVariant rc; + if (m_brushManager.value(property, &rc)) + return rc; + return QtVariantPropertyManager::value(property); +} + +int DesignerPropertyManager::valueType(int propertyType) const +{ + switch (propertyType) { + case QVariant::Palette: + case QVariant::UInt: + case QVariant::LongLong: + case QVariant::ULongLong: + case QVariant::Url: + case QVariant::ByteArray: + case QVariant::StringList: + case QVariant::Brush: + return propertyType; + default: + break; + } + if (propertyType == designerFlagTypeId()) + return QVariant::UInt; + if (propertyType == designerAlignmentTypeId()) + return QVariant::UInt; + if (propertyType == designerPixmapTypeId()) + return propertyType; + if (propertyType == designerIconTypeId()) + return propertyType; + if (propertyType == designerStringTypeId()) + return propertyType; + if (propertyType == designerKeySequenceTypeId()) + return propertyType; + return QtVariantPropertyManager::valueType(propertyType); +} + +void DesignerPropertyManager::setValue(QtProperty *property, const QVariant &value) +{ + const PropertyFlagDataMap::iterator fit = m_flagValues.find(property); + + if (fit != m_flagValues.end()) { + if (value.type() != QVariant::UInt && !value.canConvert(QVariant::UInt)) + return; + + const uint v = value.toUInt(); + + FlagData data = fit.value(); + if (data.val == v) + return; + + // set Value + + const QList values = data.values; + const QList subFlags = m_propertyToFlags.value(property); + const int subFlagCount = subFlags.count(); + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + const uint val = values.at(i); + const bool checked = (val == 0) ? (v == 0) : ((val & v) == val); + subFlag->setValue(checked); + } + + for (int i = 0; i < subFlagCount; ++i) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(i)); + const uint val = values.at(i); + const bool checked = (val == 0) ? (v == 0) : ((val & v) == val); + bool enabled = true; + if (val == 0) { + if (checked) + enabled = false; + } else if (bitCount(val) > 1) { + // Disabled if all flags contained in the mask are checked + uint currentMask = 0; + for (int j = 0; j < subFlagCount; ++j) { + QtVariantProperty *subFlag = variantProperty(subFlags.at(j)); + if (bitCount(values.at(j)) == 1) + currentMask |= subFlag->value().toBool() ? values.at(j) : 0; + } + if ((currentMask & values.at(i)) == values.at(i)) + enabled = false; + } + subFlag->setEnabled(enabled); + } + + data.val = v; + fit.value() = data; + + emit QtVariantPropertyManager::valueChanged(property, data.val); + emit propertyChanged(property); + + return; + } else if (m_alignValues.contains(property)) { + if (value.type() != QVariant::UInt && !value.canConvert(QVariant::UInt)) + return; + + const uint v = value.toUInt(); + + uint val = m_alignValues.value(property); + + if (val == v) + return; + + QtVariantProperty *alignH = variantProperty(m_propertyToAlignH.value(property)); + QtVariantProperty *alignV = variantProperty(m_propertyToAlignV.value(property)); + + if (alignH) + alignH->setValue(alignToIndexH(v)); + if (alignV) + alignV->setValue(alignToIndexV(v)); + + m_alignValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_stringValues.contains(property)) { + if (value.userType() != designerStringTypeId()) + return; + + const PropertySheetStringValue v = qvariant_cast(value); + + const PropertySheetStringValue val = m_stringValues.value(property); + + if (val == v) + return; + + QtVariantProperty *comment = variantProperty(m_stringToComment.value(property)); + QtVariantProperty *translatable = variantProperty(m_stringToTranslatable.value(property)); + QtVariantProperty *disambiguation = variantProperty(m_stringToDisambiguation.value(property)); + + if (comment) + comment->setValue(v.comment()); + if (translatable) + translatable->setValue(v.translatable()); + if (disambiguation) + disambiguation->setValue(v.disambiguation()); + + m_stringValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(v)); + emit propertyChanged(property); + + return; + } else if (m_keySequenceValues.contains(property)) { + if (value.userType() != designerKeySequenceTypeId()) + return; + + const PropertySheetKeySequenceValue v = qvariant_cast(value); + + const PropertySheetKeySequenceValue val = m_keySequenceValues.value(property); + + if (val == v) + return; + + QtVariantProperty *comment = variantProperty(m_keySequenceToComment.value(property)); + QtVariantProperty *translatable = variantProperty(m_keySequenceToTranslatable.value(property)); + QtVariantProperty *disambiguation = variantProperty(m_keySequenceToDisambiguation.value(property)); + + if (comment) + comment->setValue(v.comment()); + if (translatable) + translatable->setValue(v.translatable()); + if (disambiguation) + disambiguation->setValue(v.disambiguation()); + + m_keySequenceValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(v)); + emit propertyChanged(property); + + return; + } else if (m_paletteValues.contains(property)) { + if (value.type() != QVariant::Palette && !value.canConvert(QVariant::Palette)) + return; + + QPalette p = qvariant_cast(value); + + PaletteData data = m_paletteValues.value(property); + + const uint mask = p.resolve(); + p = p.resolve(data.superPalette); + p.resolve(mask); + + if (data.val == p && data.val.resolve() == p.resolve()) + return; + + data.val = p; + m_paletteValues[property] = data; + + emit QtVariantPropertyManager::valueChanged(property, data.val); + emit propertyChanged(property); + + return; + } else if (m_iconValues.contains(property)) { + if (value.userType() != designerIconTypeId()) + return; + + const PropertySheetIconValue icon = qvariant_cast(value); + + const PropertySheetIconValue oldIcon = m_iconValues.value(property); + if (icon == oldIcon) + return; + + m_iconValues[property] = icon; + + QIcon defaultIcon = m_defaultIcons.value(property); + if (!icon.paths().isEmpty()) { + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + qdesigner_internal::FormWindowBase *fwb = qobject_cast(formWindow); + if (fwb) + defaultIcon = fwb->iconCache()->icon(icon); + } + + QMap, PropertySheetPixmapValue> iconPaths = icon.paths(); + + QMap, QtProperty *> subProperties = m_propertyToIconSubProperties.value(property); + QMapIterator, QtProperty *> itSub(subProperties); + while (itSub.hasNext()) { + const QPair pair = itSub.next().key(); + QtVariantProperty *subProperty = variantProperty(itSub.value()); + bool hasPath = iconPaths.contains(pair); + subProperty->setModified(hasPath); + subProperty->setValue(QVariant::fromValue(iconPaths.value(pair))); + subProperty->setAttribute(QLatin1String(defaultResourceAttributeC), + defaultIcon.pixmap(16, 16, pair.first, pair.second)); + } + QtVariantProperty *themeSubProperty = variantProperty(m_propertyToTheme.value(property)); + if (themeSubProperty) { + const QString theme = icon.theme(); + themeSubProperty->setModified(!theme.isEmpty()); + themeSubProperty->setValue(theme); + } + + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(icon)); + emit propertyChanged(property); + + QString toolTip; + const QMap, PropertySheetPixmapValue>::ConstIterator itNormalOff = + iconPaths.constFind(qMakePair(QIcon::Normal, QIcon::Off)); + if (itNormalOff != iconPaths.constEnd()) + toolTip = itNormalOff.value().path(); + property->setToolTip(toolTip); + + return; + } else if (m_pixmapValues.contains(property)) { + if (value.userType() != designerPixmapTypeId()) + return; + + const PropertySheetPixmapValue pixmap = qvariant_cast(value); + + const PropertySheetPixmapValue oldPixmap = m_pixmapValues.value(property); + if (pixmap == oldPixmap) + return; + + m_pixmapValues[property] = pixmap; + + emit QtVariantPropertyManager::valueChanged(property, QVariant::fromValue(pixmap)); + emit propertyChanged(property); + + property->setToolTip(pixmap.path()); + + return; + } else if (m_uintValues.contains(property)) { + if (value.type() != QVariant::UInt && !value.canConvert(QVariant::UInt)) + return; + + const uint v = value.toUInt(0); + + const uint oldValue = m_uintValues.value(property); + if (v == oldValue) + return; + + m_uintValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_longLongValues.contains(property)) { + if (value.type() != QVariant::LongLong && !value.canConvert(QVariant::LongLong)) + return; + + const qlonglong v = value.toLongLong(0); + + const qlonglong oldValue = m_longLongValues.value(property); + if (v == oldValue) + return; + + m_longLongValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_uLongLongValues.contains(property)) { + if (value.type() != QVariant::ULongLong && !value.canConvert(QVariant::ULongLong)) + return; + + qulonglong v = value.toULongLong(0); + + qulonglong oldValue = m_uLongLongValues.value(property); + if (v == oldValue) + return; + + m_uLongLongValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_urlValues.contains(property)) { + if (value.type() != QVariant::Url && !value.canConvert(QVariant::Url)) + return; + + const QUrl v = value.toUrl(); + + const QUrl oldValue = m_urlValues.value(property); + if (v == oldValue) + return; + + m_urlValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_byteArrayValues.contains(property)) { + if (value.type() != QVariant::ByteArray && !value.canConvert(QVariant::ByteArray)) + return; + + const QByteArray v = value.toByteArray(); + + const QByteArray oldValue = m_byteArrayValues.value(property); + if (v == oldValue) + return; + + m_byteArrayValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } else if (m_stringListValues.contains(property)) { + if (value.type() != QVariant::StringList && !value.canConvert(QVariant::StringList)) + return; + + const QStringList v = value.toStringList(); + + const QStringList oldValue = m_stringListValues.value(property); + if (v == oldValue) + return; + + m_stringListValues[property] = v; + + emit QtVariantPropertyManager::valueChanged(property, v); + emit propertyChanged(property); + + return; + } + switch (m_brushManager.setValue(this, property, value)) { + case BrushPropertyManager::Unchanged: + return; + case BrushPropertyManager::Changed: + emit QtVariantPropertyManager::valueChanged(property, value); + emit propertyChanged(property); + return; + default: + break; + } + m_fontManager.setValue(this, property, value); + QtVariantPropertyManager::setValue(property, value); + if (QtVariantPropertyManager::valueType(property) == QVariant::String) + property->setToolTip(DesignerPropertyManager::value(property).toString()); + else if (QtVariantPropertyManager::valueType(property) == designerStringTypeId()) + property->setToolTip(qvariant_cast(DesignerPropertyManager::value(property)).value()); + else if (QtVariantPropertyManager::valueType(property) == designerKeySequenceTypeId()) + property->setToolTip(qvariant_cast(DesignerPropertyManager::value(property)).value()); + else if (QtVariantPropertyManager::valueType(property) == QVariant::Bool) + property->setToolTip(QtVariantPropertyManager::valueText(property)); +} + +void DesignerPropertyManager::initializeProperty(QtProperty *property) +{ + m_resetMap[property] = false; + + const int type = propertyType(property); + m_fontManager.preInitializeProperty(property, type, m_resetMap); + switch (type) { + case QVariant::Palette: + m_paletteValues[property] = PaletteData(); + break; + case QVariant::String: + m_stringAttributes[property] = ValidationSingleLine; + m_stringFontAttributes[property] = QApplication::font(); + m_stringThemeAttributes[property] = false; + break; + case QVariant::UInt: + m_uintValues[property] = 0; + break; + case QVariant::LongLong: + m_longLongValues[property] = 0; + break; + case QVariant::ULongLong: + m_uLongLongValues[property] = 0; + break; + case QVariant::Url: + m_urlValues[property] = QUrl(); + break; + case QVariant::ByteArray: + m_byteArrayValues[property] = 0; + break; + case QVariant::StringList: + m_stringListValues[property] = QStringList(); + break; + case QVariant::Brush: + m_brushManager.initializeProperty(this, property, enumTypeId()); + break; + default: + if (type == designerFlagTypeId()) { + m_flagValues[property] = FlagData(); + m_propertyToFlags[property] = QList(); + } else if (type == designerAlignmentTypeId()) { + const uint align = Qt::AlignLeft | Qt::AlignVCenter; + m_alignValues[property] = align; + + QtVariantProperty *alignH = addProperty(enumTypeId(), tr("Horizontal")); + QStringList namesH; + namesH << indexHToString(0) << indexHToString(1) << indexHToString(2) << indexHToString(3); + alignH->setAttribute(QLatin1String("enumNames"), namesH); + alignH->setValue(alignToIndexH(align)); + m_propertyToAlignH[property] = alignH; + m_alignHToProperty[alignH] = property; + property->addSubProperty(alignH); + + QtVariantProperty *alignV = addProperty(enumTypeId(), tr("Vertical")); + QStringList namesV; + namesV << indexVToString(0) << indexVToString(1) << indexVToString(2); + alignV->setAttribute(QLatin1String("enumNames"), namesV); + alignV->setValue(alignToIndexV(align)); + m_propertyToAlignV[property] = alignV; + m_alignVToProperty[alignV] = property; + property->addSubProperty(alignV); + } else if (type == designerPixmapTypeId()) { + m_pixmapValues[property] = PropertySheetPixmapValue(); + m_defaultPixmaps[property] = QPixmap(); + } else if (type == designerIconTypeId()) { + m_iconValues[property] = PropertySheetIconValue(); + m_defaultIcons[property] = QIcon(); + + QtVariantProperty *themeProp = addProperty(QVariant::String, tr("Theme")); + themeProp->setAttribute(QLatin1String(themeAttributeC), true); + m_iconSubPropertyToProperty[themeProp] = property; + m_propertyToTheme[property] = themeProp; + m_resetMap[themeProp] = true; + property->addSubProperty(themeProp); + + createIconSubProperty(property, QIcon::Normal, QIcon::Off, tr("Normal Off")); + createIconSubProperty(property, QIcon::Normal, QIcon::On, tr("Normal On")); + createIconSubProperty(property, QIcon::Disabled, QIcon::Off, tr("Disabled Off")); + createIconSubProperty(property, QIcon::Disabled, QIcon::On, tr("Disabled On")); + createIconSubProperty(property, QIcon::Active, QIcon::Off, tr("Active Off")); + createIconSubProperty(property, QIcon::Active, QIcon::On, tr("Active On")); + createIconSubProperty(property, QIcon::Selected, QIcon::Off, tr("Selected Off")); + createIconSubProperty(property, QIcon::Selected, QIcon::On, tr("Selected On")); + } else if (type == designerStringTypeId()) { + PropertySheetStringValue val; + m_stringValues[property] = val; + m_stringAttributes[property] = ValidationMultiLine; + m_stringFontAttributes[property] = QApplication::font(); + m_stringThemeAttributes[property] = false; + + QtVariantProperty *translatable = addProperty(QVariant::Bool, tr("translatable")); + translatable->setValue(val.translatable()); + m_stringToTranslatable[property] = translatable; + m_translatableToString[translatable] = property; + property->addSubProperty(translatable); + + QtVariantProperty *disambiguation = addProperty(QVariant::String, tr("disambiguation")); + disambiguation->setValue(val.disambiguation()); + m_stringToDisambiguation[property] = disambiguation; + m_disambiguationToString[disambiguation] = property; + property->addSubProperty(disambiguation); + + QtVariantProperty *comment = addProperty(QVariant::String, tr("comment")); + comment->setValue(val.comment()); + m_stringToComment[property] = comment; + m_commentToString[comment] = property; + property->addSubProperty(comment); + } else if (type == designerKeySequenceTypeId()) { + PropertySheetKeySequenceValue val; + m_keySequenceValues[property] = val; + + QtVariantProperty *translatable = addProperty(QVariant::Bool, tr("translatable")); + translatable->setValue(val.translatable()); + m_keySequenceToTranslatable[property] = translatable; + m_translatableToKeySequence[translatable] = property; + property->addSubProperty(translatable); + + QtVariantProperty *disambiguation = addProperty(QVariant::String, tr("disambiguation")); + disambiguation->setValue(val.disambiguation()); + m_keySequenceToDisambiguation[property] = disambiguation; + m_disambiguationToKeySequence[disambiguation] = property; + property->addSubProperty(disambiguation); + + QtVariantProperty *comment = addProperty(QVariant::String, tr("comment")); + comment->setValue(val.comment()); + m_keySequenceToComment[property] = comment; + m_commentToKeySequence[comment] = property; + property->addSubProperty(comment); + } + } + + QtVariantPropertyManager::initializeProperty(property); + m_fontManager.postInitializeProperty(this, property, type, DesignerPropertyManager::enumTypeId()); + if (type == QVariant::Double) + setAttribute(property, QLatin1String("decimals"), 6); +} + +void DesignerPropertyManager::createIconSubProperty(QtProperty *iconProperty, QIcon::Mode mode, QIcon::State state, const QString &subName) +{ + QPair pair = qMakePair(mode, state); + QtVariantProperty *subProp = addProperty(DesignerPropertyManager::designerPixmapTypeId(), subName); + m_propertyToIconSubProperties[iconProperty][pair] = subProp; + m_iconSubPropertyToState[subProp] = pair; + m_iconSubPropertyToProperty[subProp] = iconProperty; + m_resetMap[subProp] = true; + iconProperty->addSubProperty(subProp); +} + +void DesignerPropertyManager::uninitializeProperty(QtProperty *property) +{ + m_resetMap.remove(property); + + QListIterator itProp(m_propertyToFlags[property]); + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) { + delete prop; + m_flagToProperty.remove(prop); + } + } + m_propertyToFlags.remove(property); + m_flagValues.remove(property); + + QtProperty *alignH = m_propertyToAlignH.value(property); + if (alignH) { + delete alignH; + m_alignHToProperty.remove(alignH); + } + QtProperty *alignV = m_propertyToAlignV.value(property); + if (alignV) { + delete alignV; + m_alignVToProperty.remove(alignV); + } + + QtProperty *stringComment = m_stringToComment.value(property); + if (stringComment) { + delete stringComment; + m_commentToString.remove(stringComment); + } + + QtProperty *stringTranslatable = m_stringToTranslatable.value(property); + if (stringTranslatable) { + delete stringTranslatable; + m_translatableToString.remove(stringTranslatable); + } + + QtProperty *stringDisambiguation = m_stringToDisambiguation.value(property); + if (stringDisambiguation) { + delete stringDisambiguation; + m_disambiguationToString.remove(stringDisambiguation); + } + + QtProperty *keySequenceComment = m_keySequenceToComment.value(property); + if (keySequenceComment) { + delete keySequenceComment; + m_commentToKeySequence.remove(keySequenceComment); + } + + QtProperty *keySequenceTranslatable = m_keySequenceToTranslatable.value(property); + if (keySequenceTranslatable) { + delete keySequenceTranslatable; + m_translatableToKeySequence.remove(keySequenceTranslatable); + } + + QtProperty *keySequenceDisambiguation = m_keySequenceToDisambiguation.value(property); + if (keySequenceDisambiguation) { + delete keySequenceDisambiguation; + m_disambiguationToKeySequence.remove(keySequenceDisambiguation); + } + + QtProperty *iconTheme = m_propertyToTheme.value(property); + if (iconTheme) { + delete iconTheme; + m_iconSubPropertyToProperty.remove(iconTheme); + } + + m_propertyToAlignH.remove(property); + m_propertyToAlignV.remove(property); + + m_stringToComment.remove(property); + m_stringToTranslatable.remove(property); + m_stringToDisambiguation.remove(property); + m_stringValues.remove(property); + m_stringAttributes.remove(property); + m_stringFontAttributes.remove(property); + + m_keySequenceToComment.remove(property); + m_keySequenceToTranslatable.remove(property); + m_keySequenceToDisambiguation.remove(property); + m_keySequenceValues.remove(property); + + m_paletteValues.remove(property); + + m_iconValues.remove(property); + m_defaultIcons.remove(property); + + m_pixmapValues.remove(property); + m_defaultPixmaps.remove(property); + + QMap, QtProperty *> iconSubProperties = m_propertyToIconSubProperties.value(property); + QMapIterator, QtProperty *> itIcon(iconSubProperties); + while (itIcon.hasNext()) { + QtProperty *subIcon = itIcon.next().value(); + delete subIcon; + m_iconSubPropertyToState.remove(subIcon); + m_iconSubPropertyToProperty.remove(subIcon); + } + m_propertyToIconSubProperties.remove(property); + m_iconSubPropertyToState.remove(property); + m_iconSubPropertyToProperty.remove(property); + + m_uintValues.remove(property); + m_longLongValues.remove(property); + m_uLongLongValues.remove(property); + m_urlValues.remove(property); + m_byteArrayValues.remove(property); + m_stringListValues.remove(property); + + m_fontManager.uninitializeProperty(property); + m_brushManager.uninitializeProperty(property); + + QtVariantPropertyManager::uninitializeProperty(property); +} + + +bool DesignerPropertyManager::resetFontSubProperty(QtProperty *property) +{ + return m_fontManager.resetFontSubProperty(this, property); +} + +bool DesignerPropertyManager::resetIconSubProperty(QtProperty *property) +{ + QtProperty *iconProperty = m_iconSubPropertyToProperty.value(property); + if (!iconProperty) + return false; + + if (m_pixmapValues.contains(property)) { + QtVariantProperty *pixmapProperty = variantProperty(property); + pixmapProperty->setValue(QVariant::fromValue(PropertySheetPixmapValue())); + return true; + } else if (m_propertyToTheme.contains(iconProperty)) { + QtVariantProperty *themeProperty = variantProperty(property); + themeProperty->setValue(QString()); + return true; + } + return false; +} + +// -------- DesignerEditorFactory +DesignerEditorFactory::DesignerEditorFactory(QDesignerFormEditorInterface *core, QObject *parent) : + QtVariantEditorFactory(parent), + m_resetDecorator(new ResetDecorator(this)), + m_changingPropertyValue(false), + m_core(core), + m_spacing(-1) +{ + connect(m_resetDecorator, SIGNAL(resetProperty(QtProperty*)), this, SIGNAL(resetProperty(QtProperty*))); +} + +DesignerEditorFactory::~DesignerEditorFactory() +{ +} + +void DesignerEditorFactory::setSpacing(int spacing) +{ + m_spacing = spacing; + m_resetDecorator->setSpacing(spacing); +} + +void DesignerEditorFactory::setFormWindowBase(qdesigner_internal::FormWindowBase *fwb) +{ + m_fwb = fwb; + DesignerPixmapCache *cache = 0; + if (fwb) + cache = fwb->pixmapCache(); + QMapIterator itPixmapEditor(m_editorToPixmapProperty); + while (itPixmapEditor.hasNext()) { + PixmapEditor *pe = itPixmapEditor.next().key(); + pe->setPixmapCache(cache); + } + QMapIterator itIconEditor(m_editorToIconProperty); + while (itIconEditor.hasNext()) { + PixmapEditor *pe = itIconEditor.next().key(); + pe->setPixmapCache(cache); + } +} + +void DesignerEditorFactory::connectPropertyManager(QtVariantPropertyManager *manager) +{ + m_resetDecorator->connectPropertyManager(manager); + connect(manager, SIGNAL(attributeChanged(QtProperty*,QString,QVariant)), + this, SLOT(slotAttributeChanged(QtProperty*,QString,QVariant))); + connect(manager, SIGNAL(valueChanged(QtProperty*,QVariant)), + this, SLOT(slotValueChanged(QtProperty*,QVariant))); + connect(manager, SIGNAL(propertyChanged(QtProperty*)), + this, SLOT(slotPropertyChanged(QtProperty*))); + QtVariantEditorFactory::connectPropertyManager(manager); +} + +void DesignerEditorFactory::disconnectPropertyManager(QtVariantPropertyManager *manager) +{ + m_resetDecorator->disconnectPropertyManager(manager); + disconnect(manager, SIGNAL(attributeChanged(QtProperty*,QString,QVariant)), + this, SLOT(slotAttributeChanged(QtProperty*,QString,QVariant))); + disconnect(manager, SIGNAL(valueChanged(QtProperty*,QVariant)), + this, SLOT(slotValueChanged(QtProperty*,QVariant))); + disconnect(manager, SIGNAL(propertyChanged(QtProperty*)), + this, SLOT(slotPropertyChanged(QtProperty*))); + QtVariantEditorFactory::disconnectPropertyManager(manager); +} + +// A helper that calls a setter with a value on a pointer list of editor objects. +// Could use QList instead of EditorContainer/Editor, but that crashes VS 6. +template +static inline void applyToEditors(const EditorContainer &list, void (Editor::*setter)(SetterParameter), const Value &value) +{ + typedef Q_TYPENAME EditorContainer::const_iterator ListIterator; + if (list.empty()) { + return; + } + const ListIterator end = list.constEnd(); + for (ListIterator it = list.constBegin(); it != end; ++it) { + Editor &editor = *(*it); + (editor.*setter)(value); + } +} + +void DesignerEditorFactory::slotAttributeChanged(QtProperty *property, const QString &attribute, const QVariant &value) +{ + QtVariantPropertyManager *manager = propertyManager(property); + const int type = manager->propertyType(property); + if (type == DesignerPropertyManager::designerPixmapTypeId() && attribute == QLatin1String(defaultResourceAttributeC)) { + const QPixmap pixmap = qvariant_cast(value); + applyToEditors(m_pixmapPropertyToEditors.value(property), &PixmapEditor::setDefaultPixmap, pixmap); + } else if (type == DesignerPropertyManager::designerStringTypeId() || type == QVariant::String) { + if (attribute == QLatin1String(validationModesAttributeC)) { + const TextPropertyValidationMode validationMode = static_cast(value.toInt()); + applyToEditors(m_stringPropertyToEditors.value(property), &TextEditor::setTextPropertyValidationMode, validationMode); + } + if (attribute == QLatin1String(fontAttributeC)) { + const QFont font = qvariant_cast(value); + applyToEditors(m_stringPropertyToEditors.value(property), &TextEditor::setRichTextDefaultFont, font); + } + if (attribute == QLatin1String(themeAttributeC)) { + const bool themeEnabled = value.toBool(); + applyToEditors(m_stringPropertyToEditors.value(property), &TextEditor::setIconThemeModeEnabled, themeEnabled); + } + } else if (type == QVariant::Palette && attribute == QLatin1String(superPaletteAttributeC)) { + const QPalette palette = qvariant_cast(value); + applyToEditors(m_palettePropertyToEditors.value(property), &PaletteEditorButton::setSuperPalette, palette); + } +} + +void DesignerEditorFactory::slotPropertyChanged(QtProperty *property) +{ + QtVariantPropertyManager *manager = propertyManager(property); + const int type = manager->propertyType(property); + if (type == DesignerPropertyManager::designerIconTypeId()) { + QPixmap defaultPixmap; + if (!property->isModified()) + defaultPixmap = qvariant_cast(manager->attributeValue(property, QLatin1String(defaultResourceAttributeC))).pixmap(16, 16); + else if (m_fwb) + defaultPixmap = m_fwb->iconCache()->icon(qvariant_cast(manager->value(property))).pixmap(16, 16); + QList editors = m_iconPropertyToEditors.value(property); + QListIterator it(editors); + while (it.hasNext()) { + PixmapEditor *editor = it.next(); + editor->setDefaultPixmap(defaultPixmap); + } + } +} + +void DesignerEditorFactory::slotValueChanged(QtProperty *property, const QVariant &value) +{ + if (m_changingPropertyValue) + return; + + QtVariantPropertyManager *manager = propertyManager(property); + const int type = manager->propertyType(property); + switch (type) { + case QVariant::String: + applyToEditors(m_stringPropertyToEditors.value(property), &TextEditor::setText, value.toString()); + break; + case QVariant::Palette: + applyToEditors(m_palettePropertyToEditors.value(property), &PaletteEditorButton::setPalette, qvariant_cast(value)); + break; + case QVariant::UInt: + applyToEditors(m_uintPropertyToEditors.value(property), &QLineEdit::setText, QString::number(value.toUInt())); + break; + case QVariant::LongLong: + applyToEditors(m_longLongPropertyToEditors.value(property), &QLineEdit::setText, QString::number(value.toLongLong())); + break; + case QVariant::ULongLong: + applyToEditors(m_uLongLongPropertyToEditors.value(property), &QLineEdit::setText, QString::number(value.toULongLong())); + break; + case QVariant::Url: + applyToEditors(m_urlPropertyToEditors.value(property), &TextEditor::setText, value.toUrl().toString()); + break; + case QVariant::ByteArray: + applyToEditors(m_byteArrayPropertyToEditors.value(property), &TextEditor::setText, QString::fromUtf8(value.toByteArray())); + break; + case QVariant::StringList: + applyToEditors(m_stringListPropertyToEditors.value(property), &StringListEditorButton::setStringList, value.toStringList()); + break; + default: + if (type == DesignerPropertyManager::designerIconTypeId()) { + PropertySheetIconValue iconValue = qvariant_cast(value); + const QString theme = iconValue.theme(); + applyToEditors(m_iconPropertyToEditors.value(property), &PixmapEditor::setTheme, iconValue.theme()); + applyToEditors(m_iconPropertyToEditors.value(property), &PixmapEditor::setPath, iconValue.pixmap(QIcon::Normal, QIcon::Off).path()); + } else if (type == DesignerPropertyManager::designerPixmapTypeId()) { + applyToEditors(m_pixmapPropertyToEditors.value(property), &PixmapEditor::setPath, qvariant_cast(value).path()); + } else if (type == DesignerPropertyManager::designerStringTypeId()) { + applyToEditors(m_stringPropertyToEditors.value(property), &TextEditor::setText, qvariant_cast(value).value()); + } else if (type == DesignerPropertyManager::designerKeySequenceTypeId()) { + applyToEditors(m_keySequencePropertyToEditors.value(property), &QtKeySequenceEdit::setKeySequence, qvariant_cast(value).value()); + } + break; + } +} + +TextEditor *DesignerEditorFactory::createTextEditor(QWidget *parent, TextPropertyValidationMode vm, const QString &value) +{ + TextEditor *rc = new TextEditor(m_core, parent); + rc->setText(value); + rc->setSpacing(m_spacing); + rc->setTextPropertyValidationMode(vm); + connect(rc, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + return rc; +} + +QWidget *DesignerEditorFactory::createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QWidget *editor = 0; + const int type = manager->propertyType(property); + switch (type) { + case QVariant::Bool: { + editor = QtVariantEditorFactory::createEditor(manager, property, parent); + QtBoolEdit *boolEdit = qobject_cast(editor); + if (boolEdit) + boolEdit->setTextVisible(false); + } + break; + case QVariant::String: { + const TextPropertyValidationMode tvm = static_cast(manager->attributeValue(property, QLatin1String(validationModesAttributeC)).toInt()); + TextEditor *ed = createTextEditor(parent, tvm, manager->value(property).toString()); + const QVariant richTextDefaultFont = manager->attributeValue(property, QLatin1String(fontAttributeC)); + if (richTextDefaultFont.type() == QVariant::Font) + ed->setRichTextDefaultFont(qvariant_cast(richTextDefaultFont)); + const bool themeEnabled = manager->attributeValue(property, QLatin1String(themeAttributeC)).toBool(); + ed->setIconThemeModeEnabled(themeEnabled); + m_stringPropertyToEditors[property].append(ed); + m_editorToStringProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotStringTextChanged(QString))); + editor = ed; + } + break; + case QVariant::Palette: { + PaletteEditorButton *ed = new PaletteEditorButton(m_core, qvariant_cast(manager->value(property)), parent); + ed->setSuperPalette(qvariant_cast(manager->attributeValue(property, QLatin1String(superPaletteAttributeC)))); + m_palettePropertyToEditors[property].append(ed); + m_editorToPaletteProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(paletteChanged(QPalette)), this, SLOT(slotPaletteChanged(QPalette))); + editor = ed; + } + break; + case QVariant::UInt: { + QLineEdit *ed = new QLineEdit(parent); + ed->setValidator(new QULongLongValidator(0, UINT_MAX, ed)); + ed->setText(QString::number(manager->value(property).toUInt())); + m_uintPropertyToEditors[property].append(ed); + m_editorToUintProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotUintChanged(QString))); + editor = ed; + } + break; + case QVariant::LongLong: { + QLineEdit *ed = new QLineEdit(parent); + ed->setValidator(new QLongLongValidator(ed)); + ed->setText(QString::number(manager->value(property).toLongLong())); + m_longLongPropertyToEditors[property].append(ed); + m_editorToLongLongProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotLongLongChanged(QString))); + editor = ed; + } + break; + case QVariant::ULongLong: { + QLineEdit *ed = new QLineEdit(parent); + ed->setValidator(new QULongLongValidator(ed)); + ed->setText(QString::number(manager->value(property).toULongLong())); + m_uLongLongPropertyToEditors[property].append(ed); + m_editorToULongLongProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotULongLongChanged(QString))); + editor = ed; + } + break; + case QVariant::Url: { + TextEditor *ed = createTextEditor(parent, ValidationURL, manager->value(property).toUrl().toString()); + ed->setUpdateMode(TextPropertyEditor::UpdateOnFinished); + m_urlPropertyToEditors[property].append(ed); + m_editorToUrlProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotUrlChanged(QString))); + editor = ed; + } + break; + case QVariant::ByteArray: { + TextEditor *ed = createTextEditor(parent, ValidationMultiLine, QString::fromUtf8(manager->value(property).toByteArray())); + m_byteArrayPropertyToEditors[property].append(ed); + m_editorToByteArrayProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotByteArrayChanged(QString))); + editor = ed; + } + break; + case QVariant::StringList: { + StringListEditorButton *ed = new StringListEditorButton(manager->value(property).toStringList(), parent); + m_stringListPropertyToEditors[property].append(ed); + m_editorToStringListProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(stringListChanged(QStringList)), this, SLOT(slotStringListChanged(QStringList))); + editor = ed; + } + break; + default: + if (type == DesignerPropertyManager::designerPixmapTypeId()) { + PixmapEditor *ed = new PixmapEditor(m_core, parent); + ed->setPixmapCache(m_fwb->pixmapCache()); + ed->setPath(qvariant_cast(manager->value(property)).path()); + ed->setDefaultPixmap(qvariant_cast(manager->attributeValue(property, QLatin1String(defaultResourceAttributeC)))); + ed->setSpacing(m_spacing); + m_pixmapPropertyToEditors[property].append(ed); + m_editorToPixmapProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(pathChanged(QString)), this, SLOT(slotPixmapChanged(QString))); + editor = ed; + } else if (type == DesignerPropertyManager::designerIconTypeId()) { + PixmapEditor *ed = new PixmapEditor(m_core, parent); + ed->setPixmapCache(m_fwb->pixmapCache()); + ed->setIconThemeModeEnabled(true); + PropertySheetIconValue value = qvariant_cast(manager->value(property)); + ed->setTheme(value.theme()); + ed->setPath(value.pixmap(QIcon::Normal, QIcon::Off).path()); + QPixmap defaultPixmap; + if (!property->isModified()) + defaultPixmap = qvariant_cast(manager->attributeValue(property, QLatin1String(defaultResourceAttributeC))).pixmap(16, 16); + else if (m_fwb) + defaultPixmap = m_fwb->iconCache()->icon(value).pixmap(16, 16); + ed->setDefaultPixmap(defaultPixmap); + ed->setSpacing(m_spacing); + m_iconPropertyToEditors[property].append(ed); + m_editorToIconProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(pathChanged(QString)), this, SLOT(slotIconChanged(QString))); + connect(ed, SIGNAL(themeChanged(QString)), this, SLOT(slotIconThemeChanged(QString))); + editor = ed; + } else if (type == DesignerPropertyManager::designerStringTypeId()) { + const TextPropertyValidationMode tvm = static_cast(manager->attributeValue(property, QLatin1String(validationModesAttributeC)).toInt()); + TextEditor *ed = createTextEditor(parent, tvm, qvariant_cast(manager->value(property)).value()); + const QVariant richTextDefaultFont = manager->attributeValue(property, QLatin1String(fontAttributeC)); + if (richTextDefaultFont.type() == QVariant::Font) + ed->setRichTextDefaultFont(qvariant_cast(richTextDefaultFont)); + m_stringPropertyToEditors[property].append(ed); + m_editorToStringProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(textChanged(QString)), this, SLOT(slotStringTextChanged(QString))); + editor = ed; + } else if (type == DesignerPropertyManager::designerKeySequenceTypeId()) { + QtKeySequenceEdit *ed = new QtKeySequenceEdit(parent); + ed->setKeySequence(qvariant_cast(manager->value(property)).value()); + m_keySequencePropertyToEditors[property].append(ed); + m_editorToKeySequenceProperty[ed] = property; + connect(ed, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(ed, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(slotKeySequenceChanged(QKeySequence))); + editor = ed; + } else { + editor = QtVariantEditorFactory::createEditor(manager, property, parent); + } + break; + } + return m_resetDecorator->editor(editor, + manager->variantProperty(property)->attributeValue(QLatin1String(resettableAttributeC)).toBool(), + manager, property, parent); +} + +template +bool removeEditor(QObject *object, + QMap > *propertyToEditors, + QMap *editorToProperty) +{ + if (!propertyToEditors) + return false; + if (!editorToProperty) + return false; + QMapIterator it(*editorToProperty); + while (it.hasNext()) { + Editor editor = it.next().key(); + if (editor == object) { + QtProperty *prop = it.value(); + (*propertyToEditors)[prop].removeAll(editor); + if ((*propertyToEditors)[prop].count() == 0) + propertyToEditors->remove(prop); + editorToProperty->remove(editor); + return true; + } + } + return false; +} + +void DesignerEditorFactory::slotEditorDestroyed(QObject *object) +{ + if (removeEditor(object, &m_stringPropertyToEditors, &m_editorToStringProperty)) + return; + if (removeEditor(object, &m_keySequencePropertyToEditors, &m_editorToKeySequenceProperty)) + return; + if (removeEditor(object, &m_palettePropertyToEditors, &m_editorToPaletteProperty)) + return; + if (removeEditor(object, &m_pixmapPropertyToEditors, &m_editorToPixmapProperty)) + return; + if (removeEditor(object, &m_iconPropertyToEditors, &m_editorToIconProperty)) + return; + if (removeEditor(object, &m_uintPropertyToEditors, &m_editorToUintProperty)) + return; + if (removeEditor(object, &m_longLongPropertyToEditors, &m_editorToLongLongProperty)) + return; + if (removeEditor(object, &m_uLongLongPropertyToEditors, &m_editorToULongLongProperty)) + return; + if (removeEditor(object, &m_urlPropertyToEditors, &m_editorToUrlProperty)) + return; + if (removeEditor(object, &m_byteArrayPropertyToEditors, &m_editorToByteArrayProperty)) + return; + if (removeEditor(object, &m_stringListPropertyToEditors, &m_editorToStringListProperty)) + return; +} + +template +bool updateManager(QtVariantEditorFactory *factory, bool *changingPropertyValue, + const QMap &editorToProperty, QWidget *editor, const QVariant &value) +{ + if (!editor) + return false; + QMapIterator it(editorToProperty); + while (it.hasNext()) { + if (it.next().key() == editor) { + QtProperty *prop = it.value(); + QtVariantPropertyManager *manager = factory->propertyManager(prop); + *changingPropertyValue = true; + manager->variantProperty(prop)->setValue(value); + *changingPropertyValue = false; + return true; + } + } + return false; +} + +void DesignerEditorFactory::slotUintChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToUintProperty, qobject_cast(sender()), value.toUInt()); +} + +void DesignerEditorFactory::slotLongLongChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToLongLongProperty, qobject_cast(sender()), value.toLongLong()); +} + +void DesignerEditorFactory::slotULongLongChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToULongLongProperty, qobject_cast(sender()), value.toULongLong()); +} + +void DesignerEditorFactory::slotUrlChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToUrlProperty, qobject_cast(sender()), QUrl(value)); +} + +void DesignerEditorFactory::slotByteArrayChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToByteArrayProperty, qobject_cast(sender()), value.toUtf8()); +} + +void DesignerEditorFactory::slotStringTextChanged(const QString &value) +{ + QMapIterator it(m_editorToStringProperty); + while (it.hasNext()) { + if (it.next().key() == sender()) { + QtProperty *prop = it.value(); + QtVariantPropertyManager *manager = propertyManager(prop); + QtVariantProperty *varProp = manager->variantProperty(prop); + QVariant val = varProp->value(); + if (val.userType() == DesignerPropertyManager::designerStringTypeId()) { + PropertySheetStringValue strVal = qvariant_cast(val); + strVal.setValue(value); + // Disable translation if no translation subproperties exist. + if (varProp->subProperties().empty()) + strVal.setTranslatable(false); + val = QVariant::fromValue(strVal); + } else { + val = QVariant(value); + } + m_changingPropertyValue = true; + manager->variantProperty(prop)->setValue(val); + m_changingPropertyValue = false; + } + } +} + +void DesignerEditorFactory::slotKeySequenceChanged(const QKeySequence &value) +{ + QMapIterator it(m_editorToKeySequenceProperty); + while (it.hasNext()) { + if (it.next().key() == sender()) { + QtProperty *prop = it.value(); + QtVariantPropertyManager *manager = propertyManager(prop); + QtVariantProperty *varProp = manager->variantProperty(prop); + QVariant val = varProp->value(); + if (val.userType() == DesignerPropertyManager::designerKeySequenceTypeId()) { + PropertySheetKeySequenceValue keyVal = qvariant_cast(val); + keyVal.setValue(value); + val = QVariant::fromValue(keyVal); + } else { + val = QVariant::fromValue(value); + } + m_changingPropertyValue = true; + manager->variantProperty(prop)->setValue(val); + m_changingPropertyValue = false; + } + } +} + +void DesignerEditorFactory::slotPaletteChanged(const QPalette &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToPaletteProperty, qobject_cast(sender()), QVariant::fromValue(value)); +} + +void DesignerEditorFactory::slotPixmapChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToPixmapProperty, qobject_cast(sender()), + QVariant::fromValue(PropertySheetPixmapValue(value))); +} + +void DesignerEditorFactory::slotIconChanged(const QString &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToIconProperty, qobject_cast(sender()), + QVariant::fromValue(PropertySheetIconValue(PropertySheetPixmapValue(value)))); +} + +void DesignerEditorFactory::slotIconThemeChanged(const QString &value) +{ + PropertySheetIconValue icon; + icon.setTheme(value); + updateManager(this, &m_changingPropertyValue, m_editorToIconProperty, qobject_cast(sender()), + QVariant::fromValue(icon)); +} + +void DesignerEditorFactory::slotStringListChanged(const QStringList &value) +{ + updateManager(this, &m_changingPropertyValue, m_editorToStringListProperty, qobject_cast(sender()), QVariant::fromValue(value)); +} + +ResetDecorator::~ResetDecorator() +{ + QList editors = m_resetWidgetToProperty.keys(); + QListIterator it(editors); + while (it.hasNext()) + delete it.next(); +} + +void ResetDecorator::connectPropertyManager(QtAbstractPropertyManager *manager) +{ + connect(manager, SIGNAL(propertyChanged(QtProperty*)), + this, SLOT(slotPropertyChanged(QtProperty*))); +} + +void ResetDecorator::disconnectPropertyManager(QtAbstractPropertyManager *manager) +{ + disconnect(manager, SIGNAL(propertyChanged(QtProperty*)), + this, SLOT(slotPropertyChanged(QtProperty*))); +} + +void ResetDecorator::setSpacing(int spacing) +{ + m_spacing = spacing; +} + +QWidget *ResetDecorator::editor(QWidget *subEditor, bool resettable, QtAbstractPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + Q_UNUSED(manager) + + ResetWidget *resetWidget = 0; + if (resettable) { + resetWidget = new ResetWidget(property, parent); + resetWidget->setSpacing(m_spacing); + resetWidget->setResetEnabled(property->isModified()); + resetWidget->setValueText(property->valueText()); + resetWidget->setValueIcon(property->valueIcon()); + resetWidget->setAutoFillBackground(true); + connect(resetWidget, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*))); + connect(resetWidget, SIGNAL(resetProperty(QtProperty*)), this, SIGNAL(resetProperty(QtProperty*))); + m_createdResetWidgets[property].append(resetWidget); + m_resetWidgetToProperty[resetWidget] = property; + } + if (subEditor) { + if (resetWidget) { + subEditor->setParent(resetWidget); + resetWidget->setWidget(subEditor); + } + } + if (resetWidget) + return resetWidget; + return subEditor; +} + +void ResetDecorator::slotPropertyChanged(QtProperty *property) +{ + QMap >::ConstIterator prIt = m_createdResetWidgets.constFind(property); + if (prIt == m_createdResetWidgets.constEnd()) + return; + + const QList editors = prIt.value(); + const QList::ConstIterator cend = editors.constEnd(); + for (QList::ConstIterator itEditor = editors.constBegin(); itEditor != cend; ++itEditor) { + ResetWidget *widget = *itEditor; + widget->setResetEnabled(property->isModified()); + widget->setValueText(property->valueText()); + widget->setValueIcon(property->valueIcon()); + } +} + +void ResetDecorator::slotEditorDestroyed(QObject *object) +{ + const QMap::ConstIterator rcend = m_resetWidgetToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_resetWidgetToProperty.constBegin(); itEditor != rcend; ++itEditor) { + if (itEditor.key() == object) { + ResetWidget *editor = itEditor.key(); + QtProperty *property = itEditor.value(); + m_resetWidgetToProperty.remove(editor); + m_createdResetWidgets[property].removeAll(editor); + if (m_createdResetWidgets[property].isEmpty()) + m_createdResetWidgets.remove(property); + return; + } + } +} + +} + +QT_END_NAMESPACE + +#include "moc_designerpropertymanager.cpp" +#include diff --git a/src/designer/components/propertyeditor/designerpropertymanager.h b/src/designer/components/propertyeditor/designerpropertymanager.h new file mode 100644 index 000000000..b521a59d5 --- /dev/null +++ b/src/designer/components/propertyeditor/designerpropertymanager.h @@ -0,0 +1,315 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERPROPERTYMANAGER_H +#define DESIGNERPROPERTYMANAGER_H + +#include "qtvariantproperty.h" +#include "brushpropertymanager.h" +#include "fontpropertymanager.h" + +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QPair DesignerIntPair; +typedef QList DesignerFlagList; + +class QDesignerFormEditorInterface; +class QLineEdit; +class QUrl; +class QtKeySequenceEdit; + +namespace qdesigner_internal +{ + +class ResetWidget; + +class TextEditor; +class PaletteEditorButton; +class PixmapEditor; +class StringListEditorButton; +class FormWindowBase; + +class ResetDecorator : public QObject +{ + Q_OBJECT +public: + ResetDecorator(QObject *parent = 0) : QObject(parent), m_spacing(-1) {} + ~ResetDecorator(); + + void connectPropertyManager(QtAbstractPropertyManager *manager); + QWidget *editor(QWidget *subEditor, bool resettable, QtAbstractPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtAbstractPropertyManager *manager); + void setSpacing(int spacing); +signals: + void resetProperty(QtProperty *property); +private slots: + void slotPropertyChanged(QtProperty *property); + void slotEditorDestroyed(QObject *object); +private: + QMap > m_createdResetWidgets; + QMap m_resetWidgetToProperty; + int m_spacing; +}; + +class DesignerPropertyManager : public QtVariantPropertyManager +{ + Q_OBJECT +public: + explicit DesignerPropertyManager(QDesignerFormEditorInterface *core, QObject *parent = 0); + ~DesignerPropertyManager(); + + virtual QStringList attributes(int propertyType) const; + virtual int attributeType(int propertyType, const QString &attribute) const; + + virtual QVariant attributeValue(const QtProperty *property, const QString &attribute) const; + virtual bool isPropertyTypeSupported(int propertyType) const; + virtual QVariant value(const QtProperty *property) const; + virtual int valueType(int propertyType) const; + virtual QString valueText(const QtProperty *property) const; + virtual QIcon valueIcon(const QtProperty *property) const; + + bool resetFontSubProperty(QtProperty *property); + bool resetIconSubProperty(QtProperty *subProperty); + + void reloadResourceProperties(); + + static int designerFlagTypeId(); + static int designerFlagListTypeId(); + static int designerAlignmentTypeId(); + static int designerPixmapTypeId(); + static int designerIconTypeId(); + static int designerStringTypeId(); + static int designerKeySequenceTypeId(); + + void setObject(QObject *object) { m_object = object; } + +public Q_SLOTS: + virtual void setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value); + virtual void setValue(QtProperty *property, const QVariant &value); +Q_SIGNALS: + // sourceOfChange - a subproperty (or just property) which caused a change + //void valueChanged(QtProperty *property, const QVariant &value, QtProperty *sourceOfChange); + void valueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling); +protected: + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private Q_SLOTS: + void slotValueChanged(QtProperty *property, const QVariant &value); + void slotPropertyDestroyed(QtProperty *property); +private: + void createIconSubProperty(QtProperty *iconProperty, QIcon::Mode mode, QIcon::State state, const QString &subName); + + typedef QMap PropertyBoolMap; + PropertyBoolMap m_resetMap; + + int bitCount(int mask) const; + struct FlagData + { + FlagData() : val(0) {} + uint val; + DesignerFlagList flags; + QList values; + }; + typedef QMap PropertyFlagDataMap; + PropertyFlagDataMap m_flagValues; + typedef QMap > PropertyToPropertyListMap; + PropertyToPropertyListMap m_propertyToFlags; + QMap m_flagToProperty; + + int alignToIndexH(uint align) const; + int alignToIndexV(uint align) const; + uint indexHToAlign(int idx) const; + uint indexVToAlign(int idx) const; + QString indexHToString(int idx) const; + QString indexVToString(int idx) const; + QMap m_alignValues; + typedef QMap PropertyToPropertyMap; + PropertyToPropertyMap m_propertyToAlignH; + PropertyToPropertyMap m_propertyToAlignV; + PropertyToPropertyMap m_alignHToProperty; + PropertyToPropertyMap m_alignVToProperty; + + QMap, QtProperty *> > m_propertyToIconSubProperties; + QMap > m_iconSubPropertyToState; + PropertyToPropertyMap m_iconSubPropertyToProperty; + PropertyToPropertyMap m_propertyToTheme; + + QMap m_stringValues; + QMap m_stringToComment; + QMap m_stringToTranslatable; + QMap m_stringToDisambiguation; + + QMap m_commentToString; + QMap m_translatableToString; + QMap m_disambiguationToString; + + QMap m_keySequenceValues; + QMap m_keySequenceToComment; + QMap m_keySequenceToTranslatable; + QMap m_keySequenceToDisambiguation; + + QMap m_commentToKeySequence; + QMap m_translatableToKeySequence; + QMap m_disambiguationToKeySequence; + + struct PaletteData + { + QPalette val; + QPalette superPalette; + }; + typedef QMap PropertyPaletteDataMap; + PropertyPaletteDataMap m_paletteValues; + + QMap m_pixmapValues; + QMap m_iconValues; + + QMap m_uintValues; + QMap m_longLongValues; + QMap m_uLongLongValues; + QMap m_urlValues; + QMap m_byteArrayValues; + QMap m_stringListValues; + + typedef QMap PropertyIntMap; + PropertyIntMap m_stringAttributes; + typedef QMap PropertyFontMap; + PropertyFontMap m_stringFontAttributes; + PropertyBoolMap m_stringThemeAttributes; + + BrushPropertyManager m_brushManager; + FontPropertyManager m_fontManager; + + QMap m_defaultPixmaps; + QMap m_defaultIcons; + + bool m_changingSubValue; + QDesignerFormEditorInterface *m_core; + + QObject *m_object; + + QtProperty *m_sourceOfChange; +}; + +class DesignerEditorFactory : public QtVariantEditorFactory +{ + Q_OBJECT +public: + explicit DesignerEditorFactory(QDesignerFormEditorInterface *core, QObject *parent = 0); + ~DesignerEditorFactory(); + void setSpacing(int spacing); + void setFormWindowBase(FormWindowBase *fwb); +signals: + void resetProperty(QtProperty *property); +protected: + void connectPropertyManager(QtVariantPropertyManager *manager); + QWidget *createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtVariantPropertyManager *manager); +private slots: + void slotEditorDestroyed(QObject *object); + void slotAttributeChanged(QtProperty *property, const QString &attribute, const QVariant &value); + void slotPropertyChanged(QtProperty *property); + void slotValueChanged(QtProperty *property, const QVariant &value); + void slotStringTextChanged(const QString &value); + void slotKeySequenceChanged(const QKeySequence &value); + void slotPaletteChanged(const QPalette &value); + void slotPixmapChanged(const QString &value); + void slotIconChanged(const QString &value); + void slotIconThemeChanged(const QString &value); + void slotUintChanged(const QString &value); + void slotLongLongChanged(const QString &value); + void slotULongLongChanged(const QString &value); + void slotUrlChanged(const QString &value); + void slotByteArrayChanged(const QString &value); + void slotStringListChanged(const QStringList &value); +private: + TextEditor *createTextEditor(QWidget *parent, TextPropertyValidationMode vm, const QString &value); + + ResetDecorator *m_resetDecorator; + bool m_changingPropertyValue; + QDesignerFormEditorInterface *m_core; + FormWindowBase *m_fwb; + + int m_spacing; + + QMap > m_stringPropertyToEditors; + QMap m_editorToStringProperty; + QMap > m_keySequencePropertyToEditors; + QMap m_editorToKeySequenceProperty; + QMap > m_palettePropertyToEditors; + QMap m_editorToPaletteProperty; + QMap > m_pixmapPropertyToEditors; + QMap m_editorToPixmapProperty; + QMap > m_iconPropertyToEditors; + QMap m_editorToIconProperty; + QMap > m_uintPropertyToEditors; + QMap m_editorToUintProperty; + QMap > m_longLongPropertyToEditors; + QMap m_editorToLongLongProperty; + QMap > m_uLongLongPropertyToEditors; + QMap m_editorToULongLongProperty; + QMap > m_urlPropertyToEditors; + QMap m_editorToUrlProperty; + QMap > m_byteArrayPropertyToEditors; + QMap m_editorToByteArrayProperty; + QMap > m_stringListPropertyToEditors; + QMap m_editorToStringListProperty; +}; + +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(DesignerIntPair) +Q_DECLARE_METATYPE(DesignerFlagList) + +#endif + diff --git a/src/designer/components/propertyeditor/fontmapping.xml b/src/designer/components/propertyeditor/fontmapping.xml new file mode 100644 index 000000000..1d008327d --- /dev/null +++ b/src/designer/components/propertyeditor/fontmapping.xml @@ -0,0 +1,73 @@ + + + + + + +]> + + +DejaVu SansDejaVu Sans [&qe;] +DejaVu SansDejaVu Sans [&qe;] +DejaVu SansDejaVu Sans [&qe;] +DejaVu SansDejaVu Sans [&qe;] +DejaVu Sans MonoDejaVu Sans Mono [&qe;] +DejaVu Sans MonoDejaVu Sans Mono [&qe;] +DejaVu Sans MonoDejaVu Sans Mono [&qe;] +DejaVu Sans MonoDejaVu Sans Mono [&qe;] +DejaVu SerifDejaVu Serif [&qe;] +DejaVu SerifDejaVu Serif [&qe;] +DejaVu SerifDejaVu Serif [&qe;] +DejaVu SerifDejaVu Serif [&qe;] +Bitstream Vera SansBitstream Vera Sans [&qe;] +Bitstream Vera SansBitstream Vera Sans [&qe;] +Bitstream Vera SansBitstream Vera Sans [&qe;] +Bitstream Vera SansBitstream Vera Sans [&qe;] +Bitstream Vera Sans MonoBitstream Vera Sans Mono [&qe;] +Bitstream Vera Sans MonoBitstream Vera Sans Mono [&qe;] +Bitstream Vera Sans MonoBitstream Vera Sans Mono [&qe;] +Bitstream Vera Sans MonoBitstream Vera Sans Mono [&qe;] +Bitstream Vera SerifBitstream Vera Serif [&qe;] +Bitstream Vera SerifBitstream Vera Serif [&qe;] + diff --git a/src/designer/components/propertyeditor/fontpropertymanager.cpp b/src/designer/components/propertyeditor/fontpropertymanager.cpp new file mode 100644 index 000000000..8dc256222 --- /dev/null +++ b/src/designer/components/propertyeditor/fontpropertymanager.cpp @@ -0,0 +1,377 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fontpropertymanager.h" +#include "qtpropertymanager.h" +#include "qtvariantproperty.h" +#include "qtpropertybrowserutils_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + static const char *aliasingC[] = { + QT_TRANSLATE_NOOP("FontPropertyManager", "PreferDefault"), + QT_TRANSLATE_NOOP("FontPropertyManager", "NoAntialias"), + QT_TRANSLATE_NOOP("FontPropertyManager", "PreferAntialias") + }; + + FontPropertyManager::FontPropertyManager() : + m_createdFontProperty(0) + { + const int nameCount = sizeof(aliasingC)/sizeof(const char *); + for (int i = 0; i < nameCount; i++) + m_aliasingEnumNames.push_back(QCoreApplication::translate("FontPropertyManager", aliasingC[i])); + + QString errorMessage; + if (!readFamilyMapping(&m_familyMappings, &errorMessage)) { + designerWarning(errorMessage); + } + + } + + void FontPropertyManager::preInitializeProperty(QtProperty *property, + int type, + ResetMap &resetMap) + { + if (m_createdFontProperty) { + PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(m_createdFontProperty); + if (it == m_propertyToFontSubProperties.end()) + it = m_propertyToFontSubProperties.insert(m_createdFontProperty, PropertyList()); + const int index = it.value().size(); + m_fontSubPropertyToFlag.insert(property, index); + it.value().push_back(property); + m_fontSubPropertyToProperty[property] = m_createdFontProperty; + resetMap[property] = true; + } + + if (type == QVariant::Font) + m_createdFontProperty = property; + } + + // Map the font family names to display names retrieved from the XML configuration + static QStringList designerFamilyNames(QStringList families, const FontPropertyManager::NameMap &nm) + { + if (nm.empty()) + return families; + + const FontPropertyManager::NameMap::const_iterator ncend = nm.constEnd(); + const QStringList::iterator end = families.end(); + for (QStringList::iterator it = families.begin(); it != end; ++it) { + const FontPropertyManager::NameMap::const_iterator nit = nm.constFind(*it); + if (nit != ncend) + *it = nit.value(); + } + return families; + } + + void FontPropertyManager::postInitializeProperty(QtVariantPropertyManager *vm, + QtProperty *property, + int type, + int enumTypeId) + { + if (type != QVariant::Font) + return; + + // This will cause a recursion + QtVariantProperty *antialiasing = vm->addProperty(enumTypeId, QCoreApplication::translate("FontPropertyManager", "Antialiasing")); + const QFont font = qvariant_cast(vm->variantProperty(property)->value()); + + antialiasing->setAttribute(QLatin1String("enumNames"), m_aliasingEnumNames); + antialiasing->setValue(antialiasingToIndex(font.styleStrategy())); + property->addSubProperty(antialiasing); + + m_propertyToAntialiasing[property] = antialiasing; + m_antialiasingToProperty[antialiasing] = property; + // Fiddle family names + if (!m_familyMappings.empty()) { + const PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(m_createdFontProperty); + QtVariantProperty *familyProperty = vm->variantProperty(it.value().front()); + const QString enumNamesAttribute = QLatin1String("enumNames"); + QStringList plainFamilyNames = familyProperty->attributeValue(enumNamesAttribute).toStringList(); + // Did someone load fonts or something? + if (m_designerFamilyNames.size() != plainFamilyNames.size()) + m_designerFamilyNames = designerFamilyNames(plainFamilyNames, m_familyMappings); + familyProperty->setAttribute(enumNamesAttribute, m_designerFamilyNames); + } + // Next + m_createdFontProperty = 0; + } + + bool FontPropertyManager::uninitializeProperty(QtProperty *property) + { + const PropertyToPropertyMap::iterator ait = m_propertyToAntialiasing.find(property); + if (ait != m_propertyToAntialiasing.end()) { + QtProperty *antialiasing = ait.value(); + m_antialiasingToProperty.remove(antialiasing); + m_propertyToAntialiasing.erase(ait); + delete antialiasing; + } + + PropertyToSubPropertiesMap::iterator sit = m_propertyToFontSubProperties.find(property); + if (sit == m_propertyToFontSubProperties.end()) + return false; + + m_propertyToFontSubProperties.erase(sit); + m_fontSubPropertyToFlag.remove(property); + m_fontSubPropertyToProperty.remove(property); + + return true; + } + + void FontPropertyManager::slotPropertyDestroyed(QtProperty *property) + { + removeAntialiasingProperty(property); + } + + void FontPropertyManager::removeAntialiasingProperty(QtProperty *property) + { + const PropertyToPropertyMap::iterator ait = m_antialiasingToProperty.find(property); + if (ait == m_antialiasingToProperty.end()) + return; + m_propertyToAntialiasing[ait.value()] = 0; + m_antialiasingToProperty.erase(ait); + } + + bool FontPropertyManager::resetFontSubProperty(QtVariantPropertyManager *vm, QtProperty *property) + { + const PropertyToPropertyMap::iterator it = m_fontSubPropertyToProperty.find(property); + if (it == m_fontSubPropertyToProperty.end()) + return false; + + QtVariantProperty *fontProperty = vm->variantProperty(it.value()); + + QVariant v = fontProperty->value(); + QFont font = qvariant_cast(v); + unsigned mask = font.resolve(); + const unsigned flag = fontFlag(m_fontSubPropertyToFlag.value(property)); + + mask &= ~flag; + font.resolve(mask); + v.setValue(font); + fontProperty->setValue(v); + return true; + } + + int FontPropertyManager::antialiasingToIndex(QFont::StyleStrategy antialias) + { + switch (antialias) { + case QFont::PreferDefault: return 0; + case QFont::NoAntialias: return 1; + case QFont::PreferAntialias: return 2; + default: break; + } + return 0; + } + + QFont::StyleStrategy FontPropertyManager::indexToAntialiasing(int idx) + { + switch (idx) { + case 0: return QFont::PreferDefault; + case 1: return QFont::NoAntialias; + case 2: return QFont::PreferAntialias; + } + return QFont::PreferDefault; + } + + unsigned FontPropertyManager::fontFlag(int idx) + { + switch (idx) { + case 0: return QFont::FamilyResolved; + case 1: return QFont::SizeResolved; + case 2: return QFont::WeightResolved; + case 3: return QFont::StyleResolved; + case 4: return QFont::UnderlineResolved; + case 5: return QFont::StrikeOutResolved; + case 6: return QFont::KerningResolved; + case 7: return QFont::StyleStrategyResolved; + } + return 0; + } + + FontPropertyManager::ValueChangedResult FontPropertyManager::valueChanged(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value) + { + QtProperty *antialiasingProperty = m_antialiasingToProperty.value(property, 0); + if (!antialiasingProperty) { + if (m_propertyToFontSubProperties.contains(property)) { + updateModifiedState(property, value); + } + return NoMatch; + } + + QtVariantProperty *fontProperty = vm->variantProperty(antialiasingProperty); + const QFont::StyleStrategy newValue = indexToAntialiasing(value.toInt()); + + QFont font = qvariant_cast(fontProperty->value()); + const QFont::StyleStrategy oldValue = font.styleStrategy(); + if (newValue == oldValue) + return Unchanged; + + font.setStyleStrategy(newValue); + fontProperty->setValue(QVariant::fromValue(font)); + return Changed; + } + + void FontPropertyManager::updateModifiedState(QtProperty *property, const QVariant &value) + { + const PropertyToSubPropertiesMap::iterator it = m_propertyToFontSubProperties.find(property); + if (it == m_propertyToFontSubProperties.end()) + return; + + const PropertyList &subProperties = it.value(); + + QFont font = qvariant_cast(value); + const unsigned mask = font.resolve(); + + const int count = subProperties.size(); + for (int index = 0; index < count; index++) { + const unsigned flag = fontFlag(index); + subProperties.at(index)->setModified(mask & flag); + } + } + + void FontPropertyManager::setValue(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value) + { + updateModifiedState(property, value); + + if (QtProperty *antialiasingProperty = m_propertyToAntialiasing.value(property, 0)) { + QtVariantProperty *antialiasing = vm->variantProperty(antialiasingProperty); + if (antialiasing) { + QFont font = qvariant_cast(value); + antialiasing->setValue(antialiasingToIndex(font.styleStrategy())); + } + } + } + + /* Parse a mappings file of the form: + * + * DejaVu SansDejaVu Sans [CE] + * ... which is used to display on which platforms fonts are available.*/ + + static const char *rootTagC = "fontmappings"; + static const char *mappingTagC = "mapping"; + static const char *familyTagC = "family"; + static const char *displayTagC = "display"; + + static QString msgXmlError(const QXmlStreamReader &r, const QString& fileName) + { + return QString::fromUtf8("An error has been encountered at line %1 of %2: %3:").arg(r.lineNumber()).arg(fileName, r.errorString()); + } + + /* Switch stages when encountering a start element (state table) */ + enum ParseStage { ParseBeginning, ParseWithinRoot, ParseWithinMapping, ParseWithinFamily, + ParseWithinDisplay, ParseError }; + + static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement) + { + switch (currentStage) { + case ParseBeginning: + return startElement == QLatin1String(rootTagC) ? ParseWithinRoot : ParseError; + case ParseWithinRoot: + case ParseWithinDisplay: // Next mapping, was in + return startElement == QLatin1String(mappingTagC) ? ParseWithinMapping : ParseError; + case ParseWithinMapping: + return startElement == QLatin1String(familyTagC) ? ParseWithinFamily : ParseError; + case ParseWithinFamily: + return startElement == QLatin1String(displayTagC) ? ParseWithinDisplay : ParseError; + case ParseError: + break; + } + return ParseError; + } + + bool FontPropertyManager::readFamilyMapping(NameMap *rc, QString *errorMessage) + { + rc->clear(); + const QString fileName = QLatin1String(":/trolltech/propertyeditor/fontmapping.xml"); + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly)) { + *errorMessage = QString::fromUtf8("Unable to open %1: %2").arg(fileName, file.errorString()); + return false; + } + + QXmlStreamReader reader(&file); + QXmlStreamReader::TokenType token; + + QString family; + ParseStage stage = ParseBeginning; + do { + token = reader.readNext(); + switch (token) { + case QXmlStreamReader::Invalid: + *errorMessage = msgXmlError(reader, fileName); + return false; + case QXmlStreamReader::StartElement: + stage = nextStage(stage, reader.name()); + switch (stage) { + case ParseError: + reader.raiseError(QString::fromUtf8("Unexpected element <%1>.").arg(reader.name().toString())); + *errorMessage = msgXmlError(reader, fileName); + return false; + case ParseWithinFamily: + family = reader.readElementText(); + break; + case ParseWithinDisplay: + rc->insert(family, reader.readElementText()); + break; + default: + break; + } + default: + break; + } + } while (token != QXmlStreamReader::EndDocument); + return true; + } + +} + +QT_END_NAMESPACE diff --git a/src/designer/components/propertyeditor/fontpropertymanager.h b/src/designer/components/propertyeditor/fontpropertymanager.h new file mode 100644 index 000000000..0bb8d6707 --- /dev/null +++ b/src/designer/components/propertyeditor/fontpropertymanager.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FONTPROPERTYMANAGER_H +#define FONTPROPERTYMANAGER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QtProperty; +class QtVariantPropertyManager; + +class QString; +class QVariant; + +namespace qdesigner_internal { + +/* FontPropertyManager: A mixin for DesignerPropertyManager that manages font + * properties. Adds an antialiasing subproperty and reset flags/mask handling + * for the other subproperties. It also modifies the font family + * enumeration names, which it reads from an XML mapping file that + * contains annotations indicating the platform the font is available on. */ + +class FontPropertyManager { + Q_DISABLE_COPY(FontPropertyManager) + +public: + FontPropertyManager(); + + typedef QMap ResetMap; + typedef QMap NameMap; + + // Call before QtVariantPropertyManager::initializeProperty. + void preInitializeProperty(QtProperty *property, int type, ResetMap &resetMap); + // Call after QtVariantPropertyManager::initializeProperty. This will trigger + // a recursion for the sub properties + void postInitializeProperty(QtVariantPropertyManager *vm, QtProperty *property, int type, int enumTypeId); + + bool uninitializeProperty(QtProperty *property); + + // Call from QtPropertyManager's propertyDestroyed signal + void slotPropertyDestroyed(QtProperty *property); + + bool resetFontSubProperty(QtVariantPropertyManager *vm, QtProperty *subProperty); + + // Call from slotValueChanged(). + enum ValueChangedResult { NoMatch, Unchanged, Changed }; + ValueChangedResult valueChanged(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value); + + // Call from setValue() before calling setValue() on QtVariantPropertyManager. + void setValue(QtVariantPropertyManager *vm, QtProperty *property, const QVariant &value); + + static bool readFamilyMapping(NameMap *rc, QString *errorMessage); + +private: + typedef QMap PropertyToPropertyMap; + typedef QList PropertyList; + typedef QMap PropertyToSubPropertiesMap; + + void removeAntialiasingProperty(QtProperty *); + void updateModifiedState(QtProperty *property, const QVariant &value); + static int antialiasingToIndex(QFont::StyleStrategy antialias); + static QFont::StyleStrategy indexToAntialiasing(int idx); + static unsigned fontFlag(int idx); + + PropertyToPropertyMap m_propertyToAntialiasing; + PropertyToPropertyMap m_antialiasingToProperty; + + PropertyToSubPropertiesMap m_propertyToFontSubProperties; + QMap m_fontSubPropertyToFlag; + PropertyToPropertyMap m_fontSubPropertyToProperty; + QtProperty *m_createdFontProperty; + QStringList m_aliasingEnumNames; + // Font families with Designer annotations + QStringList m_designerFamilyNames; + NameMap m_familyMappings; +}; + +} + +QT_END_NAMESPACE + +#endif // FONTPROPERTYMANAGER_H diff --git a/src/designer/components/propertyeditor/newdynamicpropertydialog.cpp b/src/designer/components/propertyeditor/newdynamicpropertydialog.cpp new file mode 100644 index 000000000..dee350d03 --- /dev/null +++ b/src/designer/components/propertyeditor/newdynamicpropertydialog.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newdynamicpropertydialog.h" +#include "ui_newdynamicpropertydialog.h" +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +NewDynamicPropertyDialog::NewDynamicPropertyDialog(QDesignerDialogGuiInterface *dialogGui, + QWidget *parent) : + QDialog(parent), + m_dialogGui(dialogGui), + m_ui(new Ui::NewDynamicPropertyDialog) +{ + m_ui->setupUi(this); + connect(m_ui->m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(nameChanged(QString))); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_ui->m_comboBox->addItem(QLatin1String("String"), QVariant(QVariant::String)); + m_ui->m_comboBox->addItem(QLatin1String("StringList"), QVariant(QVariant::StringList)); + m_ui->m_comboBox->addItem(QLatin1String("Char"), QVariant(QVariant::Char)); + m_ui->m_comboBox->addItem(QLatin1String("ByteArray"), QVariant(QVariant::ByteArray)); + m_ui->m_comboBox->addItem(QLatin1String("Url"), QVariant(QVariant::Url)); + m_ui->m_comboBox->addItem(QLatin1String("Bool"), QVariant(QVariant::Bool)); + m_ui->m_comboBox->addItem(QLatin1String("Int"), QVariant(QVariant::Int)); + m_ui->m_comboBox->addItem(QLatin1String("UInt"), QVariant(QVariant::UInt)); + m_ui->m_comboBox->addItem(QLatin1String("LongLong"), QVariant(QVariant::LongLong)); + m_ui->m_comboBox->addItem(QLatin1String("ULongLong"), QVariant(QVariant::ULongLong)); + m_ui->m_comboBox->addItem(QLatin1String("Double"), QVariant(QVariant::Double)); + m_ui->m_comboBox->addItem(QLatin1String("Size"), QVariant(QVariant::Size)); + m_ui->m_comboBox->addItem(QLatin1String("SizeF"), QVariant(QVariant::SizeF)); + m_ui->m_comboBox->addItem(QLatin1String("Point"), QVariant(QVariant::Point)); + m_ui->m_comboBox->addItem(QLatin1String("PointF"), QVariant(QVariant::PointF)); + m_ui->m_comboBox->addItem(QLatin1String("Rect"), QVariant(QVariant::Rect)); + m_ui->m_comboBox->addItem(QLatin1String("RectF"), QVariant(QVariant::RectF)); + m_ui->m_comboBox->addItem(QLatin1String("Date"), QVariant(QVariant::Date)); + m_ui->m_comboBox->addItem(QLatin1String("Time"), QVariant(QVariant::Time)); + m_ui->m_comboBox->addItem(QLatin1String("DateTime"), QVariant(QVariant::DateTime)); + m_ui->m_comboBox->addItem(QLatin1String("Font"), QVariant(QVariant::Font)); + m_ui->m_comboBox->addItem(QLatin1String("Palette"), QVariant(QVariant::Palette)); + m_ui->m_comboBox->addItem(QLatin1String("Color"), QVariant(QVariant::Color)); + m_ui->m_comboBox->addItem(QLatin1String("Pixmap"), QVariant(QVariant::Pixmap)); + m_ui->m_comboBox->addItem(QLatin1String("Icon"), QVariant(QVariant::Icon)); + m_ui->m_comboBox->addItem(QLatin1String("Cursor"), QVariant(QVariant::Cursor)); + m_ui->m_comboBox->addItem(QLatin1String("SizePolicy"), QVariant(QVariant::SizePolicy)); + m_ui->m_comboBox->addItem(QLatin1String("KeySequence"), QVariant(QVariant::KeySequence)); + + m_ui->m_comboBox->setCurrentIndex(0); // String + setOkButtonEnabled(false); +} + +void NewDynamicPropertyDialog::setOkButtonEnabled(bool e) +{ + m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(e); +} + +NewDynamicPropertyDialog::~NewDynamicPropertyDialog() +{ + delete m_ui; +} + +void NewDynamicPropertyDialog::setReservedNames(const QStringList &names) +{ + m_reservedNames = names; +} + +void NewDynamicPropertyDialog::setPropertyType(QVariant::Type t) +{ + const int index = m_ui->m_comboBox->findData(QVariant(t)); + if (index != -1) + m_ui->m_comboBox->setCurrentIndex(index); +} + +QString NewDynamicPropertyDialog::propertyName() const +{ + return m_ui->m_lineEdit->text(); +} + +QVariant NewDynamicPropertyDialog::propertyValue() const +{ + const int index = m_ui->m_comboBox->currentIndex(); + if (index == -1) + return QVariant(); + return m_ui->m_comboBox->itemData(index); +} + +void NewDynamicPropertyDialog::information(const QString &message) +{ + m_dialogGui->message(this, QDesignerDialogGuiInterface::PropertyEditorMessage, QMessageBox::Information, tr("Set Property Name"), message); +} + +void NewDynamicPropertyDialog::nameChanged(const QString &s) +{ + setOkButtonEnabled(!s.isEmpty()); +} + +bool NewDynamicPropertyDialog::validatePropertyName(const QString& name) +{ + if (m_reservedNames.contains(name)) { + information(tr("The current object already has a property named '%1'.\nPlease select another, unique one.").arg(name)); + return false; + } + if (!QDesignerPropertySheet::internalDynamicPropertiesEnabled() && name.startsWith(QLatin1String("_q_"))) { + information(tr("The '_q_' prefix is reserved for the Qt library.\nPlease select another name.")); + return false; + } + return true; +} + +void NewDynamicPropertyDialog::on_m_buttonBox_clicked(QAbstractButton *btn) +{ + const int role = m_ui->m_buttonBox->buttonRole(btn); + switch (role) { + case QDialogButtonBox::RejectRole: + reject(); + break; + case QDialogButtonBox::AcceptRole: + if (validatePropertyName(propertyName())) + accept(); + break; + } +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/newdynamicpropertydialog.h b/src/designer/components/propertyeditor/newdynamicpropertydialog.h new file mode 100644 index 000000000..ad1e96b15 --- /dev/null +++ b/src/designer/components/propertyeditor/newdynamicpropertydialog.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWDYNAMICPROPERTYDIALOG_P_H +#define NEWDYNAMICPROPERTYDIALOG_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 "propertyeditor_global.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QAbstractButton; +class QDesignerDialogGuiInterface; + +namespace qdesigner_internal { + +namespace Ui +{ + class NewDynamicPropertyDialog; +} + +class QT_PROPERTYEDITOR_EXPORT NewDynamicPropertyDialog: public QDialog +{ + Q_OBJECT +public: + explicit NewDynamicPropertyDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent = 0); + ~NewDynamicPropertyDialog(); + + void setReservedNames(const QStringList &names); + void setPropertyType(QVariant::Type t); + + QString propertyName() const; + QVariant propertyValue() const; + +private slots: + + void on_m_buttonBox_clicked(QAbstractButton *btn); + void nameChanged(const QString &); + +private: + bool validatePropertyName(const QString& name); + void setOkButtonEnabled(bool e); + void information(const QString &message); + + QDesignerDialogGuiInterface *m_dialogGui; + Ui::NewDynamicPropertyDialog *m_ui; + QStringList m_reservedNames; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // NEWDYNAMICPROPERTYDIALOG_P_H diff --git a/src/designer/components/propertyeditor/newdynamicpropertydialog.ui b/src/designer/components/propertyeditor/newdynamicpropertydialog.ui new file mode 100644 index 000000000..2aa91f3e9 --- /dev/null +++ b/src/designer/components/propertyeditor/newdynamicpropertydialog.ui @@ -0,0 +1,106 @@ + + qdesigner_internal::NewDynamicPropertyDialog + + + + 0 + 0 + 340 + 118 + + + + Create Dynamic Property + + + + + + + + + 220 + 0 + + + + + + + + + 0 + 0 + + + + Property Name + + + + + + + + + + + + horizontalSpacer + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + Property Type + + + + + + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + diff --git a/src/designer/components/propertyeditor/paletteeditor.cpp b/src/designer/components/propertyeditor/paletteeditor.cpp new file mode 100644 index 000000000..a0e3f358a --- /dev/null +++ b/src/designer/components/propertyeditor/paletteeditor.cpp @@ -0,0 +1,618 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "paletteeditor.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +enum { BrushRole = 33 }; + +PaletteEditor::PaletteEditor(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + m_currentColorGroup(QPalette::Active), + m_paletteModel(new PaletteModel(this)), + m_modelUpdated(false), + m_paletteUpdated(false), + m_compute(true), + m_core(core) +{ + ui.setupUi(this); + ui.paletteView->setModel(m_paletteModel); + updatePreviewPalette(); + updateStyledButton(); + ui.paletteView->setModel(m_paletteModel); + ColorDelegate *delegate = new ColorDelegate(core, this); + ui.paletteView->setItemDelegate(delegate); + ui.paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers); + connect(m_paletteModel, SIGNAL(paletteChanged(QPalette)), + this, SLOT(paletteChanged(QPalette))); + ui.paletteView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui.paletteView->setDragEnabled(true); + ui.paletteView->setDropIndicatorShown(true); + ui.paletteView->setRootIsDecorated(false); + ui.paletteView->setColumnHidden(2, true); + ui.paletteView->setColumnHidden(3, true); +} + +PaletteEditor::~PaletteEditor() +{ +} + +QPalette PaletteEditor::palette() const +{ + return m_editPalette; +} + +void PaletteEditor::setPalette(const QPalette &palette) +{ + m_editPalette = palette; + const uint mask = palette.resolve(); + for (int i = 0; i < (int)QPalette::NColorRoles; i++) { + if (!(mask & (1 << i))) { + m_editPalette.setBrush(QPalette::Active, static_cast(i), + m_parentPalette.brush(QPalette::Active, static_cast(i))); + m_editPalette.setBrush(QPalette::Inactive, static_cast(i), + m_parentPalette.brush(QPalette::Inactive, static_cast(i))); + m_editPalette.setBrush(QPalette::Disabled, static_cast(i), + m_parentPalette.brush(QPalette::Disabled, static_cast(i))); + } + } + m_editPalette.resolve(mask); + updatePreviewPalette(); + updateStyledButton(); + m_paletteUpdated = true; + if (!m_modelUpdated) + m_paletteModel->setPalette(m_editPalette, m_parentPalette); + m_paletteUpdated = false; +} + +void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette) +{ + m_parentPalette = parentPalette; + setPalette(palette); +} + +void PaletteEditor::on_buildButton_colorChanged(const QColor &) +{ + buildPalette(); +} + +void PaletteEditor::on_activeRadio_clicked() +{ + m_currentColorGroup = QPalette::Active; + updatePreviewPalette(); +} + +void PaletteEditor::on_inactiveRadio_clicked() +{ + m_currentColorGroup = QPalette::Inactive; + updatePreviewPalette(); +} + +void PaletteEditor::on_disabledRadio_clicked() +{ + m_currentColorGroup = QPalette::Disabled; + updatePreviewPalette(); +} + +void PaletteEditor::on_computeRadio_clicked() +{ + if (m_compute) + return; + ui.paletteView->setColumnHidden(2, true); + ui.paletteView->setColumnHidden(3, true); + m_compute = true; + m_paletteModel->setCompute(true); +} + +void PaletteEditor::on_detailsRadio_clicked() +{ + if (!m_compute) + return; + const int w = ui.paletteView->columnWidth(1); + ui.paletteView->setColumnHidden(2, false); + ui.paletteView->setColumnHidden(3, false); + QHeaderView *header = ui.paletteView->header(); + header->resizeSection(1, w / 3); + header->resizeSection(2, w / 3); + header->resizeSection(3, w / 3); + m_compute = false; + m_paletteModel->setCompute(false); +} + +void PaletteEditor::paletteChanged(const QPalette &palette) +{ + m_modelUpdated = true; + if (!m_paletteUpdated) + setPalette(palette); + m_modelUpdated = false; +} + +void PaletteEditor::buildPalette() +{ + const QColor btn = ui.buildButton->color(); + const QPalette temp = QPalette(btn); + setPalette(temp); +} + +void PaletteEditor::updatePreviewPalette() +{ + const QPalette::ColorGroup g = currentColorGroup(); + // build the preview palette + const QPalette currentPalette = palette(); + QPalette previewPalette; + for (int i = QPalette::WindowText; i < QPalette::NColorRoles; i++) { + const QPalette::ColorRole r = static_cast(i); + const QBrush br = currentPalette.brush(g, r); + previewPalette.setBrush(QPalette::Active, r, br); + previewPalette.setBrush(QPalette::Inactive, r, br); + previewPalette.setBrush(QPalette::Disabled, r, br); + } + ui.previewFrame->setPreviewPalette(previewPalette); + + const bool enabled = g != QPalette::Disabled; + ui.previewFrame->setEnabled(enabled); + ui.previewFrame->setSubWindowActive(g != QPalette::Inactive); +} + +void PaletteEditor::updateStyledButton() +{ + ui.buildButton->setColor(palette().color(QPalette::Active, QPalette::Button)); +} + +QPalette PaletteEditor::getPalette(QDesignerFormEditorInterface *core, QWidget* parent, const QPalette &init, + const QPalette &parentPal, int *ok) +{ + PaletteEditor dlg(core, parent); + QPalette parentPalette(parentPal); + uint mask = init.resolve(); + for (int i = 0; i < (int)QPalette::NColorRoles; i++) { + if (!(mask & (1 << i))) { + parentPalette.setBrush(QPalette::Active, static_cast(i), + init.brush(QPalette::Active, static_cast(i))); + parentPalette.setBrush(QPalette::Inactive, static_cast(i), + init.brush(QPalette::Inactive, static_cast(i))); + parentPalette.setBrush(QPalette::Disabled, static_cast(i), + init.brush(QPalette::Disabled, static_cast(i))); + } + } + dlg.setPalette(init, parentPalette); + + const int result = dlg.exec(); + if (ok) *ok = result; + + return result == QDialog::Accepted ? dlg.palette() : init; +} + +////////////////////// + +PaletteModel::PaletteModel(QObject *parent) : + QAbstractTableModel(parent), + m_compute(true) +{ + const QMetaObject *meta = metaObject(); + const int index = meta->indexOfProperty("colorRole"); + const QMetaProperty p = meta->property(index); + const QMetaEnum e = p.enumerator(); + for (int r = QPalette::WindowText; r < QPalette::NColorRoles; r++) { + m_roleNames[static_cast(r)] = QLatin1String(e.key(r)); + } +} + +int PaletteModel::rowCount(const QModelIndex &) const +{ + return m_roleNames.count(); +} + +int PaletteModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant PaletteModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row() < 0 || index.row() >= QPalette::NColorRoles) + return QVariant(); + if (index.column() < 0 || index.column() >= 4) + return QVariant(); + + if (index.column() == 0) { + if (role == Qt::DisplayRole) + return m_roleNames[static_cast(index.row())]; + if (role == Qt::EditRole) { + const uint mask = m_palette.resolve(); + if (mask & (1 << index.row())) + return true; + return false; + } + return QVariant(); + } + if (role == BrushRole) + return m_palette.brush(columnToGroup(index.column()), + static_cast(index.row())); + return QVariant(); +} + +bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (index.column() != 0 && role == BrushRole) { + const QBrush br = qvariant_cast(value); + const QPalette::ColorRole r = static_cast(index.row()); + const QPalette::ColorGroup g = columnToGroup(index.column()); + m_palette.setBrush(g, r, br); + + QModelIndex idxBegin = PaletteModel::index(r, 0); + QModelIndex idxEnd = PaletteModel::index(r, 3); + if (m_compute) { + m_palette.setBrush(QPalette::Inactive, r, br); + switch (r) { + case QPalette::WindowText: + case QPalette::Text: + case QPalette::ButtonText: + case QPalette::Base: + break; + case QPalette::Dark: + m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Dark, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Text, br); + m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br); + idxBegin = PaletteModel::index(0, 0); + idxEnd = PaletteModel::index(m_roleNames.count() - 1, 3); + break; + case QPalette::Window: + m_palette.setBrush(QPalette::Disabled, QPalette::Base, br); + m_palette.setBrush(QPalette::Disabled, QPalette::Window, br); + idxBegin = PaletteModel::index(QPalette::Base, 0); + break; + case QPalette::Highlight: + //m_palette.setBrush(QPalette::Disabled, QPalette::Highlight, c.dark(120)); + break; + default: + m_palette.setBrush(QPalette::Disabled, r, br); + break; + } + } + emit paletteChanged(m_palette); + emit dataChanged(idxBegin, idxEnd); + return true; + } + if (index.column() == 0 && role == Qt::EditRole) { + uint mask = m_palette.resolve(); + const bool isMask = qvariant_cast(value); + const int r = index.row(); + if (isMask) + mask |= (1 << r); + else { + m_palette.setBrush(QPalette::Active, static_cast(r), + m_parentPalette.brush(QPalette::Active, static_cast(r))); + m_palette.setBrush(QPalette::Inactive, static_cast(r), + m_parentPalette.brush(QPalette::Inactive, static_cast(r))); + m_palette.setBrush(QPalette::Disabled, static_cast(r), + m_parentPalette.brush(QPalette::Disabled, static_cast(r))); + + mask &= ~(1 << index.row()); + } + m_palette.resolve(mask); + emit paletteChanged(m_palette); + const QModelIndex idxEnd = PaletteModel::index(r, 3); + emit dataChanged(index, idxEnd); + return true; + } + return false; +} + +Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + return Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + +QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + if (section == 0) + return tr("Color Role"); + if (section == groupToColumn(QPalette::Active)) + return tr("Active"); + if (section == groupToColumn(QPalette::Inactive)) + return tr("Inactive"); + if (section == groupToColumn(QPalette::Disabled)) + return tr("Disabled"); + } + return QVariant(); +} + +QPalette PaletteModel::getPalette() const +{ + return m_palette; +} + +void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette) +{ + m_parentPalette = parentPalette; + m_palette = palette; + const QModelIndex idxBegin = index(0, 0); + const QModelIndex idxEnd = index(m_roleNames.count() - 1, 3); + emit dataChanged(idxBegin, idxEnd); +} + +QPalette::ColorGroup PaletteModel::columnToGroup(int index) const +{ + if (index == 1) + return QPalette::Active; + if (index == 2) + return QPalette::Inactive; + return QPalette::Disabled; +} + +int PaletteModel::groupToColumn(QPalette::ColorGroup group) const +{ + if (group == QPalette::Active) + return 1; + if (group == QPalette::Inactive) + return 2; + return 3; +} + +////////////////////////// + +BrushEditor::BrushEditor(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + m_button(new QtColorButton(this)), + m_changed(false), + m_core(core) +{ + QLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->addWidget(m_button); + connect(m_button, SIGNAL(colorChanged(QColor)), this, SLOT(brushChanged())); + setFocusProxy(m_button); +} + +void BrushEditor::setBrush(const QBrush &brush) +{ + m_button->setColor(brush.color()); + m_changed = false; +} + +QBrush BrushEditor::brush() const +{ + return QBrush(m_button->color()); +} + +void BrushEditor::brushChanged() +{ + m_changed = true; + emit changed(this); +} + +bool BrushEditor::changed() const +{ + return m_changed; +} + +////////////////////////// + +RoleEditor::RoleEditor(QWidget *parent) : + QWidget(parent), + m_label(new QLabel(this)), + m_edited(false) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + + layout->addWidget(m_label); + m_label->setAutoFillBackground(true); + m_label->setIndent(3); // ### hardcode it should have the same value of textMargin in QItemDelegate + setFocusProxy(m_label); + + QToolButton *button = new QToolButton(this); + button->setToolButtonStyle(Qt::ToolButtonIconOnly); + button->setIcon(createIconSet(QLatin1String("resetproperty.png"))); + button->setIconSize(QSize(8,8)); + button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); + layout->addWidget(button); + connect(button, SIGNAL(clicked()), this, SLOT(emitResetProperty())); +} + +void RoleEditor::setLabel(const QString &label) +{ + m_label->setText(label); +} + +void RoleEditor::setEdited(bool on) +{ + QFont font; + if (on == true) { + font.setBold(on); + } + m_label->setFont(font); + m_edited = on; +} + +bool RoleEditor::edited() const +{ + return m_edited; +} + +void RoleEditor::emitResetProperty() +{ + setEdited(false); + emit changed(this); +} + +////////////////////////// +ColorDelegate::ColorDelegate(QDesignerFormEditorInterface *core, QObject *parent) : + QItemDelegate(parent), + m_core(core) +{ +} + +QWidget *ColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, + const QModelIndex &index) const +{ + QWidget *ed = 0; + if (index.column() == 0) { + RoleEditor *editor = new RoleEditor(parent); + connect(editor, SIGNAL(changed(QWidget*)), this, SIGNAL(commitData(QWidget*))); + //editor->setFocusPolicy(Qt::NoFocus); + //editor->installEventFilter(const_cast(this)); + ed = editor; + } else { + BrushEditor *editor = new BrushEditor(m_core, parent); + connect(editor, SIGNAL(changed(QWidget*)), this, SIGNAL(commitData(QWidget*))); + editor->setFocusPolicy(Qt::NoFocus); + editor->installEventFilter(const_cast(this)); + ed = editor; + } + return ed; +} + +void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const +{ + if (index.column() == 0) { + const bool mask = qvariant_cast(index.model()->data(index, Qt::EditRole)); + RoleEditor *editor = static_cast(ed); + editor->setEdited(mask); + const QString colorName = qvariant_cast(index.model()->data(index, Qt::DisplayRole)); + editor->setLabel(colorName); + } else { + const QBrush br = qvariant_cast(index.model()->data(index, BrushRole)); + BrushEditor *editor = static_cast(ed); + editor->setBrush(br); + } +} + +void ColorDelegate::setModelData(QWidget *ed, QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (index.column() == 0) { + RoleEditor *editor = static_cast(ed); + const bool mask = editor->edited(); + model->setData(index, mask, Qt::EditRole); + } else { + BrushEditor *editor = static_cast(ed); + if (editor->changed()) { + QBrush br = editor->brush(); + model->setData(index, br, BrushRole); + } + } +} + +void ColorDelegate::updateEditorGeometry(QWidget *ed, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QItemDelegate::updateEditorGeometry(ed, option, index); + ed->setGeometry(ed->geometry().adjusted(0, 0, -1, -1)); +} + +void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, + const QModelIndex &index) const +{ + QStyleOptionViewItem option = opt; + const bool mask = qvariant_cast(index.model()->data(index, Qt::EditRole)); + if (index.column() == 0 && mask) { + option.font.setBold(true); + } + QBrush br = qvariant_cast(index.model()->data(index, BrushRole)); + if (br.style() == Qt::LinearGradientPattern || + br.style() == Qt::RadialGradientPattern || + br.style() == Qt::ConicalGradientPattern) { + painter->save(); + painter->translate(option.rect.x(), option.rect.y()); + painter->scale(option.rect.width(), option.rect.height()); + QGradient gr = *(br.gradient()); + gr.setCoordinateMode(QGradient::LogicalMode); + br = QBrush(gr); + painter->fillRect(0, 0, 1, 1, br); + painter->restore(); + } else { + painter->save(); + painter->setBrushOrigin(option.rect.x(), option.rect.y()); + painter->fillRect(option.rect, br); + painter->restore(); + } + QItemDelegate::paint(painter, option, index); + + + const QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option)); + const QPen oldPen = painter->pen(); + painter->setPen(QPen(color)); + + painter->drawLine(option.rect.right(), option.rect.y(), + option.rect.right(), option.rect.bottom()); + painter->drawLine(option.rect.x(), option.rect.bottom(), + option.rect.right(), option.rect.bottom()); + painter->setPen(oldPen); +} + +QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + return QItemDelegate::sizeHint(opt, index) + QSize(4, 4); +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/paletteeditor.h b/src/designer/components/propertyeditor/paletteeditor.h new file mode 100644 index 000000000..c752093a3 --- /dev/null +++ b/src/designer/components/propertyeditor/paletteeditor.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PALETTEEDITOR_H +#define PALETTEEDITOR_H + +#include "ui_paletteeditor.h" +#include + +QT_BEGIN_NAMESPACE + +class QListView; +class QLabel; +class QtColorButton; +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class PaletteEditor: public QDialog +{ + Q_OBJECT +public: + virtual ~PaletteEditor(); + + static QPalette getPalette(QDesignerFormEditorInterface *core, + QWidget* parent, const QPalette &init = QPalette(), + const QPalette &parentPal = QPalette(), int *result = 0); + + QPalette palette() const; + void setPalette(const QPalette &palette); + void setPalette(const QPalette &palette, const QPalette &parentPalette); + +private slots: + + void on_buildButton_colorChanged(const QColor &); + void on_activeRadio_clicked(); + void on_inactiveRadio_clicked(); + void on_disabledRadio_clicked(); + void on_computeRadio_clicked(); + void on_detailsRadio_clicked(); + + void paletteChanged(const QPalette &palette); + +protected: + +private: + PaletteEditor(QDesignerFormEditorInterface *core, QWidget *parent); + void buildPalette(); + + void updatePreviewPalette(); + void updateStyledButton(); + + QPalette::ColorGroup currentColorGroup() const + { return m_currentColorGroup; } + + Ui::PaletteEditor ui; + QPalette m_editPalette; + QPalette m_parentPalette; + QPalette::ColorGroup m_currentColorGroup; + class PaletteModel *m_paletteModel; + bool m_modelUpdated; + bool m_paletteUpdated; + bool m_compute; + QDesignerFormEditorInterface *m_core; +}; + + +class PaletteModel : public QAbstractTableModel +{ + Q_OBJECT + Q_PROPERTY(QPalette::ColorRole colorRole READ colorRole) +public: + explicit PaletteModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QPalette getPalette() const; + void setPalette(const QPalette &palette, const QPalette &parentPalette); + + QPalette::ColorRole colorRole() const { return QPalette::NoRole; } + void setCompute(bool on) { m_compute = on; } +signals: + void paletteChanged(const QPalette &palette); +private: + + QPalette::ColorGroup columnToGroup(int index) const; + int groupToColumn(QPalette::ColorGroup group) const; + + QPalette m_palette; + QPalette m_parentPalette; + QMap m_roleNames; + bool m_compute; +}; + +class BrushEditor : public QWidget +{ + Q_OBJECT +public: + explicit BrushEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0); + + void setBrush(const QBrush &brush); + QBrush brush() const; + bool changed() const; +signals: + void changed(QWidget *widget); +private slots: + void brushChanged(); +private: + QtColorButton *m_button; + bool m_changed; + QDesignerFormEditorInterface *m_core; +}; + +class RoleEditor : public QWidget +{ + Q_OBJECT +public: + explicit RoleEditor(QWidget *parent = 0); + + void setLabel(const QString &label); + void setEdited(bool on); + bool edited() const; +signals: + void changed(QWidget *widget); +private slots: + void emitResetProperty(); +private: + QLabel *m_label; + bool m_edited; +}; + +class ColorDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + explicit ColorDelegate(QDesignerFormEditorInterface *core, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void setEditorData(QWidget *ed, const QModelIndex &index) const; + void setModelData(QWidget *ed, QAbstractItemModel *model, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *ed, + const QStyleOptionViewItem &option, const QModelIndex &index) const; + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &opt, + const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const; +private: + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PALETTEEDITOR_H diff --git a/src/designer/components/propertyeditor/paletteeditor.ui b/src/designer/components/propertyeditor/paletteeditor.ui new file mode 100644 index 000000000..d78972a7c --- /dev/null +++ b/src/designer/components/propertyeditor/paletteeditor.ui @@ -0,0 +1,264 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::PaletteEditor + + + + 0 + 0 + 365 + 409 + + + + + 7 + 7 + 0 + 0 + + + + Edit Palette + + + + 9 + + + 6 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Tune Palette + + + + 9 + + + 6 + + + + + + 7 + 13 + 0 + 0 + + + + + + + + + + + + 0 + 200 + + + + + + + + Show Details + + + + + + + Compute Details + + + true + + + + + + + Quick + + + + + + + + + + + 5 + 7 + 0 + 0 + + + + Preview + + + + 8 + + + 6 + + + + + Disabled + + + + + + + Inactive + + + + + + + Active + + + true + + + + + + + + 7 + 7 + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + QtColorButton + QToolButton +
qtcolorbutton.h
+
+ + qdesigner_internal::PreviewFrame + QWidget +
previewframe.h
+
+
+ + + + buttonBox + accepted() + qdesigner_internal::PaletteEditor + accept() + + + 180 + 331 + + + 134 + 341 + + + + + buttonBox + rejected() + qdesigner_internal::PaletteEditor + reject() + + + 287 + 329 + + + 302 + 342 + + + + +
diff --git a/src/designer/components/propertyeditor/paletteeditorbutton.cpp b/src/designer/components/propertyeditor/paletteeditorbutton.cpp new file mode 100644 index 000000000..b0437aeff --- /dev/null +++ b/src/designer/components/propertyeditor/paletteeditorbutton.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "paletteeditorbutton.h" +#include "paletteeditor.h" + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +PaletteEditorButton::PaletteEditorButton(QDesignerFormEditorInterface *core, const QPalette &palette, QWidget *parent) + : QToolButton(parent), + m_palette(palette) +{ + m_core = core; + setFocusPolicy(Qt::NoFocus); + setText(tr("Change Palette")); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + + connect(this, SIGNAL(clicked()), this, SLOT(showPaletteEditor())); +} + +PaletteEditorButton::~PaletteEditorButton() +{ +} + +void PaletteEditorButton::setPalette(const QPalette &palette) +{ + m_palette = palette; +} + +void PaletteEditorButton::setSuperPalette(const QPalette &palette) +{ + m_superPalette = palette; +} + +void PaletteEditorButton::showPaletteEditor() +{ + int result; + QPalette p = QPalette(); + QPalette pal = PaletteEditor::getPalette(m_core, 0, m_palette, m_superPalette, &result); + if (result == QDialog::Accepted) { + m_palette = pal; + emit paletteChanged(m_palette); + } +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/paletteeditorbutton.h b/src/designer/components/propertyeditor/paletteeditorbutton.h new file mode 100644 index 000000000..21285d4fd --- /dev/null +++ b/src/designer/components/propertyeditor/paletteeditorbutton.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PALETTEEDITORBUTTON_H +#define PALETTEEDITORBUTTON_H + +#include "propertyeditor_global.h" + +#include +#include + +#include "abstractformeditor.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QT_PROPERTYEDITOR_EXPORT PaletteEditorButton: public QToolButton +{ + Q_OBJECT +public: + PaletteEditorButton(QDesignerFormEditorInterface *core, const QPalette &palette, QWidget *parent = 0); + virtual ~PaletteEditorButton(); + + void setSuperPalette(const QPalette &palette); + inline QPalette palette() const + { return m_palette; } + +signals: + void paletteChanged(const QPalette &palette); + +public slots: + void setPalette(const QPalette &palette); + +private slots: + void showPaletteEditor(); + +private: + QPalette m_palette; + QPalette m_superPalette; + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PALETTEEDITORBUTTON_H diff --git a/src/designer/components/propertyeditor/previewframe.cpp b/src/designer/components/propertyeditor/previewframe.cpp new file mode 100644 index 000000000..3a61886b5 --- /dev/null +++ b/src/designer/components/propertyeditor/previewframe.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "previewframe.h" +#include "previewwidget.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + class PreviewMdiArea: public QMdiArea { + public: + PreviewMdiArea(QWidget *parent = 0) : QMdiArea(parent) {} + protected: + bool viewportEvent ( QEvent * event ); + }; + + bool PreviewMdiArea::viewportEvent (QEvent * event) { + if (event->type() != QEvent::Paint) + return QMdiArea::viewportEvent (event); + QWidget *paintWidget = viewport(); + QPainter p(paintWidget); + p.fillRect(rect(), paintWidget->palette().color(backgroundRole()).dark()); + p.setPen(QPen(Qt::white)); + //: Palette editor background + p.drawText(0, height() / 2, width(), height(), Qt::AlignHCenter, + QCoreApplication::translate("qdesigner_internal::PreviewMdiArea", "The moose in the noose\nate the goose who was loose.")); + return true; + } + +PreviewFrame::PreviewFrame(QWidget *parent) : + QFrame(parent), + m_mdiArea(new PreviewMdiArea(this)) +{ + m_mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + setLineWidth(1); + + QVBoxLayout *vbox = new QVBoxLayout(this); + vbox->setMargin(0); + vbox->addWidget(m_mdiArea); + + setMinimumSize(ensureMdiSubWindow()->minimumSizeHint()); +} + +void PreviewFrame::setPreviewPalette(const QPalette &pal) +{ + ensureMdiSubWindow()->widget()->setPalette(pal); +} + +void PreviewFrame::setSubWindowActive(bool active) +{ + m_mdiArea->setActiveSubWindow (active ? ensureMdiSubWindow() : static_cast(0)); +} + +QMdiSubWindow *PreviewFrame::ensureMdiSubWindow() +{ + if (!m_mdiSubWindow) { + PreviewWidget *previewWidget = new PreviewWidget(m_mdiArea); + m_mdiSubWindow = m_mdiArea->addSubWindow(previewWidget, Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint); + m_mdiSubWindow->move(10,10); + m_mdiSubWindow->showMaximized(); + } + + const Qt::WindowStates state = m_mdiSubWindow->windowState(); + if (state & Qt::WindowMinimized) + m_mdiSubWindow->setWindowState(state & ~Qt::WindowMinimized); + + return m_mdiSubWindow; +} +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/previewframe.h b/src/designer/components/propertyeditor/previewframe.h new file mode 100644 index 000000000..17808a86e --- /dev/null +++ b/src/designer/components/propertyeditor/previewframe.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREVIEWFRAME_H +#define PREVIEWFRAME_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QMdiArea; +class QMdiSubWindow; + +namespace qdesigner_internal { + +class PreviewFrame: public QFrame +{ + Q_OBJECT +public: + explicit PreviewFrame(QWidget *parent); + + void setPreviewPalette(const QPalette &palette); + void setSubWindowActive(bool active); + +private: + // The user can on some platforms close the mdi child by invoking the system menu. + // Ensure a child is present. + QMdiSubWindow *ensureMdiSubWindow(); + QMdiArea *m_mdiArea; + QPointer m_mdiSubWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/src/designer/components/propertyeditor/previewwidget.cpp b/src/designer/components/propertyeditor/previewwidget.cpp new file mode 100644 index 000000000..c01256d29 --- /dev/null +++ b/src/designer/components/propertyeditor/previewwidget.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "previewwidget.h" + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +PreviewWidget::PreviewWidget(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); +} + +PreviewWidget::~PreviewWidget() +{ +} + + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/previewwidget.h b/src/designer/components/propertyeditor/previewwidget.h new file mode 100644 index 000000000..c90cefa5d --- /dev/null +++ b/src/designer/components/propertyeditor/previewwidget.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREVIEWWIDGET_H +#define PREVIEWWIDGET_H + +#include "ui_previewwidget.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class PreviewWidget: public QWidget +{ + Q_OBJECT +public: + explicit PreviewWidget(QWidget *parent); + virtual ~PreviewWidget(); + +private: + Ui::PreviewWidget ui; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PREVIEWWIDGET_H diff --git a/src/designer/components/propertyeditor/previewwidget.ui b/src/designer/components/propertyeditor/previewwidget.ui new file mode 100644 index 000000000..e09c0481b --- /dev/null +++ b/src/designer/components/propertyeditor/previewwidget.ui @@ -0,0 +1,238 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::PreviewWidget + + + + 0 + 0 + 471 + 251 + + + + + 1 + 1 + 0 + 0 + + + + Preview Window + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + LineEdit + + + + + + + + ComboBox + + + + + + + + 0 + + + 6 + + + + + + + + PushButton + + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + + 32767 + 50 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + + + + ButtonGroup2 + + + + 9 + + + 6 + + + + + CheckBox1 + + + true + + + + + + + CheckBox2 + + + + + + + + + + ButtonGroup + + + + 9 + + + 6 + + + + + RadioButton1 + + + true + + + + + + + RadioButton2 + + + + + + + RadioButton3 + + + + + + + + + + + diff --git a/src/designer/components/propertyeditor/propertyeditor.cmake b/src/designer/components/propertyeditor/propertyeditor.cmake new file mode 100644 index 000000000..1ece25595 --- /dev/null +++ b/src/designer/components/propertyeditor/propertyeditor.cmake @@ -0,0 +1,38 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/propertyeditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/designerpropertymanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/paletteeditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/paletteeditorbutton.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/stringlisteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/stringlisteditorbutton.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/previewwidget.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/previewframe.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/newdynamicpropertydialog.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/brushpropertymanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/fontpropertymanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/propertyeditor_global.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/qlonglongvalidator.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/propertyeditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/designerpropertymanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/paletteeditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/paletteeditorbutton.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/stringlisteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/stringlisteditorbutton.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/previewwidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/previewframe.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/newdynamicpropertydialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/brushpropertymanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/fontpropertymanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/qlonglongvalidator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/propertyeditor.qrc + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/paletteeditor.ui + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/stringlisteditor.ui + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/previewwidget.ui + ${CMAKE_CURRENT_SOURCE_DIR}/propertyeditor/newdynamicpropertydialog.ui +) + diff --git a/src/designer/components/propertyeditor/propertyeditor.cpp b/src/designer/components/propertyeditor/propertyeditor.cpp new file mode 100644 index 000000000..f3fb13ca3 --- /dev/null +++ b/src/designer/components/propertyeditor/propertyeditor.cpp @@ -0,0 +1,1297 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertyeditor.h" + +#include "qttreepropertybrowser.h" +#include "qtbuttonpropertybrowser.h" +#include "qtvariantproperty.h" +#include "designerpropertymanager.h" +#include "qdesigner_propertysheet_p.h" +#include "formwindowbase_p.h" +#include "filterwidget_p.h" // For FilterWidget + +#include "newdynamicpropertydialog.h" +#include "dynamicpropertysheet.h" +#include "shared_enums_p.h" + +// sdk +#include +#include +#include +#include +#include +#include +// shared +#include +#include +#include +#include +#ifdef Q_OS_WIN +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static const char *SettingsGroupC = "PropertyEditor"; +#if QT_VERSION >= 0x040500 +static const char *ViewKeyC = "View"; +#endif +static const char *ColorKeyC = "Colored"; +static const char *SortedKeyC = "Sorted"; +static const char *ExpansionKeyC = "ExpandedItems"; +static const char *SplitterPositionKeyC = "SplitterPosition"; + +enum SettingsView { TreeView, ButtonView }; + +QT_BEGIN_NAMESPACE + +// --------------------------------------------------------------------------------- + +namespace qdesigner_internal { + +// ----------- ElidingLabel +// QLabel does not support text eliding so we need a helper class + +class ElidingLabel : public QWidget +{ +public: + ElidingLabel(const QString &text = QString(), QWidget *parent = 0) + : QWidget(parent), + m_text(text), + m_mode(Qt::ElideRight) { + setContentsMargins(3, 2, 3, 2); + } + QSize sizeHint() const; + void paintEvent(QPaintEvent *e); + void setText(const QString &text) { + m_text = text; + updateGeometry(); + } + void setElidemode(Qt::TextElideMode mode) { + m_mode = mode; + updateGeometry(); + } +private: + QString m_text; + Qt::TextElideMode m_mode; +}; + +QSize ElidingLabel::sizeHint() const +{ + QSize size = fontMetrics().boundingRect(m_text).size(); + size += QSize(contentsMargins().left() + contentsMargins().right(), + contentsMargins().top() + contentsMargins().bottom()); + return size; +} + +void ElidingLabel::paintEvent(QPaintEvent *) { + QPainter painter(this); + painter.setPen(QColor(0, 0, 0, 60)); + painter.setBrush(QColor(255, 255, 255, 40)); + painter.drawRect(rect().adjusted(0, 0, -1, -1)); + painter.setPen(palette().windowText().color()); + painter.drawText(contentsRect(), Qt::AlignLeft, + fontMetrics().elidedText(m_text, Qt::ElideRight, width(), 0)); +} + + +// ----------- PropertyEditor::Strings + +PropertyEditor::Strings::Strings() : + m_fontProperty(QLatin1String("font")), + m_qLayoutWidget(QLatin1String("QLayoutWidget")), + m_designerPrefix(QLatin1String("QDesigner")), + m_layout(QLatin1String("Layout")), + m_validationModeAttribute(QLatin1String("validationMode")), + m_fontAttribute(QLatin1String("font")), + m_superPaletteAttribute(QLatin1String("superPalette")), + m_enumNamesAttribute(QLatin1String("enumNames")), + m_resettableAttribute(QLatin1String("resettable")), + m_flagsAttribute(QLatin1String("flags")) +{ + m_alignmentProperties.insert(QLatin1String("alignment")); + m_alignmentProperties.insert(QLatin1String("layoutLabelAlignment")); // QFormLayout + m_alignmentProperties.insert(QLatin1String("layoutFormAlignment")); +} + +// ----------- PropertyEditor + +QDesignerMetaDataBaseItemInterface* PropertyEditor::metaDataBaseItem() const +{ + QObject *o = object(); + if (!o) + return 0; + QDesignerMetaDataBaseInterface *db = core()->metaDataBase(); + if (!db) + return 0; + return db->item(o); +} + +void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMainContainer) +{ + const StringPropertyParameters params = textPropertyValidationMode(core(), m_object, property->propertyName(), isMainContainer); + // Does a meta DB entry exist - add comment + const bool hasComment = params.second; + property->setAttribute(m_strings.m_validationModeAttribute, params.first); + // assuming comment cannot appear or disappear for the same property in different object instance + if (!hasComment) + qDeleteAll(property->subProperties()); +} + +void PropertyEditor::setupPaletteProperty(QtVariantProperty *property) +{ + QPalette value = qvariant_cast(property->value()); + QPalette superPalette = QPalette(); + QWidget *currentWidget = qobject_cast(m_object); + if (currentWidget) { + if (currentWidget->isWindow()) + superPalette = QApplication::palette(currentWidget); + else { + if (currentWidget->parentWidget()) + superPalette = currentWidget->parentWidget()->palette(); + } + } + m_updatingBrowser = true; + property->setAttribute(m_strings.m_superPaletteAttribute, superPalette); + m_updatingBrowser = false; +} + +static inline QToolButton *createDropDownButton(QAction *defaultAction, QWidget *parent = 0) +{ + QToolButton *rc = new QToolButton(parent); + rc->setDefaultAction(defaultAction); + rc->setPopupMode(QToolButton::InstantPopup); + return rc; +} + +PropertyEditor::PropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerPropertyEditor(parent, flags), + m_core(core), + m_propertySheet(0), + m_currentBrowser(0), + m_treeBrowser(0), + m_propertyManager(new DesignerPropertyManager(m_core, this)), + m_dynamicGroup(0), + m_updatingBrowser(false), + m_stackedWidget(new QStackedWidget), + m_filterWidget(new FilterWidget(0, FilterWidget::LayoutAlignNone)), + m_buttonIndex(-1), + m_treeIndex(-1), + m_addDynamicAction(new QAction(createIconSet(QLatin1String("plus.png")), tr("Add Dynamic Property..."), this)), + m_removeDynamicAction(new QAction(createIconSet(QLatin1String("minus.png")), tr("Remove Dynamic Property"), this)), + m_sortingAction(new QAction(createIconSet(QLatin1String("sort.png")), tr("Sorting"), this)), + m_coloringAction(new QAction(createIconSet(QLatin1String("color.png")), tr("Color Groups"), this)), + m_treeAction(new QAction(tr("Tree View"), this)), + m_buttonAction(new QAction(tr("Drop Down Button View"), this)), + m_classLabel(new ElidingLabel), + m_sorting(false), + m_coloring(false), + m_brightness(false) +{ + QVector colors; + colors.reserve(6); + colors.push_back(QColor(255, 230, 191)); + colors.push_back(QColor(255, 255, 191)); + colors.push_back(QColor(191, 255, 191)); + colors.push_back(QColor(199, 255, 255)); + colors.push_back(QColor(234, 191, 255)); + colors.push_back(QColor(255, 191, 239)); + m_colors.reserve(colors.count()); + const int darknessFactor = 250; + for (int i = 0; i < colors.count(); i++) { + QColor c = colors.at(i); + m_colors.push_back(qMakePair(c, c.darker(darknessFactor))); + } + QColor dynamicColor(191, 207, 255); + QColor layoutColor(255, 191, 191); + m_dynamicColor = qMakePair(dynamicColor, dynamicColor.darker(darknessFactor)); + m_layoutColor = qMakePair(layoutColor, layoutColor.darker(darknessFactor)); + + updateForegroundBrightness(); + + QActionGroup *actionGroup = new QActionGroup(this); + + m_treeAction->setCheckable(true); + m_treeAction->setIcon(createIconSet(QLatin1String("widgets/listview.png"))); + m_buttonAction->setCheckable(true); + m_buttonAction->setIcon(createIconSet(QLatin1String("dropdownbutton.png"))); + + actionGroup->addAction(m_treeAction); + actionGroup->addAction(m_buttonAction); + connect(actionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotViewTriggered(QAction*))); + + // Add actions + QActionGroup *addDynamicActionGroup = new QActionGroup(this); + connect(addDynamicActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotAddDynamicProperty(QAction*))); + + QMenu *addDynamicActionMenu = new QMenu(this); + m_addDynamicAction->setMenu(addDynamicActionMenu); + m_addDynamicAction->setEnabled(false); + QAction *addDynamicAction = addDynamicActionGroup->addAction(tr("String...")); + addDynamicAction->setData(static_cast(QVariant::String)); + addDynamicActionMenu->addAction(addDynamicAction); + addDynamicAction = addDynamicActionGroup->addAction(tr("Bool...")); + addDynamicAction->setData(static_cast(QVariant::Bool)); + addDynamicActionMenu->addAction(addDynamicAction); + addDynamicActionMenu->addSeparator(); + addDynamicAction = addDynamicActionGroup->addAction(tr("Other...")); + addDynamicAction->setData(static_cast(QVariant::Invalid)); + addDynamicActionMenu->addAction(addDynamicAction); + // remove + m_removeDynamicAction->setEnabled(false); + connect(m_removeDynamicAction, SIGNAL(triggered()), this, SLOT(slotRemoveDynamicProperty())); + // Configure + QAction *configureAction = new QAction(tr("Configure Property Editor"), this); + configureAction->setIcon(createIconSet(QLatin1String("configure.png"))); + QMenu *configureMenu = new QMenu(this); + configureAction->setMenu(configureMenu); + + m_sortingAction->setCheckable(true); + connect(m_sortingAction, SIGNAL(toggled(bool)), this, SLOT(slotSorting(bool))); + + m_coloringAction->setCheckable(true); + connect(m_coloringAction, SIGNAL(toggled(bool)), this, SLOT(slotColoring(bool))); + + configureMenu->addAction(m_sortingAction); + configureMenu->addAction(m_coloringAction); +#if QT_VERSION >= 0x04FF00 + configureMenu->addSeparator(); + configureMenu->addAction(m_treeAction); + configureMenu->addAction(m_buttonAction); +#endif + // Assemble toolbar + QToolBar *toolBar = new QToolBar; + toolBar->addWidget(m_filterWidget); + toolBar->addWidget(createDropDownButton(m_addDynamicAction)); + toolBar->addAction(m_removeDynamicAction); + toolBar->addWidget(createDropDownButton(configureAction)); + // Views + QScrollArea *buttonScroll = new QScrollArea(m_stackedWidget); + m_buttonBrowser = new QtButtonPropertyBrowser(buttonScroll); + buttonScroll->setWidgetResizable(true); + buttonScroll->setWidget(m_buttonBrowser); + m_buttonIndex = m_stackedWidget->addWidget(buttonScroll); + connect(m_buttonBrowser, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentItemChanged(QtBrowserItem*))); + + m_treeBrowser = new QtTreePropertyBrowser(m_stackedWidget); + m_treeBrowser->setRootIsDecorated(false); + m_treeBrowser->setPropertiesWithoutValueMarked(true); + m_treeBrowser->setResizeMode(QtTreePropertyBrowser::Interactive); + m_treeIndex = m_stackedWidget->addWidget(m_treeBrowser); + connect(m_treeBrowser, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentItemChanged(QtBrowserItem*))); + connect(m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(setFilter(QString))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(toolBar); + layout->addWidget(m_classLabel); + layout->addSpacerItem(new QSpacerItem(0,1)); + layout->addWidget(m_stackedWidget); + layout->setMargin(0); + layout->setSpacing(0); + + m_treeFactory = new DesignerEditorFactory(m_core, this); + m_treeFactory->setSpacing(0); + m_groupFactory = new DesignerEditorFactory(m_core, this); + QtVariantPropertyManager *variantManager = m_propertyManager; + m_buttonBrowser->setFactoryForManager(variantManager, m_groupFactory); + m_treeBrowser->setFactoryForManager(variantManager, m_treeFactory); + + m_stackedWidget->setCurrentIndex(m_treeIndex); + m_currentBrowser = m_treeBrowser; + m_treeAction->setChecked(true); + + connect(m_groupFactory, SIGNAL(resetProperty(QtProperty*)), this, SLOT(slotResetProperty(QtProperty*))); + connect(m_treeFactory, SIGNAL(resetProperty(QtProperty*)), this, SLOT(slotResetProperty(QtProperty*))); + connect(variantManager, SIGNAL(valueChanged(QtProperty*,QVariant,bool)), this, SLOT(slotValueChanged(QtProperty*,QVariant,bool))); + + // retrieve initial settings + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(SettingsGroupC)); +#if QT_VERSION >= 0x040500 + const SettingsView view = settings->value(QLatin1String(ViewKeyC), TreeView).toInt() == TreeView ? TreeView : ButtonView; +#endif + // Coloring not available unless treeview and not sorted + m_sorting = settings->value(QLatin1String(SortedKeyC), false).toBool(); + m_coloring = settings->value(QLatin1String(ColorKeyC), true).toBool(); + const QVariantMap expansionState = settings->value(QLatin1String(ExpansionKeyC), QVariantMap()).toMap(); + const int splitterPosition = settings->value(QLatin1String(SplitterPositionKeyC), 150).toInt(); + settings->endGroup(); + // Apply settings + m_sortingAction->setChecked(m_sorting); + m_coloringAction->setChecked(m_coloring); + m_treeBrowser->setSplitterPosition(splitterPosition); +#if QT_VERSION >= 0x040500 + switch (view) { + case TreeView: + m_currentBrowser = m_treeBrowser; + m_stackedWidget->setCurrentIndex(m_treeIndex); + m_treeAction->setChecked(true); + break; + case ButtonView: + m_currentBrowser = m_buttonBrowser; + m_stackedWidget->setCurrentIndex(m_buttonIndex); + m_buttonAction->setChecked(true); + break; + } +#endif + // Restore expansionState from QVariant map + if (!expansionState.empty()) { + const QVariantMap::const_iterator cend = expansionState.constEnd(); + for (QVariantMap::const_iterator it = expansionState.constBegin(); it != cend; ++it) + m_expansionState.insert(it.key(), it.value().toBool()); + } + updateActionsState(); +} + +PropertyEditor::~PropertyEditor() +{ + storeExpansionState(); + saveSettings(); +} + +void PropertyEditor::saveSettings() const +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(SettingsGroupC)); +#if QT_VERSION >= 0x040500 + settings->setValue(QLatin1String(ViewKeyC), QVariant(m_treeAction->isChecked() ? TreeView : ButtonView)); +#endif + settings->setValue(QLatin1String(ColorKeyC), QVariant(m_coloring)); + settings->setValue(QLatin1String(SortedKeyC), QVariant(m_sorting)); + // Save last expansionState as QVariant map + QVariantMap expansionState; + if (!m_expansionState.empty()) { + const QMap::const_iterator cend = m_expansionState.constEnd(); + for (QMap::const_iterator it = m_expansionState.constBegin(); it != cend; ++it) + expansionState.insert(it.key(), QVariant(it.value())); + } + settings->setValue(QLatin1String(ExpansionKeyC), expansionState); + settings->setValue(QLatin1String(SplitterPositionKeyC), m_treeBrowser->splitterPosition()); + settings->endGroup(); +} + +void PropertyEditor::setExpanded(QtBrowserItem *item, bool expanded) +{ + if (m_buttonBrowser == m_currentBrowser) + m_buttonBrowser->setExpanded(item, expanded); + else if (m_treeBrowser == m_currentBrowser) + m_treeBrowser->setExpanded(item, expanded); +} + +bool PropertyEditor::isExpanded(QtBrowserItem *item) const +{ + if (m_buttonBrowser == m_currentBrowser) + return m_buttonBrowser->isExpanded(item); + else if (m_treeBrowser == m_currentBrowser) + return m_treeBrowser->isExpanded(item); + return false; +} + +void PropertyEditor::setItemVisible(QtBrowserItem *item, bool visible) +{ + if (m_currentBrowser == m_treeBrowser) { + m_treeBrowser->setItemVisible(item, visible); + } else { + qWarning("** WARNING %s is not implemented for this browser.", Q_FUNC_INFO); + } +} + +bool PropertyEditor::isItemVisible(QtBrowserItem *item) const +{ + return m_currentBrowser == m_treeBrowser ? m_treeBrowser->isItemVisible(item) : true; +} + +/* Default handling of items not found in the map: + * - Top-level items (classes) are assumed to be expanded + * - Anything below (properties) is assumed to be collapsed + * That is, the map is required, the state cannot be stored in a set */ + +void PropertyEditor::storePropertiesExpansionState(const QList &items) +{ + const QChar bar = QLatin1Char('|'); + QListIterator itProperty(items); + while (itProperty.hasNext()) { + QtBrowserItem *propertyItem = itProperty.next(); + if (!propertyItem->children().empty()) { + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const QMap::const_iterator itGroup = m_propertyToGroup.constFind(property); + if (itGroup != m_propertyToGroup.constEnd()) { + QString key = itGroup.value(); + key += bar; + key += propertyName; + m_expansionState[key] = isExpanded(propertyItem); + } + } + } +} + +void PropertyEditor::storeExpansionState() +{ + const QList items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + storePropertiesExpansionState(items); + } else { + QListIterator itGroup(items); + while (itGroup.hasNext()) { + QtBrowserItem *item = itGroup.next(); + const QString groupName = item->property()->propertyName(); + QList propertyItems = item->children(); + if (!propertyItems.empty()) + m_expansionState[groupName] = isExpanded(item); + + // properties stuff here + storePropertiesExpansionState(propertyItems); + } + } +} + +void PropertyEditor::collapseAll() +{ + QList items = m_currentBrowser->topLevelItems(); + QListIterator itGroup(items); + while (itGroup.hasNext()) + setExpanded(itGroup.next(), false); +} + +void PropertyEditor::applyPropertiesExpansionState(const QList &items) +{ + const QChar bar = QLatin1Char('|'); + QListIterator itProperty(items); + while (itProperty.hasNext()) { + const QMap::const_iterator excend = m_expansionState.constEnd(); + QtBrowserItem *propertyItem = itProperty.next(); + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const QMap::const_iterator itGroup = m_propertyToGroup.constFind(property); + if (itGroup != m_propertyToGroup.constEnd()) { + QString key = itGroup.value(); + key += bar; + key += propertyName; + const QMap::const_iterator pit = m_expansionState.constFind(key); + if (pit != excend) + setExpanded(propertyItem, pit.value()); + else + setExpanded(propertyItem, false); + } + } +} + +void PropertyEditor::applyExpansionState() +{ + const QList items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + applyPropertiesExpansionState(items); + } else { + QListIterator itTopLevel(items); + const QMap::const_iterator excend = m_expansionState.constEnd(); + while (itTopLevel.hasNext()) { + QtBrowserItem *item = itTopLevel.next(); + const QString groupName = item->property()->propertyName(); + const QMap::const_iterator git = m_expansionState.constFind(groupName); + if (git != excend) + setExpanded(item, git.value()); + else + setExpanded(item, true); + // properties stuff here + applyPropertiesExpansionState(item->children()); + } + } +} + +int PropertyEditor::applyPropertiesFilter(const QList &items) +{ + int showCount = 0; + const bool matchAll = m_filterPattern.isEmpty(); + QListIterator itProperty(items); + while (itProperty.hasNext()) { + QtBrowserItem *propertyItem = itProperty.next(); + QtProperty *property = propertyItem->property(); + const QString propertyName = property->propertyName(); + const bool showProperty = matchAll || propertyName.contains(m_filterPattern, Qt::CaseInsensitive); + setItemVisible(propertyItem, showProperty); + if (showProperty) + showCount++; + } + return showCount; +} + +void PropertyEditor::applyFilter() +{ + const QList items = m_currentBrowser->topLevelItems(); + if (m_sorting) { + applyPropertiesFilter(items); + } else { + QListIterator itTopLevel(items); + while (itTopLevel.hasNext()) { + QtBrowserItem *item = itTopLevel.next(); + setItemVisible(item, applyPropertiesFilter(item->children())); + } + } +} + +void PropertyEditor::clearView() +{ + m_currentBrowser->clear(); +} + +bool PropertyEditor::event(QEvent *event) +{ + if (event->type() == QEvent::PaletteChange) + updateForegroundBrightness(); + + return QDesignerPropertyEditor::event(event); +} + +void PropertyEditor::updateForegroundBrightness() +{ + QColor c = palette().color(QPalette::Text); + bool newBrightness = qRound(0.3 * c.redF() + 0.59 * c.greenF() + 0.11 * c.blueF()); + + if (m_brightness == newBrightness) + return; + + m_brightness = newBrightness; + + updateColors(); +} + +QColor PropertyEditor::propertyColor(QtProperty *property) const +{ + if (!m_coloring) + return QColor(); + + QtProperty *groupProperty = property; + + QMap::ConstIterator itProp = m_propertyToGroup.constFind(property); + if (itProp != m_propertyToGroup.constEnd()) + groupProperty = m_nameToGroup.value(itProp.value()); + + const int groupIdx = m_groups.indexOf(groupProperty); + QPair pair; + if (groupIdx != -1) { + if (groupProperty == m_dynamicGroup) + pair = m_dynamicColor; + else if (isLayoutGroup(groupProperty)) + pair = m_layoutColor; + else + pair = m_colors[groupIdx % m_colors.count()]; + } + if (!m_brightness) + return pair.first; + return pair.second; +} + +void PropertyEditor::fillView() +{ + if (m_sorting) { + QMapIterator itProperty(m_nameToProperty); + while (itProperty.hasNext()) { + QtVariantProperty *property = itProperty.next().value(); + m_currentBrowser->addProperty(property); + } + } else { + QListIterator itGroup(m_groups); + while (itGroup.hasNext()) { + QtProperty *group = itGroup.next(); + QtBrowserItem *item = m_currentBrowser->addProperty(group); + if (m_currentBrowser == m_treeBrowser) + m_treeBrowser->setBackgroundColor(item, propertyColor(group)); + group->setModified(m_currentBrowser == m_treeBrowser); + } + } +} + +bool PropertyEditor::isLayoutGroup(QtProperty *group) const +{ + return group->propertyName() == m_strings.m_layout; +} + +void PropertyEditor::updateActionsState() +{ + m_coloringAction->setEnabled(m_treeAction->isChecked() && !m_sortingAction->isChecked()); +} + +void PropertyEditor::slotViewTriggered(QAction *action) +{ + storeExpansionState(); + collapseAll(); + { + UpdateBlocker ub(this); + clearView(); + int idx = 0; + if (action == m_treeAction) { + m_currentBrowser = m_treeBrowser; + idx = m_treeIndex; + } else if (action == m_buttonAction) { + m_currentBrowser = m_buttonBrowser; + idx = m_buttonIndex; + } + fillView(); + m_stackedWidget->setCurrentIndex(idx); + applyExpansionState(); + applyFilter(); + } + updateActionsState(); +} + +void PropertyEditor::slotSorting(bool sort) +{ + if (sort == m_sorting) + return; + + storeExpansionState(); + m_sorting = sort; + collapseAll(); + { + UpdateBlocker ub(this); + clearView(); + m_treeBrowser->setRootIsDecorated(sort); + fillView(); + applyExpansionState(); + applyFilter(); + } + updateActionsState(); +} + +void PropertyEditor::updateColors() +{ + if (m_treeBrowser && m_currentBrowser == m_treeBrowser) { + QList items = m_treeBrowser->topLevelItems(); + QListIterator itItem(items); + while (itItem.hasNext()) { + QtBrowserItem *item = itItem.next(); + m_treeBrowser->setBackgroundColor(item, propertyColor(item->property())); + } + } +} + +void PropertyEditor::slotColoring(bool coloring) +{ + if (coloring == m_coloring) + return; + + m_coloring = coloring; + + updateColors(); +} + +void PropertyEditor::slotAddDynamicProperty(QAction *action) +{ + if (!m_propertySheet) + return; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension(m_core->extensionManager(), m_object); + + if (!dynamicSheet) + return; + + QString newName; + QVariant newValue; + { // Make sure the dialog is closed before the signal is emitted. + const QVariant::Type type = static_cast(action->data().toInt()); + NewDynamicPropertyDialog dlg(core()->dialogGui(), m_currentBrowser); + if (type != QVariant::Invalid) + dlg.setPropertyType(type); + + QStringList reservedNames; + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; i++) { + if (!dynamicSheet->isDynamicProperty(i) || m_propertySheet->isVisible(i)) + reservedNames.append(m_propertySheet->propertyName(i)); + } + dlg.setReservedNames(reservedNames); + if (dlg.exec() == QDialog::Rejected) + return; + newName = dlg.propertyName(); + newValue = dlg.propertyValue(); + } + m_recentlyAddedDynamicProperty = newName; + emit addDynamicProperty(newName, newValue); +} + +QDesignerFormEditorInterface *PropertyEditor::core() const +{ + return m_core; +} + +bool PropertyEditor::isReadOnly() const +{ + return false; +} + +void PropertyEditor::setReadOnly(bool /*readOnly*/) +{ + qDebug() << "PropertyEditor::setReadOnly() request"; +} + +void PropertyEditor::setPropertyValue(const QString &name, const QVariant &value, bool changed) +{ + const QMap::const_iterator it = m_nameToProperty.constFind(name); + if (it == m_nameToProperty.constEnd()) + return; + QtVariantProperty *property = it.value(); + updateBrowserValue(property, value); + property->setModified(changed); +} + +/* Quick update that assumes the actual count of properties has not changed + * N/A when for example executing a layout command and margin properties appear. */ +void PropertyEditor::updatePropertySheet() +{ + if (!m_propertySheet) + return; + + updateToolBarLabel(); + + const int propertyCount = m_propertySheet->count(); + const QMap::const_iterator npcend = m_nameToProperty.constEnd(); + for (int i = 0; i < propertyCount; ++i) { + const QString propertyName = m_propertySheet->propertyName(i); + QMap::const_iterator it = m_nameToProperty.constFind(propertyName); + if (it != npcend) + updateBrowserValue(it.value(), m_propertySheet->property(i)); + } +} + +static inline QLayout *layoutOfQLayoutWidget(QObject *o) +{ + if (o->isWidgetType() && !qstrcmp(o->metaObject()->className(), "QLayoutWidget")) + return static_cast(o)->layout(); + return 0; +} + +void PropertyEditor::updateToolBarLabel() +{ + QString objectName; + QString className; + if (m_object) { + if (QLayout *l = layoutOfQLayoutWidget(m_object)) + objectName = l->objectName(); + else + objectName = m_object->objectName(); + className = realClassName(m_object); + } + + m_classLabel->setVisible(!objectName.isEmpty() || !className.isEmpty()); + m_classLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + QString classLabelText; + if (!objectName.isEmpty()) + classLabelText += objectName + QLatin1String(" : "); + classLabelText += className; + + m_classLabel->setText(classLabelText); + m_classLabel->setToolTip(tr("Object: %1\nClass: %2").arg(objectName).arg(className)); +} + +void PropertyEditor::updateBrowserValue(QtVariantProperty *property, const QVariant &value) +{ + QVariant v = value; + const int type = property->propertyType(); + if (type == QtVariantPropertyManager::enumTypeId()) { + const PropertySheetEnumValue e = qvariant_cast(v); + v = e.metaEnum.keys().indexOf(e.metaEnum.valueToKey(e.value)); + } else if (type == DesignerPropertyManager::designerFlagTypeId()) { + const PropertySheetFlagValue f = qvariant_cast(v); + v = QVariant(f.value); + } else if (type == DesignerPropertyManager::designerAlignmentTypeId()) { + const PropertySheetFlagValue f = qvariant_cast(v); + v = QVariant(f.value); + } + QDesignerPropertySheet *sheet = qobject_cast(m_core->extensionManager()->extension(m_object, Q_TYPEID(QDesignerPropertySheetExtension))); + int index = -1; + if (sheet) + index = sheet->indexOf(property->propertyName()); + if (sheet && m_propertyToGroup.contains(property)) { // don't do it for comments since property sheet doesn't keep them + property->setEnabled(sheet->isEnabled(index)); + } + + // Rich text string property with comment: Store/Update the font the rich text editor dialog starts out with + if (type == QVariant::String && !property->subProperties().empty()) { + const int fontIndex = m_propertySheet->indexOf(m_strings.m_fontProperty); + if (fontIndex != -1) + property->setAttribute(m_strings.m_fontAttribute, m_propertySheet->property(fontIndex)); + } + + m_updatingBrowser = true; + property->setValue(v); + if (sheet && sheet->isResourceProperty(index)) + property->setAttribute(QLatin1String("defaultResource"), sheet->defaultResourceProperty(index)); + m_updatingBrowser = false; +} + +int PropertyEditor::toBrowserType(const QVariant &value, const QString &propertyName) const +{ + if (value.canConvert()) { + if (m_strings.m_alignmentProperties.contains(propertyName)) + return DesignerPropertyManager::designerAlignmentTypeId(); + return DesignerPropertyManager::designerFlagTypeId(); + } + if (value.canConvert()) + return DesignerPropertyManager::enumTypeId(); + + return value.userType(); +} + +QString PropertyEditor::realClassName(QObject *object) const +{ + if (!object) + return QString(); + + QString className = QLatin1String(object->metaObject()->className()); + const QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + if (QDesignerWidgetDataBaseItemInterface *widgetItem = db->item(db->indexOfObject(object, true))) { + className = widgetItem->name(); + + if (object->isWidgetType() && className == m_strings.m_qLayoutWidget + && static_cast(object)->layout()) { + className = QLatin1String(static_cast(object)->layout()->metaObject()->className()); + } + } + + if (className.startsWith(m_strings.m_designerPrefix)) + className.remove(1, m_strings.m_designerPrefix.size() - 1); + + return className; +} + +static QString msgUnsupportedType(const QString &propertyName, unsigned type) +{ + QString rc; + QTextStream str(&rc); + str << "The property \"" << propertyName << "\" of type " << type; + if (type == QVariant::Invalid) { + str << " (invalid) "; + } else { + if (type < QVariant::UserType) { + if (const char *typeName = QVariant::typeToName(static_cast(type))) + str << " (" << typeName << ") "; + } else { + str << " (user type) "; + } + } + str << " is not supported yet!"; + return rc; +} + +void PropertyEditor::setObject(QObject *object) +{ + QDesignerFormWindowInterface *oldFormWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + // In the first setObject() call following the addition of a dynamic property, focus and edit it. + const bool editNewDynamicProperty = object != 0 && m_object == object && !m_recentlyAddedDynamicProperty.isEmpty(); + m_object = object; + m_propertyManager->setObject(object); + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(m_object); + FormWindowBase *fwb = qobject_cast(formWindow); + m_treeFactory->setFormWindowBase(fwb); + m_groupFactory->setFormWindowBase(fwb); + + storeExpansionState(); + + UpdateBlocker ub(this); + + updateToolBarLabel(); + + QMap toRemove = m_nameToProperty; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension(m_core->extensionManager(), m_object); + const QDesignerPropertySheet *sheet = qobject_cast(m_core->extensionManager()->extension(m_object, Q_TYPEID(QDesignerPropertySheetExtension))); + + // Optimizization: Instead of rebuilding the complete list every time, compile a list of properties to remove, + // remove them, traverse the sheet, in case property exists just set a value, otherwise - create it. + QExtensionManager *m = m_core->extensionManager(); + + m_propertySheet = qobject_cast(m->extension(object, Q_TYPEID(QDesignerPropertySheetExtension))); + if (m_propertySheet) { + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; ++i) { + if (!m_propertySheet->isVisible(i)) + continue; + + const QString propertyName = m_propertySheet->propertyName(i); + if (m_propertySheet->indexOf(propertyName) != i) + continue; + const QString groupName = m_propertySheet->propertyGroup(i); + const QMap::const_iterator rit = toRemove.constFind(propertyName); + if (rit != toRemove.constEnd()) { + QtVariantProperty *property = rit.value(); + if (m_propertyToGroup.value(property) == groupName && toBrowserType(m_propertySheet->property(i), propertyName) == property->propertyType()) + toRemove.remove(propertyName); + } + } + } + + QMapIterator itRemove(toRemove); + while (itRemove.hasNext()) { + itRemove.next(); + + QtVariantProperty *property = itRemove.value(); + m_nameToProperty.remove(itRemove.key()); + m_propertyToGroup.remove(property); + delete property; + } + + if (oldFormWindow != formWindow) + reloadResourceProperties(); + + bool isMainContainer = false; + if (QWidget *widget = qobject_cast(object)) { + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(widget)) { + isMainContainer = (fw->mainContainer() == widget); + } + } + m_groups.clear(); + + if (m_propertySheet) { + QtProperty *lastProperty = 0; + QtProperty *lastGroup = 0; + const int propertyCount = m_propertySheet->count(); + for (int i = 0; i < propertyCount; ++i) { + if (!m_propertySheet->isVisible(i)) + continue; + + const QString propertyName = m_propertySheet->propertyName(i); + if (m_propertySheet->indexOf(propertyName) != i) + continue; + const QVariant value = m_propertySheet->property(i); + + const int type = toBrowserType(value, propertyName); + + QtVariantProperty *property = m_nameToProperty.value(propertyName, 0); + bool newProperty = property == 0; + if (newProperty) { + property = m_propertyManager->addProperty(type, propertyName); + if (property) { + newProperty = true; + if (type == DesignerPropertyManager::enumTypeId()) { + const PropertySheetEnumValue e = qvariant_cast(value); + QStringList names; + QStringListIterator it(e.metaEnum.keys()); + while (it.hasNext()) + names.append(it.next()); + m_updatingBrowser = true; + property->setAttribute(m_strings.m_enumNamesAttribute, names); + m_updatingBrowser = false; + } else if (type == DesignerPropertyManager::designerFlagTypeId()) { + const PropertySheetFlagValue f = qvariant_cast(value); + QList > flags; + QStringListIterator it(f.metaFlags.keys()); + while (it.hasNext()) { + const QString name = it.next(); + const uint val = f.metaFlags.keyToValue(name); + flags.append(qMakePair(name, val)); + } + m_updatingBrowser = true; + QVariant v; + v.setValue(flags); + property->setAttribute(m_strings.m_flagsAttribute, v); + m_updatingBrowser = false; + } + } + } + + if (property != 0) { + const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(i)) + || (sheet && sheet->isDefaultDynamicProperty(i)); + switch (type) { + case QVariant::Palette: + setupPaletteProperty(property); + break; + case QVariant::KeySequence: + //addCommentProperty(property, propertyName); + break; + default: + break; + } + if (type == QVariant::String || type == qMetaTypeId()) + setupStringProperty(property, isMainContainer); + property->setAttribute(m_strings.m_resettableAttribute, m_propertySheet->hasReset(i)); + + const QString groupName = m_propertySheet->propertyGroup(i); + QtVariantProperty *groupProperty = 0; + + if (newProperty) { + QMap::const_iterator itPrev = m_nameToProperty.insert(propertyName, property); + m_propertyToGroup[property] = groupName; + if (m_sorting) { + QtProperty *previous = 0; + if (itPrev != m_nameToProperty.constBegin()) + previous = (--itPrev).value(); + m_currentBrowser->insertProperty(property, previous); + } + } + const QMap::const_iterator gnit = m_nameToGroup.constFind(groupName); + if (gnit != m_nameToGroup.constEnd()) { + groupProperty = gnit.value(); + } else { + groupProperty = m_propertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), groupName); + QtBrowserItem *item = 0; + if (!m_sorting) + item = m_currentBrowser->insertProperty(groupProperty, lastGroup); + m_nameToGroup[groupName] = groupProperty; + m_groups.append(groupProperty); + if (dynamicProperty) + m_dynamicGroup = groupProperty; + if (m_currentBrowser == m_treeBrowser && item) { + m_treeBrowser->setBackgroundColor(item, propertyColor(groupProperty)); + groupProperty->setModified(true); + } + } + /* Group changed or new group. Append to last subproperty of + * that group. Note that there are cases in which a derived + * property sheet appends fake properties for the class + * which will appear after the layout group properties + * (QWizardPage). To make them appear at the end of the + * actual class group, goto last element. */ + if (lastGroup != groupProperty) { + lastGroup = groupProperty; + lastProperty = 0; // Append at end + const QList subProperties = lastGroup->subProperties(); + if (!subProperties.empty()) + lastProperty = subProperties.back(); + lastGroup = groupProperty; + } + if (!m_groups.contains(groupProperty)) + m_groups.append(groupProperty); + if (newProperty) + groupProperty->insertSubProperty(property, lastProperty); + + lastProperty = property; + + updateBrowserValue(property, value); + + property->setModified(m_propertySheet->isChanged(i)); + if (propertyName == QLatin1String("geometry") && type == QVariant::Rect) { + QList subProperties = property->subProperties(); + foreach (QtProperty *subProperty, subProperties) { + const QString subPropertyName = subProperty->propertyName(); + if (subPropertyName == QLatin1String("X") || subPropertyName == QLatin1String("Y")) + subProperty->setEnabled(!isMainContainer); + } + } + } else { + qWarning("%s", qPrintable(msgUnsupportedType(propertyName, type))); + } + } + } + QMap groups = m_nameToGroup; + QMapIterator itGroup(groups); + while (itGroup.hasNext()) { + QtVariantProperty *groupProperty = itGroup.next().value(); + if (groupProperty->subProperties().empty()) { + if (groupProperty == m_dynamicGroup) + m_dynamicGroup = 0; + delete groupProperty; + m_nameToGroup.remove(itGroup.key()); + } + } + const bool addEnabled = dynamicSheet ? dynamicSheet->dynamicPropertiesAllowed() : false; + m_addDynamicAction->setEnabled(addEnabled); + m_removeDynamicAction->setEnabled(false); + applyExpansionState(); + applyFilter(); + // In the first setObject() call following the addition of a dynamic property, focus and edit it. + if (editNewDynamicProperty) { + // Have QApplication process the events related to completely closing the modal 'add' dialog, + // otherwise, we cannot focus the property editor in docked mode. + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + editProperty(m_recentlyAddedDynamicProperty); + } + m_recentlyAddedDynamicProperty.clear(); + m_filterWidget->setEnabled(object); +} + +void PropertyEditor::reloadResourceProperties() +{ + m_updatingBrowser = true; + m_propertyManager->reloadResourceProperties(); + m_updatingBrowser = false; +} + +QtBrowserItem *PropertyEditor::nonFakePropertyBrowserItem(QtBrowserItem *item) const +{ + // Top-level properties are QObject/QWidget groups, etc. Find first item property below + // which should be nonfake + const QList topLevelItems = m_currentBrowser->topLevelItems(); + do { + if (topLevelItems.contains(item->parent())) + return item; + item = item->parent(); + } while (item); + return 0; +} + +QString PropertyEditor::currentPropertyName() const +{ + if (QtBrowserItem *browserItem = m_currentBrowser->currentItem()) + if (QtBrowserItem *topLevelItem = nonFakePropertyBrowserItem(browserItem)) { + return topLevelItem->property()->propertyName(); + } + return QString(); +} + +void PropertyEditor::slotResetProperty(QtProperty *property) +{ + QDesignerFormWindowInterface *form = m_core->formWindowManager()->activeFormWindow(); + if (!form) + return; + + if (m_propertyManager->resetFontSubProperty(property)) + return; + + if (m_propertyManager->resetIconSubProperty(property)) + return; + + if (!m_propertyToGroup.contains(property)) + return; + + emit resetProperty(property->propertyName()); +} + +void PropertyEditor::slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling) +{ + if (m_updatingBrowser) + return; + + if (!m_propertySheet) + return; + + QtVariantProperty *varProp = m_propertyManager->variantProperty(property); + + if (!varProp) + return; + + if (!m_propertyToGroup.contains(property)) + return; + + if (varProp->propertyType() == QtVariantPropertyManager::enumTypeId()) { + PropertySheetEnumValue e = qvariant_cast(m_propertySheet->property(m_propertySheet->indexOf(property->propertyName()))); + const int val = value.toInt(); + const QString valName = varProp->attributeValue(m_strings.m_enumNamesAttribute).toStringList().at(val); + bool ok = false; + e.value = e.metaEnum.parseEnum(valName, &ok); + Q_ASSERT(ok); + QVariant v; + v.setValue(e); + emitPropertyValueChanged(property->propertyName(), v, true); + return; + } + + emitPropertyValueChanged(property->propertyName(), value, enableSubPropertyHandling); +} + +bool PropertyEditor::isDynamicProperty(const QtBrowserItem* item) const +{ + if (!item) + return false; + + const QDesignerDynamicPropertySheetExtension *dynamicSheet = + qt_extension(m_core->extensionManager(), m_object); + + if (!dynamicSheet) + return false; + + if (m_propertyToGroup.contains(item->property()) + && dynamicSheet->isDynamicProperty(m_propertySheet->indexOf(item->property()->propertyName()))) + return true; + return false; +} + +void PropertyEditor::editProperty(const QString &name) +{ + // find the browser item belonging to the property, make it current and edit it + QtBrowserItem *browserItem = 0; + if (QtVariantProperty *property = m_nameToProperty.value(name, 0)) { + const QList items = m_currentBrowser->items(property); + if (items.size() == 1) + browserItem = items.front(); + } + if (browserItem == 0) + return; + m_currentBrowser->setFocus(Qt::OtherFocusReason); + if (m_currentBrowser == m_treeBrowser) { // edit is currently only supported in tree view + m_treeBrowser->editItem(browserItem); + } else { + m_currentBrowser->setCurrentItem(browserItem); + } +} + +void PropertyEditor::slotCurrentItemChanged(QtBrowserItem *item) +{ + m_removeDynamicAction->setEnabled(isDynamicProperty(item)); + +} + +void PropertyEditor::slotRemoveDynamicProperty() +{ + if (QtBrowserItem* item = m_currentBrowser->currentItem()) + if (isDynamicProperty(item)) + emit removeDynamicProperty(item->property()->propertyName()); +} + +void PropertyEditor::setFilter(const QString &pattern) +{ + m_filterPattern = pattern; + applyFilter(); +} +} + +QT_END_NAMESPACE + +#include +#include diff --git a/src/designer/components/propertyeditor/propertyeditor.h b/src/designer/components/propertyeditor/propertyeditor.h new file mode 100644 index 000000000..49a998ddb --- /dev/null +++ b/src/designer/components/propertyeditor/propertyeditor.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPERTYEDITOR_H +#define PROPERTYEDITOR_H + +#include "propertyeditor_global.h" +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class DomProperty; +class QDesignerMetaDataBaseItemInterface; +class QDesignerPropertySheetExtension; + +class QtAbstractPropertyBrowser; +class QtButtonPropertyBrowser; +class QtTreePropertyBrowser; +class QtProperty; +class QtVariantProperty; +class QtBrowserItem; +class QStackedWidget; + +namespace qdesigner_internal { + +class StringProperty; +class DesignerPropertyManager; +class DesignerEditorFactory; +class FilterWidget; +class ElidingLabel; + +class QT_PROPERTYEDITOR_EXPORT PropertyEditor: public QDesignerPropertyEditor +{ + Q_OBJECT +public: + explicit PropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~PropertyEditor(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual bool isReadOnly() const; + virtual void setReadOnly(bool readOnly); + virtual void setPropertyValue(const QString &name, const QVariant &value, bool changed = true); + virtual void updatePropertySheet(); + + virtual void setObject(QObject *object); + + void reloadResourceProperties(); + + virtual QObject *object() const + { return m_object; } + + virtual QString currentPropertyName() const; + +protected: + + bool event(QEvent *event); + +private slots: + void slotResetProperty(QtProperty *property); + void slotValueChanged(QtProperty *property, const QVariant &value, bool enableSubPropertyHandling); + void slotViewTriggered(QAction *action); + void slotAddDynamicProperty(QAction *action); + void slotRemoveDynamicProperty(); + void slotSorting(bool sort); + void slotColoring(bool color); + void slotCurrentItemChanged(QtBrowserItem*); + void setFilter(const QString &pattern); + +private: + void updateBrowserValue(QtVariantProperty *property, const QVariant &value); + void updateToolBarLabel(); + int toBrowserType(const QVariant &value, const QString &propertyName) const; + QString removeScope(const QString &value) const; + QDesignerMetaDataBaseItemInterface *metaDataBaseItem() const; + void setupStringProperty(QtVariantProperty *property, bool isMainContainer); + void setupPaletteProperty(QtVariantProperty *property); + QString realClassName(QObject *object) const; + void storeExpansionState(); + void applyExpansionState(); + void storePropertiesExpansionState(const QList &items); + void applyPropertiesExpansionState(const QList &items); + void applyFilter(); + int applyPropertiesFilter(const QList &items); + void setExpanded(QtBrowserItem *item, bool expanded); + bool isExpanded(QtBrowserItem *item) const; + void setItemVisible(QtBrowserItem *item, bool visible); + bool isItemVisible(QtBrowserItem *item) const; + void collapseAll(); + void clearView(); + void fillView(); + bool isLayoutGroup(QtProperty *group) const; + void updateColors(); + void updateForegroundBrightness(); + QColor propertyColor(QtProperty *property) const; + void updateActionsState(); + QtBrowserItem *nonFakePropertyBrowserItem(QtBrowserItem *item) const; + void saveSettings() const; + void editProperty(const QString &name); + bool isDynamicProperty(const QtBrowserItem* item) const; + + struct Strings { + Strings(); + QSet m_alignmentProperties; + const QString m_fontProperty; + const QString m_qLayoutWidget; + const QString m_designerPrefix; + const QString m_layout; + const QString m_validationModeAttribute; + const QString m_fontAttribute; + const QString m_superPaletteAttribute; + const QString m_enumNamesAttribute; + const QString m_resettableAttribute; + const QString m_flagsAttribute; + }; + + const Strings m_strings; + QDesignerFormEditorInterface *m_core; + QDesignerPropertySheetExtension *m_propertySheet; + QtAbstractPropertyBrowser *m_currentBrowser; + QtButtonPropertyBrowser *m_buttonBrowser; + QtTreePropertyBrowser *m_treeBrowser; + DesignerPropertyManager *m_propertyManager; + DesignerEditorFactory *m_treeFactory; + DesignerEditorFactory *m_groupFactory; + QPointer m_object; + QMap m_nameToProperty; + QMap m_propertyToGroup; + QMap m_nameToGroup; + QList m_groups; + QtProperty *m_dynamicGroup; + QString m_recentlyAddedDynamicProperty; + bool m_updatingBrowser; + + QStackedWidget *m_stackedWidget; + FilterWidget *m_filterWidget; + int m_buttonIndex; + int m_treeIndex; + QAction *m_addDynamicAction; + QAction *m_removeDynamicAction; + QAction *m_sortingAction; + QAction *m_coloringAction; + QAction *m_treeAction; + QAction *m_buttonAction; + ElidingLabel *m_classLabel; + + bool m_sorting; + bool m_coloring; + + QMap m_expansionState; + + QString m_filterPattern; + QVector > m_colors; + QPair m_dynamicColor; + QPair m_layoutColor; + + bool m_brightness; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROPERTYEDITOR_H diff --git a/src/designer/components/propertyeditor/propertyeditor.qrc b/src/designer/components/propertyeditor/propertyeditor.qrc new file mode 100644 index 000000000..68008eca8 --- /dev/null +++ b/src/designer/components/propertyeditor/propertyeditor.qrc @@ -0,0 +1,5 @@ + + + fontmapping.xml + + diff --git a/src/designer/components/propertyeditor/propertyeditor_global.h b/src/designer/components/propertyeditor/propertyeditor_global.h new file mode 100644 index 000000000..1d048d4a1 --- /dev/null +++ b/src/designer/components/propertyeditor/propertyeditor_global.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPERTYEDITOR_GLOBAL_H +#define PROPERTYEDITOR_GLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +#ifdef QT_PROPERTYEDITOR_LIBRARY +# define QT_PROPERTYEDITOR_EXPORT +#else +# define QT_PROPERTYEDITOR_EXPORT +#endif +#else +#define QT_PROPERTYEDITOR_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // PROPERTYEDITOR_GLOBAL_H diff --git a/src/designer/components/propertyeditor/qlonglongvalidator.cpp b/src/designer/components/propertyeditor/qlonglongvalidator.cpp new file mode 100644 index 000000000..bac55d919 --- /dev/null +++ b/src/designer/components/propertyeditor/qlonglongvalidator.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlonglongvalidator.h" + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +// ---------------------------------------------------------------------------- +QLongLongValidator::QLongLongValidator(QObject * parent) + : QValidator(parent), + b(Q_UINT64_C(0x8000000000000000)), t(Q_UINT64_C(0x7FFFFFFFFFFFFFFF)) +{ +} + +QLongLongValidator::QLongLongValidator(qlonglong minimum, qlonglong maximum, + QObject * parent) + : QValidator(parent), b(minimum), t(maximum) +{ +} + +QLongLongValidator::~QLongLongValidator() +{ + // nothing +} + +QValidator::State QLongLongValidator::validate(QString & input, int &) const +{ + if (input.contains(QLatin1Char(' '))) + return Invalid; + if (input.isEmpty() || (b < 0 && input == QString(QLatin1Char('-')))) + return Intermediate; + bool ok; + qlonglong entered = input.toLongLong(&ok); + if (!ok || (entered < 0 && b >= 0)) { + return Invalid; + } else if (entered >= b && entered <= t) { + return Acceptable; + } else { + if (entered >= 0) + return (entered > t) ? Invalid : Intermediate; + else + return (entered < b) ? Invalid : Intermediate; + } +} + +void QLongLongValidator::setRange(qlonglong bottom, qlonglong top) +{ + b = bottom; + t = top; +} + +void QLongLongValidator::setBottom(qlonglong bottom) +{ + setRange(bottom, top()); +} + +void QLongLongValidator::setTop(qlonglong top) +{ + setRange(bottom(), top); +} + + +// ---------------------------------------------------------------------------- +QULongLongValidator::QULongLongValidator(QObject * parent) + : QValidator(parent), + b(0), t(Q_UINT64_C(0xFFFFFFFFFFFFFFFF)) +{ +} + +QULongLongValidator::QULongLongValidator(qulonglong minimum, qulonglong maximum, + QObject * parent) + : QValidator(parent), b(minimum), t(maximum) +{ +} + +QULongLongValidator::~QULongLongValidator() +{ + // nothing +} + +QValidator::State QULongLongValidator::validate(QString & input, int &) const +{ + if (input.isEmpty()) + return Intermediate; + + bool ok; + qulonglong entered = input.toULongLong(&ok); + if (input.contains(QLatin1Char(' ')) || input.contains(QLatin1Char('-')) || !ok) + return Invalid; + + if (entered >= b && entered <= t) + return Acceptable; + + return Invalid; +} + +void QULongLongValidator::setRange(qulonglong bottom, qulonglong top) +{ + b = bottom; + t = top; +} + +void QULongLongValidator::setBottom(qulonglong bottom) +{ + setRange(bottom, top()); +} + +void QULongLongValidator::setTop(qulonglong top) +{ + setRange(bottom(), top); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/qlonglongvalidator.h b/src/designer/components/propertyeditor/qlonglongvalidator.h new file mode 100644 index 000000000..e81410559 --- /dev/null +++ b/src/designer/components/propertyeditor/qlonglongvalidator.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLONGLONGVALIDATOR_H +#define QLONGLONGVALIDATOR_H + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QLongLongValidator : public QValidator +{ + Q_OBJECT + Q_PROPERTY(qlonglong bottom READ bottom WRITE setBottom) + Q_PROPERTY(qlonglong top READ top WRITE setTop) + +public: + explicit QLongLongValidator(QObject * parent); + QLongLongValidator(qlonglong bottom, qlonglong top, QObject * parent); + ~QLongLongValidator(); + + QValidator::State validate(QString &, int &) const; + + void setBottom(qlonglong); + void setTop(qlonglong); + virtual void setRange(qlonglong bottom, qlonglong top); + + qlonglong bottom() const { return b; } + qlonglong top() const { return t; } + +private: + Q_DISABLE_COPY(QLongLongValidator) + + qlonglong b; + qlonglong t; +}; + +// ---------------------------------------------------------------------------- +class QULongLongValidator : public QValidator +{ + Q_OBJECT + Q_PROPERTY(qulonglong bottom READ bottom WRITE setBottom) + Q_PROPERTY(qulonglong top READ top WRITE setTop) + +public: + explicit QULongLongValidator(QObject * parent); + QULongLongValidator(qulonglong bottom, qulonglong top, QObject * parent); + ~QULongLongValidator(); + + QValidator::State validate(QString &, int &) const; + + void setBottom(qulonglong); + void setTop(qulonglong); + virtual void setRange(qulonglong bottom, qulonglong top); + + qulonglong bottom() const { return b; } + qulonglong top() const { return t; } + +private: + Q_DISABLE_COPY(QULongLongValidator) + + qulonglong b; + qulonglong t; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QLONGLONGVALIDATOR_H diff --git a/src/designer/components/propertyeditor/stringlisteditor.cpp b/src/designer/components/propertyeditor/stringlisteditor.cpp new file mode 100644 index 000000000..1280a68f1 --- /dev/null +++ b/src/designer/components/propertyeditor/stringlisteditor.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stringlisteditor.h" +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +StringListEditor::StringListEditor(QWidget *parent) + : QDialog(parent), m_model(new QStringListModel(this)) +{ + setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + listView->setModel(m_model); + + connect(listView->selectionModel(), + SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(currentIndexChanged(QModelIndex,QModelIndex))); + connect(listView->itemDelegate(), + SIGNAL(closeEditor(QWidget*,QAbstractItemDelegate::EndEditHint)), + this, SLOT(currentValueChanged())); + + QIcon upIcon = createIconSet(QString::fromUtf8("up.png")); + QIcon downIcon = createIconSet(QString::fromUtf8("down.png")); + QIcon minusIcon = createIconSet(QString::fromUtf8("minus.png")); + QIcon plusIcon = createIconSet(QString::fromUtf8("plus.png")); + upButton->setIcon(upIcon); + downButton->setIcon(downIcon); + newButton->setIcon(plusIcon); + deleteButton->setIcon(minusIcon); + + updateUi(); +} + +StringListEditor::~StringListEditor() +{ +} + +QStringList StringListEditor::getStringList(QWidget *parent, const QStringList &init, int *result) +{ + StringListEditor dlg(parent); + dlg.setStringList(init); + int res = dlg.exec(); + if (result) + *result = res; + return (res == QDialog::Accepted) ? dlg.stringList() : init; +} + +void StringListEditor::setStringList(const QStringList &stringList) +{ + m_model->setStringList(stringList); + updateUi(); +} + +QStringList StringListEditor::stringList() const +{ + return m_model->stringList(); +} + +void StringListEditor::currentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + Q_UNUSED(previous); + setCurrentIndex(current.row()); + updateUi(); +} + +void StringListEditor::currentValueChanged() +{ + setCurrentIndex(currentIndex()); + updateUi(); +} + +void StringListEditor::on_upButton_clicked() +{ + int from = currentIndex(); + int to = currentIndex() - 1; + QString value = stringAt(from); + removeString(from); + insertString(to, value); + setCurrentIndex(to); + updateUi(); +} + +void StringListEditor::on_downButton_clicked() +{ + int from = currentIndex(); + int to = currentIndex() + 1; + QString value = stringAt(from); + removeString(from); + insertString(to, value); + setCurrentIndex(to); + updateUi(); +} + +void StringListEditor::on_newButton_clicked() +{ + int to = currentIndex(); + if (to == -1) + to = count() - 1; + ++to; + insertString(to, QString()); + setCurrentIndex(to); + updateUi(); + editString(to); +} + +void StringListEditor::on_deleteButton_clicked() +{ + removeString(currentIndex()); + setCurrentIndex(currentIndex()); + updateUi(); +} + +void StringListEditor::on_valueEdit_textEdited(const QString &text) +{ + setStringAt(currentIndex(), text); +} + +void StringListEditor::updateUi() +{ + upButton->setEnabled((count() > 1) && (currentIndex() > 0)); + downButton->setEnabled((count() > 1) && (currentIndex() >= 0) && (currentIndex() < (count() - 1))); + deleteButton->setEnabled(currentIndex() != -1); + valueEdit->setEnabled(currentIndex() != -1); +} + +int StringListEditor::currentIndex() const +{ + return listView->currentIndex().row(); +} + +void StringListEditor::setCurrentIndex(int index) +{ + QModelIndex modelIndex = m_model->index(index, 0); + if (listView->currentIndex() != modelIndex) + listView->setCurrentIndex(modelIndex); + valueEdit->setText(stringAt(index)); +} + +int StringListEditor::count() const +{ + return m_model->rowCount(); +} + +QString StringListEditor::stringAt(int index) const +{ + return qvariant_cast(m_model->data(m_model->index(index, 0), Qt::DisplayRole)); +} + +void StringListEditor::setStringAt(int index, const QString &value) +{ + m_model->setData(m_model->index(index, 0), value); +} + +void StringListEditor::removeString(int index) +{ + m_model->removeRows(index, 1); +} + +void StringListEditor::insertString(int index, const QString &value) +{ + m_model->insertRows(index, 1); + m_model->setData(m_model->index(index, 0), value); +} + +void StringListEditor::editString(int index) +{ + listView->edit(m_model->index(index, 0)); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/stringlisteditor.h b/src/designer/components/propertyeditor/stringlisteditor.h new file mode 100644 index 000000000..d7a748d47 --- /dev/null +++ b/src/designer/components/propertyeditor/stringlisteditor.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STRINGLISTEDITOR_H +#define STRINGLISTEDITOR_H + +#include "ui_stringlisteditor.h" +#include + +QT_BEGIN_NAMESPACE +class QStringListModel; + +namespace qdesigner_internal { + +class StringListEditor : public QDialog, private Ui::Dialog +{ + Q_OBJECT +public: + ~StringListEditor(); + void setStringList(const QStringList &stringList); + QStringList stringList() const; + + static QStringList getStringList( + QWidget *parent, const QStringList &init = QStringList(), int *result = 0); + +private slots: + void on_upButton_clicked(); + void on_downButton_clicked(); + void on_newButton_clicked(); + void on_deleteButton_clicked(); + void on_valueEdit_textEdited(const QString &text); + void currentIndexChanged(const QModelIndex ¤t, const QModelIndex &previous); + void currentValueChanged(); + +private: + StringListEditor(QWidget *parent = 0); + void updateUi(); + int currentIndex() const; + void setCurrentIndex(int index); + int count() const; + QString stringAt(int index) const; + void setStringAt(int index, const QString &value); + void removeString(int index); + void insertString(int index, const QString &value); + void editString(int index); + + QStringListModel *m_model; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // STRINGLISTEDITOR_H diff --git a/src/designer/components/propertyeditor/stringlisteditor.ui b/src/designer/components/propertyeditor/stringlisteditor.ui new file mode 100644 index 000000000..8c9e5eea0 --- /dev/null +++ b/src/designer/components/propertyeditor/stringlisteditor.ui @@ -0,0 +1,265 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + 9 + + + 6 + + + + + StringList + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + 0 + + + 6 + + + + + New String + + + &New + + + Qt::ToolButtonTextBesideIcon + + + + + + + Delete String + + + &Delete + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + 0 + + + 6 + + + + + &Value: + + + valueEdit + + + + + + + + + + + + + + 0 + + + 6 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Move String Up + + + Up + + + + + + + Move String Down + + + Down + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + qdesigner_internal::Dialog + accept() + + + 258 + 283 + + + 138 + 294 + + + + + buttonBox + rejected() + qdesigner_internal::Dialog + reject() + + + 350 + 284 + + + 369 + 295 + + + + + diff --git a/src/designer/components/propertyeditor/stringlisteditorbutton.cpp b/src/designer/components/propertyeditor/stringlisteditorbutton.cpp new file mode 100644 index 000000000..32b34985c --- /dev/null +++ b/src/designer/components/propertyeditor/stringlisteditorbutton.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stringlisteditorbutton.h" +#include "stringlisteditor.h" + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +StringListEditorButton::StringListEditorButton( + const QStringList &stringList, QWidget *parent) + : QToolButton(parent), m_stringList(stringList) +{ + setFocusPolicy(Qt::NoFocus); + setText(tr("Change String List")); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + + connect(this, SIGNAL(clicked()), this, SLOT(showStringListEditor())); +} + +StringListEditorButton::~StringListEditorButton() +{ +} + +void StringListEditorButton::setStringList(const QStringList &stringList) +{ + m_stringList = stringList; +} + +void StringListEditorButton::showStringListEditor() +{ + int result; + QStringList lst = StringListEditor::getStringList(0, m_stringList, &result); + if (result == QDialog::Accepted) { + m_stringList = lst; + emit stringListChanged(m_stringList); + } +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/propertyeditor/stringlisteditorbutton.h b/src/designer/components/propertyeditor/stringlisteditorbutton.h new file mode 100644 index 000000000..d3ffb9bb6 --- /dev/null +++ b/src/designer/components/propertyeditor/stringlisteditorbutton.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STRINGLISTEDITORBUTTON_H +#define STRINGLISTEDITORBUTTON_H + +#include "propertyeditor_global.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QT_PROPERTYEDITOR_EXPORT StringListEditorButton: public QToolButton +{ + Q_OBJECT +public: + explicit StringListEditorButton(const QStringList &stringList, QWidget *parent = 0); + virtual ~StringListEditorButton(); + + inline QStringList stringList() const + { return m_stringList; } + +signals: + void stringListChanged(const QStringList &stringList); + +public slots: + void setStringList(const QStringList &stringList); + +private slots: + void showStringListEditor(); + +private: + QStringList m_stringList; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // STRINGLISTEDITORBUTTON_H diff --git a/src/designer/components/qdesigner_components.cpp b/src/designer/components/qdesigner_components.cpp new file mode 100644 index 000000000..5e6791939 --- /dev/null +++ b/src/designer/components/qdesigner_components.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "qtresourceview_p.h" +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// ### keep it in sync with Q_IMPORT_PLUGIN in qplugin.h +#define DECLARE_PLUGIN_INSTANCE(PLUGIN) \ + extern QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGIN(); \ + class Static##PLUGIN##PluginInstance { public: \ + Static##PLUGIN##PluginInstance() { \ + QT_PREPEND_NAMESPACE(qRegisterStaticPluginInstanceFunction) \ + (&qt_plugin_instance_##PLUGIN); \ + } \ + }; + +#define INIT_PLUGIN_INSTANCE(PLUGIN) \ + do { \ + Static##PLUGIN##PluginInstance instance; \ + Q_UNUSED(instance); \ + } while (0) + +DECLARE_PLUGIN_INSTANCE(SignalSlotEditorPlugin) +DECLARE_PLUGIN_INSTANCE(BuddyEditorPlugin) +DECLARE_PLUGIN_INSTANCE(TabOrderEditorPlugin) + +static void initResources() +{ + // Q_INIT_RESOURCE only usable in functions in global namespace + Q_INIT_RESOURCE(formeditor); + Q_INIT_RESOURCE(widgetbox); + Q_INIT_RESOURCE(propertyeditor); +} + + +static void initInstances() +{ + static bool plugins_initialized = false; + + if (!plugins_initialized) { + INIT_PLUGIN_INSTANCE(SignalSlotEditorPlugin); + INIT_PLUGIN_INSTANCE(BuddyEditorPlugin); + INIT_PLUGIN_INSTANCE(TabOrderEditorPlugin); + plugins_initialized = true; + } +} + +QT_BEGIN_NAMESPACE + +/*! + \class QDesignerComponents + \brief The QDesignerComponents class provides a central resource for the various components + used in the \QD user interface. + \inmodule QtDesigner + \internal + + The QDesignerComponents class is a factory for each of the standard components present + in the \QD user interface. It is mostly useful for developers who want to implement + a standalone form editing environment using \QD's components, or who need to integrate + \QD's components into an existing integrated development environment (IDE). + + \sa QDesignerFormEditorInterface, QDesignerObjectInspectorInterface, + QDesignerPropertyEditorInterface, QDesignerWidgetBoxInterface +*/ + +/*! + Initializes the resources used by the components.*/ +void QDesignerComponents::initializeResources() +{ + initResources(); +} + +/*! + Initializes the plugins used by the components.*/ +void QDesignerComponents::initializePlugins(QDesignerFormEditorInterface *core) +{ + qdesigner_internal::QDesignerIntegration::initializePlugins(core); +} + +/*! + Constructs a form editor interface with the given \a parent.*/ +QDesignerFormEditorInterface *QDesignerComponents::createFormEditor(QObject *parent) +{ + initInstances(); + return new qdesigner_internal::FormEditor(parent); +} + +/*! + Returns a new task menu with the given \a parent for the \a core interface.*/ +QObject *QDesignerComponents::createTaskMenu(QDesignerFormEditorInterface *core, QObject *parent) +{ + return new qdesigner_internal::TaskMenuComponent(core, parent); +} + +static inline int qtMajorVersion(int qtVersion) { return qtVersion >> 16; } +static inline int qtMinorVersion(int qtVersion) { return (qtVersion >> 8) & 0xFF; } +static inline void setMinorVersion(int minorVersion, int *qtVersion) +{ + *qtVersion &= ~0xFF00; + *qtVersion |= minorVersion << 8; +} + +// Build the version-dependent name of the user widget box file, '$HOME.designer/widgetbox4.4.xml' +static inline QString widgetBoxFileName(int qtVersion, const QDesignerLanguageExtension *lang = 0) +{ + QString rc; { + const QChar dot = QLatin1Char('.'); + QTextStream str(&rc); + str << QDir::homePath() << QDir::separator() << QLatin1String(".designer") << QDir::separator() + << QLatin1String("widgetbox"); + // The naming convention using the version was introduced with 4.4 + const int major = qtMajorVersion(qtVersion); + const int minor = qtMinorVersion(qtVersion); + if (major >= 4 && minor >= 4) + str << major << dot << minor; + if (lang) + str << dot << lang->uiExtension(); + str << QLatin1String(".xml"); + } + return rc; +} + +/*! + Returns a new widget box interface with the given \a parent for the \a core interface.*/ +QDesignerWidgetBoxInterface *QDesignerComponents::createWidgetBox(QDesignerFormEditorInterface *core, QWidget *parent) +{ + qdesigner_internal::WidgetBox *widgetBox = new qdesigner_internal::WidgetBox(core, parent); + + const QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core); + + do { + if (lang) { + const QString languageWidgetBox = lang->widgetBoxContents(); + if (!languageWidgetBox.isEmpty()) { + widgetBox->loadContents(lang->widgetBoxContents()); + break; + } + } + + widgetBox->setFileName(QLatin1String(":/trolltech/widgetbox/widgetbox.xml")); + widgetBox->load(); + } while (false); + + const QString userWidgetBoxFile = widgetBoxFileName(QT_VERSION, lang); + + widgetBox->setFileName(userWidgetBoxFile); + if (!QFileInfo(userWidgetBoxFile).exists()) { + // check previous version, that is, are we running the new version for the first time + // If so, try to copy the old widget box file + if (const int minv = qtMinorVersion(QT_VERSION)) { + int oldVersion = QT_VERSION; + setMinorVersion(minv - 1, &oldVersion); + const QString oldWidgetBoxFile = widgetBoxFileName(oldVersion, lang); + if (QFileInfo(oldWidgetBoxFile).exists()) + QFile::copy(oldWidgetBoxFile, userWidgetBoxFile); + } + } + widgetBox->load(); + + return widgetBox; +} + +/*! + Returns a new property editor interface with the given \a parent for the \a core interface.*/ +QDesignerPropertyEditorInterface *QDesignerComponents::createPropertyEditor(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new qdesigner_internal::PropertyEditor(core, parent); +} + +/*! + Returns a new object inspector interface with the given \a parent for the \a core interface.*/ +QDesignerObjectInspectorInterface *QDesignerComponents::createObjectInspector(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new qdesigner_internal::ObjectInspector(core, parent); +} + +/*! + Returns a new action editor interface with the given \a parent for the \a core interface.*/ +QDesignerActionEditorInterface *QDesignerComponents::createActionEditor(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new qdesigner_internal::ActionEditor(core, parent); +} + +/*! + Returns a new resource editor with the given \a parent for the \a core interface.*/ +QWidget *QDesignerComponents::createResourceEditor(QDesignerFormEditorInterface *core, QWidget *parent) +{ + if (QDesignerLanguageExtension *lang = qt_extension(core->extensionManager(), core)) { + QWidget *w = lang->createResourceBrowser(parent); + if (w) + return w; + } + QtResourceView *resourceView = new QtResourceView(core, parent); + resourceView->setResourceModel(core->resourceModel()); + resourceView->setSettingsKey(QLatin1String("ResourceBrowser")); + qdesigner_internal::QDesignerIntegration *designerIntegration = qobject_cast(core->integration()); + // Note for integrators: make sure you call createResourceEditor() after you instantiated your subclass of designer integration + // (designer doesn't do that since by default editing resources is enabled) + if (designerIntegration) + resourceView->setResourceEditingEnabled(designerIntegration->isResourceEditingEnabled()); + return resourceView; +} + +/*! + Returns a new signal-slot editor with the given \a parent for the \a core interface.*/ +QWidget *QDesignerComponents::createSignalSlotEditor(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new qdesigner_internal::SignalSlotEditorWindow(core, parent); +} + +QT_END_NAMESPACE + diff --git a/src/designer/components/signalsloteditor/connectdialog.cpp b/src/designer/components/signalsloteditor/connectdialog.cpp new file mode 100644 index 000000000..268297bd5 --- /dev/null +++ b/src/designer/components/signalsloteditor/connectdialog.cpp @@ -0,0 +1,336 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "connectdialog_p.h" +#include "signalslot_utils_p.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList ListWidgetItems; +} + +static QString realClassName(QDesignerFormEditorInterface *core, QWidget *widget) +{ + QString class_name = QLatin1String(widget->metaObject()->className()); + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int idx = wdb->indexOfObject(widget); + if (idx != -1) + class_name = wdb->item(idx)->name(); + return class_name; +} + +static QString widgetLabel(QDesignerFormEditorInterface *core, QWidget *widget) +{ + return QString::fromUtf8("%1 (%2)") + .arg(qdesigner_internal::realObjectName(core, widget)) + .arg(realClassName(core, widget)); +} + +namespace qdesigner_internal { + +ConnectDialog::ConnectDialog(QDesignerFormWindowInterface *formWindow, + QWidget *source, QWidget *destination, + QWidget *parent) : + QDialog(parent), + m_source(source), + m_destination(destination), + m_sourceMode(widgetMode(m_source, formWindow)), + m_destinationMode(widgetMode(m_destination, formWindow)), + m_formWindow(formWindow) +{ + m_ui.setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + connect(m_ui.signalList, SIGNAL(itemClicked(QListWidgetItem*)), + this, SLOT(selectSignal(QListWidgetItem*))); + connect(m_ui.slotList, SIGNAL(itemClicked(QListWidgetItem*)), + this, SLOT(selectSlot(QListWidgetItem*))); + m_ui.slotList->setEnabled(false); + + QPushButton *ok_button = okButton(); + ok_button->setDefault(true); + ok_button->setEnabled(false); + + connect(m_ui.showAllCheckBox, SIGNAL(toggled(bool)), this, SLOT(populateLists())); + + QDesignerFormEditorInterface *core = m_formWindow->core(); + m_ui.signalGroupBox->setTitle(widgetLabel(core, source)); + m_ui.slotGroupBox->setTitle(widgetLabel(core, destination)); + + m_ui.editSignalsButton->setEnabled(m_sourceMode != NormalWidget); + connect(m_ui.editSignalsButton, SIGNAL(clicked()), this, SLOT(editSignals())); + + m_ui.editSlotsButton->setEnabled(m_destinationMode != NormalWidget); + connect(m_ui.editSlotsButton, SIGNAL(clicked()), this, SLOT(editSlots())); + + populateLists(); +} + +ConnectDialog::WidgetMode ConnectDialog::widgetMode(QWidget *w, QDesignerFormWindowInterface *formWindow) +{ + QDesignerFormEditorInterface *core = formWindow->core(); + if (qt_extension(core->extensionManager(), core)) + return NormalWidget; + + if (w == formWindow || formWindow->mainContainer() == w) + return MainContainer; + + if (isPromoted(formWindow->core(), w)) + return PromotedWidget; + + return NormalWidget; +} + +QPushButton *ConnectDialog::okButton() +{ + return m_ui.buttonBox->button(QDialogButtonBox::Ok); +} + +void ConnectDialog::setOkButtonEnabled(bool e) +{ + okButton()->setEnabled(e); +} + +void ConnectDialog::populateLists() +{ + populateSignalList(); +} + +void ConnectDialog::setSignalSlot(const QString &signal, const QString &slot) +{ + ListWidgetItems sigItems = m_ui.signalList->findItems(signal, Qt::MatchExactly); + + if (sigItems.empty()) { + m_ui.showAllCheckBox->setChecked(true); + sigItems = m_ui.signalList->findItems(signal, Qt::MatchExactly); + } + + if (!sigItems.empty()) { + selectSignal(sigItems.front()); + ListWidgetItems slotItems = m_ui.slotList->findItems(slot, Qt::MatchExactly); + if (slotItems.empty()) { + m_ui.showAllCheckBox->setChecked(true); + slotItems = m_ui.slotList->findItems(slot, Qt::MatchExactly); + } + if (!slotItems.empty()) + selectSlot(slotItems.front()); + } +} + +bool ConnectDialog::showAllSignalsSlots() const +{ + return m_ui.showAllCheckBox->isChecked(); +} + +void ConnectDialog::setShowAllSignalsSlots(bool showIt) +{ + m_ui.showAllCheckBox->setChecked(showIt); +} + +void ConnectDialog::selectSignal(QListWidgetItem *item) +{ + if (item) { + m_ui.signalList->setCurrentItem(item); + populateSlotList(item->text()); + m_ui.slotList->setEnabled(true); + setOkButtonEnabled(!m_ui.slotList->selectedItems().isEmpty()); + } else { + m_ui.signalList->clearSelection(); + populateSlotList(); + m_ui.slotList->setEnabled(false); + setOkButtonEnabled(false); + } +} + +void ConnectDialog::selectSlot(QListWidgetItem *item) +{ + if (item) { + m_ui.slotList->setCurrentItem(item); + } else { + m_ui.slotList->clearSelection(); + } + setOkButtonEnabled(true); +} + +QString ConnectDialog::signal() const +{ + const ListWidgetItems item_list = m_ui.signalList->selectedItems(); + if (item_list.size() != 1) + return QString(); + return item_list.at(0)->text(); +} + +QString ConnectDialog::slot() const +{ + const ListWidgetItems item_list = m_ui.slotList->selectedItems(); + if (item_list.size() != 1) + return QString(); + return item_list.at(0)->text(); +} + +void ConnectDialog::populateSlotList(const QString &signal) +{ + QString selectedName; + if (const QListWidgetItem * item = m_ui.slotList->currentItem()) + selectedName = item->text(); + + m_ui.slotList->clear(); + + QMap memberToClassName = getMatchingSlots(m_formWindow->core(), m_destination, signal, showAllSignalsSlots()); + + QFont font = QApplication::font(); + font.setItalic(true); + QVariant variantFont = QVariant::fromValue(font); + + QListWidgetItem *curr = 0; + QMap::ConstIterator itMember = memberToClassName.constBegin(); + const QMap::ConstIterator itMemberEnd = memberToClassName.constEnd(); + while (itMember != itMemberEnd) { + const QString member = itMember.key(); + const bool qt3Slot = isQt3Slot(m_formWindow->core(), m_destination, member); + + QListWidgetItem *item = new QListWidgetItem(m_ui.slotList); + item->setText(member); + if (member == selectedName) + curr = item; + + if (qt3Slot) { + item->setData(Qt::FontRole, variantFont); + item->setData(Qt::ForegroundRole, Qt::red); + } + ++itMember; + } + + if (curr) + m_ui.slotList->setCurrentItem(curr); + + if (m_ui.slotList->selectedItems().isEmpty()) + setOkButtonEnabled(false); +} + +void ConnectDialog::populateSignalList() +{ + QString selectedName; + if (const QListWidgetItem *item = m_ui.signalList->currentItem()) + selectedName = item->text(); + + m_ui.signalList->clear(); + + QMap memberToClassName = getSignals(m_formWindow->core(), m_source, showAllSignalsSlots()); + + QFont font = QApplication::font(); + font.setItalic(true); + QVariant variantFont = QVariant::fromValue(font); + + QListWidgetItem *curr = 0; + QMap::ConstIterator itMember = memberToClassName.constBegin(); + const QMap::ConstIterator itMemberEnd = memberToClassName.constEnd(); + while (itMember != itMemberEnd) { + const QString member = itMember.key(); + const bool qt3Signal = isQt3Signal(m_formWindow->core(), m_source, member); + + QListWidgetItem *item = new QListWidgetItem(m_ui.signalList); + item->setText(member); + if (!selectedName.isEmpty() && member == selectedName) + curr = item; + + if (qt3Signal) { + item->setData(Qt::FontRole, variantFont); + item->setData(Qt::ForegroundRole, Qt::red); + } + ++itMember; + } + + if (curr) { + m_ui.signalList->setCurrentItem(curr); + } else { + selectedName.clear(); + } + + populateSlotList(selectedName); + if (!curr) + m_ui.slotList->setEnabled(false); +} + +void ConnectDialog::editSignals() +{ + editSignalsSlots(m_source, m_sourceMode, SignalSlotDialog::FocusSignals); +} + +void ConnectDialog::editSlots() +{ + editSignalsSlots(m_destination, m_destinationMode, SignalSlotDialog::FocusSlots); +} + +void ConnectDialog::editSignalsSlots(QWidget *w, WidgetMode mode, int signalSlotDialogModeInt) +{ + const SignalSlotDialog::FocusMode signalSlotDialogMode = static_cast(signalSlotDialogModeInt); + switch (mode) { + case NormalWidget: + break; + case MainContainer: + if (SignalSlotDialog::editMetaDataBase(m_formWindow, w, this, signalSlotDialogMode)) + populateLists(); + break; + case PromotedWidget: + if (SignalSlotDialog::editPromotedClass(m_formWindow->core(), w, this, signalSlotDialogMode)) + populateLists(); + break; + } +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/signalsloteditor/connectdialog.ui b/src/designer/components/signalsloteditor/connectdialog.ui new file mode 100644 index 000000000..568516a42 --- /dev/null +++ b/src/designer/components/signalsloteditor/connectdialog.ui @@ -0,0 +1,150 @@ + + ConnectDialog + + + + 0 + 0 + 585 + 361 + + + + Configure Connection + + + + + + GroupBox + + + + + + Qt::ElideMiddle + + + + + + + + + Edit... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + GroupBox + + + + + + Qt::ElideMiddle + + + + + + + + + Edit... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Show signals and slots inherited from QWidget + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ConnectDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ConnectDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/designer/components/signalsloteditor/connectdialog_p.h b/src/designer/components/signalsloteditor/connectdialog_p.h new file mode 100644 index 000000000..dbb9bd207 --- /dev/null +++ b/src/designer/components/signalsloteditor/connectdialog_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONNECTDIALOG_H +#define CONNECTDIALOG_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_connectdialog.h" +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QPushButton; + +namespace qdesigner_internal { + +class ConnectDialog : public QDialog +{ + Q_OBJECT +public: + ConnectDialog(QDesignerFormWindowInterface *formWindow, QWidget *sender, QWidget *receiver, QWidget *parent = 0); + + QString signal() const; + QString slot() const; + + void setSignalSlot(const QString &signal, const QString &slot); + + bool showAllSignalsSlots() const; + void setShowAllSignalsSlots(bool showIt); + +private slots: + void populateLists(); + void selectSignal(QListWidgetItem *item); + void selectSlot(QListWidgetItem *item); + void populateSignalList(); + void populateSlotList(const QString &signal = QString()); + void editSignals(); + void editSlots(); + +private: + enum WidgetMode { NormalWidget, MainContainer, PromotedWidget }; + + static WidgetMode widgetMode(QWidget *w, QDesignerFormWindowInterface *formWindow); + QPushButton *okButton(); + void setOkButtonEnabled(bool); + void editSignalsSlots(QWidget *w, WidgetMode mode, int signalSlotDialogMode); + + QWidget *m_source; + QWidget *m_destination; + const WidgetMode m_sourceMode; + const WidgetMode m_destinationMode; + QDesignerFormWindowInterface *m_formWindow; + Ui::ConnectDialog m_ui; +}; + +} + +QT_END_NAMESPACE + +#endif // CONNECTDIALOG_H diff --git a/src/designer/components/signalsloteditor/signalslot_utils.cpp b/src/designer/components/signalsloteditor/signalslot_utils.cpp new file mode 100644 index 000000000..45a5df751 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalslot_utils.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalslot_utils_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +typedef QPair ClassNameSignaturePair; + +// Find all member functions that match a predicate on the signature string +// using the member sheet and the fake methods stored in the widget +// database and the meta data base. +// Assign a pair of to OutputIterator. + +template +static void memberList(QDesignerFormEditorInterface *core, + QObject *object, + qdesigner_internal::MemberType member_type, + bool showAll, + SignaturePredicate predicate, + OutputIterator it) +{ + if (!object) + return; + // 1) member sheet + const QDesignerMemberSheetExtension *members = qt_extension(core->extensionManager(), object); + Q_ASSERT(members != 0); + const int count = members->count(); + for (int i = 0; i < count; ++i) { + if (!members->isVisible(i)) + continue; + + if (member_type == qdesigner_internal::SignalMember && !members->isSignal(i)) + continue; + + if (member_type == qdesigner_internal::SlotMember && !members->isSlot(i)) + continue; + + if (!showAll && members->inheritedFromWidget(i)) + continue; + + const QString signature = members->signature(i); + if (predicate(signature)) { + *it = ClassNameSignaturePair(members->declaredInClass(i), signature); + ++it; + } + } + // 2) fake slots from widget DB + const qdesigner_internal::WidgetDataBase *wdb = qobject_cast(core->widgetDataBase()); + if (!wdb) + return; + const int idx = wdb->indexOfObject(object); + Q_ASSERT(idx != -1); + // get the promoted class name + const qdesigner_internal::WidgetDataBaseItem *wdbItem = static_cast(wdb->item(idx)); + const QString className = wdbItem->name(); + + const QStringList wdbFakeMethods = member_type == qdesigner_internal::SlotMember ? wdbItem->fakeSlots() : wdbItem->fakeSignals(); + if (!wdbFakeMethods.empty()) + foreach (const QString &fakeMethod, wdbFakeMethods) + if (predicate(fakeMethod)) { + *it = ClassNameSignaturePair(className, fakeMethod); + ++it; + } + // 3) fake slots from meta DB + qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast(core->metaDataBase()); + if (!metaDataBase) + return; + + if (const qdesigner_internal::MetaDataBaseItem *mdbItem = metaDataBase->metaDataBaseItem(object)) { + const QStringList mdbFakeMethods = member_type == qdesigner_internal::SlotMember ? mdbItem->fakeSlots() : mdbItem->fakeSignals(); + if (!mdbFakeMethods.empty()) + foreach (const QString &fakeMethod, mdbFakeMethods) + if (predicate(fakeMethod)) { + *it = ClassNameSignaturePair(className, fakeMethod); + ++it; + } + } +} + +namespace { + // Predicate that matches the exact signature string + class EqualsPredicate { + public: + EqualsPredicate(const QString &pattern) : m_pattern(pattern) {} + bool operator()(const QString &s) const { return s == m_pattern; } + private: + const QString m_pattern; + }; + // Predicate for a QString member signature that matches signals up with slots and vice versa + class SignalMatchesSlotPredicate { + public: + SignalMatchesSlotPredicate(QDesignerFormEditorInterface *core, const QString &peer, qdesigner_internal::MemberType memberType); + bool operator()(const QString &s) const; + + private: + bool signalMatchesSlot(const QString &signal, const QString &slot) const; + + const QString m_peer; + qdesigner_internal::MemberType m_memberType; + const QDesignerLanguageExtension *m_lang; + }; + + SignalMatchesSlotPredicate::SignalMatchesSlotPredicate(QDesignerFormEditorInterface *core, const QString &peer, qdesigner_internal::MemberType memberType) : + m_peer(peer), + m_memberType(memberType), + m_lang(qt_extension(core->extensionManager(), core)) + { + } + + bool SignalMatchesSlotPredicate::operator()(const QString &s) const + { + return m_memberType == qdesigner_internal::SlotMember ? signalMatchesSlot(m_peer, s) : signalMatchesSlot(s, m_peer); + } + + bool SignalMatchesSlotPredicate::signalMatchesSlot(const QString &signal, const QString &slot) const + { + if (m_lang) + return m_lang->signalMatchesSlot(signal, slot); + + return QDesignerMemberSheet::signalMatchesSlot(signal, slot); + } + + // Output iterator for a pair of pair of + // that builds the reverse class list for reverseClassesMemberFunctions() + // (for the combos of the ToolWindow) + class ReverseClassesMemberIterator { + public: + ReverseClassesMemberIterator(qdesigner_internal::ClassesMemberFunctions *result); + + ReverseClassesMemberIterator &operator*() { return *this; } + ReverseClassesMemberIterator &operator++(int) { return *this; } + ReverseClassesMemberIterator &operator++() { return *this; } + void operator=(const ClassNameSignaturePair &classNameSignature); + + private: + qdesigner_internal::ClassesMemberFunctions *m_result; + QString m_lastClassName; + QStringList *m_memberList; + }; + + ReverseClassesMemberIterator::ReverseClassesMemberIterator(qdesigner_internal::ClassesMemberFunctions *result) : + m_result(result), + m_memberList(0) + { + } + + void ReverseClassesMemberIterator::operator=(const ClassNameSignaturePair &classNameSignature) + { + // prepend a new entry if class changes + if (!m_memberList || classNameSignature.first != m_lastClassName) { + m_lastClassName = classNameSignature.first; + m_result->push_front(qdesigner_internal::ClassMemberFunctions(m_lastClassName)); + m_memberList = &(m_result->front().m_memberList); + } + m_memberList->push_back(classNameSignature.second); + } + + // Output iterator for a pair of pair of + // that adds the signatures to a string list + class SignatureIterator { + public: + SignatureIterator(QMap *result) : m_result(result) {} + + SignatureIterator &operator*() { return *this; } + SignatureIterator &operator++(int) { return *this; } + SignatureIterator &operator++() { return *this; } + void operator=(const ClassNameSignaturePair &classNameSignature) { + m_result->insert(classNameSignature.second, classNameSignature.first); + } + + private: + QMap *m_result; + }; +} + +static inline bool truePredicate(const QString &) { return true; } + +namespace qdesigner_internal { + + ClassMemberFunctions::ClassMemberFunctions(const QString &class_name) : + m_className(class_name) + { + } + + bool signalMatchesSlot(QDesignerFormEditorInterface *core, const QString &signal, const QString &slot) + { + const SignalMatchesSlotPredicate predicate(core, signal, qdesigner_internal::SlotMember); + return predicate(slot); + } + + // return classes and members in reverse class order to + // populate of the combo of the ToolWindow + ClassesMemberFunctions reverseClassesMemberFunctions(const QString &obj_name, MemberType member_type, + const QString &peer, QDesignerFormWindowInterface *form) + { + QObject *object = 0; + if (obj_name == form->mainContainer()->objectName()) { + object = form->mainContainer(); + } else { + object = form->mainContainer()->findChild(obj_name); + } + if (!object) + return ClassesMemberFunctions(); + QDesignerFormEditorInterface *core = form->core(); + + ClassesMemberFunctions rc; + memberList(form->core(), object, member_type, true, SignalMatchesSlotPredicate(core, peer, member_type), + ReverseClassesMemberIterator(&rc)); + return rc; + } + + QMap getSignals(QDesignerFormEditorInterface *core, QObject *object, bool showAll) + { + QMap rc; + memberList(core, object, SignalMember, showAll, truePredicate, SignatureIterator(&rc)); + return rc; + } + + bool isQt3Signal(QDesignerFormEditorInterface *core, + QObject *object, const QString &signalSignature) + { + if (const QDesignerMemberSheetExtension *members + = qt_extension(core->extensionManager(), object)) { + const int count = members->count(); + for (int i = 0; i < count; ++i) + if (members->isSignal(i) && members->signature(i) == signalSignature) { + const QDesignerMemberSheet *memberSheet + = qobject_cast(core->extensionManager()->extension(object, + Q_TYPEID(QDesignerMemberSheetExtension))); + return (memberSheet && memberSheet->isQt3Signal(i)); + } + } + + return false; + } + + bool isQt3Slot(QDesignerFormEditorInterface *core, + QObject *object, const QString &slotSignature) + { + if (const QDesignerMemberSheetExtension *members + = qt_extension(core->extensionManager(), object)) { + Q_ASSERT(members != 0); + const int count = members->count(); + for (int i = 0; i < count; ++i) + if (members->isSlot(i) && members->signature(i) == slotSignature) { + const QDesignerMemberSheet *memberSheet + = qobject_cast(core->extensionManager()->extension(object, + Q_TYPEID(QDesignerMemberSheetExtension))); + return (memberSheet && memberSheet->isQt3Slot(i)); + } + } + return false; + } + + QMap getMatchingSlots(QDesignerFormEditorInterface *core, QObject *object, const QString &signalSignature, bool showAll) + { + QMap rc; + memberList(core, object, SlotMember, showAll, SignalMatchesSlotPredicate(core, signalSignature, qdesigner_internal::SlotMember), SignatureIterator(&rc)); + return rc; + } + + bool memberFunctionListContains(QDesignerFormEditorInterface *core, QObject *object, MemberType type, const QString &signature) + { + QMap rc; + memberList(core, object, type, true, EqualsPredicate(signature), SignatureIterator(&rc)); + return !rc.empty(); + } + + // ### deprecated + QString realObjectName(QDesignerFormEditorInterface *core, QObject *object) + { + if (!object) + return QString(); + + const QDesignerMetaDataBaseInterface *mdb = core->metaDataBase(); + if (const QDesignerMetaDataBaseItemInterface *item = mdb->item(object)) + return item->name(); + + return object->objectName(); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/src/designer/components/signalsloteditor/signalslot_utils_p.h b/src/designer/components/signalsloteditor/signalslot_utils_p.h new file mode 100644 index 000000000..4c9613328 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalslot_utils_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTUTILS_P_H +#define SIGNALSLOTUTILS_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 +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +enum MemberType { SignalMember, SlotMember }; + +// member to class name +QMap getSignals(QDesignerFormEditorInterface *core, QObject *object, bool showAll); +QMap getMatchingSlots(QDesignerFormEditorInterface *core, QObject *object, + const QString &signalSignature, bool showAll); + +bool memberFunctionListContains(QDesignerFormEditorInterface *core, QObject *object, MemberType type, const QString &signature); + +// Members functions listed by class they were inherited from +struct ClassMemberFunctions +{ + ClassMemberFunctions() {} + ClassMemberFunctions(const QString &_class_name); + + QString m_className; + QStringList m_memberList; +}; + +typedef QList ClassesMemberFunctions; + +// Return classes and members in reverse class order to +// populate of the combo of the ToolWindow. + +ClassesMemberFunctions reverseClassesMemberFunctions(const QString &obj_name, MemberType member_type, + const QString &peer, QDesignerFormWindowInterface *form); + +bool signalMatchesSlot(QDesignerFormEditorInterface *core, const QString &signal, const QString &slot); + +QString realObjectName(QDesignerFormEditorInterface *core, QObject *object); + +bool isQt3Signal(QDesignerFormEditorInterface *core, QObject *object, const QString &signalSignature); +bool isQt3Slot(QDesignerFormEditorInterface *core, QObject *object, const QString &signalSignature); + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTUTILS_P_H diff --git a/src/designer/components/signalsloteditor/signalsloteditor.cmake b/src/designer/components/signalsloteditor/signalsloteditor.cmake new file mode 100644 index 000000000..1bc083d12 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor.cmake @@ -0,0 +1,24 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalslot_utils_p.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/connectdialog_p.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_tool.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_plugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_global.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_p.h + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditorwindow.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalslot_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/connectdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_tool.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_plugin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditor_instance.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/signalsloteditorwindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/signalsloteditor/connectdialog.ui +) + diff --git a/src/designer/components/signalsloteditor/signalsloteditor.cpp b/src/designer/components/signalsloteditor/signalsloteditor.cpp new file mode 100644 index 000000000..17814c361 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor.cpp @@ -0,0 +1,530 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalsloteditor.h" +#include "signalsloteditor_p.h" +#include "connectdialog_p.h" +#include "signalslot_utils_p.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +/******************************************************************************* +** SignalSlotConnection +*/ + +SignalSlotConnection::SignalSlotConnection(ConnectionEdit *edit, QWidget *source, QWidget *target) + : Connection(edit, source, target) +{ +} + +DomConnection *SignalSlotConnection::toUi() const +{ + DomConnection *result = new DomConnection; + + result->setElementSender(sender()); + result->setElementSignal(signal()); + result->setElementReceiver(receiver()); + result->setElementSlot(slot()); + + DomConnectionHints *hints = new DomConnectionHints; + QList list; + + QPoint sp = endPointPos(EndPoint::Source); + QPoint tp = endPointPos(EndPoint::Target); + + DomConnectionHint *hint = new DomConnectionHint; + hint->setAttributeType(QLatin1String("sourcelabel")); + hint->setElementX(sp.x()); + hint->setElementY(sp.y()); + list.append(hint); + + hint = new DomConnectionHint; + hint->setAttributeType(QLatin1String("destinationlabel")); + hint->setElementX(tp.x()); + hint->setElementY(tp.y()); + list.append(hint); + + hints->setElementHint(list); + result->setElementHints(hints); + + return result; +} + +void SignalSlotConnection::setSignal(const QString &signal) +{ + m_signal = signal; + setLabel(EndPoint::Source, m_signal); +} + +void SignalSlotConnection::setSlot(const QString &slot) +{ + m_slot = slot; + setLabel(EndPoint::Target, m_slot); +} + +QString SignalSlotConnection::sender() const +{ + QObject *source = object(EndPoint::Source); + if (!source) + return QString(); + + SignalSlotEditor *edit = qobject_cast(this->edit()); + Q_ASSERT(edit != 0); + + return realObjectName(edit->formWindow()->core(), source); +} + +QString SignalSlotConnection::receiver() const +{ + QObject *sink = object(EndPoint::Target); + if (!sink) + return QString(); + + SignalSlotEditor *edit = qobject_cast(this->edit()); + Q_ASSERT(edit != 0); + return realObjectName(edit->formWindow()->core(), sink); +} + +void SignalSlotConnection::updateVisibility() +{ + Connection::updateVisibility(); + if (isVisible() && (signal().isEmpty() || slot().isEmpty())) + setVisible(false); +} + +QString SignalSlotConnection::toString() const +{ + return QCoreApplication::translate("SignalSlotConnection", "SENDER(%1), SIGNAL(%2), RECEIVER(%3), SLOT(%4)") + .arg(sender()).arg(signal()).arg(receiver()).arg(slot()); +} + +SignalSlotConnection::State SignalSlotConnection::isValid(const QWidget *background) const +{ + const QObject *source = object(EndPoint::Source); + if (!source) + return ObjectDeleted; + + const QObject *target = object(EndPoint::Target); + if (!target) + return ObjectDeleted; + + if (m_slot.isEmpty() || m_signal.isEmpty()) + return InvalidMethod; + + if (const QWidget *sourceWidget = qobject_cast(source)) + if (!background->isAncestorOf(sourceWidget)) + return NotAncestor; + + if (const QWidget *targetWidget = qobject_cast(target)) + if (!background->isAncestorOf(targetWidget)) + return NotAncestor; + + return Valid; +} + +/******************************************************************************* +** Commands +*/ + +class SetMemberCommand : public QUndoCommand, public CETypes +{ +public: + SetMemberCommand(SignalSlotConnection *con, EndPoint::Type type, + const QString &member, SignalSlotEditor *editor); + virtual void redo(); + virtual void undo(); +private: + const QString m_old_member; + const QString m_new_member; + const EndPoint::Type m_type; + SignalSlotConnection *m_con; + SignalSlotEditor *m_editor; +}; + +SetMemberCommand::SetMemberCommand(SignalSlotConnection *con, EndPoint::Type type, + const QString &member, SignalSlotEditor *editor) : + m_old_member(type == EndPoint::Source ? con->signal() : con->slot()), + m_new_member(member), + m_type(type), + m_con(con), + m_editor(editor) +{ + if (type == EndPoint::Source) + setText(QApplication::translate("Command", "Change signal")); + else + setText(QApplication::translate("Command", "Change slot")); +} + +void SetMemberCommand::redo() +{ + m_con->update(); + if (m_type == EndPoint::Source) + m_con->setSignal(m_new_member); + else + m_con->setSlot(m_new_member); + m_con->update(); + emit m_editor->connectionChanged(m_con); +} + +void SetMemberCommand::undo() +{ + m_con->update(); + if (m_type == EndPoint::Source) + m_con->setSignal(m_old_member); + else + m_con->setSlot(m_old_member); + m_con->update(); + emit m_editor->connectionChanged(m_con); +} + +// Command to modify a connection +class ModifyConnectionCommand : public QDesignerFormWindowCommand +{ +public: + explicit ModifyConnectionCommand(QDesignerFormWindowInterface *form, + SignalSlotConnection *conn, + const QString &newSignal, + const QString &newSlot); + virtual void redo(); + virtual void undo(); + +private: + SignalSlotConnection *m_conn; + const QString m_oldSignal; + const QString m_oldSlot; + const QString m_newSignal; + const QString m_newSlot; +}; + +ModifyConnectionCommand::ModifyConnectionCommand(QDesignerFormWindowInterface *form, + SignalSlotConnection *conn, + const QString &newSignal, + const QString &newSlot) : + QDesignerFormWindowCommand(QCoreApplication::translate("Command", "Change signal-slot connection"), form), + m_conn(conn), + m_oldSignal(conn->signal()), + m_oldSlot(conn->slot()), + m_newSignal(newSignal), + m_newSlot(newSlot) +{ +} + +void ModifyConnectionCommand::redo() +{ + m_conn->setSignal(m_newSignal); + m_conn->setSlot(m_newSlot); +} + +void ModifyConnectionCommand::undo() +{ + m_conn->setSignal(m_oldSignal); + m_conn->setSlot(m_oldSlot); +} + +/******************************************************************************* +** SignalSlotEditor +*/ + +SignalSlotEditor::SignalSlotEditor(QDesignerFormWindowInterface *form_window, QWidget *parent) : + ConnectionEdit(parent, form_window), + m_form_window(form_window), + m_showAllSignalsSlots(false) +{ +} + +void SignalSlotEditor::modifyConnection(Connection *con) +{ + SignalSlotConnection *sigslot_con = static_cast(con); + ConnectDialog dialog(m_form_window, + sigslot_con->widget(EndPoint::Source), + sigslot_con->widget(EndPoint::Target), + m_form_window->core()->topLevel()); + + dialog.setSignalSlot(sigslot_con->signal(), sigslot_con->slot()); + dialog.setShowAllSignalsSlots(m_showAllSignalsSlots); + + if (dialog.exec() == QDialog::Accepted) { + const QString newSignal = dialog.signal(); + const QString newSlot = dialog.slot(); + if (sigslot_con->signal() != newSignal || sigslot_con->slot() != newSlot) { + ModifyConnectionCommand *cmd = new ModifyConnectionCommand(m_form_window, sigslot_con, newSignal, newSlot); + m_form_window->commandHistory()->push(cmd); + } + } + + m_showAllSignalsSlots = dialog.showAllSignalsSlots(); +} + +Connection *SignalSlotEditor::createConnection(QWidget *source, QWidget *destination) +{ + SignalSlotConnection *con = 0; + + Q_ASSERT(source != 0); + Q_ASSERT(destination != 0); + + ConnectDialog dialog(m_form_window, source, destination, m_form_window->core()->topLevel()); + dialog.setShowAllSignalsSlots(m_showAllSignalsSlots); + + if (dialog.exec() == QDialog::Accepted) { + con = new SignalSlotConnection(this, source, destination); + con->setSignal(dialog.signal()); + con->setSlot(dialog.slot()); + } + + m_showAllSignalsSlots = dialog.showAllSignalsSlots(); + + return con; +} + +DomConnections *SignalSlotEditor::toUi() const +{ + DomConnections *result = new DomConnections; + QList list; + + const int count = connectionCount(); + for (int i = 0; i < count; ++i) { + const SignalSlotConnection *con = static_cast(connection(i)); + Q_ASSERT(con != 0); + + // If a widget's parent has been removed or moved to a different form, + // and the parent was not a managed widget + // (a page in a tab widget), we never get a widgetRemoved(). So we filter out + // these child widgets here (check QPointer and verify ancestor). + // Also, the user might demote a promoted widget or remove a fake + // slot in the editor, which causes the connection to become invalid + // once he doubleclicks on the method combo. + switch (con->isValid(background())) { + case SignalSlotConnection::Valid: + list.append(con->toUi()); + break; + case SignalSlotConnection::ObjectDeleted: + case SignalSlotConnection::InvalidMethod: + case SignalSlotConnection::NotAncestor: + break; + } + } + result->setElementConnection(list); + return result; +} + +QObject *SignalSlotEditor::objectByName(QWidget *topLevel, const QString &name) const +{ + if (name.isEmpty()) + return 0; + + Q_ASSERT(topLevel); + QObject *object = 0; + if (topLevel->objectName() == name) + object = topLevel; + else + object = topLevel->findChild(name); + const QDesignerMetaDataBaseInterface *mdb = formWindow()->core()->metaDataBase(); + if (mdb->item(object)) + return object; + return 0; +} + +void SignalSlotEditor::fromUi(const DomConnections *connections, QWidget *parent) +{ + if (connections == 0) + return; + + setBackground(parent); + clear(); + const QList list = connections->elementConnection(); + foreach (const DomConnection *dom_con, list) { + QObject *source = objectByName(parent, dom_con->elementSender()); + if (source == 0) { + qDebug("SignalSlotEditor::fromUi(): no source widget called \"%s\"", + dom_con->elementSender().toUtf8().constData()); + continue; + } + QObject *destination = objectByName(parent, dom_con->elementReceiver()); + if (destination == 0) { + qDebug("SignalSlotEditor::fromUi(): no destination widget called \"%s\"", + dom_con->elementReceiver().toUtf8().constData()); + continue; + } + + QPoint sp = QPoint(20, 20), tp = QPoint(20, 20); + const DomConnectionHints *dom_hints = dom_con->elementHints(); + if (dom_hints != 0) { + QList list = dom_hints->elementHint(); + foreach (DomConnectionHint *hint, list) { + QString attr_type = hint->attributeType(); + QPoint p = QPoint(hint->elementX(), hint->elementY()); + if (attr_type == QLatin1String("sourcelabel")) + sp = p; + else if (attr_type == QLatin1String("destinationlabel")) + tp = p; + } + } + + SignalSlotConnection *con = new SignalSlotConnection(this); + + con->setEndPoint(EndPoint::Source, source, sp); + con->setEndPoint(EndPoint::Target, destination, tp); + con->setSignal(dom_con->elementSignal()); + con->setSlot(dom_con->elementSlot()); + addConnection(con); + } +} + +static bool skipWidget(const QWidget *w) +{ + const QString name = QLatin1String(w->metaObject()->className()); + if (name == QLatin1String("QDesignerWidget")) + return true; + if (name == QLatin1String("QLayoutWidget")) + return true; + if (name == QLatin1String("qdesigner_internal::FormWindow")) + return true; + if (name == QLatin1String("Spacer")) + return true; + return false; +} + +QWidget *SignalSlotEditor::widgetAt(const QPoint &pos) const +{ + QWidget *widget = ConnectionEdit::widgetAt(pos); + + if (widget == m_form_window->mainContainer()) + return widget; + + for (; widget != 0; widget = widget->parentWidget()) { + QDesignerMetaDataBaseItemInterface *item = m_form_window->core()->metaDataBase()->item(widget); + if (item == 0) + continue; + if (skipWidget(widget)) + continue; + break; + } + + return widget; +} + +void SignalSlotEditor::setSignal(SignalSlotConnection *con, const QString &member) +{ + if (member == con->signal()) + return; + + m_form_window->beginCommand(QApplication::translate("Command", "Change signal")); + undoStack()->push(new SetMemberCommand(con, EndPoint::Source, member, this)); + if (!signalMatchesSlot(m_form_window->core(), member, con->slot())) + undoStack()->push(new SetMemberCommand(con, EndPoint::Target, QString(), this)); + m_form_window->endCommand(); +} + +void SignalSlotEditor::setSlot(SignalSlotConnection *con, const QString &member) +{ + if (member == con->slot()) + return; + + m_form_window->beginCommand(QApplication::translate("Command", "Change slot")); + undoStack()->push(new SetMemberCommand(con, EndPoint::Target, member, this)); + if (!signalMatchesSlot(m_form_window->core(), con->signal(), member)) + undoStack()->push(new SetMemberCommand(con, EndPoint::Source, QString(), this)); + m_form_window->endCommand(); +} + +void SignalSlotEditor::setSource(Connection *_con, const QString &obj_name) +{ + SignalSlotConnection *con = static_cast(_con); + + if (con->sender() == obj_name) + return; + + m_form_window->beginCommand(QApplication::translate("Command", "Change sender")); + ConnectionEdit::setSource(con, obj_name); + + QObject *sourceObject = con->object(EndPoint::Source); + + if (!memberFunctionListContains(m_form_window->core(), sourceObject, SignalMember, con->signal())) + undoStack()->push(new SetMemberCommand(con, EndPoint::Source, QString(), this)); + + m_form_window->endCommand(); +} + +void SignalSlotEditor::setTarget(Connection *_con, const QString &obj_name) +{ + SignalSlotConnection *con = static_cast(_con); + + if (con->receiver() == obj_name) + return; + + m_form_window->beginCommand(QApplication::translate("Command", "Change receiver")); + ConnectionEdit::setTarget(con, obj_name); + + QObject *targetObject = con->object(EndPoint::Target); + if (!memberFunctionListContains(m_form_window->core(), targetObject, SlotMember, con->slot())) + undoStack()->push(new SetMemberCommand(con, EndPoint::Target, QString(), this)); + + m_form_window->endCommand(); +} + +void SignalSlotEditor::addEmptyConnection() +{ + SignalSlotConnection *con = new SignalSlotConnection(this); + undoStack()->push(new AddConnectionCommand(this, con)); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include +#include diff --git a/src/designer/components/signalsloteditor/signalsloteditor.h b/src/designer/components/signalsloteditor/signalsloteditor.h new file mode 100644 index 000000000..76a8e4f93 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITOR_H +#define SIGNALSLOTEDITOR_H + +#include "signalsloteditor_global.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class DomConnections; + +namespace qdesigner_internal { + +class SignalSlotConnection; + +class QT_SIGNALSLOTEDITOR_EXPORT SignalSlotEditor : public ConnectionEdit +{ + Q_OBJECT + +public: + SignalSlotEditor(QDesignerFormWindowInterface *form_window, QWidget *parent); + + virtual void setSignal(SignalSlotConnection *con, const QString &member); + virtual void setSlot(SignalSlotConnection *con, const QString &member); + virtual void setSource(Connection *con, const QString &obj_name); + virtual void setTarget(Connection *con, const QString &obj_name); + + DomConnections *toUi() const; + void fromUi(const DomConnections *connections, QWidget *parent); + + QDesignerFormWindowInterface *formWindow() const { return m_form_window; } + + QObject *objectByName(QWidget *topLevel, const QString &name) const; + + void addEmptyConnection(); + +protected: + virtual QWidget *widgetAt(const QPoint &pos) const; + +private: + virtual Connection *createConnection(QWidget *source, QWidget *destination); + virtual void modifyConnection(Connection *con); + + QDesignerFormWindowInterface *m_form_window; + bool m_showAllSignalsSlots; + + friend class SetMemberCommand; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTEDITOR_H diff --git a/src/designer/components/signalsloteditor/signalsloteditor_global.h b/src/designer/components/signalsloteditor/signalsloteditor_global.h new file mode 100644 index 000000000..4f4893ead --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITOR_GLOBAL_H +#define SIGNALSLOTEDITOR_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_SIGNALSLOTEDITOR_LIBRARY +# define QT_SIGNALSLOTEDITOR_EXPORT +#else +# define QT_SIGNALSLOTEDITOR_EXPORT +#endif +#else +#define QT_SIGNALSLOTEDITOR_EXPORT +#endif + +#endif // SIGNALSLOTEDITOR_GLOBAL_H diff --git a/src/designer/components/signalsloteditor/signalsloteditor_instance.cpp b/src/designer/components/signalsloteditor/signalsloteditor_instance.cpp new file mode 100644 index 000000000..3c974d63b --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_instance.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "signalsloteditor_plugin.h" + +QT_USE_NAMESPACE + +using namespace qdesigner_internal; + +Q_EXPORT_PLUGIN(SignalSlotEditorPlugin) diff --git a/src/designer/components/signalsloteditor/signalsloteditor_p.h b/src/designer/components/signalsloteditor/signalsloteditor_p.h new file mode 100644 index 000000000..c897622fd --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITOR_P_H +#define SIGNALSLOTEDITOR_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 +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class DomConnection; + +namespace qdesigner_internal { + +class SignalSlotEditor; + +class SignalSlotConnection : public Connection +{ +public: + explicit SignalSlotConnection(ConnectionEdit *edit, QWidget *source = 0, QWidget *target = 0); + + void setSignal(const QString &signal); + void setSlot(const QString &slot); + + QString sender() const; + QString receiver() const; + inline QString signal() const { return m_signal; } + inline QString slot() const { return m_slot; } + + DomConnection *toUi() const; + + virtual void updateVisibility(); + + enum State { Valid, ObjectDeleted, InvalidMethod, NotAncestor }; + State isValid(const QWidget *background) const; + + // format for messages, etc. + QString toString() const; + +private: + QString m_signal, m_slot; +}; + +class ConnectionModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit ConnectionModel(QObject *parent = 0); + void setEditor(SignalSlotEditor *editor = 0); + + virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::DisplayRole); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + + QModelIndex connectionToIndex(Connection *con) const; + Connection *indexToConnection(const QModelIndex &index) const; + void updateAll(); + +private slots: + void connectionAdded(Connection *con); + void connectionRemoved(int idx); + void aboutToRemoveConnection(Connection *con); + void aboutToAddConnection(int idx); + void connectionChanged(Connection *con); + +private: + QPointer m_editor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTEDITOR_P_H diff --git a/src/designer/components/signalsloteditor/signalsloteditor_plugin.cpp b/src/designer/components/signalsloteditor/signalsloteditor_plugin.cpp new file mode 100644 index 000000000..111eb5ff3 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_plugin.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalsloteditor_plugin.h" +#include "signalsloteditor_tool.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +SignalSlotEditorPlugin::SignalSlotEditorPlugin() + : m_initialized(false), m_action(0) +{ +} + +SignalSlotEditorPlugin::~SignalSlotEditorPlugin() +{ +} + +bool SignalSlotEditorPlugin::isInitialized() const +{ + return m_initialized; +} + +void SignalSlotEditorPlugin::initialize(QDesignerFormEditorInterface *core) +{ + Q_ASSERT(!isInitialized()); + + m_action = new QAction(tr("Edit Signals/Slots"), this); + m_action->setObjectName(QLatin1String("__qt_edit_signals_slots_action")); + m_action->setShortcut(tr("F4")); + QIcon icon = QIcon::fromTheme("designer-edit-signals", + QIcon(core->resourceLocation() + QLatin1String("/signalslottool.png"))); + m_action->setIcon(icon); + m_action->setEnabled(false); + + setParent(core); + m_core = core; + m_initialized = true; + + connect(core->formWindowManager(), SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), + this, SLOT(addFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(formWindowRemoved(QDesignerFormWindowInterface*)), + this, SLOT(removeFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface*))); +} + +QDesignerFormEditorInterface *SignalSlotEditorPlugin::core() const +{ + return m_core; +} + +void SignalSlotEditorPlugin::addFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == false); + + SignalSlotEditorTool *tool = new SignalSlotEditorTool(formWindow, this); + connect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + m_tools[formWindow] = tool; + formWindow->registerTool(tool); +} + +void SignalSlotEditorPlugin::removeFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == true); + + SignalSlotEditorTool *tool = m_tools.value(formWindow); + m_tools.remove(formWindow); + disconnect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + // ### FIXME disable the tool + + delete tool; +} + +QAction *SignalSlotEditorPlugin::action() const +{ + return m_action; +} + +void SignalSlotEditorPlugin::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + m_action->setEnabled(formWindow != 0); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/signalsloteditor/signalsloteditor_plugin.h b/src/designer/components/signalsloteditor/signalsloteditor_plugin.h new file mode 100644 index 000000000..58398a2a7 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_plugin.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITOR_PLUGIN_H +#define SIGNALSLOTEDITOR_PLUGIN_H + +#include "signalsloteditor_global.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class SignalSlotEditorTool; + +class QT_SIGNALSLOTEDITOR_EXPORT SignalSlotEditorPlugin: public QObject, public QDesignerFormEditorPluginInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerFormEditorPluginInterface) +public: + SignalSlotEditorPlugin(); + virtual ~SignalSlotEditorPlugin(); + + virtual bool isInitialized() const; + virtual void initialize(QDesignerFormEditorInterface *core); + virtual QAction *action() const; + + virtual QDesignerFormEditorInterface *core() const; + +public slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + +private slots: + void addFormWindow(QDesignerFormWindowInterface *formWindow); + void removeFormWindow(QDesignerFormWindowInterface *formWindow); + +private: + QPointer m_core; + QHash m_tools; + bool m_initialized; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTEDITOR_PLUGIN_H diff --git a/src/designer/components/signalsloteditor/signalsloteditor_tool.cpp b/src/designer/components/signalsloteditor/signalsloteditor_tool.cpp new file mode 100644 index 000000000..04517bcf5 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_tool.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalsloteditor_tool.h" +#include "signalsloteditor.h" +#include "ui4_p.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +SignalSlotEditorTool::SignalSlotEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent) + : QDesignerFormWindowToolInterface(parent), + m_formWindow(formWindow), + m_action(new QAction(tr("Edit Signals/Slots"), this)) +{ +} + +SignalSlotEditorTool::~SignalSlotEditorTool() +{ +} + +QDesignerFormEditorInterface *SignalSlotEditorTool::core() const +{ + return m_formWindow->core(); +} + +QDesignerFormWindowInterface *SignalSlotEditorTool::formWindow() const +{ + return m_formWindow; +} + +bool SignalSlotEditorTool::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) +{ + Q_UNUSED(widget); + Q_UNUSED(managedWidget); + Q_UNUSED(event); + + return false; +} + +QWidget *SignalSlotEditorTool::editor() const +{ + if (!m_editor) { + Q_ASSERT(formWindow() != 0); + m_editor = new qdesigner_internal::SignalSlotEditor(formWindow(), 0); + connect(formWindow(), SIGNAL(mainContainerChanged(QWidget*)), m_editor, SLOT(setBackground(QWidget*))); + connect(formWindow(), SIGNAL(changed()), + m_editor, SLOT(updateBackground())); + } + + return m_editor; +} + +QAction *SignalSlotEditorTool::action() const +{ + return m_action; +} + +void SignalSlotEditorTool::activated() +{ + m_editor->enableUpdateBackground(true); +} + +void SignalSlotEditorTool::deactivated() +{ + m_editor->enableUpdateBackground(false); +} + +void SignalSlotEditorTool::saveToDom(DomUI *ui, QWidget*) +{ + ui->setElementConnections(m_editor->toUi()); +} + +void SignalSlotEditorTool::loadFromDom(DomUI *ui, QWidget *mainContainer) +{ + m_editor->fromUi(ui->elementConnections(), mainContainer); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/signalsloteditor/signalsloteditor_tool.h b/src/designer/components/signalsloteditor/signalsloteditor_tool.h new file mode 100644 index 000000000..ba5c8138b --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditor_tool.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITOR_TOOL_H +#define SIGNALSLOTEDITOR_TOOL_H + +#include "signalsloteditor_global.h" +#include "signalsloteditor.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QAction; + +namespace qdesigner_internal { + +class SignalSlotEditor; + +class QT_SIGNALSLOTEDITOR_EXPORT SignalSlotEditorTool: public QDesignerFormWindowToolInterface +{ + Q_OBJECT +public: + explicit SignalSlotEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent = 0); + virtual ~SignalSlotEditorTool(); + + virtual QDesignerFormEditorInterface *core() const; + virtual QDesignerFormWindowInterface *formWindow() const; + + virtual QWidget *editor() const; + + QAction *action() const; + + virtual void activated(); + virtual void deactivated(); + + virtual bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event); + + virtual void saveToDom(DomUI *ui, QWidget *mainContainer); + virtual void loadFromDom(DomUI *ui, QWidget *mainContainer); + +private: + QDesignerFormWindowInterface *m_formWindow; + mutable QPointer m_editor; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTEDITOR_TOOL_H diff --git a/src/designer/components/signalsloteditor/signalsloteditorwindow.cpp b/src/designer/components/signalsloteditor/signalsloteditorwindow.cpp new file mode 100644 index 000000000..8d684d1c3 --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditorwindow.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalsloteditorwindow.h" +#include "signalsloteditor_p.h" +#include "signalsloteditor.h" +#include "qdesigner_integration_p.h" +#include "signalslot_utils_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 + +QT_BEGIN_NAMESPACE + +// Add suitable form widgets to a list of objects for the signal slot +// editor. Prevent special widgets from showing up there. +static void addWidgetToObjectList(const QWidget *w, QStringList &r) +{ + const QMetaObject *mo = w->metaObject(); + if (mo != &QLayoutWidget::staticMetaObject && mo != &Spacer::staticMetaObject) { + const QString name = w->objectName().trimmed(); + if (!name.isEmpty()) + r.push_back(name); + } +} + +static QStringList objectNameList(QDesignerFormWindowInterface *form) +{ + typedef QList ActionList; + typedef QList ButtonGroupList; + + QStringList result; + + QWidget *mainContainer = form->mainContainer(); + if (!mainContainer) + return result; + + // Add main container container pages (QStatusBar, QWizardPages) etc. + // to the list. Pages of containers on the form are not added, however. + if (const QDesignerContainerExtension *c = qt_extension(form->core()->extensionManager(), mainContainer)) { + const int count = c->count(); + for (int i = 0 ; i < count; i++) + addWidgetToObjectList(c->widget(i), result); + } + + const QDesignerFormWindowCursorInterface *cursor = form->cursor(); + const int widgetCount = cursor->widgetCount(); + for (int i = 0; i < widgetCount; ++i) + addWidgetToObjectList(cursor->widget(i), result); + + const QDesignerMetaDataBaseInterface *mdb = form->core()->metaDataBase(); + + // Add managed actions and actions with managed menus + const ActionList actions = mainContainer->findChildren(); + if (!actions.empty()) { + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) { + QAction *a = *it; + if (!a->isSeparator()) { + if (QMenu *menu = a->menu()) { + if (mdb->item(menu)) + result.push_back(menu->objectName()); + } else { + if (mdb->item(a)) + result.push_back(a->objectName()); + } + } + } + } + + // Add managed buttons groups + const ButtonGroupList buttonGroups = mainContainer->findChildren(); + if (!buttonGroups.empty()) { + const ButtonGroupList::const_iterator cend = buttonGroups.constEnd(); + for (ButtonGroupList::const_iterator it = buttonGroups.constBegin(); it != cend; ++it) + if (mdb->item(*it)) + result.append((*it)->objectName()); + } + + result.sort(); + return result; +} + +namespace qdesigner_internal { + +// ------------ ConnectionModel + +ConnectionModel::ConnectionModel(QObject *parent) : + QAbstractItemModel(parent) +{ +} + +void ConnectionModel::setEditor(SignalSlotEditor *editor) +{ + if (m_editor == editor) + return; + + if (m_editor) { + disconnect(m_editor, SIGNAL(connectionAdded(Connection*)), + this, SLOT(connectionAdded(Connection*))); + disconnect(m_editor, SIGNAL(connectionRemoved(int)), + this, SLOT(connectionRemoved(int))); + disconnect(m_editor, SIGNAL(aboutToRemoveConnection(Connection*)), + this, SLOT(aboutToRemoveConnection(Connection*))); + disconnect(m_editor, SIGNAL(aboutToAddConnection(int)), + this, SLOT(aboutToAddConnection(int))); + disconnect(m_editor, SIGNAL(connectionChanged(Connection*)), + this, SLOT(connectionChanged(Connection*))); + } + m_editor = editor; + if (m_editor) { + connect(m_editor, SIGNAL(connectionAdded(Connection*)), + this, SLOT(connectionAdded(Connection*))); + connect(m_editor, SIGNAL(connectionRemoved(int)), + this, SLOT(connectionRemoved(int))); + connect(m_editor, SIGNAL(aboutToRemoveConnection(Connection*)), + this, SLOT(aboutToRemoveConnection(Connection*))); + connect(m_editor, SIGNAL(aboutToAddConnection(int)), + this, SLOT(aboutToAddConnection(int))); + connect(m_editor, SIGNAL(connectionChanged(Connection*)), + this, SLOT(connectionChanged(Connection*))); + } + reset(); +} + +QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (orientation == Qt::Vertical || role != Qt::DisplayRole) + return QVariant(); + + static const QVariant senderTitle = tr("Sender"); + static const QVariant signalTitle = tr("Signal"); + static const QVariant receiverTitle = tr("Receiver"); + static const QVariant slotTitle = tr("Slot"); + + switch (section) { + case 0: + return senderTitle; + case 1: + return signalTitle; + case 2: + return receiverTitle; + case 3: + return slotTitle; + } + return QVariant(); +} + +QModelIndex ConnectionModel::index(int row, int column, + const QModelIndex &parent) const +{ + if (parent.isValid() || !m_editor) + return QModelIndex(); + if (row < 0 || row >= m_editor->connectionCount()) + return QModelIndex(); + return createIndex(row, column); +} + +Connection *ConnectionModel::indexToConnection(const QModelIndex &index) const +{ + if (!index.isValid() || !m_editor) + return 0; + if (index.row() < 0 || index.row() >= m_editor->connectionCount()) + return 0; + return m_editor->connection(index.row()); +} + +QModelIndex ConnectionModel::connectionToIndex(Connection *con) const +{ + Q_ASSERT(m_editor); + return createIndex(m_editor->indexOfConnection(con), 0); +} + +QModelIndex ConnectionModel::parent(const QModelIndex&) const +{ + return QModelIndex(); +} + +int ConnectionModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid() || !m_editor) + return 0; + return m_editor->connectionCount(); +} + +int ConnectionModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return 4; +} + +QVariant ConnectionModel::data(const QModelIndex &index, int role) const +{ + if ((role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole && role != Qt::ForegroundRole) || !m_editor) + return QVariant(); + + if (index.row() < 0 || index.row() >= m_editor->connectionCount()) { + return QVariant(); + } + + const SignalSlotConnection *con = static_cast(m_editor->connection(index.row())); + Q_ASSERT(con != 0); + + if (role == Qt::FontRole || role == Qt::ForegroundRole) { + bool isQt3Member = false; + if (index.column() == 1) { + QDesignerFormEditorInterface *core = m_editor->formWindow()->core(); + isQt3Member = isQt3Signal(core, con->object(CETypes::EndPoint::Source), con->signal()); + } else if (index.column() == 3) { + QDesignerFormEditorInterface *core = m_editor->formWindow()->core(); + isQt3Member = isQt3Signal(core, con->object(CETypes::EndPoint::Target), con->slot()); + } + if (isQt3Member) { + if (role == Qt::ForegroundRole) + return Qt::red; + QFont font = QApplication::font(); + font.setItalic(true); + return font; + } + return QVariant(); + } + + static const QVariant senderDefault = tr(""); + static const QVariant signalDefault = tr(""); + static const QVariant receiverDefault = tr(""); + static const QVariant slotDefault = tr(""); + + switch (index.column()) { + case 0: { + const QString sender = con->sender(); + if (sender.isEmpty()) + return senderDefault; + return sender; + } + case 1: { + const QString signal = con->signal(); + if (signal.isEmpty()) + return signalDefault; + return signal; + } + case 2: { + const QString receiver = con->receiver(); + if (receiver.isEmpty()) + return receiverDefault; + return receiver; + } + case 3: { + const QString slot = con->slot(); + if (slot.isEmpty()) + return slotDefault; + return slot; + } + } + return QVariant(); +} + +bool ConnectionModel::setData(const QModelIndex &index, const QVariant &data, int) +{ + if (!index.isValid() || !m_editor) + return false; + if (data.type() != QVariant::String) + return false; + + SignalSlotConnection *con = static_cast(m_editor->connection(index.row())); + QDesignerFormWindowInterface *form = m_editor->formWindow(); + + QString s = data.toString(); + switch (index.column()) { + case 0: + if (!s.isEmpty() && !objectNameList(form).contains(s)) + s.clear(); + m_editor->setSource(con, s); + break; + case 1: + if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Source), SignalMember, s)) + s.clear(); + m_editor->setSignal(con, s); + break; + case 2: + if (!s.isEmpty() && !objectNameList(form).contains(s)) + s.clear(); + m_editor->setTarget(con, s); + break; + case 3: + if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Target), SlotMember, s)) + s.clear(); + m_editor->setSlot(con, s); + break; + } + + return true; +} + +void ConnectionModel::connectionAdded(Connection*) +{ + endInsertRows(); +} + +void ConnectionModel::connectionRemoved(int) +{ + endRemoveRows(); +} + +void ConnectionModel::aboutToRemoveConnection(Connection *con) +{ + Q_ASSERT(m_editor); + int idx = m_editor->indexOfConnection(con); + beginRemoveRows(QModelIndex(), idx, idx); +} + +void ConnectionModel::aboutToAddConnection(int idx) +{ + Q_ASSERT(m_editor); + beginInsertRows(QModelIndex(), idx, idx); +} + +Qt::ItemFlags ConnectionModel::flags(const QModelIndex&) const +{ + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; +} + +void ConnectionModel::connectionChanged(Connection *con) +{ + Q_ASSERT(m_editor); + const int idx = m_editor->indexOfConnection(con); + SignalSlotConnection *changedCon = static_cast(m_editor->connection(idx)); + SignalSlotConnection *c = 0; + for (int i=0; iconnectionCount(); ++i) { + if (i == idx) + continue; + c = static_cast(m_editor->connection(i)); + if (c->sender() == changedCon->sender() && c->signal() == changedCon->signal() + && c->receiver() == changedCon->receiver() && c->slot() == changedCon->slot()) { + const QString message = tr("The connection already exists!
%1").arg(changedCon->toString()); + m_editor->formWindow()->core()->dialogGui()->message(m_editor->parentWidget(), QDesignerDialogGuiInterface::SignalSlotEditorMessage, + QMessageBox::Warning, tr("Signal and Slot Editor"), message, QMessageBox::Ok); + break; + } + } + emit dataChanged(createIndex(idx, 0), createIndex(idx, 3)); +} + +void ConnectionModel::updateAll() +{ + emit dataChanged(index(0, 0), index(rowCount(), columnCount())); +} +} + +namespace { +// ---------------------- InlineEditorModel + +class InlineEditorModel : public QStandardItemModel +{ + Q_OBJECT +public: + enum { TitleItem = 1 }; + + InlineEditorModel(int rows, int cols, QObject *parent = 0); + + void addTitle(const QString &title); + void addTextList(const QMap &text_list); + void addText(const QString &text); + bool isTitle(int idx) const; + + int findText(const QString &text) const; + + virtual Qt::ItemFlags flags(const QModelIndex &index) const; +}; + +InlineEditorModel::InlineEditorModel(int rows, int cols, QObject *parent) + : QStandardItemModel(rows, cols, parent) +{ +} + +void InlineEditorModel::addTitle(const QString &title) +{ + const int cnt = rowCount(); + insertRows(cnt, 1); + QModelIndex cat_idx = index(cnt, 0); + setData(cat_idx, title + QLatin1Char(':'), Qt::DisplayRole); + setData(cat_idx, TitleItem, Qt::UserRole); + QFont font = QApplication::font(); + font.setBold(true); + setData(cat_idx, font, Qt::FontRole); +} + +bool InlineEditorModel::isTitle(int idx) const +{ + if (idx == -1) + return false; + + return data(index(idx, 0), Qt::UserRole).toInt() == TitleItem; +} + +void InlineEditorModel::addText(const QString &text) +{ + const int cnt = rowCount(); + insertRows(cnt, 1); + setData(index(cnt, 0), text, Qt::DisplayRole); +} + +void InlineEditorModel::addTextList(const QMap &text_list) +{ + int cnt = rowCount(); + insertRows(cnt, text_list.size()); + QFont font = QApplication::font(); + font.setItalic(true); + QVariant fontVariant = QVariant::fromValue(font); + QMap::ConstIterator it = text_list.constBegin(); + const QMap::ConstIterator itEnd = text_list.constEnd(); + while (it != itEnd) { + const QModelIndex text_idx = index(cnt++, 0); + setData(text_idx, it.key(), Qt::DisplayRole); + if (it.value()) { + setData(text_idx, fontVariant, Qt::FontRole); + setData(text_idx, Qt::red, Qt::ForegroundRole); + } + ++it; + } +} + +Qt::ItemFlags InlineEditorModel::flags(const QModelIndex &index) const +{ + if (isTitle(index.row())) + return Qt::ItemIsEnabled; + else + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +int InlineEditorModel::findText(const QString &text) const +{ + const int cnt = rowCount(); + for (int i = 0; i < cnt; ++i) { + const QModelIndex idx = index(i, 0); + if (data(idx, Qt::UserRole).toInt() == TitleItem) + continue; + if (data(idx, Qt::DisplayRole).toString() == text) + return i; + } + return -1; +} + +// ------------ InlineEditor +class InlineEditor : public QComboBox +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText USER true) +public: + InlineEditor(QWidget *parent = 0); + + QString text() const; + void setText(const QString &text); + + void addTitle(const QString &title); + void addText(const QString &text); + void addTextList(const QMap &text_list); + +private slots: + void checkSelection(int idx); + +private: + InlineEditorModel *m_model; + int m_idx; +}; + +InlineEditor::InlineEditor(QWidget *parent) : + QComboBox(parent), + m_idx(-1) +{ + setModel(m_model = new InlineEditorModel(0, 4, this)); + setFrame(false); + m_idx = -1; + connect(this, SIGNAL(activated(int)), this, SLOT(checkSelection(int))); +} + +void InlineEditor::checkSelection(int idx) +{ + if (idx == m_idx) + return; + + if (m_model->isTitle(idx)) + setCurrentIndex(m_idx); + else + m_idx = idx; +} + +void InlineEditor::addTitle(const QString &title) +{ + m_model->addTitle(title); +} + +void InlineEditor::addTextList(const QMap &text_list) +{ + m_model->addTextList(text_list); +} + +void InlineEditor::addText(const QString &text) +{ + m_model->addText(text); +} + +QString InlineEditor::text() const +{ + return currentText(); +} + +void InlineEditor::setText(const QString &text) +{ + m_idx = m_model->findText(text); + if (m_idx == -1) + m_idx = 0; + setCurrentIndex(m_idx); +} + +// ------------------ ConnectionDelegate + +class ConnectionDelegate : public QItemDelegate +{ + Q_OBJECT +public: + ConnectionDelegate(QWidget *parent = 0); + + void setForm(QDesignerFormWindowInterface *form); + + virtual QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + +private slots: + void emitCommitData(); + +private: + QDesignerFormWindowInterface *m_form; +}; + +ConnectionDelegate::ConnectionDelegate(QWidget *parent) + : QItemDelegate(parent) +{ + m_form = 0; + + static QItemEditorFactory *factory = 0; + if (factory == 0) { + factory = new QItemEditorFactory; + QItemEditorCreatorBase *creator + = new QItemEditorCreator("text"); + factory->registerEditor(QVariant::String, creator); + } + + setItemEditorFactory(factory); +} + +void ConnectionDelegate::setForm(QDesignerFormWindowInterface *form) +{ + m_form = form; +} + +QWidget *ConnectionDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + if (m_form == 0) + return 0; + + QWidget *w = QItemDelegate::createEditor(parent, option, index); + InlineEditor *inline_editor = qobject_cast(w); + Q_ASSERT(inline_editor != 0); + const QAbstractItemModel *model = index.model(); + + const QModelIndex obj_name_idx = model->index(index.row(), index.column() <= 1 ? 0 : 2); + const QString obj_name = model->data(obj_name_idx, Qt::DisplayRole).toString(); + + switch (index.column()) { + case 0: + case 2: { // object names + QStringList obj_name_list = objectNameList(m_form); + QMap markedNameList; + markedNameList.insert(tr(""), false); + inline_editor->addTextList(markedNameList); + markedNameList.clear(); + foreach (const QString &name, obj_name_list) + markedNameList.insert(name, false); + inline_editor->addTextList(markedNameList); + } + break; + case 1: + case 3: { // signals, slots + const qdesigner_internal::MemberType type = index.column() == 1 ? qdesigner_internal::SignalMember : qdesigner_internal::SlotMember; + const QModelIndex peer_index = model->index(index.row(), type == qdesigner_internal::SignalMember ? 3 : 1); + const QString peer = model->data(peer_index, Qt::DisplayRole).toString(); + + const qdesigner_internal::ClassesMemberFunctions class_list = qdesigner_internal::reverseClassesMemberFunctions(obj_name, type, peer, m_form); + + QObject *object = 0; + if (obj_name == m_form->mainContainer()->objectName()) { + object = m_form->mainContainer(); + } else { + object = m_form->mainContainer()->findChild(obj_name); + } + inline_editor->addText(type == qdesigner_internal::SignalMember ? tr("") : tr("")); + foreach (const qdesigner_internal::ClassMemberFunctions &class_info, class_list) { + if (class_info.m_className.isEmpty() || class_info.m_memberList.isEmpty()) + continue; + QStringList memberList = class_info.m_memberList; + QMap markedMemberList; + foreach (const QString &member, memberList) { + bool mark = false; + if (type == qdesigner_internal::SignalMember) + mark = qdesigner_internal::isQt3Signal(m_form->core(), object, member); + else + mark = qdesigner_internal::isQt3Slot(m_form->core(), object, member); + + if (!mark) + markedMemberList.insert(member, mark); + } + inline_editor->addTitle(class_info.m_className); + inline_editor->addTextList(markedMemberList); + } + } + break; + default: + break; + } + + connect(inline_editor, SIGNAL(activated(int)), this, SLOT(emitCommitData())); + + return inline_editor; +} + +void ConnectionDelegate::emitCommitData() +{ + InlineEditor *editor = qobject_cast(sender()); + emit commitData(editor); +} + +} + +namespace qdesigner_internal { + +/******************************************************************************* +** SignalSlotEditorWindow +*/ + +SignalSlotEditorWindow::SignalSlotEditorWindow(QDesignerFormEditorInterface *core, + QWidget *parent) : + QWidget(parent), + m_view(new QTreeView), + m_editor(0), + m_add_button(new QToolButton), + m_remove_button(new QToolButton), + m_core(core), + m_model(new ConnectionModel(this)), + m_proxy_model(new QSortFilterProxyModel(this)), + m_handling_selection_change(false) +{ + m_proxy_model->setSourceModel(m_model); + m_view->setModel(m_proxy_model); + m_view->setSortingEnabled(true); + m_view->setItemDelegate(new ConnectionDelegate(this)); + m_view->setEditTriggers(QAbstractItemView::DoubleClicked + | QAbstractItemView::EditKeyPressed); + m_view->setRootIsDecorated(false); + m_view->setTextElideMode (Qt::ElideMiddle); + connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateUi())); + connect(m_view->header(), SIGNAL(sectionDoubleClicked(int)), m_view, SLOT(resizeColumnToContents(int))); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + + QToolBar *toolBar = new QToolBar; + toolBar->setIconSize(QSize(22, 22)); + m_add_button->setIcon(createIconSet(QLatin1String("plus.png"))); + connect(m_add_button, SIGNAL(clicked()), this, SLOT(addConnection())); + toolBar->addWidget(m_add_button); + + m_remove_button->setIcon(createIconSet(QLatin1String("minus.png"))); + connect(m_remove_button, SIGNAL(clicked()), this, SLOT(removeConnection())); + toolBar->addWidget(m_remove_button); + + layout->addWidget(toolBar); + layout->addWidget(m_view); + + connect(core->formWindowManager(), + SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(setActiveFormWindow(QDesignerFormWindowInterface*))); + + updateUi(); +} + +void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *form) +{ + QDesignerIntegration *integration = qobject_cast(m_core->integration()); + + if (!m_editor.isNull()) { + disconnect(m_view->selectionModel(), + SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(updateEditorSelection(QModelIndex))); + disconnect(m_editor, SIGNAL(connectionSelected(Connection*)), + this, SLOT(updateDialogSelection(Connection*))); + if (integration) { + disconnect(integration, SIGNAL(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString)), + this, SLOT(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString))); + } + } + + m_editor = form->findChild(); + m_model->setEditor(m_editor); + if (!m_editor.isNull()) { + ConnectionDelegate *delegate + = qobject_cast(m_view->itemDelegate()); + if (delegate != 0) + delegate->setForm(form); + + connect(m_view->selectionModel(), + SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(updateEditorSelection(QModelIndex))); + connect(m_editor, SIGNAL(connectionSelected(Connection*)), + this, SLOT(updateDialogSelection(Connection*))); + if (integration) { + connect(integration, SIGNAL(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString)), + this, SLOT(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString))); + } + } + + updateUi(); +} + +void SignalSlotEditorWindow::updateDialogSelection(Connection *con) +{ + if (m_handling_selection_change || m_editor == 0) + return; + + QModelIndex index = m_proxy_model->mapFromSource(m_model->connectionToIndex(con)); + if (index == m_view->currentIndex()) + return; + m_handling_selection_change = true; + m_view->setCurrentIndex(index); + m_handling_selection_change = false; + + updateUi(); +} + +void SignalSlotEditorWindow::updateEditorSelection(const QModelIndex &index) +{ + if (m_handling_selection_change || m_editor == 0) + return; + + if (m_editor == 0) + return; + + Connection *con = m_model->indexToConnection(m_proxy_model->mapToSource(index)); + if (m_editor->selected(con)) + return; + m_handling_selection_change = true; + m_editor->selectNone(); + m_editor->setSelected(con, true); + m_handling_selection_change = false; + + updateUi(); +} + +void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *, const QString &, const QString &) +{ + if (m_editor) + m_model->updateAll(); +} + +void SignalSlotEditorWindow::addConnection() +{ + if (m_editor.isNull()) + return; + + m_editor->addEmptyConnection(); + updateUi(); +} + +void SignalSlotEditorWindow::removeConnection() +{ + if (m_editor.isNull()) + return; + + m_editor->deleteSelected(); + updateUi(); +} + +void SignalSlotEditorWindow::updateUi() +{ + m_add_button->setEnabled(!m_editor.isNull()); + m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include "moc_signalsloteditorwindow.cpp" +#include diff --git a/src/designer/components/signalsloteditor/signalsloteditorwindow.h b/src/designer/components/signalsloteditor/signalsloteditorwindow.h new file mode 100644 index 000000000..a1f8bffcb --- /dev/null +++ b/src/designer/components/signalsloteditor/signalsloteditorwindow.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIGNALSLOTEDITORWINDOW_H +#define SIGNALSLOTEDITORWINDOW_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class QModelIndex; +class QSortFilterProxyModel; +class QTreeView; +class QToolButton; + +namespace qdesigner_internal { + +class SignalSlotEditor; +class ConnectionModel; +class Connection; + +class SignalSlotEditorWindow : public QWidget +{ + Q_OBJECT +public: + explicit SignalSlotEditorWindow(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +public slots: + void setActiveFormWindow(QDesignerFormWindowInterface *form); + +private slots: + void updateDialogSelection(Connection *con); + void updateEditorSelection(const QModelIndex &index); + + void objectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName); + + void addConnection(); + void removeConnection(); + void updateUi(); + +private: + QTreeView *m_view; + QPointer m_editor; + QToolButton *m_add_button, *m_remove_button; + QDesignerFormEditorInterface *m_core; + ConnectionModel *m_model; + QSortFilterProxyModel *m_proxy_model; + bool m_handling_selection_change; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SIGNALSLOTEDITORWINDOW_H diff --git a/src/designer/components/tabordereditor/tabordereditor.cmake b/src/designer/components/tabordereditor/tabordereditor.cmake new file mode 100644 index 000000000..b74b565af --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor.cmake @@ -0,0 +1,15 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_plugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_tool.h + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_global.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_tool.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_plugin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tabordereditor/tabordereditor_instance.cpp +) diff --git a/src/designer/components/tabordereditor/tabordereditor.cpp b/src/designer/components/tabordereditor/tabordereditor.cpp new file mode 100644 index 000000000..926cec531 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor.cpp @@ -0,0 +1,434 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tabordereditor.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +namespace { + enum { VBOX_MARGIN = 1, HBOX_MARGIN = 4, BG_ALPHA = 32 }; +} + +static QRect fixRect(const QRect &r) +{ + return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1); +} + +namespace qdesigner_internal { + +TabOrderEditor::TabOrderEditor(QDesignerFormWindowInterface *form, QWidget *parent) : + QWidget(parent), + m_form_window(form), + m_bg_widget(0), + m_undo_stack(form->commandHistory()), + m_font_metrics(font()), + m_current_index(0), + m_beginning(true) +{ + connect(form, SIGNAL(widgetRemoved(QWidget*)), this, SLOT(widgetRemoved(QWidget*))); + + QFont tabFont = font(); + tabFont.setPointSize(tabFont.pointSize()*2); + tabFont.setBold(true); + setFont(tabFont); + m_font_metrics = QFontMetrics(tabFont); + setAttribute(Qt::WA_MouseTracking, true); +} + +QDesignerFormWindowInterface *TabOrderEditor::formWindow() const +{ + return m_form_window; +} + +void TabOrderEditor::setBackground(QWidget *background) +{ + if (background == m_bg_widget) { + return; + } + + m_bg_widget = background; + updateBackground(); +} + +void TabOrderEditor::updateBackground() +{ + if (m_bg_widget == 0) { + // nothing to do + return; + } + + initTabOrder(); + update(); +} + +void TabOrderEditor::widgetRemoved(QWidget*) +{ + initTabOrder(); +} + +void TabOrderEditor::showEvent(QShowEvent *e) +{ + QWidget::showEvent(e); + updateBackground(); +} + +QRect TabOrderEditor::indicatorRect(int index) const +{ + if (index < 0 || index >= m_tab_order_list.size()) + return QRect(); + + const QWidget *w = m_tab_order_list.at(index); + const QString text = QString::number(index + 1); + + const QPoint tl = mapFromGlobal(w->mapToGlobal(w->rect().topLeft())); + const QSize size = m_font_metrics.size(Qt::TextSingleLine, text); + QRect r(tl - QPoint(size.width(), size.height())/2, size); + r = QRect(r.left() - HBOX_MARGIN, r.top() - VBOX_MARGIN, + r.width() + HBOX_MARGIN*2, r.height() + VBOX_MARGIN*2); + + return r; +} + +static bool isWidgetVisible(QWidget *widget) +{ + while (widget && widget->parentWidget()) { + if (!widget->isVisibleTo(widget->parentWidget())) + return false; + + widget = widget->parentWidget(); + } + + return true; +} + +void TabOrderEditor::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setClipRegion(e->region()); + + int cur = m_current_index - 1; + if (m_beginning == false && cur < 0) + cur = m_tab_order_list.size() - 1; + + for (int i = 0; i < m_tab_order_list.size(); ++i) { + QWidget *widget = m_tab_order_list.at(i); + if (!isWidgetVisible(widget)) + continue; + + const QRect r = indicatorRect(i); + + QColor c = Qt::darkGreen; + if (i == cur) + c = Qt::red; + else if (i > cur) + c = Qt::blue; + p.setPen(c); + c.setAlpha(BG_ALPHA); + p.setBrush(c); + p.drawRect(fixRect(r)); + + p.setPen(Qt::white); + p.drawText(r, QString::number(i + 1), QTextOption(Qt::AlignCenter)); + } +} + +bool TabOrderEditor::skipWidget(QWidget *w) const +{ + if (qobject_cast(w) + || w == formWindow()->mainContainer() + || w->isHidden()) + return true; + + if (!formWindow()->isManaged(w)) { + return true; + } + + QExtensionManager *ext = formWindow()->core()->extensionManager(); + if (const QDesignerPropertySheetExtension *sheet = qt_extension(ext, w)) { + const int index = sheet->indexOf(QLatin1String("focusPolicy")); + if (index != -1) { + bool ok = false; + Qt::FocusPolicy q = (Qt::FocusPolicy) Utils::valueOf(sheet->property(index), &ok); + return !ok || !(q & Qt::TabFocus); + } + } + + return true; +} + +void TabOrderEditor::initTabOrder() +{ + m_tab_order_list.clear(); + + QDesignerFormEditorInterface *core = formWindow()->core(); + + if (const QDesignerMetaDataBaseItemInterface *item = core->metaDataBase()->item(formWindow())) { + m_tab_order_list = item->tabOrder(); + } + + // Remove any widgets that have been removed form the form + for (int i = 0; i < m_tab_order_list.size(); ) { + QWidget *w = m_tab_order_list.at(i); + if (!formWindow()->mainContainer()->isAncestorOf(w) || skipWidget(w)) + m_tab_order_list.removeAt(i); + else + ++i; + } + + // Append any widgets that are in the form but are not in the tab order + QList childQueue; + childQueue.append(formWindow()->mainContainer()); + while (!childQueue.isEmpty()) { + QWidget *child = childQueue.takeFirst(); + childQueue += qvariant_cast(child->property("_q_widgetOrder")); + + if (skipWidget(child)) + continue; + + if (!m_tab_order_list.contains(child)) + m_tab_order_list.append(child); + } + + // Just in case we missed some widgets + QDesignerFormWindowCursorInterface *cursor = formWindow()->cursor(); + for (int i = 0; i < cursor->widgetCount(); ++i) { + + QWidget *widget = cursor->widget(i); + if (skipWidget(widget)) + continue; + + if (!m_tab_order_list.contains(widget)) + m_tab_order_list.append(widget); + } + + m_indicator_region = QRegion(); + for (int i = 0; i < m_tab_order_list.size(); ++i) { + if (m_tab_order_list.at(i)->isVisible()) + m_indicator_region |= indicatorRect(i); + } + + if (m_current_index >= m_tab_order_list.size()) + m_current_index = m_tab_order_list.size() - 1; + if (m_current_index < 0) + m_current_index = 0; +} + +void TabOrderEditor::mouseMoveEvent(QMouseEvent *e) +{ + e->accept(); +#ifndef QT_NO_CURSOR + if (m_indicator_region.contains(e->pos())) + setCursor(Qt::PointingHandCursor); + else + setCursor(QCursor()); +#endif +} + +int TabOrderEditor::widgetIndexAt(const QPoint &pos) const +{ + int target_index = -1; + for (int i = 0; i < m_tab_order_list.size(); ++i) { + if (!m_tab_order_list.at(i)->isVisible()) + continue; + if (indicatorRect(i).contains(pos)) { + target_index = i; + break; + } + } + + return target_index; +} + +void TabOrderEditor::mousePressEvent(QMouseEvent *e) +{ + e->accept(); + + if (!m_indicator_region.contains(e->pos())) { + if (QWidget *child = m_bg_widget->childAt(e->pos())) { + QDesignerFormEditorInterface *core = m_form_window->core(); + if (core->widgetFactory()->isPassiveInteractor(child)) { + + QMouseEvent event(QEvent::MouseButtonPress, + child->mapFromGlobal(e->globalPos()), + e->button(), e->buttons(), e->modifiers()); + + qApp->sendEvent(child, &event); + + QMouseEvent event2(QEvent::MouseButtonRelease, + child->mapFromGlobal(e->globalPos()), + e->button(), e->buttons(), e->modifiers()); + + qApp->sendEvent(child, &event2); + + updateBackground(); + } + } + return; + } + + if (e->button() != Qt::LeftButton) + return; + + const int target_index = widgetIndexAt(e->pos()); + if (target_index == -1) + return; + + m_beginning = false; + + if (e->modifiers() & Qt::ControlModifier) { + m_current_index = target_index + 1; + if (m_current_index >= m_tab_order_list.size()) + m_current_index = 0; + update(); + return; + } + + if (m_current_index == -1) + return; + + m_tab_order_list.swap(target_index, m_current_index); + + ++m_current_index; + if (m_current_index == m_tab_order_list.size()) + m_current_index = 0; + + TabOrderCommand *cmd = new TabOrderCommand(formWindow()); + cmd->init(m_tab_order_list); + formWindow()->commandHistory()->push(cmd); +} + +void TabOrderEditor::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu menu(this); + const int target_index = widgetIndexAt(e->pos()); + QAction *setIndex = menu.addAction(tr("Start from Here")); + setIndex->setEnabled(target_index >= 0); + + QAction *resetIndex = menu.addAction(tr("Restart")); + menu.addSeparator(); + QAction *showDialog = menu.addAction(tr("Tab Order List...")); + showDialog->setEnabled(m_tab_order_list.size() > 1); + + QAction *result = menu.exec(e->globalPos()); + if (result == resetIndex) { + m_current_index = 0; + m_beginning = true; + update(); + } else if (result == setIndex) { + m_beginning = false; + m_current_index = target_index + 1; + if (m_current_index >= m_tab_order_list.size()) + m_current_index = 0; + update(); + } else if (result == showDialog) { + showTabOrderDialog(); + } +} + +void TabOrderEditor::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) + return; + + const int target_index = widgetIndexAt(e->pos()); + if (target_index >= 0) + return; + + m_beginning = true; + m_current_index = 0; + update(); +} + +void TabOrderEditor::resizeEvent(QResizeEvent *e) +{ + updateBackground(); + QWidget::resizeEvent(e); +} + +void TabOrderEditor::showTabOrderDialog() +{ + if (m_tab_order_list.size() < 2) + return; + OrderDialog dlg(this); + dlg.setWindowTitle(tr("Tab Order List")); + dlg.setDescription(tr("Tab Order")); + dlg.setFormat(OrderDialog::TabOrderFormat); + dlg.setPageList(m_tab_order_list); + + if (dlg.exec() == QDialog::Rejected) + return; + + const QWidgetList newOrder = dlg.pageList(); + if (newOrder == m_tab_order_list) + return; + + m_tab_order_list = newOrder; + TabOrderCommand *cmd = new TabOrderCommand(formWindow()); + cmd->init(m_tab_order_list); + formWindow()->commandHistory()->push(cmd); + update(); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/tabordereditor/tabordereditor.h b/src/designer/components/tabordereditor/tabordereditor.h new file mode 100644 index 000000000..9487ee086 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABORDEREDITOR_H +#define TABORDEREDITOR_H + +#include "tabordereditor_global.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QUndoStack; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class QT_TABORDEREDITOR_EXPORT TabOrderEditor : public QWidget +{ + Q_OBJECT + +public: + TabOrderEditor(QDesignerFormWindowInterface *form, QWidget *parent); + + QDesignerFormWindowInterface *formWindow() const; + +public slots: + void setBackground(QWidget *background); + void updateBackground(); + void widgetRemoved(QWidget*); + void initTabOrder(); + +private slots: + void showTabOrderDialog(); + +protected: + virtual void paintEvent(QPaintEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void contextMenuEvent(QContextMenuEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void showEvent(QShowEvent *e); + +private: + QRect indicatorRect(int index) const; + int widgetIndexAt(const QPoint &pos) const; + bool skipWidget(QWidget *w) const; + + QPointer m_form_window; + + QWidgetList m_tab_order_list; + + QWidget *m_bg_widget; + QUndoStack *m_undo_stack; + QRegion m_indicator_region; + + QFontMetrics m_font_metrics; + int m_current_index; + bool m_beginning; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/src/designer/components/tabordereditor/tabordereditor_global.h b/src/designer/components/tabordereditor/tabordereditor_global.h new file mode 100644 index 000000000..7f6bbded8 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABORDEREDITOR_GLOBAL_H +#define TABORDEREDITOR_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_TABORDEREDITOR_LIBRARY +# define QT_TABORDEREDITOR_EXPORT +#else +# define QT_TABORDEREDITOR_EXPORT +#endif +#else +#define QT_TABORDEREDITOR_EXPORT +#endif + +#endif // TABORDEREDITOR_GLOBAL_H diff --git a/src/designer/components/tabordereditor/tabordereditor_instance.cpp b/src/designer/components/tabordereditor/tabordereditor_instance.cpp new file mode 100644 index 000000000..2c386ad72 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_instance.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "tabordereditor_plugin.h" + +QT_USE_NAMESPACE +using namespace qdesigner_internal; + +Q_EXPORT_PLUGIN(TabOrderEditorPlugin) diff --git a/src/designer/components/tabordereditor/tabordereditor_plugin.cpp b/src/designer/components/tabordereditor/tabordereditor_plugin.cpp new file mode 100644 index 000000000..53cce64e4 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_plugin.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "tabordereditor_plugin.h" +#include "tabordereditor_tool.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +TabOrderEditorPlugin::TabOrderEditorPlugin() + : m_initialized(false) +{ +} + +TabOrderEditorPlugin::~TabOrderEditorPlugin() +{ +} + +bool TabOrderEditorPlugin::isInitialized() const +{ + return m_initialized; +} + +void TabOrderEditorPlugin::initialize(QDesignerFormEditorInterface *core) +{ + Q_ASSERT(!isInitialized()); + + m_action = new QAction(tr("Edit Tab Order"), this); + m_action->setObjectName(QLatin1String("_qt_edit_tab_order_action")); + QIcon icon = QIcon::fromTheme("designer-edit-tabs", + QIcon(core->resourceLocation() + QLatin1String("/tabordertool.png"))); + m_action->setIcon(icon); + m_action->setEnabled(false); + + setParent(core); + m_core = core; + m_initialized = true; + + connect(core->formWindowManager(), SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), + this, SLOT(addFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(formWindowRemoved(QDesignerFormWindowInterface*)), + this, SLOT(removeFormWindow(QDesignerFormWindowInterface*))); + + connect(core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(activeFormWindowChanged(QDesignerFormWindowInterface*))); +} + +void TabOrderEditorPlugin::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + m_action->setEnabled(formWindow != 0); +} + +QDesignerFormEditorInterface *TabOrderEditorPlugin::core() const +{ + return m_core; +} + +void TabOrderEditorPlugin::addFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == false); + + TabOrderEditorTool *tool = new TabOrderEditorTool(formWindow, this); + m_tools[formWindow] = tool; + connect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + formWindow->registerTool(tool); +} + +void TabOrderEditorPlugin::removeFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_ASSERT(formWindow != 0); + Q_ASSERT(m_tools.contains(formWindow) == true); + + TabOrderEditorTool *tool = m_tools.value(formWindow); + m_tools.remove(formWindow); + disconnect(m_action, SIGNAL(triggered()), tool->action(), SLOT(trigger())); + // ### FIXME disable the tool + + delete tool; +} + +QAction *TabOrderEditorPlugin::action() const +{ + return m_action; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/tabordereditor/tabordereditor_plugin.h b/src/designer/components/tabordereditor/tabordereditor_plugin.h new file mode 100644 index 000000000..451686a6a --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_plugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABORDEREDITOR_PLUGIN_H +#define TABORDEREDITOR_PLUGIN_H + +#include "tabordereditor_global.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QAction; + +namespace qdesigner_internal { + +class TabOrderEditorTool; + +class QT_TABORDEREDITOR_EXPORT TabOrderEditorPlugin: public QObject, public QDesignerFormEditorPluginInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerFormEditorPluginInterface) +public: + TabOrderEditorPlugin(); + virtual ~TabOrderEditorPlugin(); + + virtual bool isInitialized() const; + virtual void initialize(QDesignerFormEditorInterface *core); + QAction *action() const; + + virtual QDesignerFormEditorInterface *core() const; + +public slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + +private slots: + void addFormWindow(QDesignerFormWindowInterface *formWindow); + void removeFormWindow(QDesignerFormWindowInterface *formWindow); + +private: + QPointer m_core; + QHash m_tools; + bool m_initialized; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TABORDEREDITOR_PLUGIN_H diff --git a/src/designer/components/tabordereditor/tabordereditor_tool.cpp b/src/designer/components/tabordereditor/tabordereditor_tool.cpp new file mode 100644 index 000000000..47a8e4d65 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_tool.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tabordereditor_tool.h" +#include "tabordereditor.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +TabOrderEditorTool::TabOrderEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent) + : QDesignerFormWindowToolInterface(parent), + m_formWindow(formWindow), + m_action(new QAction(tr("Edit Tab Order"), this)) +{ +} + +TabOrderEditorTool::~TabOrderEditorTool() +{ +} + +QDesignerFormEditorInterface *TabOrderEditorTool::core() const +{ + return m_formWindow->core(); +} + +QDesignerFormWindowInterface *TabOrderEditorTool::formWindow() const +{ + return m_formWindow; +} + +bool TabOrderEditorTool::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event) +{ + Q_UNUSED(widget); + Q_UNUSED(managedWidget); + + if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) + return true; + + return false; +} + +QWidget *TabOrderEditorTool::editor() const +{ + if (!m_editor) { + Q_ASSERT(formWindow() != 0); + m_editor = new TabOrderEditor(formWindow(), 0); + connect(formWindow(), SIGNAL(mainContainerChanged(QWidget*)), m_editor, SLOT(setBackground(QWidget*))); + } + + return m_editor; +} + +void TabOrderEditorTool::activated() +{ + connect(formWindow(), SIGNAL(changed()), + m_editor, SLOT(updateBackground())); +} + +void TabOrderEditorTool::deactivated() +{ + disconnect(formWindow(), SIGNAL(changed()), + m_editor, SLOT(updateBackground())); +} + +QAction *TabOrderEditorTool::action() const +{ + return m_action; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/tabordereditor/tabordereditor_tool.h b/src/designer/components/tabordereditor/tabordereditor_tool.h new file mode 100644 index 000000000..cb8a1df99 --- /dev/null +++ b/src/designer/components/tabordereditor/tabordereditor_tool.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABORDEREDITOR_TOOL_H +#define TABORDEREDITOR_TOOL_H + +#include "tabordereditor_global.h" + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QAction; + +namespace qdesigner_internal { + +class TabOrderEditor; + +class QT_TABORDEREDITOR_EXPORT TabOrderEditorTool: public QDesignerFormWindowToolInterface +{ + Q_OBJECT +public: + explicit TabOrderEditorTool(QDesignerFormWindowInterface *formWindow, QObject *parent = 0); + virtual ~TabOrderEditorTool(); + + virtual QDesignerFormEditorInterface *core() const; + virtual QDesignerFormWindowInterface *formWindow() const; + + virtual QWidget *editor() const; + virtual QAction *action() const; + + virtual void activated(); + virtual void deactivated(); + + virtual bool handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event); + +private: + QDesignerFormWindowInterface *m_formWindow; + mutable QPointer m_editor; + QAction *m_action; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TABORDEREDITOR_TOOL_H diff --git a/src/designer/components/taskmenu/button_taskmenu.cpp b/src/designer/components/taskmenu/button_taskmenu.cpp new file mode 100644 index 000000000..92034c285 --- /dev/null +++ b/src/designer/components/taskmenu/button_taskmenu.cpp @@ -0,0 +1,710 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "button_taskmenu.h" +#include "inplace_editor.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QButtonGroup*) + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +enum { debugButtonMenu = 0 }; + +typedef QList ButtonList; +typedef QList ButtonGroupList; + +// ButtonGroupCommand: Base for commands handling button groups and button lists +// addButtonsToGroup() and removeButtonsFromGroup() are low-level helpers for +// adding/removing members to/from existing groups. +// +// createButtonGroup()/breakButtonGroup() create and remove the groups from scratch. +// When using them in a command, the 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 ButtonGroupCommand : public QDesignerFormWindowCommand { + +protected: + ButtonGroupCommand(const QString &description, QDesignerFormWindowInterface *formWindow); + + void initialize(const ButtonList &bl, QButtonGroup *buttonGroup); + + // Helper: Add the buttons to the group + void addButtonsToGroup(); + // Helper; Remove the buttons + void removeButtonsFromGroup(); + + // Create the button group in Designer + void createButtonGroup(); + // Remove the button group from Designer + void breakButtonGroup(); + +public: + static QString nameList(const ButtonList& bl); + static ButtonGroupList managedButtonGroups(const QDesignerFormWindowInterface *formWindow); + +private: + ButtonList m_buttonList; + QButtonGroup *m_buttonGroup; +}; + +ButtonGroupCommand::ButtonGroupCommand(const QString &description, QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(description, formWindow), + m_buttonGroup(0) +{ +} + +void ButtonGroupCommand::initialize(const ButtonList &bl, QButtonGroup *buttonGroup) +{ + m_buttonList = bl; + m_buttonGroup = buttonGroup; +} + +void ButtonGroupCommand::addButtonsToGroup() +{ + if (debugButtonMenu) + qDebug() << "Adding " << m_buttonList << " to " << m_buttonGroup; + const ButtonList::const_iterator cend = m_buttonList.constEnd(); + for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) + m_buttonGroup->addButton(*it); +} + +void ButtonGroupCommand::removeButtonsFromGroup() +{ + if (debugButtonMenu) + qDebug() << "Removing " << m_buttonList << " from " << m_buttonGroup; + const ButtonList::const_iterator cend = m_buttonList.constEnd(); + for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) + m_buttonGroup->removeButton(*it); +} + +void ButtonGroupCommand::createButtonGroup() +{ + if (debugButtonMenu) + qDebug() << "Creating " << m_buttonGroup << " from " << m_buttonList; + + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + core->metaDataBase()->add(m_buttonGroup); + addButtonsToGroup(); + // Make button group visible + core->objectInspector()->setFormWindow(fw); +} + +void ButtonGroupCommand::breakButtonGroup() +{ + if (debugButtonMenu) + qDebug() << "Removing " << m_buttonGroup << " consisting of " << m_buttonList; + + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + // Button group was selected, that is, break was invoked via its context menu. Remove it from property editor, select the buttons + if (core->propertyEditor()->object() == m_buttonGroup) { + fw->clearSelection(false); + const ButtonList::const_iterator cend = m_buttonList.constEnd(); + for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) + fw->selectWidget(*it, true); + } + // Now remove and refresh object inspector + removeButtonsFromGroup(); + // Notify components (for example, signal slot editor) + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast(fw)) + fwb->emitObjectRemoved(m_buttonGroup); + core->metaDataBase()->remove(m_buttonGroup); + core->objectInspector()->setFormWindow(fw); +} + +QString ButtonGroupCommand::nameList(const ButtonList& bl) +{ + QString rc; + const QChar quote = QLatin1Char('\''); + const QString separator = QLatin1String(", "); + const int size = bl.size(); + for (int i = 0; i < size; i++) { + if (i) + rc += separator; + rc += quote; + rc += bl[i]->objectName(); + rc += quote; + } + return rc; + +} + +ButtonGroupList ButtonGroupCommand::managedButtonGroups(const QDesignerFormWindowInterface *formWindow) +{ + const QDesignerMetaDataBaseInterface *mdb = formWindow->core()->metaDataBase(); + ButtonGroupList bl; + // Check 1st order children for managed button groups + const QObjectList children = formWindow->mainContainer()->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + if (!(*it)->isWidgetType()) + if (QButtonGroup *bg = qobject_cast(*it)) + if (mdb->item(bg)) + bl.push_back(bg); + } + return bl; +} + +// --------------- CreateButtonGroupCommand +// This command might be executed in a macro with a remove +// command to move buttons from one group to a new one. +class CreateButtonGroupCommand : public ButtonGroupCommand { +public: + CreateButtonGroupCommand(QDesignerFormWindowInterface *formWindow); + bool init(const ButtonList &bl); + + virtual void undo() { breakButtonGroup(); } + virtual void redo() { createButtonGroup(); } +}; + +CreateButtonGroupCommand::CreateButtonGroupCommand(QDesignerFormWindowInterface *formWindow) : + ButtonGroupCommand(QApplication::translate("Command", "Create button group"), formWindow) +{ +} + +bool CreateButtonGroupCommand::init(const ButtonList &bl) +{ + if (bl.empty()) + return false; + QDesignerFormWindowInterface *fw = formWindow(); + QButtonGroup *buttonGroup = new QButtonGroup(fw->mainContainer()); + buttonGroup->setObjectName(QLatin1String("buttonGroup")); + fw->ensureUniqueObjectName(buttonGroup); + initialize(bl, buttonGroup); + return true; +} + +// --------------- BreakButtonGroupCommand +class BreakButtonGroupCommand : public ButtonGroupCommand { +public: + BreakButtonGroupCommand(QDesignerFormWindowInterface *formWindow); + bool init(QButtonGroup *group); + + virtual void undo() { createButtonGroup(); } + virtual void redo() { breakButtonGroup(); } +}; + +BreakButtonGroupCommand::BreakButtonGroupCommand(QDesignerFormWindowInterface *formWindow) : + ButtonGroupCommand(QApplication::translate("Command", "Break button group"), formWindow) +{ +} + +bool BreakButtonGroupCommand::init(QButtonGroup *group) +{ + if (!group) + return false; + initialize(group->buttons(), group); + setText(QApplication::translate("Command", "Break button group '%1'").arg(group->objectName())); + return true; +} + +// --------------- AddButtonsToGroupCommand +// This command might be executed in a macro with a remove +// command to move buttons from one group to a new one. +class AddButtonsToGroupCommand : public ButtonGroupCommand { +public: + AddButtonsToGroupCommand(QDesignerFormWindowInterface *formWindow); + void init(const ButtonList &bl, QButtonGroup *group); + + virtual void undo() { removeButtonsFromGroup(); } + virtual void redo() { addButtonsToGroup(); } +}; + +AddButtonsToGroupCommand::AddButtonsToGroupCommand(QDesignerFormWindowInterface *formWindow) : + ButtonGroupCommand(QApplication::translate("Command", "Add buttons to group"), formWindow) +{ +} + +void AddButtonsToGroupCommand::init(const ButtonList &bl, QButtonGroup *group) +{ + initialize(bl, group); + //: Command description for adding buttons to a QButtonGroup + setText(QApplication::translate("Command", "Add '%1' to '%2'").arg(nameList(bl), group->objectName())); +} + +//-------------------- RemoveButtonsFromGroupCommand +class RemoveButtonsFromGroupCommand : public ButtonGroupCommand { +public: + RemoveButtonsFromGroupCommand(QDesignerFormWindowInterface *formWindow); + bool init(const ButtonList &bl); + + virtual void undo() { addButtonsToGroup(); } + virtual void redo() { removeButtonsFromGroup(); } +}; + +RemoveButtonsFromGroupCommand::RemoveButtonsFromGroupCommand(QDesignerFormWindowInterface *formWindow) : + ButtonGroupCommand(QApplication::translate("Command", "Remove buttons from group"), formWindow) +{ +} + +bool RemoveButtonsFromGroupCommand::init(const ButtonList &bl) +{ + if (bl.empty()) + return false; + QButtonGroup *group = bl.front()->group(); + if (!group) + return false; + if (bl.size() >= group->buttons().size()) + return false; + initialize(bl, group); + //: Command description for removing buttons from a QButtonGroup + setText(QApplication::translate("Command", "Remove '%1' from '%2'").arg(nameList(bl), group->objectName())); + return true; +} + +// -------- ButtonGroupMenu +ButtonGroupMenu::ButtonGroupMenu(QObject *parent) : + QObject(parent), + m_selectGroupAction(new QAction(tr("Select members"), this)), + m_breakGroupAction(new QAction(tr("Break"), this)), + m_formWindow(0), + m_buttonGroup(0), + m_currentButton(0) +{ + connect(m_breakGroupAction, SIGNAL(triggered()), this, SLOT(breakGroup())); + connect(m_selectGroupAction, SIGNAL(triggered()), this, SLOT(selectGroup())); +} + +void ButtonGroupMenu::initialize(QDesignerFormWindowInterface *formWindow, QButtonGroup *buttonGroup, QAbstractButton *currentButton) +{ + m_buttonGroup = buttonGroup; + m_currentButton = currentButton; + m_formWindow = formWindow; + Q_ASSERT(m_formWindow); + + const bool canBreak = buttonGroup != 0; + m_breakGroupAction->setEnabled(canBreak); + m_selectGroupAction->setEnabled(canBreak); +} + +void ButtonGroupMenu::selectGroup() +{ + // Select and make current button "current" again by selecting it last (if there is any) + const ButtonList buttons = m_buttonGroup->buttons(); + m_formWindow->clearSelection(false); + const ButtonList::const_iterator cend = buttons.constEnd(); + for (ButtonList::const_iterator it = buttons.constBegin(); it != cend; ++it) + if (*it != m_currentButton) + m_formWindow->selectWidget(*it, true); + if (m_currentButton) + m_formWindow->selectWidget(m_currentButton, true); +} + +void ButtonGroupMenu::breakGroup() +{ + BreakButtonGroupCommand *cmd = new BreakButtonGroupCommand(m_formWindow); + if (cmd->init(m_buttonGroup)) { + // Need a macro since the command might trigger additional commands + QUndoStack *history = m_formWindow->commandHistory(); + history->beginMacro(cmd->text()); + history->push(cmd); + history->endMacro(); + } else { + qWarning("** WARNING Failed to initialize BreakButtonGroupCommand!"); + delete cmd; + } +} + +// ButtonGroupTaskMenu +ButtonGroupTaskMenu::ButtonGroupTaskMenu(QButtonGroup *buttonGroup, QObject *parent) : + QObject(parent), + m_buttonGroup(buttonGroup) +{ + m_taskActions.push_back(m_menu.breakGroupAction()); + m_taskActions.push_back(m_menu.selectGroupAction()); +} + +QAction *ButtonGroupTaskMenu::preferredEditAction() const +{ + return m_menu.selectGroupAction(); +} + +QList ButtonGroupTaskMenu::taskActions() const +{ + m_menu.initialize(QDesignerFormWindowInterface::findFormWindow(m_buttonGroup), m_buttonGroup); + return m_taskActions; +} + +// -------- Text area editor +class ButtonTextTaskMenuInlineEditor : public TaskMenuInlineEditor +{ +public: + ButtonTextTaskMenuInlineEditor(QAbstractButton *button, QObject *parent); + +protected: + virtual QRect editRectangle() const; +}; + +ButtonTextTaskMenuInlineEditor::ButtonTextTaskMenuInlineEditor(QAbstractButton *button, QObject *parent) : + TaskMenuInlineEditor(button, ValidationMultiLine, QLatin1String("text"), parent) +{ +} + +QRect ButtonTextTaskMenuInlineEditor::editRectangle() const +{ + QWidget *w = widget(); + QStyleOptionButton opt; + opt.init(w); + return w->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, w); +} + +// -------- Command link button description editor +class LinkDescriptionTaskMenuInlineEditor : public TaskMenuInlineEditor +{ +public: + LinkDescriptionTaskMenuInlineEditor(QAbstractButton *button, QObject *parent); + +protected: + virtual QRect editRectangle() const; +}; + +LinkDescriptionTaskMenuInlineEditor::LinkDescriptionTaskMenuInlineEditor(QAbstractButton *button, QObject *parent) : + TaskMenuInlineEditor(button, ValidationMultiLine, QLatin1String("description"), parent) +{ +} + +QRect LinkDescriptionTaskMenuInlineEditor::editRectangle() const +{ + QWidget *w = widget(); // TODO: What is the exact description area? + QStyleOptionButton opt; + opt.init(w); + return w->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, w); +} + +// ----------- ButtonTaskMenu: + +ButtonTaskMenu::ButtonTaskMenu(QAbstractButton *button, QObject *parent) : + QDesignerTaskMenu(button, parent), + m_assignGroupSubMenu(new QMenu), + m_assignActionGroup(0), + m_assignToGroupSubMenuAction(new QAction(tr("Assign to button group"), this)), + m_currentGroupSubMenu(new QMenu), + m_currentGroupSubMenuAction(new QAction(tr("Button group"), this)), + m_createGroupAction(new QAction(tr("New button group"), this)), + m_preferredEditAction(new QAction(tr("Change text..."), this)), + m_removeFromGroupAction(new QAction(tr("None"), this)) +{ + connect(m_createGroupAction, SIGNAL(triggered()), this, SLOT(createGroup())); + TaskMenuInlineEditor *textEditor = new ButtonTextTaskMenuInlineEditor(button, this); + connect(m_preferredEditAction, SIGNAL(triggered()), textEditor, SLOT(editText())); + connect(m_removeFromGroupAction, SIGNAL(triggered()), this, SLOT(removeFromGroup())); + + m_assignToGroupSubMenuAction->setMenu(m_assignGroupSubMenu); + + m_currentGroupSubMenu->addAction(m_groupMenu.breakGroupAction()); + m_currentGroupSubMenu->addAction(m_groupMenu.selectGroupAction()); + m_currentGroupSubMenuAction->setMenu(m_currentGroupSubMenu); + + + m_taskActions.append(m_preferredEditAction); + m_taskActions.append(m_assignToGroupSubMenuAction); + m_taskActions.append(m_currentGroupSubMenuAction); + m_taskActions.append(createSeparator()); +} + +ButtonTaskMenu::~ButtonTaskMenu() +{ + delete m_assignGroupSubMenu; + delete m_currentGroupSubMenu; +} + +QAction *ButtonTaskMenu::preferredEditAction() const +{ + return m_preferredEditAction; +} + +bool ButtonTaskMenu::refreshAssignMenu(const QDesignerFormWindowInterface *fw, int buttonCount, SelectionType st, QButtonGroup *currentGroup) +{ + // clear + if (m_assignActionGroup) { + delete m_assignActionGroup; + m_assignActionGroup = 0; + } + m_assignGroupSubMenu->clear(); + if (st == OtherSelection) + return false; + + + // Assign to new: Need several + const bool canAssignToNewGroup = buttonCount > 1; + m_createGroupAction->setEnabled(canAssignToNewGroup); + if (canAssignToNewGroup) + m_assignGroupSubMenu->addAction(m_createGroupAction); + + // Assign to other + const ButtonGroupList bl = ButtonGroupCommand::managedButtonGroups(fw); + // Groups: Any groups to add to except the current? + const int groupCount = bl.size(); + const bool hasAddGroups = groupCount > 1 || (groupCount == 1 && !bl.contains(currentGroup)); + if (hasAddGroups) { + if (!m_assignGroupSubMenu->isEmpty()) + m_assignGroupSubMenu->addSeparator(); + // Create a new action group + m_assignActionGroup = new QActionGroup(this); + connect(m_assignActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(addToGroup(QAction*))); + + const ButtonGroupList::const_iterator cend = bl.constEnd(); + for (ButtonGroupList::const_iterator it = bl.constBegin(); it != cend; ++it) { + QButtonGroup *bg = *it; + if (*it != currentGroup) { + QAction *a = new QAction(bg->objectName(), m_assignGroupSubMenu); + a->setData(QVariant::fromValue(bg)); + m_assignActionGroup->addAction(a); + m_assignGroupSubMenu->addAction(a); + } + } + } + // Can remove: A homogenous selection of another group that does not completely break it. + const bool canRemoveFromGroup = st == GroupedButtonSelection; + m_removeFromGroupAction->setEnabled(canRemoveFromGroup); + if (canRemoveFromGroup) { + if (!m_assignGroupSubMenu->isEmpty()) + m_assignGroupSubMenu->addSeparator(); + m_assignGroupSubMenu->addAction(m_removeFromGroupAction); + } + return !m_assignGroupSubMenu->isEmpty(); +} + +QList ButtonTaskMenu::taskActions() const +{ + ButtonTaskMenu *ncThis = const_cast(this); + QButtonGroup *buttonGroup = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + const SelectionType st = selectionType(fw->cursor(), &buttonGroup); + + m_groupMenu.initialize(fw, buttonGroup, button()); + const bool hasAssignOptions = ncThis->refreshAssignMenu(fw, fw->cursor()->selectedWidgetCount(), st, buttonGroup); + m_assignToGroupSubMenuAction->setVisible(hasAssignOptions); + // add/remove + switch (st) { + case UngroupedButtonSelection: + case OtherSelection: + m_currentGroupSubMenuAction->setVisible(false); + break; + case GroupedButtonSelection: + m_currentGroupSubMenuAction->setText(tr("Button group '%1'").arg(buttonGroup->objectName())); + m_currentGroupSubMenuAction->setVisible(true); + break; + } + + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + + +void ButtonTaskMenu::insertAction(int index, QAction *a) +{ + m_taskActions.insert(index, a); +} + +/* Create a button list from the cursor selection */ +static ButtonList buttonList(const QDesignerFormWindowCursorInterface *cursor) +{ + ButtonList rc; + const int selectionCount = cursor->selectedWidgetCount(); + for (int i = 0; i < selectionCount; i++) { + QAbstractButton *ab = qobject_cast(cursor->selectedWidget(i)); + Q_ASSERT(ab); + rc += ab; + } + return rc; +} + +// Create a command to remove the buttons from their group +// If it would leave an empty or 1-member group behind, create a break command instead + +static QUndoCommand *createRemoveButtonsCommand(QDesignerFormWindowInterface *fw, const ButtonList &bl) +{ + + QButtonGroup *bg = bl.front()->group(); + // Complete group or 1-member group? + if (bl.size() >= bg->buttons().size() - 1) { + BreakButtonGroupCommand *breakCmd = new BreakButtonGroupCommand(fw); + if (!breakCmd->init(bg)) { + qWarning("** WARNING Failed to initialize BreakButtonGroupCommand!"); + delete breakCmd; + return 0; + } + return breakCmd; + } + // Just remove the buttons + + RemoveButtonsFromGroupCommand *removeCmd = new RemoveButtonsFromGroupCommand(fw); + if (!removeCmd->init(bl)) { + qWarning("** WARNING Failed to initialize RemoveButtonsFromGroupCommand!"); + delete removeCmd; + return 0; + } + return removeCmd; +} + +void ButtonTaskMenu::createGroup() +{ + QDesignerFormWindowInterface *fw = formWindow(); + const ButtonList bl = buttonList(fw->cursor()); + // Do we need to remove the buttons from an existing group? + QUndoCommand *removeCmd = 0; + if (bl.front()->group()) { + removeCmd = createRemoveButtonsCommand(fw, bl); + if (!removeCmd) + return; + } + // Add cmd + CreateButtonGroupCommand *addCmd = new CreateButtonGroupCommand(fw); + if (!addCmd->init(bl)) { + qWarning("** WARNING Failed to initialize CreateButtonGroupCommand!"); + delete addCmd; + return; + } + // Need a macro [even if we only have the add command] since the command might trigger additional commands + QUndoStack *history = fw->commandHistory(); + history->beginMacro(addCmd->text()); + if (removeCmd) + history->push(removeCmd); + history->push(addCmd); + history->endMacro(); +} + +QAbstractButton *ButtonTaskMenu::button() const +{ + return qobject_cast(widget()); +} + +// Figure out if we have a homogenous selections (buttons of the same group or no group) +ButtonTaskMenu::SelectionType ButtonTaskMenu::selectionType(const QDesignerFormWindowCursorInterface *cursor, QButtonGroup **ptrToGroup) const +{ + const int selectionCount = cursor->selectedWidgetCount(); + if (!selectionCount) + return OtherSelection; + + QButtonGroup *commonGroup = 0; + for (int i = 0; i < selectionCount; i++) { + if (const QAbstractButton *ab = qobject_cast(cursor->selectedWidget(i))) { + QButtonGroup *buttonGroup = ab->group(); + if (i) { + if (buttonGroup != commonGroup) + return OtherSelection; + } else { + commonGroup = buttonGroup; + } + } else { + return OtherSelection; + } + } + + if (ptrToGroup) + *ptrToGroup = commonGroup; + + return commonGroup ? GroupedButtonSelection : UngroupedButtonSelection; +} + +void ButtonTaskMenu::addToGroup(QAction *a) +{ + QButtonGroup *bg = qvariant_cast(a->data()); + Q_ASSERT(bg); + + QDesignerFormWindowInterface *fw = formWindow(); + const ButtonList bl = buttonList(fw->cursor()); + // Do we need to remove the buttons from an existing group? + QUndoCommand *removeCmd = 0; + if (bl.front()->group()) { + removeCmd = createRemoveButtonsCommand(fw, bl); + if (!removeCmd) + return; + } + AddButtonsToGroupCommand *addCmd = new AddButtonsToGroupCommand(fw); + addCmd->init(bl, bg); + + QUndoStack *history = fw->commandHistory(); + if (removeCmd) { + history->beginMacro(addCmd->text()); + history->push(removeCmd); + history->push(addCmd); + history->endMacro(); + } else { + history->push(addCmd); + } +} + +void ButtonTaskMenu::removeFromGroup() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (QUndoCommand *cmd = createRemoveButtonsCommand(fw, buttonList(fw->cursor()))) + fw->commandHistory()->push(cmd); +} + +// -------------- CommandLinkButtonTaskMenu + +CommandLinkButtonTaskMenu::CommandLinkButtonTaskMenu(QCommandLinkButton *button, QObject *parent) : + ButtonTaskMenu(button, parent) +{ + TaskMenuInlineEditor *descriptonEditor = new LinkDescriptionTaskMenuInlineEditor(button, this); + QAction *descriptionAction = new QAction(tr("Change description..."), this); + connect(descriptionAction, SIGNAL(triggered()), descriptonEditor, SLOT(editText())); + insertAction(1, descriptionAction); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/button_taskmenu.h b/src/designer/components/taskmenu/button_taskmenu.h new file mode 100644 index 000000000..a6ddc93b8 --- /dev/null +++ b/src/designer/components/taskmenu/button_taskmenu.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BUTTON_TASKMENU_H +#define BUTTON_TASKMENU_H + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QMenu; +class QActionGroup; +class QDesignerFormWindowCursorInterface; + +namespace qdesigner_internal { + +// ButtonGroupMenu: Mixin menu for the 'select members'/'break group' options of +// the task menu of buttons and button group +class ButtonGroupMenu : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ButtonGroupMenu) +public: + ButtonGroupMenu(QObject *parent = 0); + + void initialize(QDesignerFormWindowInterface *formWindow, + QButtonGroup *buttonGroup = 0, + /* Current button for selection in ButtonMode */ + QAbstractButton *currentButton = 0); + + QAction *selectGroupAction() const { return m_selectGroupAction; } + QAction *breakGroupAction() const { return m_breakGroupAction; } + +private slots: + void selectGroup(); + void breakGroup(); + +private: + QAction *m_selectGroupAction; + QAction *m_breakGroupAction; + + QDesignerFormWindowInterface *m_formWindow; + QButtonGroup *m_buttonGroup; + QAbstractButton *m_currentButton; +}; + +// Task menu extension of a QButtonGroup +class ButtonGroupTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_DISABLE_COPY(ButtonGroupTaskMenu) + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit ButtonGroupTaskMenu(QButtonGroup *buttonGroup, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QButtonGroup *m_buttonGroup; + QList m_taskActions; + mutable ButtonGroupMenu m_menu; +}; + +// Task menu extension of a QAbstractButton +class ButtonTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT + Q_DISABLE_COPY(ButtonTaskMenu) +public: + explicit ButtonTaskMenu(QAbstractButton *button, QObject *parent = 0); + virtual ~ButtonTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + + QAbstractButton *button() const; + +protected: + void insertAction(int index, QAction *a); + +private slots: + void createGroup(); + void addToGroup(QAction *a); + void removeFromGroup(); + +private: + enum SelectionType { + OtherSelection, + UngroupedButtonSelection, + GroupedButtonSelection + }; + + SelectionType selectionType(const QDesignerFormWindowCursorInterface *cursor, QButtonGroup ** ptrToGroup = 0) const; + bool refreshAssignMenu(const QDesignerFormWindowInterface *fw, int buttonCount, SelectionType st, QButtonGroup *currentGroup); + QMenu *createGroupSelectionMenu(const QDesignerFormWindowInterface *fw); + + QList m_taskActions; + mutable ButtonGroupMenu m_groupMenu; + QMenu *m_assignGroupSubMenu; + QActionGroup *m_assignActionGroup; + QAction *m_assignToGroupSubMenuAction; + QMenu *m_currentGroupSubMenu; + QAction *m_currentGroupSubMenuAction; + + QAction *m_createGroupAction; + QAction *m_preferredEditAction; + QAction *m_removeFromGroupAction; +}; + +// Task menu extension of a QCommandLinkButton +class CommandLinkButtonTaskMenu: public ButtonTaskMenu +{ + Q_OBJECT + Q_DISABLE_COPY(CommandLinkButtonTaskMenu) +public: + explicit CommandLinkButtonTaskMenu(QCommandLinkButton *button, QObject *parent = 0); +}; + +typedef ExtensionFactory ButtonGroupTaskMenuFactory; +typedef ExtensionFactory CommandLinkButtonTaskMenuFactory; +typedef ExtensionFactory ButtonTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // BUTTON_TASKMENU_H diff --git a/src/designer/components/taskmenu/combobox_taskmenu.cpp b/src/designer/components/taskmenu/combobox_taskmenu.cpp new file mode 100644 index 000000000..8e8759b1a --- /dev/null +++ b/src/designer/components/taskmenu/combobox_taskmenu.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "combobox_taskmenu.h" +#include "listwidgeteditor.h" +#include "qdesigner_utils_p.h" +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +ComboBoxTaskMenu::ComboBoxTaskMenu(QComboBox *button, QObject *parent) + : QDesignerTaskMenu(button, parent), + m_comboBox(button) +{ + m_editItemsAction = new QAction(this); + m_editItemsAction->setText(tr("Edit Items...")); + connect(m_editItemsAction, SIGNAL(triggered()), this, SLOT(editItems())); + m_taskActions.append(m_editItemsAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +ComboBoxTaskMenu::~ComboBoxTaskMenu() +{ +} + +QAction *ComboBoxTaskMenu::preferredEditAction() const +{ + return m_editItemsAction; +} + +QList ComboBoxTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void ComboBoxTaskMenu::editItems() +{ + m_formWindow = QDesignerFormWindowInterface::findFormWindow(m_comboBox); + if (m_formWindow.isNull()) + return; + + Q_ASSERT(m_comboBox != 0); + + ListWidgetEditor dlg(m_formWindow, m_comboBox->window()); + ListContents oldItems = dlg.fillContentsFromComboBox(m_comboBox); + if (dlg.exec() == QDialog::Accepted) { + ListContents items = dlg.contents(); + if (items != oldItems) { + ChangeListContentsCommand *cmd = new ChangeListContentsCommand(m_formWindow); + cmd->init(m_comboBox, oldItems, items); + cmd->setText(tr("Change Combobox Contents")); + m_formWindow->commandHistory()->push(cmd); + } + } +} + +ComboBoxTaskMenuFactory::ComboBoxTaskMenuFactory(const QString &iid, QExtensionManager *extensionManager) : + ExtensionFactory(iid, extensionManager) +{ +} + +QComboBox *ComboBoxTaskMenuFactory::checkObject(QObject *qObject) const +{ + QComboBox *combo = qobject_cast(qObject); + if (!combo) + return 0; + if (qobject_cast(combo)) + return 0; + return combo; +} + +void ComboBoxTaskMenu::updateSelection() +{ + if (m_editor) + m_editor->deleteLater(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/combobox_taskmenu.h b/src/designer/components/taskmenu/combobox_taskmenu.h new file mode 100644 index 000000000..2b7ac7633 --- /dev/null +++ b/src/designer/components/taskmenu/combobox_taskmenu.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMBOBOX_TASKMENU_H +#define COMBOBOX_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QLineEdit; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class ComboBoxTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit ComboBoxTaskMenu(QComboBox *button, + QObject *parent = 0); + virtual ~ComboBoxTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editItems(); + void updateSelection(); + +private: + QComboBox *m_comboBox; + QPointer m_formWindow; + QPointer m_editor; + mutable QList m_taskActions; + QAction *m_editItemsAction; +}; + +class ComboBoxTaskMenuFactory : public ExtensionFactory +{ +public: + explicit ComboBoxTaskMenuFactory(const QString &iid, QExtensionManager *extensionManager); + +private: + virtual QComboBox *checkObject(QObject *qObject) const; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // COMBOBOX_TASKMENU_H diff --git a/src/designer/components/taskmenu/containerwidget_taskmenu.cpp b/src/designer/components/taskmenu/containerwidget_taskmenu.cpp new file mode 100644 index 000000000..9da59e486 --- /dev/null +++ b/src/designer/components/taskmenu/containerwidget_taskmenu.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "containerwidget_taskmenu.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 { + +ContainerWidgetTaskMenu::ContainerWidgetTaskMenu(QWidget *widget, ContainerType type, QObject *parent) : + QDesignerTaskMenu(widget, parent), + m_type(type), + m_containerWidget(widget), + m_core(formWindow()->core()), + m_pagePromotionTaskMenu(new PromotionTaskMenu(0, PromotionTaskMenu::ModeSingleWidget, this)), + m_pageMenuAction(new QAction(this)), + m_pageMenu(new QMenu), + m_actionDeletePage(new QAction(tr("Delete"), this)) +{ + Q_ASSERT(m_core); + m_taskActions.append(createSeparator()); + + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); + + QAction *actionInsertPageAfter = new QAction(this); + connect(actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + // Empty Per-Page submenu, deletion and promotion. Updated on demand due to promotion state + switch (m_type) { + case WizardContainer: + case PageContainer: + m_taskActions.append(createSeparator()); // for the browse actions + break; + case MdiContainer: + break; + } + // submenu + m_pageMenuAction->setMenu(m_pageMenu); + m_taskActions.append(m_pageMenuAction); + // Insertion + switch (m_type) { + case WizardContainer: + case PageContainer: { // Before and after in a submenu + QAction *insertMenuAction = new QAction(tr("Insert"), this); + QMenu *insertMenu = new QMenu; + // before + QAction *actionInsertPage = new QAction(tr("Insert Page Before Current Page"), this); + connect(actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + insertMenu->addAction(actionInsertPage); + // after + actionInsertPageAfter->setText(tr("Insert Page After Current Page")); + insertMenu->addAction(actionInsertPageAfter); + + insertMenuAction->setMenu(insertMenu); + m_taskActions.append(insertMenuAction); + } + break; + case MdiContainer: // No concept of order + actionInsertPageAfter->setText(tr("Add Subwindow")); + m_taskActions.append(actionInsertPageAfter); + break; + } +} + +ContainerWidgetTaskMenu::~ContainerWidgetTaskMenu() +{ +} + +QAction *ContainerWidgetTaskMenu::preferredEditAction() const +{ + return 0; +} + +bool ContainerWidgetTaskMenu::canDeletePage() const +{ + switch (pageCount()) { + case 0: + return false; + case 1: + return m_type != PageContainer; // Do not delete last page of page-type container + default: + break; + } + return true; +} + +int ContainerWidgetTaskMenu::pageCount() const +{ + if (const QDesignerContainerExtension *ce = containerExtension()) + return ce->count(); + return 0; +} + +QString ContainerWidgetTaskMenu::pageMenuText(ContainerType ct, int index, int count) +{ + if (ct == MdiContainer) + return tr("Subwindow"); // No concept of order, same text everywhere + if (index < 0) + return tr("Page"); + return tr("Page %1 of %2").arg(index + 1).arg(count); +} + +QList ContainerWidgetTaskMenu::taskActions() const +{ + QList actions = QDesignerTaskMenu::taskActions(); + actions += m_taskActions; + // Update the page submenu, deletion and promotion. Updated on demand due to promotion state. + m_pageMenu->clear(); + m_pageMenu->addAction(m_actionDeletePage); + m_actionDeletePage->setEnabled(canDeletePage()); + const QDesignerContainerExtension *ce = containerExtension(); + const int index = ce->currentIndex(); + m_pageMenuAction->setText(pageMenuText(m_type, index, ce->count())); + if (index != -1) { // Has a page + m_pageMenuAction->setEnabled(true); + m_pagePromotionTaskMenu->setWidget(ce->widget(index)); + m_pagePromotionTaskMenu->addActions(PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::SuppressGlobalEdit, m_pageMenu); + } else { // No page + m_pageMenuAction->setEnabled(false); + } + + return actions; +} + +QDesignerFormWindowInterface *ContainerWidgetTaskMenu::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(m_containerWidget); +} + +QDesignerContainerExtension *ContainerWidgetTaskMenu::containerExtension() const +{ + QExtensionManager *mgr = m_core->extensionManager(); + return qt_extension(mgr, m_containerWidget); +} + +void ContainerWidgetTaskMenu::removeCurrentPage() +{ + if (QDesignerContainerExtension *c = containerExtension()) { + if (c->currentIndex() == -1) + return; + + QDesignerFormWindowInterface *fw = formWindow(); + DeleteContainerWidgetPageCommand *cmd = new DeleteContainerWidgetPageCommand(fw); + cmd->init(m_containerWidget, m_type); + fw->commandHistory()->push(cmd); + } +} + +void ContainerWidgetTaskMenu::addPage() +{ + if (containerExtension()) { + QDesignerFormWindowInterface *fw = formWindow(); + AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw); + cmd->init(m_containerWidget, m_type, AddContainerWidgetPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void ContainerWidgetTaskMenu::addPageAfter() +{ + if (containerExtension()) { + QDesignerFormWindowInterface *fw = formWindow(); + AddContainerWidgetPageCommand *cmd = new AddContainerWidgetPageCommand(fw); + cmd->init(m_containerWidget, m_type, AddContainerWidgetPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +// -------------- WizardContainerWidgetTaskMenu +WizardContainerWidgetTaskMenu::WizardContainerWidgetTaskMenu(QWizard *w, QObject *parent) : + ContainerWidgetTaskMenu(w, WizardContainer, parent), + m_nextAction(new QAction(tr("Next"), this)), + m_previousAction(new QAction(tr("Back"), this)) +{ + connect(m_nextAction, SIGNAL(triggered()), w, SLOT(next())); + connect(m_previousAction, SIGNAL(triggered()), w, SLOT(back())); + QList &l = containerActions(); + l.push_front(createSeparator()); + l.push_front(m_nextAction); + l.push_front(m_previousAction); + l.push_front(createSeparator()); +} + +QList WizardContainerWidgetTaskMenu::taskActions() const +{ + // Enable + const QDesignerContainerExtension *ce = containerExtension(); + const int index = ce->currentIndex(); + m_previousAction->setEnabled(index > 0); + m_nextAction->setEnabled(index >= 0 && index < (ce->count() - 1)); + return ContainerWidgetTaskMenu::taskActions(); +} + +// -------------- MdiContainerWidgetTaskMenu + +MdiContainerWidgetTaskMenu::MdiContainerWidgetTaskMenu(QMdiArea *m, QObject *parent) : + ContainerWidgetTaskMenu(m, MdiContainer, parent) +{ + initializeActions(); + connect(m_nextAction, SIGNAL(triggered()), m, SLOT(activateNextSubWindow())); + connect(m_previousAction, SIGNAL(triggered()), m , SLOT(activatePreviousSubWindow())); + connect(m_tileAction, SIGNAL(triggered()), m, SLOT(tileSubWindows())); + connect(m_cascadeAction, SIGNAL(triggered()), m, SLOT(cascadeSubWindows())); +} + +MdiContainerWidgetTaskMenu::MdiContainerWidgetTaskMenu(QWorkspace *m, QObject *parent) : + ContainerWidgetTaskMenu(m, MdiContainer, parent) +{ + initializeActions(); + connect(m_nextAction, SIGNAL(triggered()), m, SLOT(activateNextWindow())); + connect(m_previousAction, SIGNAL(triggered()), m, SLOT(activatePreviousWindow())); + connect(m_tileAction, SIGNAL(triggered()),m , SLOT(tile())); + connect(m_cascadeAction, SIGNAL(triggered()), m, SLOT(cascade())); +} + +void MdiContainerWidgetTaskMenu::initializeActions() +{ + m_nextAction =new QAction(tr("Next Subwindow"), this); + m_previousAction = new QAction(tr("Previous Subwindow"), this); + m_tileAction = new QAction(tr("Tile"), this); + m_cascadeAction = new QAction(tr("Cascade"), this); + + QList &l = containerActions(); + l.push_front(createSeparator()); + l.push_front(m_tileAction); + l.push_front(m_cascadeAction); + l.push_front(m_previousAction); + l.push_front(m_nextAction); + l.push_front(createSeparator()); +} + +QList MdiContainerWidgetTaskMenu::taskActions() const +{ + const QList rc = ContainerWidgetTaskMenu::taskActions(); + // Enable + const int count = pageCount(); + m_nextAction->setEnabled(count > 1); + m_previousAction->setEnabled(count > 1); + m_tileAction->setEnabled(count); + m_cascadeAction->setEnabled(count); + return rc; +} + +// -------------- ContainerWidgetTaskMenuFactory + +ContainerWidgetTaskMenuFactory::ContainerWidgetTaskMenuFactory(QDesignerFormEditorInterface *core, QExtensionManager *extensionManager) : + QExtensionFactory(extensionManager), + m_core(core) +{ +} + +QObject *ContainerWidgetTaskMenuFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const +{ + if (iid != QLatin1String("QDesignerInternalTaskMenuExtension") || !object->isWidgetType()) + return 0; + + QWidget *widget = qobject_cast(object); + + if (qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget)) { + // Are we using Designer's own container extensions and task menus or did + // someone provide an extra one with an addpage method, for example for a QScrollArea? + if (const WidgetDataBase *wb = qobject_cast(m_core->widgetDataBase())) { + const int idx = wb->indexOfObject(widget); + const WidgetDataBaseItem *item = static_cast(wb->item(idx)); + if (item->addPageMethod().isEmpty()) + return 0; + } + } + + if (qt_extension(extensionManager(), object) == 0) + return 0; + + if (QMdiArea* ma = qobject_cast(widget)) + return new MdiContainerWidgetTaskMenu(ma, parent); + if (QWorkspace *ws = qobject_cast(widget)) + return new MdiContainerWidgetTaskMenu(ws, parent); + if (QWizard *wz = qobject_cast(widget)) + return new WizardContainerWidgetTaskMenu(wz, parent); + return new ContainerWidgetTaskMenu(widget, PageContainer, parent); +} + +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/containerwidget_taskmenu.h b/src/designer/components/taskmenu/containerwidget_taskmenu.h new file mode 100644 index 000000000..99dfa342c --- /dev/null +++ b/src/designer/components/taskmenu/containerwidget_taskmenu.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTAINERWIDGER_TASKMENU_H +#define CONTAINERWIDGER_TASKMENU_H + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class QDesignerContainerExtension; +class QAction; +class QMdiArea; +class QWorkspace; +class QMenu; +class QWizard; + +namespace qdesigner_internal { + +class PromotionTaskMenu; + +// ContainerWidgetTaskMenu: Task menu for containers with extension + +class ContainerWidgetTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit ContainerWidgetTaskMenu(QWidget *widget, ContainerType type, QObject *parent = 0); + virtual ~ContainerWidgetTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + +protected: + QDesignerContainerExtension *containerExtension() const; + QList &containerActions() { return m_taskActions; } + int pageCount() const; + +private: + QDesignerFormWindowInterface *formWindow() const; + +private: + static QString pageMenuText(ContainerType ct, int index, int count); + bool canDeletePage() const; + + const ContainerType m_type; + QWidget *m_containerWidget; + QDesignerFormEditorInterface *m_core; + PromotionTaskMenu *m_pagePromotionTaskMenu; + QAction *m_pageMenuAction; + QMenu *m_pageMenu; + QList m_taskActions; + QAction *m_actionDeletePage; +}; + +// WizardContainerWidgetTaskMenu: Provide next/back since QWizard +// has modes in which the "Back" button is not visible. + +class WizardContainerWidgetTaskMenu : public ContainerWidgetTaskMenu { + Q_OBJECT +public: + explicit WizardContainerWidgetTaskMenu(QWizard *w, QObject *parent = 0); + + virtual QList taskActions() const; + +private: + QAction *m_nextAction; + QAction *m_previousAction; +}; + + +// MdiContainerWidgetTaskMenu: Provide tile/cascade for MDI containers in addition + +class MdiContainerWidgetTaskMenu : public ContainerWidgetTaskMenu { + Q_OBJECT +public: + explicit MdiContainerWidgetTaskMenu(QMdiArea *m, QObject *parent = 0); + explicit MdiContainerWidgetTaskMenu(QWorkspace *m, QObject *parent = 0); + + virtual QList taskActions() const; +private: + void initializeActions(); + + QAction *m_nextAction; + QAction *m_previousAction; + QAction *m_tileAction; + QAction *m_cascadeAction; +}; + +class ContainerWidgetTaskMenuFactory: public QExtensionFactory +{ + Q_OBJECT +public: + explicit ContainerWidgetTaskMenuFactory(QDesignerFormEditorInterface *core, QExtensionManager *extensionManager = 0); + +protected: + virtual QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const; + +private: + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CONTAINERWIDGER_TASKMENU_H diff --git a/src/designer/components/taskmenu/groupbox_taskmenu.cpp b/src/designer/components/taskmenu/groupbox_taskmenu.cpp new file mode 100644 index 000000000..3c63b96ae --- /dev/null +++ b/src/designer/components/taskmenu/groupbox_taskmenu.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "groupbox_taskmenu.h" +#include "inplace_editor.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// -------- GroupBoxTaskMenuInlineEditor +class GroupBoxTaskMenuInlineEditor : public TaskMenuInlineEditor +{ +public: + GroupBoxTaskMenuInlineEditor(QGroupBox *button, QObject *parent); + +protected: + virtual QRect editRectangle() const; +}; + +GroupBoxTaskMenuInlineEditor::GroupBoxTaskMenuInlineEditor(QGroupBox *w, QObject *parent) : + TaskMenuInlineEditor(w, ValidationSingleLine, QLatin1String("title"), parent) +{ +} + +QRect GroupBoxTaskMenuInlineEditor::editRectangle() const +{ + QWidget *w = widget(); + QStyleOption opt; // ## QStyleOptionGroupBox + opt.init(w); + return QRect(QPoint(), QSize(w->width(),20)); +} + +// --------------- GroupBoxTaskMenu + +GroupBoxTaskMenu::GroupBoxTaskMenu(QGroupBox *groupbox, QObject *parent) + : QDesignerTaskMenu(groupbox, parent), + m_editTitleAction(new QAction(tr("Change title..."), this)) + +{ + TaskMenuInlineEditor *editor = new GroupBoxTaskMenuInlineEditor(groupbox, this); + connect(m_editTitleAction, SIGNAL(triggered()), editor, SLOT(editText())); + m_taskActions.append(m_editTitleAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +QList GroupBoxTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +QAction *GroupBoxTaskMenu::preferredEditAction() const +{ + return m_editTitleAction; +} + +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/groupbox_taskmenu.h b/src/designer/components/taskmenu/groupbox_taskmenu.h new file mode 100644 index 000000000..8224e148f --- /dev/null +++ b/src/designer/components/taskmenu/groupbox_taskmenu.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GROUPBOX_TASKMENU_H +#define GROUPBOX_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +class InPlaceEditor; + +class GroupBoxTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit GroupBoxTaskMenu(QGroupBox *groupbox, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QAction *m_editTitleAction; + QList m_taskActions; +}; + +typedef ExtensionFactory GroupBoxTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // GROUPBOX_TASKMENU_H diff --git a/src/designer/components/taskmenu/inplace_editor.cpp b/src/designer/components/taskmenu/inplace_editor.cpp new file mode 100644 index 000000000..a6ca60011 --- /dev/null +++ b/src/designer/components/taskmenu/inplace_editor.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindow.h" +#include "inplace_editor.h" + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ----------------- InPlaceEditor + +InPlaceEditor::InPlaceEditor(QWidget *widget, + TextPropertyValidationMode validationMode, + QDesignerFormWindowInterface *fw, + const QString& text, + const QRect& r) : + TextPropertyEditor(widget, EmbeddingInPlace, validationMode), + m_InPlaceWidgetHelper(this, widget, fw) +{ + setAlignment(m_InPlaceWidgetHelper.alignment()); + setObjectName(QLatin1String("__qt__passive_m_editor")); + + setText(text); + selectAll(); + + setGeometry(QRect(widget->mapTo(widget->window(), r.topLeft()), r.size())); + setFocus(); + show(); + + connect(this, SIGNAL(editingFinished()),this, SLOT(close())); +} + + +// -------------- TaskMenuInlineEditor + +TaskMenuInlineEditor::TaskMenuInlineEditor(QWidget *w, TextPropertyValidationMode vm, + const QString &property, QObject *parent) : + QObject(parent), + m_vm(vm), + m_property(property), + m_widget(w), + m_managed(true) +{ +} + +void TaskMenuInlineEditor::editText() +{ + m_formWindow = QDesignerFormWindowInterface::findFormWindow(m_widget); + if (m_formWindow.isNull()) + return; + m_managed = m_formWindow->isManaged(m_widget); + // Close as soon as a different widget is selected + connect(m_formWindow, SIGNAL(selectionChanged()), this, SLOT(updateSelection())); + + // get old value + QDesignerFormEditorInterface *core = m_formWindow->core(); + const QDesignerPropertySheetExtension *sheet = qt_extension(core->extensionManager(), m_widget); + const int index = sheet->indexOf(m_property); + if (index == -1) + return; + m_value = qvariant_cast(sheet->property(index)); + const QString oldValue = m_value.value(); + + m_editor = new InPlaceEditor(m_widget, m_vm, m_formWindow, oldValue, editRectangle()); + connect(m_editor, SIGNAL(textChanged(QString)), this, SLOT(updateText(QString))); +} + +void TaskMenuInlineEditor::updateText(const QString &text) +{ + // In the [rare] event we are invoked on an unmanaged widget, + // do not use the cursor selection + m_value.setValue(text); + if (m_managed) { + m_formWindow->cursor()->setProperty(m_property, QVariant::fromValue(m_value)); + } else { + m_formWindow->cursor()->setWidgetProperty(m_widget, m_property, QVariant::fromValue(m_value)); + } +} + +void TaskMenuInlineEditor::updateSelection() +{ + if (m_editor) + m_editor->deleteLater(); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/inplace_editor.h b/src/designer/components/taskmenu/inplace_editor.h new file mode 100644 index 000000000..10934491e --- /dev/null +++ b/src/designer/components/taskmenu/inplace_editor.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INPLACE_EDITOR_H +#define INPLACE_EDITOR_H + +#include +#include + +#include "inplace_widget_helper.h" +#include + +#include + +QT_BEGIN_NAMESPACE + + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class InPlaceEditor: public TextPropertyEditor +{ + Q_OBJECT +public: + InPlaceEditor(QWidget *widget, + TextPropertyValidationMode validationMode, + QDesignerFormWindowInterface *fw, + const QString& text, + const QRect& r); +private: + InPlaceWidgetHelper m_InPlaceWidgetHelper; +}; + +// Base class for inline editor helpers to be embedded into a task menu. +// Inline-edits a property on a multi-selection. +// To use it for a particular widget/property, overwrite the method +// returning the edit area. + +class TaskMenuInlineEditor : public QObject { + TaskMenuInlineEditor(const TaskMenuInlineEditor&); + TaskMenuInlineEditor &operator=(const TaskMenuInlineEditor&); + Q_OBJECT + +public slots: + void editText(); + +private slots: + void updateText(const QString &text); + void updateSelection(); + +protected: + TaskMenuInlineEditor(QWidget *w, TextPropertyValidationMode vm, const QString &property, QObject *parent); + // Overwrite to return the area for the inline editor. + virtual QRect editRectangle() const = 0; + QWidget *widget() const { return m_widget; } + +private: + const TextPropertyValidationMode m_vm; + const QString m_property; + QWidget *m_widget; + QPointer m_formWindow; + QPointer m_editor; + bool m_managed; + qdesigner_internal::PropertySheetStringValue m_value; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // INPLACE_EDITOR_H diff --git a/src/designer/components/taskmenu/inplace_widget_helper.cpp b/src/designer/components/taskmenu/inplace_widget_helper.cpp new file mode 100644 index 000000000..c7f63231b --- /dev/null +++ b/src/designer/components/taskmenu/inplace_widget_helper.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractformwindow.h" +#include "inplace_widget_helper.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + InPlaceWidgetHelper::InPlaceWidgetHelper(QWidget *editorWidget, QWidget *parentWidget, QDesignerFormWindowInterface *fw) + : QObject(0), + m_editorWidget(editorWidget), + m_parentWidget(parentWidget), + m_noChildEvent(m_parentWidget->testAttribute(Qt::WA_NoChildEventsForParent)) + { + m_editorWidget->setAttribute(Qt::WA_DeleteOnClose); + m_editorWidget->setParent(m_parentWidget->window()); + m_parentWidget->installEventFilter(this); + m_editorWidget->installEventFilter(this); + connect(m_editorWidget, SIGNAL(destroyed()), fw->mainContainer(), SLOT(setFocus())); + } + + InPlaceWidgetHelper::~InPlaceWidgetHelper() + { + if (m_parentWidget) + m_parentWidget->setAttribute(Qt::WA_NoChildEventsForParent, m_noChildEvent); + } + + Qt::Alignment InPlaceWidgetHelper::alignment() const { + if (m_parentWidget->metaObject()->indexOfProperty("alignment") != -1) + return Qt::Alignment(m_parentWidget->property("alignment").toInt()); + + if (qobject_cast(m_parentWidget) + || qobject_cast(m_parentWidget) /* tool needs to be more complex */) + return Qt::AlignHCenter; + + return Qt::AlignJustify; + } + + + bool InPlaceWidgetHelper::eventFilter(QObject *object, QEvent *e) + { + if (object == m_parentWidget) { + if (e->type() == QEvent::Resize) { + const QResizeEvent *event = static_cast(e); + const QPoint localPos = m_parentWidget->geometry().topLeft(); + const QPoint globalPos = m_parentWidget->parentWidget() ? m_parentWidget->parentWidget()->mapToGlobal(localPos) : localPos; + const QPoint newPos = (m_editorWidget->parentWidget() ? m_editorWidget->parentWidget()->mapFromGlobal(globalPos) : globalPos) + + m_posOffset; + const QSize newSize = event->size() + m_sizeOffset; + m_editorWidget->setGeometry(QRect(newPos, newSize)); + } + } else if (object == m_editorWidget) { + if (e->type() == QEvent::ShortcutOverride) { + if (static_cast(e)->key() == Qt::Key_Escape) { + e->accept(); + return false; + } + } else if (e->type() == QEvent::KeyPress) { + if (static_cast(e)->key() == Qt::Key_Escape) { + e->accept(); + m_editorWidget->close(); + return true; + } + } else if (e->type() == QEvent::Show) { + const QPoint localPos = m_parentWidget->geometry().topLeft(); + const QPoint globalPos = m_parentWidget->parentWidget() ? m_parentWidget->parentWidget()->mapToGlobal(localPos) : localPos; + const QPoint newPos = m_editorWidget->parentWidget() ? m_editorWidget->parentWidget()->mapFromGlobal(globalPos) : globalPos; + m_posOffset = m_editorWidget->geometry().topLeft() - newPos; + m_sizeOffset = m_editorWidget->size() - m_parentWidget->size(); + } + } + + return QObject::eventFilter(object, e); + } +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/inplace_widget_helper.h b/src/designer/components/taskmenu/inplace_widget_helper.h new file mode 100644 index 000000000..2c1f32954 --- /dev/null +++ b/src/designer/components/taskmenu/inplace_widget_helper.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INPLACE_WIDGETHELPER_H +#define INPLACE_WIDGETHELPER_H + + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + + // A helper class to make an editor widget suitable for form inline + // editing. Derive from the editor widget class and make InPlaceWidgetHelper a member. + // + // Sets "destructive close" on the editor widget and + // wires "ESC" to it. + // Installs an event filter on the parent to listen for + // resize events and passes them on to the child. + // You might want to connect editingFinished() to close() of the editor widget. + class InPlaceWidgetHelper: public QObject + { + Q_OBJECT + public: + InPlaceWidgetHelper(QWidget *editorWidget, QWidget *parentWidget, QDesignerFormWindowInterface *fw); + virtual ~InPlaceWidgetHelper(); + + virtual bool eventFilter(QObject *object, QEvent *event); + + // returns a recommended alignment for the editor widget determined from the parent. + Qt::Alignment alignment() const; + private: + QWidget *m_editorWidget; + QPointer m_parentWidget; + const bool m_noChildEvent; + QPoint m_posOffset; + QSize m_sizeOffset; + }; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // INPLACE_WIDGETHELPER_H diff --git a/src/designer/components/taskmenu/itemlisteditor.cpp b/src/designer/components/taskmenu/itemlisteditor.cpp new file mode 100644 index 000000000..787b4f424 --- /dev/null +++ b/src/designer/components/taskmenu/itemlisteditor.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemlisteditor.h" +#include +#include +#include +#include + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class ItemPropertyBrowser : public QtTreePropertyBrowser +{ +public: + ItemPropertyBrowser() + { + setResizeMode(Interactive); + //: Sample string to determinate the width for the first column of the list item property browser + const QString widthSampleString = QCoreApplication::translate("ItemPropertyBrowser", "XX Icon Selected off"); + m_width = fontMetrics().width(widthSampleString); + setSplitterPosition(m_width); + m_width += fontMetrics().width(QLatin1String("/this/is/some/random/path")); + } + + virtual QSize sizeHint() const + { + return QSize(m_width, 1); + } + +private: + int m_width; +}; + +////////////////// Item editor /////////////// +AbstractItemEditor::AbstractItemEditor(QDesignerFormWindowInterface *form, QWidget *parent) + : QWidget(parent), + m_iconCache(qobject_cast(form)->iconCache()), + m_updatingBrowser(false) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_propertyManager = new DesignerPropertyManager(form->core(), this); + m_editorFactory = new DesignerEditorFactory(form->core(), this); + m_editorFactory->setSpacing(0); + m_propertyBrowser = new ItemPropertyBrowser; + m_propertyBrowser->setFactoryForManager((QtVariantPropertyManager *)m_propertyManager, + m_editorFactory); + + connect(m_editorFactory, SIGNAL(resetProperty(QtProperty*)), + SLOT(resetProperty(QtProperty*))); + connect(m_propertyManager, SIGNAL(valueChanged(QtProperty*,QVariant,bool)), + SLOT(propertyChanged(QtProperty*))); + connect(iconCache(), SIGNAL(reloaded()), this, SLOT(cacheReloaded())); +} + +AbstractItemEditor::~AbstractItemEditor() +{ + m_propertyBrowser->unsetFactoryForManager(m_propertyManager); +} + +static const char * const itemFlagNames[] = { + QT_TRANSLATE_NOOP("AbstractItemEditor", "Selectable"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "Editable"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "DragEnabled"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "DropEnabled"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "UserCheckable"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "Enabled"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "Tristate"), + 0 +}; + +static const char * const checkStateNames[] = { + QT_TRANSLATE_NOOP("AbstractItemEditor", "Unchecked"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "PartiallyChecked"), + QT_TRANSLATE_NOOP("AbstractItemEditor", "Checked"), + 0 +}; + +static QStringList c2qStringList(const char * const in[]) +{ + QStringList out; + for (int i = 0; in[i]; i++) + out << AbstractItemEditor::tr(in[i]); + return out; +} + +void AbstractItemEditor::setupProperties(PropertyDefinition *propList) +{ + for (int i = 0; propList[i].name; i++) { + int type = propList[i].typeFunc ? propList[i].typeFunc() : propList[i].type; + int role = propList[i].role; + QtVariantProperty *prop = m_propertyManager->addProperty(type, QLatin1String(propList[i].name)); + Q_ASSERT(prop); + if (role == Qt::ToolTipPropertyRole || role == Qt::WhatsThisPropertyRole) + prop->setAttribute(QLatin1String("validationMode"), ValidationRichText); + else if (role == Qt::DisplayPropertyRole) + prop->setAttribute(QLatin1String("validationMode"), ValidationMultiLine); + else if (role == Qt::StatusTipPropertyRole) + prop->setAttribute(QLatin1String("validationMode"), ValidationSingleLine); + else if (role == ItemFlagsShadowRole) + prop->setAttribute(QLatin1String("flagNames"), c2qStringList(itemFlagNames)); + else if (role == Qt::CheckStateRole) + prop->setAttribute(QLatin1String("enumNames"), c2qStringList(checkStateNames)); + prop->setAttribute(QLatin1String("resettable"), true); + m_properties.append(prop); + m_rootProperties.append(prop); + m_propertyToRole.insert(prop, role); + } +} + +void AbstractItemEditor::setupObject(QWidget *object) +{ + m_propertyManager->setObject(object); + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(object); + FormWindowBase *fwb = qobject_cast(formWindow); + m_editorFactory->setFormWindowBase(fwb); +} + +void AbstractItemEditor::setupEditor(QWidget *object, PropertyDefinition *propList) +{ + setupProperties(propList); + setupObject(object); +} + +void AbstractItemEditor::propertyChanged(QtProperty *property) +{ + if (m_updatingBrowser) + return; + + + BoolBlocker block(m_updatingBrowser); + QtVariantProperty *prop = m_propertyManager->variantProperty(property); + int role; + if ((role = m_propertyToRole.value(prop, -1)) == -1) + // Subproperty + return; + + if ((role == ItemFlagsShadowRole && prop->value().toInt() == (int)QListWidgetItem().flags()) + || (role == Qt::DecorationPropertyRole && !qvariant_cast(prop->value()).mask()) + || (role == Qt::FontRole && !qvariant_cast(prop->value()).resolve())) { + prop->setModified(false); + setItemData(role, QVariant()); + } else { + prop->setModified(true); + setItemData(role, prop->value()); + } + + switch (role) { + case Qt::DecorationPropertyRole: + setItemData(Qt::DecorationRole, QVariant::fromValue(iconCache()->icon(qvariant_cast(prop->value())))); + break; + case Qt::DisplayPropertyRole: + setItemData(Qt::EditRole, QVariant::fromValue(qvariant_cast(prop->value()).value())); + break; + case Qt::ToolTipPropertyRole: + setItemData(Qt::ToolTipRole, QVariant::fromValue(qvariant_cast(prop->value()).value())); + break; + case Qt::StatusTipPropertyRole: + setItemData(Qt::StatusTipRole, QVariant::fromValue(qvariant_cast(prop->value()).value())); + break; + case Qt::WhatsThisPropertyRole: + setItemData(Qt::WhatsThisRole, QVariant::fromValue(qvariant_cast(prop->value()).value())); + break; + default: + break; + } + + prop->setValue(getItemData(role)); +} + +void AbstractItemEditor::resetProperty(QtProperty *property) +{ + if (m_propertyManager->resetFontSubProperty(property)) + return; + + if (m_propertyManager->resetIconSubProperty(property)) + return; + + BoolBlocker block(m_updatingBrowser); + + QtVariantProperty *prop = m_propertyManager->variantProperty(property); + int role = m_propertyToRole.value(prop); + if (role == ItemFlagsShadowRole) + prop->setValue(QVariant::fromValue((int)QListWidgetItem().flags())); + else + prop->setValue(QVariant(prop->valueType(), (void *)0)); + prop->setModified(false); + + setItemData(role, QVariant()); + if (role == Qt::DecorationPropertyRole) + setItemData(Qt::DecorationRole, QVariant::fromValue(QIcon())); + if (role == Qt::DisplayPropertyRole) + setItemData(Qt::EditRole, QVariant::fromValue(QString())); + if (role == Qt::ToolTipPropertyRole) + setItemData(Qt::ToolTipRole, QVariant::fromValue(QString())); + if (role == Qt::StatusTipPropertyRole) + setItemData(Qt::StatusTipRole, QVariant::fromValue(QString())); + if (role == Qt::WhatsThisPropertyRole) + setItemData(Qt::WhatsThisRole, QVariant::fromValue(QString())); +} + +void AbstractItemEditor::cacheReloaded() +{ + BoolBlocker block(m_updatingBrowser); + m_propertyManager->reloadResourceProperties(); +} + +void AbstractItemEditor::updateBrowser() +{ + BoolBlocker block(m_updatingBrowser); + foreach (QtVariantProperty *prop, m_properties) { + int role = m_propertyToRole.value(prop); + QVariant val = getItemData(role); + if (!val.isValid()) { + if (role == ItemFlagsShadowRole) + val = QVariant::fromValue((int)QListWidgetItem().flags()); + else + val = QVariant((int)prop->value().userType(), (void *)0); + prop->setModified(false); + } else { + prop->setModified(true); + } + prop->setValue(val); + } + + if (m_propertyBrowser->topLevelItems().isEmpty()) + foreach (QtVariantProperty *prop, m_rootProperties) + m_propertyBrowser->addProperty(prop); +} + +void AbstractItemEditor::injectPropertyBrowser(QWidget *parent, QWidget *widget) +{ + // It is impossible to design a splitter with just one widget, so we do it by hand. + m_propertySplitter = new QSplitter; + m_propertySplitter->addWidget(widget); + m_propertySplitter->addWidget(m_propertyBrowser); + m_propertySplitter->setStretchFactor(0, 1); + m_propertySplitter->setStretchFactor(1, 0); + parent->layout()->addWidget(m_propertySplitter); +} + +////////////////// List editor /////////////// +ItemListEditor::ItemListEditor(QDesignerFormWindowInterface *form, QWidget *parent) + : AbstractItemEditor(form, parent), + m_updating(false) +{ + ui.setupUi(this); + + injectPropertyBrowser(this, ui.widget); + connect(ui.showPropertiesButton, SIGNAL(clicked()), + this, SLOT(togglePropertyBrowser())); + setPropertyBrowserVisible(false); + + QIcon upIcon = createIconSet(QString::fromUtf8("up.png")); + QIcon downIcon = createIconSet(QString::fromUtf8("down.png")); + QIcon minusIcon = createIconSet(QString::fromUtf8("minus.png")); + QIcon plusIcon = createIconSet(QString::fromUtf8("plus.png")); + ui.moveListItemUpButton->setIcon(upIcon); + ui.moveListItemDownButton->setIcon(downIcon); + ui.newListItemButton->setIcon(plusIcon); + ui.deleteListItemButton->setIcon(minusIcon); + + connect(iconCache(), SIGNAL(reloaded()), this, SLOT(cacheReloaded())); +} + +void ItemListEditor::setupEditor(QWidget *object, PropertyDefinition *propList) +{ + AbstractItemEditor::setupEditor(object, propList); + + if (ui.listWidget->count() > 0) + ui.listWidget->setCurrentRow(0); + else + updateEditor(); +} + +void ItemListEditor::setCurrentIndex(int idx) +{ + m_updating = true; + ui.listWidget->setCurrentRow(idx); + m_updating = false; +} + +void ItemListEditor::on_newListItemButton_clicked() +{ + int row = ui.listWidget->currentRow() + 1; + + QListWidgetItem *item = new QListWidgetItem(m_newItemText); + item->setData(Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(m_newItemText))); + item->setFlags(item->flags() | Qt::ItemIsEditable); + if (row < ui.listWidget->count()) + ui.listWidget->insertItem(row, item); + else + ui.listWidget->addItem(item); + emit itemInserted(row); + + ui.listWidget->setCurrentItem(item); + ui.listWidget->editItem(item); +} + +void ItemListEditor::on_deleteListItemButton_clicked() +{ + int row = ui.listWidget->currentRow(); + + if (row != -1) { + delete ui.listWidget->takeItem(row); + emit itemDeleted(row); + } + + if (row == ui.listWidget->count()) + row--; + if (row < 0) + updateEditor(); + else + ui.listWidget->setCurrentRow(row); +} + +void ItemListEditor::on_moveListItemUpButton_clicked() +{ + int row = ui.listWidget->currentRow(); + if (row <= 0) + return; // nothing to do + + ui.listWidget->insertItem(row - 1, ui.listWidget->takeItem(row)); + ui.listWidget->setCurrentRow(row - 1); + emit itemMovedUp(row); +} + +void ItemListEditor::on_moveListItemDownButton_clicked() +{ + int row = ui.listWidget->currentRow(); + if (row == -1 || row == ui.listWidget->count() - 1) + return; // nothing to do + + ui.listWidget->insertItem(row + 1, ui.listWidget->takeItem(row)); + ui.listWidget->setCurrentRow(row + 1); + emit itemMovedDown(row); +} + +void ItemListEditor::on_listWidget_currentRowChanged() +{ + updateEditor(); + if (!m_updating) + emit indexChanged(ui.listWidget->currentRow()); +} + +void ItemListEditor::on_listWidget_itemChanged(QListWidgetItem *item) +{ + if (m_updatingBrowser) + return; + + PropertySheetStringValue val = qvariant_cast(item->data(Qt::DisplayPropertyRole)); + val.setValue(item->text()); + BoolBlocker block(m_updatingBrowser); + item->setData(Qt::DisplayPropertyRole, QVariant::fromValue(val)); + + // The checkState could change, too, but if this signal is connected, + // checkState is not in the list anyway, as we are editing a header item. + emit itemChanged(ui.listWidget->currentRow(), Qt::DisplayPropertyRole, + QVariant::fromValue(val)); + updateBrowser(); +} + +void ItemListEditor::togglePropertyBrowser() +{ + setPropertyBrowserVisible(!m_propertyBrowser->isVisible()); +} + +void ItemListEditor::setPropertyBrowserVisible(bool v) +{ + ui.showPropertiesButton->setText(v ? tr("Properties &>>") : tr("Properties &<<")); + m_propertyBrowser->setVisible(v); +} + +void ItemListEditor::setItemData(int role, const QVariant &v) +{ + QListWidgetItem *item = ui.listWidget->currentItem(); + bool reLayout = false; + if ((role == Qt::EditRole && (v.toString().count(QLatin1Char('\n')) != item->data(role).toString().count(QLatin1Char('\n')))) + || role == Qt::FontRole) + reLayout = true; + QVariant newValue = v; + if (role == Qt::FontRole && newValue.type() == QVariant::Font) { + QFont oldFont = ui.listWidget->font(); + QFont newFont = qvariant_cast(newValue).resolve(oldFont); + newValue = QVariant::fromValue(newFont); + item->setData(role, QVariant()); // force the right font with the current resolve mask is set (item view bug) + } + item->setData(role, newValue); + if (reLayout) + ui.listWidget->doItemsLayout(); + emit itemChanged(ui.listWidget->currentRow(), role, newValue); +} + +QVariant ItemListEditor::getItemData(int role) const +{ + return ui.listWidget->currentItem()->data(role); +} + +void ItemListEditor::cacheReloaded() +{ + reloadIconResources(iconCache(), ui.listWidget); +} + +void ItemListEditor::updateEditor() +{ + bool currentItemEnabled = false; + + bool moveRowUpEnabled = false; + bool moveRowDownEnabled = false; + + QListWidgetItem *item = ui.listWidget->currentItem(); + if (item) { + currentItemEnabled = true; + int currentRow = ui.listWidget->currentRow(); + if (currentRow > 0) + moveRowUpEnabled = true; + if (currentRow < ui.listWidget->count() - 1) + moveRowDownEnabled = true; + } + + ui.moveListItemUpButton->setEnabled(moveRowUpEnabled); + ui.moveListItemDownButton->setEnabled(moveRowDownEnabled); + ui.deleteListItemButton->setEnabled(currentItemEnabled); + + if (item) + updateBrowser(); + else + m_propertyBrowser->clear(); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/itemlisteditor.h b/src/designer/components/taskmenu/itemlisteditor.h new file mode 100644 index 000000000..96c81f5ed --- /dev/null +++ b/src/designer/components/taskmenu/itemlisteditor.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ITEMLISTEDITOR_H +#define ITEMLISTEDITOR_H + +#include "ui_itemlisteditor.h" + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QtProperty; +class QtVariantProperty; +class QtTreePropertyBrowser; +class QSplitter; +class QVBoxLayout; + +namespace qdesigner_internal { + +class DesignerIconCache; +class DesignerPropertyManager; +class DesignerEditorFactory; + +// Utility class that ensures a bool is true while in scope. +// Courtesy of QBoolBlocker in qobject_p.h +class BoolBlocker +{ +public: + inline BoolBlocker(bool &b):block(b), reset(b){block = true;} + inline ~BoolBlocker(){block = reset; } +private: + bool █ + bool reset; +}; + +class AbstractItemEditor: public QWidget +{ + Q_OBJECT + +public: + explicit AbstractItemEditor(QDesignerFormWindowInterface *form, QWidget *parent); + ~AbstractItemEditor(); + + DesignerIconCache *iconCache() const { return m_iconCache; } + + struct PropertyDefinition { + int role; + int type; + int (*typeFunc)(); + const char *name; + }; + +private slots: + void propertyChanged(QtProperty *property); + void resetProperty(QtProperty *property); + void cacheReloaded(); + +protected: + void setupProperties(PropertyDefinition *propDefs); + void setupObject(QWidget *object); + void setupEditor(QWidget *object, PropertyDefinition *propDefs); + void injectPropertyBrowser(QWidget *parent, QWidget *widget); + void updateBrowser(); + virtual void setItemData(int role, const QVariant &v) = 0; + virtual QVariant getItemData(int role) const = 0; + + DesignerIconCache *m_iconCache; + DesignerPropertyManager *m_propertyManager; + DesignerEditorFactory *m_editorFactory; + QSplitter *m_propertySplitter; + QtTreePropertyBrowser *m_propertyBrowser; + QList m_properties; + QList m_rootProperties; + QHash m_propertyToRole; + bool m_updatingBrowser; +}; + +class ItemListEditor: public AbstractItemEditor +{ + Q_OBJECT + +public: + explicit ItemListEditor(QDesignerFormWindowInterface *form, QWidget *parent); + + void setupEditor(QWidget *object, PropertyDefinition *propDefs); + QListWidget *listWidget() const { return ui.listWidget; } + void setNewItemText(const QString &tpl) { m_newItemText = tpl; } + QString newItemText() const { return m_newItemText; } + void setCurrentIndex(int idx); + +signals: + void indexChanged(int idx); + void itemChanged(int idx, int role, const QVariant &v); + void itemInserted(int idx); + void itemDeleted(int idx); + void itemMovedUp(int idx); + void itemMovedDown(int idx); + +private slots: + void on_newListItemButton_clicked(); + void on_deleteListItemButton_clicked(); + void on_moveListItemUpButton_clicked(); + void on_moveListItemDownButton_clicked(); + void on_listWidget_currentRowChanged(); + void on_listWidget_itemChanged(QListWidgetItem * item); + void togglePropertyBrowser(); + void cacheReloaded(); + +protected: + virtual void setItemData(int role, const QVariant &v); + virtual QVariant getItemData(int role) const; + +private: + void setPropertyBrowserVisible(bool v); + void updateEditor(); + Ui::ItemListEditor ui; + bool m_updating; + QString m_newItemText; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ITEMLISTEDITOR_H diff --git a/src/designer/components/taskmenu/itemlisteditor.ui b/src/designer/components/taskmenu/itemlisteditor.ui new file mode 100644 index 000000000..89d61dbdb --- /dev/null +++ b/src/designer/components/taskmenu/itemlisteditor.ui @@ -0,0 +1,156 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::ItemListEditor + + + + 0 + 0 + 550 + 360 + + + + + + + + + + + 0 + + + + + true + + + Items List + + + + + + + + + New Item + + + &New + + + + + + + Delete Item + + + &Delete + + + + + + + Qt::Horizontal + + + + 16 + 10 + + + + + + + + Move Item Up + + + U + + + + + + + Move Item Down + + + D + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Properties &>> + + + + + + + + + + + + + diff --git a/src/designer/components/taskmenu/label_taskmenu.cpp b/src/designer/components/taskmenu/label_taskmenu.cpp new file mode 100644 index 000000000..82a08e4ba --- /dev/null +++ b/src/designer/components/taskmenu/label_taskmenu.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "label_taskmenu.h" +#include "inplace_editor.h" + +#include + +#include +#include +#include +#include + +static const char *textPropertyC = "text"; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// -------- LabelTaskMenuInlineEditor +class LabelTaskMenuInlineEditor : public TaskMenuInlineEditor +{ +public: + LabelTaskMenuInlineEditor(QLabel *button, QObject *parent); + +protected: + virtual QRect editRectangle() const; +}; + +LabelTaskMenuInlineEditor::LabelTaskMenuInlineEditor(QLabel *w, QObject *parent) : + TaskMenuInlineEditor(w, ValidationRichText, QLatin1String(textPropertyC), parent) +{ +} + +QRect LabelTaskMenuInlineEditor::editRectangle() const +{ + QStyleOptionButton opt; + opt.init(widget()); + return opt.rect; +} + +// --------------- LabelTaskMenu + +LabelTaskMenu::LabelTaskMenu(QLabel *label, QObject *parent) + : QDesignerTaskMenu(label, parent), + m_label(label), + m_editRichTextAction(new QAction(tr("Change rich text..."), this)), + m_editPlainTextAction(new QAction(tr("Change plain text..."), this)) +{ + LabelTaskMenuInlineEditor *editor = new LabelTaskMenuInlineEditor(label, this); + connect(m_editPlainTextAction, SIGNAL(triggered()), editor, SLOT(editText())); + m_taskActions.append(m_editPlainTextAction); + + connect(m_editRichTextAction, SIGNAL(triggered()), this, SLOT(editRichText())); + m_taskActions.append(m_editRichTextAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +QAction *LabelTaskMenu::preferredEditAction() const +{ + if (m_label->textFormat () == Qt::PlainText) return m_editPlainTextAction; + return Qt::mightBeRichText(m_label->text()) ? m_editRichTextAction : m_editPlainTextAction; +} + +QList LabelTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void LabelTaskMenu::editRichText() +{ + changeTextProperty(QLatin1String(textPropertyC), QString(), MultiSelectionMode, m_label->textFormat()); +} + +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/label_taskmenu.h b/src/designer/components/taskmenu/label_taskmenu.h new file mode 100644 index 000000000..3e54540aa --- /dev/null +++ b/src/designer/components/taskmenu/label_taskmenu.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LABEL_TASKMENU_H +#define LABEL_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class LabelTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit LabelTaskMenu(QLabel *button, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editRichText(); + +private: + QLabel *m_label; + QList m_taskActions; + QAction *m_editRichTextAction; + QAction *m_editPlainTextAction; +}; + +typedef ExtensionFactory LabelTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LABEL_TASKMENU_H diff --git a/src/designer/components/taskmenu/layouttaskmenu.cpp b/src/designer/components/taskmenu/layouttaskmenu.cpp new file mode 100644 index 000000000..92d5a150b --- /dev/null +++ b/src/designer/components/taskmenu/layouttaskmenu.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layouttaskmenu.h" +#include +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +// ------------ LayoutWidgetTaskMenu +LayoutWidgetTaskMenu::LayoutWidgetTaskMenu(QLayoutWidget *lw, QObject *parent) : + QObject(parent), + m_widget(lw), + m_morphMenu(new qdesigner_internal::MorphMenu(this)), + m_formLayoutMenu(new qdesigner_internal::FormLayoutMenu(this)) +{ +} + +QAction *LayoutWidgetTaskMenu::preferredEditAction() const +{ + return m_formLayoutMenu->preferredEditAction(m_widget, m_widget->formWindow()); +} + +QList LayoutWidgetTaskMenu::taskActions() const +{ + QList rc; + QDesignerFormWindowInterface *fw = m_widget->formWindow(); + m_morphMenu->populate(m_widget, fw, rc); + m_formLayoutMenu->populate(m_widget, fw, rc); + return rc; +} + +// ------------- SpacerTaskMenu +SpacerTaskMenu::SpacerTaskMenu(Spacer *, QObject *parent) : + QObject(parent) +{ +} + +QAction *SpacerTaskMenu::preferredEditAction() const +{ + return 0; +} + +QList SpacerTaskMenu::taskActions() const +{ + return QList(); +} + +QT_END_NAMESPACE + +#include diff --git a/src/designer/components/taskmenu/layouttaskmenu.h b/src/designer/components/taskmenu/layouttaskmenu.h new file mode 100644 index 000000000..de614c486 --- /dev/null +++ b/src/designer/components/taskmenu/layouttaskmenu.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LAYOUTTASKMENU_H +#define LAYOUTTASKMENU_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + class FormLayoutMenu; + class MorphMenu; +} + +// Morph menu for QLayoutWidget. +class LayoutWidgetTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit LayoutWidgetTaskMenu(QLayoutWidget *w, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QLayoutWidget *m_widget; + qdesigner_internal::MorphMenu *m_morphMenu; + qdesigner_internal::FormLayoutMenu *m_formLayoutMenu; +}; + +// Empty task menu for spacers. +class SpacerTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit SpacerTaskMenu(Spacer *bar, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +}; + +typedef qdesigner_internal::ExtensionFactory LayoutWidgetTaskMenuFactory; +typedef qdesigner_internal::ExtensionFactory SpacerTaskMenuFactory; + +QT_END_NAMESPACE + +#endif // LAYOUTTASKMENU_H diff --git a/src/designer/components/taskmenu/lineedit_taskmenu.cpp b/src/designer/components/taskmenu/lineedit_taskmenu.cpp new file mode 100644 index 000000000..4602a2aec --- /dev/null +++ b/src/designer/components/taskmenu/lineedit_taskmenu.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lineedit_taskmenu.h" +#include "inplace_editor.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// -------- LineEditTaskMenuInlineEditor +class LineEditTaskMenuInlineEditor : public TaskMenuInlineEditor +{ +public: + LineEditTaskMenuInlineEditor(QLineEdit *button, QObject *parent); + +protected: + virtual QRect editRectangle() const; +}; + +LineEditTaskMenuInlineEditor::LineEditTaskMenuInlineEditor(QLineEdit *w, QObject *parent) : + TaskMenuInlineEditor(w, ValidationSingleLine, QLatin1String("text"), parent) +{ +} + +QRect LineEditTaskMenuInlineEditor::editRectangle() const +{ + QStyleOption opt; + opt.init(widget()); + return opt.rect; +} + +// --------------- LineEditTaskMenu +LineEditTaskMenu::LineEditTaskMenu(QLineEdit *lineEdit, QObject *parent) : + QDesignerTaskMenu(lineEdit, parent), + m_editTextAction(new QAction(tr("Change text..."), this)) +{ + TaskMenuInlineEditor *editor = new LineEditTaskMenuInlineEditor(lineEdit, this); + connect(m_editTextAction, SIGNAL(triggered()), editor, SLOT(editText())); + m_taskActions.append(m_editTextAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +QAction *LineEditTaskMenu::preferredEditAction() const +{ + return m_editTextAction; +} + +QList LineEditTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/lineedit_taskmenu.h b/src/designer/components/taskmenu/lineedit_taskmenu.h new file mode 100644 index 000000000..28005da02 --- /dev/null +++ b/src/designer/components/taskmenu/lineedit_taskmenu.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINEEDIT_TASKMENU_H +#define LINEEDIT_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class LineEditTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit LineEditTaskMenu(QLineEdit *button, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QList m_taskActions; + QAction *m_editTextAction; +}; + +typedef ExtensionFactory LineEditTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LINEEDIT_TASKMENU_H diff --git a/src/designer/components/taskmenu/listwidget_taskmenu.cpp b/src/designer/components/taskmenu/listwidget_taskmenu.cpp new file mode 100644 index 000000000..37483b4a7 --- /dev/null +++ b/src/designer/components/taskmenu/listwidget_taskmenu.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "listwidget_taskmenu.h" +#include "listwidgeteditor.h" +#include "qdesigner_utils_p.h" +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +ListWidgetTaskMenu::ListWidgetTaskMenu(QListWidget *button, QObject *parent) + : QDesignerTaskMenu(button, parent), + m_listWidget(button) +{ + m_editItemsAction = new QAction(this); + m_editItemsAction->setText(tr("Edit Items...")); + connect(m_editItemsAction, SIGNAL(triggered()), this, SLOT(editItems())); + m_taskActions.append(m_editItemsAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +ListWidgetTaskMenu::~ListWidgetTaskMenu() +{ +} + +QAction *ListWidgetTaskMenu::preferredEditAction() const +{ + return m_editItemsAction; +} + +QList ListWidgetTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void ListWidgetTaskMenu::editItems() +{ + m_formWindow = QDesignerFormWindowInterface::findFormWindow(m_listWidget); + if (m_formWindow.isNull()) + return; + + Q_ASSERT(m_listWidget != 0); + + ListWidgetEditor dlg(m_formWindow, m_listWidget->window()); + ListContents oldItems = dlg.fillContentsFromListWidget(m_listWidget); + if (dlg.exec() == QDialog::Accepted) { + ListContents items = dlg.contents(); + if (items != oldItems) { + ChangeListContentsCommand *cmd = new ChangeListContentsCommand(m_formWindow); + cmd->init(m_listWidget, oldItems, items); + cmd->setText(tr("Change List Contents")); + m_formWindow->commandHistory()->push(cmd); + } + } +} + +void ListWidgetTaskMenu::updateSelection() +{ + if (m_editor) + m_editor->deleteLater(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/listwidget_taskmenu.h b/src/designer/components/taskmenu/listwidget_taskmenu.h new file mode 100644 index 000000000..6f7cbb40d --- /dev/null +++ b/src/designer/components/taskmenu/listwidget_taskmenu.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LISTWIDGET_TASKMENU_H +#define LISTWIDGET_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QLineEdit; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class ListWidgetTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit ListWidgetTaskMenu(QListWidget *button, QObject *parent = 0); + virtual ~ListWidgetTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editItems(); + void updateSelection(); + +private: + QListWidget *m_listWidget; + QPointer m_formWindow; + QPointer m_editor; + mutable QList m_taskActions; + QAction *m_editItemsAction; +}; + +typedef ExtensionFactory ListWidgetTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LISTWIDGET_TASKMENU_H diff --git a/src/designer/components/taskmenu/listwidgeteditor.cpp b/src/designer/components/taskmenu/listwidgeteditor.cpp new file mode 100644 index 000000000..28b87f78e --- /dev/null +++ b/src/designer/components/taskmenu/listwidgeteditor.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "listwidgeteditor.h" +#include +#include + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +ListWidgetEditor::ListWidgetEditor(QDesignerFormWindowInterface *form, + QWidget *parent) + : QDialog(parent) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QDialogButtonBox *buttonBox = new QDialogButtonBox; + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + connect(buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), SLOT(reject())); + + m_itemsEditor = new ItemListEditor(form, 0); + m_itemsEditor->layout()->setMargin(0); + m_itemsEditor->setNewItemText(tr("New Item")); + + QFrame *sep = new QFrame; + sep->setFrameStyle(QFrame::HLine | QFrame::Sunken); + + QBoxLayout *box = new QVBoxLayout(this); + box->addWidget(m_itemsEditor); + box->addWidget(sep); + box->addWidget(buttonBox); + + // Numbers copied from itemlisteditor.ui + // (Automatic resizing doesn't work because ui has parent). + resize(550, 360); +} + +static AbstractItemEditor::PropertyDefinition listBoxPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" }, + { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" }, + { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" }, + { Qt::FontRole, QVariant::Font, 0, "font" }, + { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" }, + { Qt::BackgroundRole, QVariant::Brush, 0, "background" }, + { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" }, + { ItemFlagsShadowRole, 0, QtVariantPropertyManager::flagTypeId, "flags" }, + { Qt::CheckStateRole, 0, QtVariantPropertyManager::enumTypeId, "checkState" }, + { 0, 0, 0, 0 } +}; + +ListContents ListWidgetEditor::fillContentsFromListWidget(QListWidget *listWidget) +{ + setWindowTitle(tr("Edit List Widget")); + + ListContents retVal; + retVal.createFromListWidget(listWidget, false); + retVal.applyToListWidget(m_itemsEditor->listWidget(), m_itemsEditor->iconCache(), true); + + m_itemsEditor->setupEditor(listWidget, listBoxPropList); + + return retVal; +} + +static AbstractItemEditor::PropertyDefinition comboBoxPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { 0, 0, 0, 0 } +}; + +ListContents ListWidgetEditor::fillContentsFromComboBox(QComboBox *comboBox) +{ + setWindowTitle(tr("Edit Combobox")); + + ListContents retVal; + retVal.createFromComboBox(comboBox); + retVal.applyToListWidget(m_itemsEditor->listWidget(), m_itemsEditor->iconCache(), true); + + m_itemsEditor->setupEditor(comboBox, comboBoxPropList); + + return retVal; +} + +ListContents ListWidgetEditor::contents() const +{ + ListContents retVal; + retVal.createFromListWidget(m_itemsEditor->listWidget(), true); + return retVal; +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/listwidgeteditor.h b/src/designer/components/taskmenu/listwidgeteditor.h new file mode 100644 index 000000000..835a9898c --- /dev/null +++ b/src/designer/components/taskmenu/listwidgeteditor.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LISTWIDGETEDITOR_H +#define LISTWIDGETEDITOR_H + +#include "itemlisteditor.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +class QListWidget; +class QComboBox; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class ListWidgetEditor: public QDialog +{ + Q_OBJECT + +public: + ListWidgetEditor(QDesignerFormWindowInterface *form, + QWidget *parent); + + ListContents fillContentsFromListWidget(QListWidget *listWidget); + ListContents fillContentsFromComboBox(QComboBox *comboBox); + ListContents contents() const; + +private: + ItemListEditor *m_itemsEditor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LISTWIDGETEDITOR_H diff --git a/src/designer/components/taskmenu/menutaskmenu.cpp b/src/designer/components/taskmenu/menutaskmenu.cpp new file mode 100644 index 000000000..e859f31f5 --- /dev/null +++ b/src/designer/components/taskmenu/menutaskmenu.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "menutaskmenu.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + // ------------ MenuTaskMenu + MenuTaskMenu::MenuTaskMenu(QDesignerMenu *menu, QObject *parent) : + QObject(parent), + m_menu(menu), + m_removeAction(new QAction(tr("Remove"), this)), + m_promotionTaskMenu(new PromotionTaskMenu(menu, PromotionTaskMenu::ModeSingleWidget, this)) + { + connect(m_removeAction, SIGNAL(triggered()), this, SLOT(removeMenu())); + } + + QAction *MenuTaskMenu::preferredEditAction() const + { + return 0; + } + + QList MenuTaskMenu::taskActions() const + { + QList rc; + rc.push_back(m_removeAction); + m_promotionTaskMenu->addActions(PromotionTaskMenu::LeadingSeparator, rc); + return rc; + } + + void MenuTaskMenu::removeMenu() + { + // Are we on a menu bar or on a menu? + QWidget *pw = m_menu->parentWidget(); + if (QDesignerMenuBar *mb = qobject_cast(pw)) { + mb->deleteMenuAction(m_menu->menuAction()); + return; + } + if (QDesignerMenu *m = qobject_cast(pw)) { + m->deleteAction(m_menu->menuAction()); + } + } + + // ------------- MenuBarTaskMenu + MenuBarTaskMenu::MenuBarTaskMenu(QDesignerMenuBar *bar, QObject *parent) : + QObject(parent), + m_bar(bar) + { + } + + QAction *MenuBarTaskMenu::preferredEditAction() const + { + return 0; + } + + QList MenuBarTaskMenu::taskActions() const + { + return m_bar->contextMenuActions(); + } +} + +QT_END_NAMESPACE + +#include diff --git a/src/designer/components/taskmenu/menutaskmenu.h b/src/designer/components/taskmenu/menutaskmenu.h new file mode 100644 index 000000000..fb34891d0 --- /dev/null +++ b/src/designer/components/taskmenu/menutaskmenu.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MENUTASKMENU_H +#define MENUTASKMENU_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + + class PromotionTaskMenu; + +// The QMenu task menu provides promotion and a remove option. The actual +// menu context options are not forwarded since they make only sense +// when a menu is being edited/visible. + +class MenuTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit MenuTaskMenu(QDesignerMenu *menu, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void removeMenu(); + +private: + QDesignerMenu *m_menu; + QAction *m_removeAction; + PromotionTaskMenu *m_promotionTaskMenu; +}; + +// The QMenuBar task menu forwards the actions of QDesignerMenuBar, +// making them available in the object inspector. + +class MenuBarTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit MenuBarTaskMenu(QDesignerMenuBar *bar, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QDesignerMenuBar *m_bar; +}; + +typedef ExtensionFactory MenuTaskMenuFactory; +typedef ExtensionFactory MenuBarTaskMenuFactory; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // MENUTASKMENU_H diff --git a/src/designer/components/taskmenu/tablewidget_taskmenu.cpp b/src/designer/components/taskmenu/tablewidget_taskmenu.cpp new file mode 100644 index 000000000..ce6a2837e --- /dev/null +++ b/src/designer/components/taskmenu/tablewidget_taskmenu.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tablewidget_taskmenu.h" +#include "tablewidgeteditor.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +TableWidgetTaskMenu::TableWidgetTaskMenu(QTableWidget *button, QObject *parent) + : QDesignerTaskMenu(button, parent), + m_tableWidget(button), + m_editItemsAction(new QAction(tr("Edit Items..."), this)) +{ + connect(m_editItemsAction, SIGNAL(triggered()), this, SLOT(editItems())); + m_taskActions.append(m_editItemsAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + + +TableWidgetTaskMenu::~TableWidgetTaskMenu() +{ +} + +QAction *TableWidgetTaskMenu::preferredEditAction() const +{ + return m_editItemsAction; +} + +QList TableWidgetTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void TableWidgetTaskMenu::editItems() +{ + m_formWindow = QDesignerFormWindowInterface::findFormWindow(m_tableWidget); + if (m_formWindow.isNull()) + return; + + Q_ASSERT(m_tableWidget != 0); + + TableWidgetEditorDialog dlg(m_formWindow, m_tableWidget->window()); + TableWidgetContents oldCont = dlg.fillContentsFromTableWidget(m_tableWidget); + if (dlg.exec() == QDialog::Accepted) { + TableWidgetContents newCont = dlg.contents(); + if (newCont != oldCont) { + ChangeTableContentsCommand *cmd = new ChangeTableContentsCommand(m_formWindow); + cmd->init(m_tableWidget, oldCont, newCont); + m_formWindow->commandHistory()->push(cmd); + } + } +} + +void TableWidgetTaskMenu::updateSelection() +{ + if (m_editor) + m_editor->deleteLater(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/tablewidget_taskmenu.h b/src/designer/components/taskmenu/tablewidget_taskmenu.h new file mode 100644 index 000000000..bbd728e95 --- /dev/null +++ b/src/designer/components/taskmenu/tablewidget_taskmenu.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABLEWIDGET_TASKMENU_H +#define TABLEWIDGET_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QLineEdit; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class TableWidgetTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit TableWidgetTaskMenu(QTableWidget *button, QObject *parent = 0); + virtual ~TableWidgetTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editItems(); + void updateSelection(); + +private: + QTableWidget *m_tableWidget; + QPointer m_formWindow; + QPointer m_editor; + mutable QList m_taskActions; + QAction *m_editItemsAction; +}; + +typedef ExtensionFactory TableWidgetTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TABLEWIDGET_TASKMENU_H diff --git a/src/designer/components/taskmenu/tablewidgeteditor.cpp b/src/designer/components/taskmenu/tablewidgeteditor.cpp new file mode 100644 index 000000000..15597c8c7 --- /dev/null +++ b/src/designer/components/taskmenu/tablewidgeteditor.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tablewidgeteditor.h" +#include +#include +#include +#include "formwindowbase_p.h" +#include "qdesigner_utils_p.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +TableWidgetEditor::TableWidgetEditor(QDesignerFormWindowInterface *form, QDialog *dialog) + : AbstractItemEditor(form, 0), m_updatingBrowser(false) +{ + m_columnEditor = new ItemListEditor(form, this); + m_columnEditor->setObjectName(QLatin1String("columnEditor")); + m_columnEditor->setNewItemText(tr("New Column")); + m_rowEditor = new ItemListEditor(form, this); + m_rowEditor->setObjectName(QLatin1String("rowEditor")); + m_rowEditor->setNewItemText(tr("New Row")); + ui.setupUi(dialog); + + injectPropertyBrowser(ui.itemsTab, ui.widget); + connect(ui.showPropertiesButton, SIGNAL(clicked()), + this, SLOT(togglePropertyBrowser())); + setPropertyBrowserVisible(false); + + ui.tabWidget->insertTab(0, m_columnEditor, tr("&Columns")); + ui.tabWidget->insertTab(1, m_rowEditor, tr("&Rows")); + ui.tabWidget->setCurrentIndex(0); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + ui.tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + + connect(iconCache(), SIGNAL(reloaded()), this, SLOT(cacheReloaded())); + + connect(ui.tableWidget, SIGNAL(currentCellChanged(int,int,int,int)), + this, SLOT(on_tableWidget_currentCellChanged(int,int,int,int))); + connect(ui.tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), + this, SLOT(on_tableWidget_itemChanged(QTableWidgetItem*))); + connect(m_columnEditor, SIGNAL(indexChanged(int)), + this, SLOT(on_columnEditor_indexChanged(int))); + connect(m_columnEditor, SIGNAL(itemChanged(int,int,QVariant)), + this, SLOT(on_columnEditor_itemChanged(int,int,QVariant))); + connect(m_columnEditor, SIGNAL(itemInserted(int)), + this, SLOT(on_columnEditor_itemInserted(int))); + connect(m_columnEditor, SIGNAL(itemDeleted(int)), + this, SLOT(on_columnEditor_itemDeleted(int))); + connect(m_columnEditor, SIGNAL(itemMovedUp(int)), + this, SLOT(on_columnEditor_itemMovedUp(int))); + connect(m_columnEditor, SIGNAL(itemMovedDown(int)), + this, SLOT(on_columnEditor_itemMovedDown(int))); + + connect(m_rowEditor, SIGNAL(indexChanged(int)), + this, SLOT(on_rowEditor_indexChanged(int))); + connect(m_rowEditor, SIGNAL(itemChanged(int,int,QVariant)), + this, SLOT(on_rowEditor_itemChanged(int,int,QVariant))); + connect(m_rowEditor, SIGNAL(itemInserted(int)), + this, SLOT(on_rowEditor_itemInserted(int))); + connect(m_rowEditor, SIGNAL(itemDeleted(int)), + this, SLOT(on_rowEditor_itemDeleted(int))); + connect(m_rowEditor, SIGNAL(itemMovedUp(int)), + this, SLOT(on_rowEditor_itemMovedUp(int))); + connect(m_rowEditor, SIGNAL(itemMovedDown(int)), + this, SLOT(on_rowEditor_itemMovedDown(int))); +} + +static AbstractItemEditor::PropertyDefinition tableHeaderPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" }, +// { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" }, + { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" }, + { Qt::FontRole, QVariant::Font, 0, "font" }, + { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" }, + { Qt::BackgroundRole, QVariant::Color, 0, "background" }, + { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" }, + { 0, 0, 0, 0 } +}; + +static AbstractItemEditor::PropertyDefinition tableItemPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" }, +// { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" }, + { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" }, + { Qt::FontRole, QVariant::Font, 0, "font" }, + { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" }, + { Qt::BackgroundRole, QVariant::Brush, 0, "background" }, + { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" }, + { ItemFlagsShadowRole, 0, QtVariantPropertyManager::flagTypeId, "flags" }, + { Qt::CheckStateRole, 0, QtVariantPropertyManager::enumTypeId, "checkState" }, + { 0, 0, 0, 0 } +}; + +TableWidgetContents TableWidgetEditor::fillContentsFromTableWidget(QTableWidget *tableWidget) +{ + TableWidgetContents tblCont; + tblCont.fromTableWidget(tableWidget, false); + tblCont.applyToTableWidget(ui.tableWidget, iconCache(), true); + + tblCont.m_verticalHeader.applyToListWidget(m_rowEditor->listWidget(), iconCache(), true); + m_rowEditor->setupEditor(tableWidget, tableHeaderPropList); + + tblCont.m_horizontalHeader.applyToListWidget(m_columnEditor->listWidget(), iconCache(), true); + m_columnEditor->setupEditor(tableWidget, tableHeaderPropList); + + setupEditor(tableWidget, tableItemPropList); + if (ui.tableWidget->columnCount() > 0 && ui.tableWidget->rowCount() > 0) + ui.tableWidget->setCurrentCell(0, 0); + + updateEditor(); + + return tblCont; +} + +TableWidgetContents TableWidgetEditor::contents() const +{ + TableWidgetContents retVal; + retVal.fromTableWidget(ui.tableWidget, true); + return retVal; +} + +void TableWidgetEditor::setItemData(int role, const QVariant &v) +{ + QTableWidgetItem *item = ui.tableWidget->currentItem(); + BoolBlocker block(m_updatingBrowser); + if (!item) { + item = new QTableWidgetItem; + ui.tableWidget->setItem(ui.tableWidget->currentRow(), ui.tableWidget->currentColumn(), item); + } + QVariant newValue = v; + if (role == Qt::FontRole && newValue.type() == QVariant::Font) { + QFont oldFont = ui.tableWidget->font(); + QFont newFont = qvariant_cast(newValue).resolve(oldFont); + newValue = QVariant::fromValue(newFont); + item->setData(role, QVariant()); // force the right font with the current resolve mask is set (item view bug) + } + item->setData(role, newValue); +} + +QVariant TableWidgetEditor::getItemData(int role) const +{ + QTableWidgetItem *item = ui.tableWidget->currentItem(); + if (!item) + return QVariant(); + return item->data(role); +} + +void TableWidgetEditor::on_tableWidget_currentCellChanged(int currentRow, int currentCol, int, int /* XXX remove me */) +{ + m_rowEditor->setCurrentIndex(currentRow); + m_columnEditor->setCurrentIndex(currentCol); + updateBrowser(); +} + +void TableWidgetEditor::on_tableWidget_itemChanged(QTableWidgetItem *item) +{ + if (m_updatingBrowser) + return; + + PropertySheetStringValue val = qvariant_cast(item->data(Qt::DisplayPropertyRole)); + val.setValue(item->text()); + BoolBlocker block(m_updatingBrowser); + item->setData(Qt::DisplayPropertyRole, QVariant::fromValue(val)); + + updateBrowser(); +} + +void TableWidgetEditor::on_columnEditor_indexChanged(int col) +{ + ui.tableWidget->setCurrentCell(ui.tableWidget->currentRow(), col); +} + +void TableWidgetEditor::on_columnEditor_itemChanged(int idx, int role, const QVariant &v) +{ + ui.tableWidget->horizontalHeaderItem(idx)->setData(role, v); +} + +void TableWidgetEditor::on_rowEditor_indexChanged(int col) +{ + ui.tableWidget->setCurrentCell(col, ui.tableWidget->currentColumn()); +} + +void TableWidgetEditor::on_rowEditor_itemChanged(int idx, int role, const QVariant &v) +{ + ui.tableWidget->verticalHeaderItem(idx)->setData(role, v); +} + +void TableWidgetEditor::setPropertyBrowserVisible(bool v) +{ + ui.showPropertiesButton->setText(v ? tr("Properties &>>") : tr("Properties &<<")); + m_propertyBrowser->setVisible(v); +} + +void TableWidgetEditor::togglePropertyBrowser() +{ + setPropertyBrowserVisible(!m_propertyBrowser->isVisible()); +} + +void TableWidgetEditor::updateEditor() +{ + const bool wasEnabled = ui.tabWidget->isTabEnabled(2); + const bool isEnabled = ui.tableWidget->columnCount() && ui.tableWidget->rowCount(); + ui.tabWidget->setTabEnabled(2, isEnabled); + if (!wasEnabled && isEnabled) + ui.tableWidget->setCurrentCell(0, 0); + + QMetaObject::invokeMethod(ui.tableWidget, "updateGeometries"); + ui.tableWidget->viewport()->update(); +} + +void TableWidgetEditor::moveColumnsLeft(int fromColumn, int toColumn) +{ + if (fromColumn >= toColumn) + return; + + QTableWidgetItem *lastItem = ui.tableWidget->takeHorizontalHeaderItem(toColumn); + for (int i = toColumn; i > fromColumn; i--) { + ui.tableWidget->setHorizontalHeaderItem(i, + ui.tableWidget->takeHorizontalHeaderItem(i - 1)); + } + ui.tableWidget->setHorizontalHeaderItem(fromColumn, lastItem); + + for (int i = 0; i < ui.tableWidget->rowCount(); i++) { + QTableWidgetItem *lastItem = ui.tableWidget->takeItem(i, toColumn); + for (int j = toColumn; j > fromColumn; j--) + ui.tableWidget->setItem(i, j, ui.tableWidget->takeItem(i, j - 1)); + ui.tableWidget->setItem(i, fromColumn, lastItem); + } +} + +void TableWidgetEditor::moveColumnsRight(int fromColumn, int toColumn) +{ + if (fromColumn >= toColumn) + return; + + QTableWidgetItem *lastItem = ui.tableWidget->takeHorizontalHeaderItem(fromColumn); + for (int i = fromColumn; i < toColumn; i++) { + ui.tableWidget->setHorizontalHeaderItem(i, + ui.tableWidget->takeHorizontalHeaderItem(i + 1)); + } + ui.tableWidget->setHorizontalHeaderItem(toColumn, lastItem); + + for (int i = 0; i < ui.tableWidget->rowCount(); i++) { + QTableWidgetItem *lastItem = ui.tableWidget->takeItem(i, fromColumn); + for (int j = fromColumn; j < toColumn; j++) + ui.tableWidget->setItem(i, j, ui.tableWidget->takeItem(i, j + 1)); + ui.tableWidget->setItem(i, toColumn, lastItem); + } +} + +void TableWidgetEditor::moveRowsDown(int fromRow, int toRow) +{ + if (fromRow >= toRow) + return; + + QTableWidgetItem *lastItem = ui.tableWidget->takeVerticalHeaderItem(toRow); + for (int i = toRow; i > fromRow; i--) { + ui.tableWidget->setVerticalHeaderItem(i, + ui.tableWidget->takeVerticalHeaderItem(i - 1)); + } + ui.tableWidget->setVerticalHeaderItem(fromRow, lastItem); + + for (int i = 0; i < ui.tableWidget->columnCount(); i++) { + QTableWidgetItem *lastItem = ui.tableWidget->takeItem(toRow, i); + for (int j = toRow; j > fromRow; j--) + ui.tableWidget->setItem(j, i, ui.tableWidget->takeItem(j - 1, i)); + ui.tableWidget->setItem(fromRow, i, lastItem); + } +} + +void TableWidgetEditor::moveRowsUp(int fromRow, int toRow) +{ + if (fromRow >= toRow) + return; + + QTableWidgetItem *lastItem = ui.tableWidget->takeVerticalHeaderItem(fromRow); + for (int i = fromRow; i < toRow; i++) { + ui.tableWidget->setVerticalHeaderItem(i, + ui.tableWidget->takeVerticalHeaderItem(i + 1)); + } + ui.tableWidget->setVerticalHeaderItem(toRow, lastItem); + + for (int i = 0; i < ui.tableWidget->columnCount(); i++) { + QTableWidgetItem *lastItem = ui.tableWidget->takeItem(fromRow, i); + for (int j = fromRow; j < toRow; j++) + ui.tableWidget->setItem(j, i, ui.tableWidget->takeItem(j + 1, i)); + ui.tableWidget->setItem(toRow, i, lastItem); + } +} + +void TableWidgetEditor::on_columnEditor_itemInserted(int idx) +{ + const int columnCount = ui.tableWidget->columnCount(); + ui.tableWidget->setColumnCount(columnCount + 1); + + QTableWidgetItem *newItem = new QTableWidgetItem(m_columnEditor->newItemText()); + newItem->setData(Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(m_columnEditor->newItemText()))); + ui.tableWidget->setHorizontalHeaderItem(columnCount, newItem); + + moveColumnsLeft(idx, columnCount); + + int row = ui.tableWidget->currentRow(); + if (row >= 0) + ui.tableWidget->setCurrentCell(row, idx); + + updateEditor(); +} + +void TableWidgetEditor::on_columnEditor_itemDeleted(int idx) +{ + const int columnCount = ui.tableWidget->columnCount(); + + moveColumnsRight(idx, columnCount - 1); + ui.tableWidget->setColumnCount(columnCount - 1); + + updateEditor(); +} + +void TableWidgetEditor::on_columnEditor_itemMovedUp(int idx) +{ + moveColumnsRight(idx - 1, idx); + + ui.tableWidget->setCurrentCell(ui.tableWidget->currentRow(), idx - 1); +} + +void TableWidgetEditor::on_columnEditor_itemMovedDown(int idx) +{ + moveColumnsLeft(idx, idx + 1); + + ui.tableWidget->setCurrentCell(ui.tableWidget->currentRow(), idx + 1); +} + +void TableWidgetEditor::on_rowEditor_itemInserted(int idx) +{ + const int rowCount = ui.tableWidget->rowCount(); + ui.tableWidget->setRowCount(rowCount + 1); + + QTableWidgetItem *newItem = new QTableWidgetItem(m_rowEditor->newItemText()); + newItem->setData(Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(m_rowEditor->newItemText()))); + ui.tableWidget->setVerticalHeaderItem(rowCount, newItem); + + moveRowsDown(idx, rowCount); + + int col = ui.tableWidget->currentColumn(); + if (col >= 0) + ui.tableWidget->setCurrentCell(idx, col); + + updateEditor(); +} + +void TableWidgetEditor::on_rowEditor_itemDeleted(int idx) +{ + const int rowCount = ui.tableWidget->rowCount(); + + moveRowsUp(idx, rowCount - 1); + ui.tableWidget->setRowCount(rowCount - 1); + + updateEditor(); +} + +void TableWidgetEditor::on_rowEditor_itemMovedUp(int idx) +{ + moveRowsUp(idx - 1, idx); + + ui.tableWidget->setCurrentCell(idx - 1, ui.tableWidget->currentColumn()); +} + +void TableWidgetEditor::on_rowEditor_itemMovedDown(int idx) +{ + moveRowsDown(idx, idx + 1); + + ui.tableWidget->setCurrentCell(idx + 1, ui.tableWidget->currentColumn()); +} + +void TableWidgetEditor::cacheReloaded() +{ + reloadIconResources(iconCache(), ui.tableWidget); +} + +TableWidgetEditorDialog::TableWidgetEditorDialog(QDesignerFormWindowInterface *form, QWidget *parent) : + QDialog(parent), m_editor(form, this) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +TableWidgetContents TableWidgetEditorDialog::fillContentsFromTableWidget(QTableWidget *tableWidget) +{ + return m_editor.fillContentsFromTableWidget(tableWidget); +} + +TableWidgetContents TableWidgetEditorDialog::contents() const +{ + return m_editor.contents(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/tablewidgeteditor.h b/src/designer/components/taskmenu/tablewidgeteditor.h new file mode 100644 index 000000000..97ee6bcb3 --- /dev/null +++ b/src/designer/components/taskmenu/tablewidgeteditor.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TABLEWIDGETEDITOR_H +#define TABLEWIDGETEDITOR_H + +#include "ui_tablewidgeteditor.h" + +#include "listwidgeteditor.h" + +#include + +QT_BEGIN_NAMESPACE + +class QTableWidget; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class FormWindowBase; +class PropertySheetIconValue; + +class TableWidgetEditor: public AbstractItemEditor +{ + Q_OBJECT +public: + explicit TableWidgetEditor(QDesignerFormWindowInterface *form, QDialog *dialog); + + TableWidgetContents fillContentsFromTableWidget(QTableWidget *tableWidget); + TableWidgetContents contents() const; + +private slots: + + void on_tableWidget_currentCellChanged(int currentRow, int currnetCol, int, int); + void on_tableWidget_itemChanged(QTableWidgetItem *item); + + void on_columnEditor_indexChanged(int idx); + void on_columnEditor_itemChanged(int idx, int role, const QVariant &v); + + void on_columnEditor_itemInserted(int idx); + void on_columnEditor_itemDeleted(int idx); + void on_columnEditor_itemMovedUp(int idx); + void on_columnEditor_itemMovedDown(int idx); + + void on_rowEditor_indexChanged(int idx); + void on_rowEditor_itemChanged(int idx, int role, const QVariant &v); + + void on_rowEditor_itemInserted(int idx); + void on_rowEditor_itemDeleted(int idx); + void on_rowEditor_itemMovedUp(int idx); + void on_rowEditor_itemMovedDown(int idx); + + void togglePropertyBrowser(); + + void cacheReloaded(); + +protected: + virtual void setItemData(int role, const QVariant &v); + virtual QVariant getItemData(int role) const; + +private: + void setPropertyBrowserVisible(bool v); + void updateEditor(); + void moveColumnsLeft(int fromColumn, int toColumn); + void moveColumnsRight(int fromColumn, int toColumn); + void moveRowsUp(int fromRow, int toRow); + void moveRowsDown(int fromRow, int toRow); + + Ui::TableWidgetEditor ui; + ItemListEditor *m_rowEditor; + ItemListEditor *m_columnEditor; + bool m_updatingBrowser; +}; + +class TableWidgetEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit TableWidgetEditorDialog(QDesignerFormWindowInterface *form, QWidget *parent); + + TableWidgetContents fillContentsFromTableWidget(QTableWidget *tableWidget); + TableWidgetContents contents() const; + +private: + TableWidgetEditor m_editor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TABLEWIDGETEDITOR_H diff --git a/src/designer/components/taskmenu/tablewidgeteditor.ui b/src/designer/components/taskmenu/tablewidgeteditor.ui new file mode 100644 index 000000000..87400c303 --- /dev/null +++ b/src/designer/components/taskmenu/tablewidgeteditor.ui @@ -0,0 +1,157 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::TableWidgetEditor + + + + 0 + 0 + 550 + 360 + + + + Edit Table Widget + + + + + + 0 + + + + &Items + + + + + + + 0 + + + + + Table Items + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Properties &>> + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + qdesigner_internal::TableWidgetEditor + accept() + + + 431 + 351 + + + 373 + 362 + + + + + buttonBox + rejected() + qdesigner_internal::TableWidgetEditor + reject() + + + 547 + 354 + + + 562 + 362 + + + + + diff --git a/src/designer/components/taskmenu/taskmenu.cmake b/src/designer/components/taskmenu/taskmenu.cmake new file mode 100644 index 000000000..543651137 --- /dev/null +++ b/src/designer/components/taskmenu/taskmenu.cmake @@ -0,0 +1,51 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/button_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/groupbox_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/label_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/lineedit_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/listwidget_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/treewidget_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/tablewidget_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/combobox_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/textedit_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/toolbar_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/containerwidget_taskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/inplace_editor.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/taskmenu_component.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/itemlisteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/listwidgeteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/treewidgeteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/tablewidgeteditor.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/inplace_widget_helper.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/menutaskmenu.h + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/layouttaskmenu.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/button_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/groupbox_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/label_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/lineedit_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/listwidget_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/treewidget_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/tablewidget_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/combobox_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/textedit_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/toolbar_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/containerwidget_taskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/inplace_editor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/taskmenu_component.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/itemlisteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/listwidgeteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/treewidgeteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/tablewidgeteditor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/inplace_widget_helper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/menutaskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/layouttaskmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/itemlisteditor.ui + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/treewidgeteditor.ui + ${CMAKE_CURRENT_SOURCE_DIR}/taskmenu/tablewidgeteditor.ui +) + diff --git a/src/designer/components/taskmenu/taskmenu_component.cpp b/src/designer/components/taskmenu/taskmenu_component.cpp new file mode 100644 index 000000000..45a2be973 --- /dev/null +++ b/src/designer/components/taskmenu/taskmenu_component.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "taskmenu_component.h" +#include "button_taskmenu.h" +#include "groupbox_taskmenu.h" +#include "label_taskmenu.h" +#include "lineedit_taskmenu.h" +#include "listwidget_taskmenu.h" +#include "treewidget_taskmenu.h" +#include "tablewidget_taskmenu.h" +#include "containerwidget_taskmenu.h" +#include "combobox_taskmenu.h" +#include "textedit_taskmenu.h" +#include "menutaskmenu.h" +#include "toolbar_taskmenu.h" +#include "layouttaskmenu.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +TaskMenuComponent::TaskMenuComponent(QDesignerFormEditorInterface *core, QObject *parent) + : QObject(parent), + m_core(core) +{ + Q_ASSERT(m_core != 0); + + QExtensionManager *mgr = core->extensionManager(); + const QString taskMenuId = QLatin1String("QDesignerInternalTaskMenuExtension"); + + ButtonTaskMenuFactory::registerExtension(mgr, taskMenuId); + CommandLinkButtonTaskMenuFactory::registerExtension(mgr, taskMenuId); // Order! + ButtonGroupTaskMenuFactory::registerExtension(mgr, taskMenuId); + + GroupBoxTaskMenuFactory::registerExtension(mgr, taskMenuId); + LabelTaskMenuFactory::registerExtension(mgr, taskMenuId); + LineEditTaskMenuFactory::registerExtension(mgr, taskMenuId); + ListWidgetTaskMenuFactory::registerExtension(mgr, taskMenuId); + TreeWidgetTaskMenuFactory::registerExtension(mgr, taskMenuId); + TableWidgetTaskMenuFactory::registerExtension(mgr, taskMenuId); + TextEditTaskMenuFactory::registerExtension(mgr, taskMenuId); + PlainTextEditTaskMenuFactory::registerExtension(mgr, taskMenuId); + MenuTaskMenuFactory::registerExtension(mgr, taskMenuId); + MenuBarTaskMenuFactory::registerExtension(mgr, taskMenuId); + ToolBarTaskMenuFactory::registerExtension(mgr, taskMenuId); + StatusBarTaskMenuFactory::registerExtension(mgr, taskMenuId); + LayoutWidgetTaskMenuFactory::registerExtension(mgr, taskMenuId); + SpacerTaskMenuFactory::registerExtension(mgr, taskMenuId); + + mgr->registerExtensions(new ContainerWidgetTaskMenuFactory(core, mgr), taskMenuId); + mgr->registerExtensions(new ComboBoxTaskMenuFactory(taskMenuId, mgr), taskMenuId); +} + +TaskMenuComponent::~TaskMenuComponent() +{ +} + +QDesignerFormEditorInterface *TaskMenuComponent::core() const +{ + return m_core; + +} +QT_END_NAMESPACE + +#include diff --git a/src/designer/components/taskmenu/taskmenu_component.h b/src/designer/components/taskmenu/taskmenu_component.h new file mode 100644 index 000000000..8eebb1462 --- /dev/null +++ b/src/designer/components/taskmenu/taskmenu_component.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TASKMENU_COMPONENT_H +#define TASKMENU_COMPONENT_H + +#include "taskmenu_global.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QT_TASKMENU_EXPORT TaskMenuComponent: public QObject +{ + Q_OBJECT +public: + explicit TaskMenuComponent(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~TaskMenuComponent(); + + QDesignerFormEditorInterface *core() const; + +private: + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TASKMENU_COMPONENT_H diff --git a/src/designer/components/taskmenu/taskmenu_global.h b/src/designer/components/taskmenu/taskmenu_global.h new file mode 100644 index 000000000..8c10de897 --- /dev/null +++ b/src/designer/components/taskmenu/taskmenu_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TASKMENU_GLOBAL_H +#define TASKMENU_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_TASKMENU_LIBRARY +# define QT_TASKMENU_EXPORT +#else +# define QT_TASKMENU_EXPORT +#endif +#else +#define QT_TASKMENU_EXPORT +#endif + +#endif // TASKMENU_GLOBAL_H diff --git a/src/designer/components/taskmenu/textedit_taskmenu.cpp b/src/designer/components/taskmenu/textedit_taskmenu.cpp new file mode 100644 index 000000000..f9dfd81a9 --- /dev/null +++ b/src/designer/components/taskmenu/textedit_taskmenu.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textedit_taskmenu.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +TextEditTaskMenu::TextEditTaskMenu(QTextEdit *textEdit, QObject *parent) : + QDesignerTaskMenu(textEdit, parent), + m_format(Qt::RichText), + m_property(QLatin1String("html")), + m_windowTitle(tr("Edit HTML")), + m_editTextAction(new QAction(tr("Change HTML..."), this)) +{ + initialize(); +} + +TextEditTaskMenu::TextEditTaskMenu(QPlainTextEdit *textEdit, QObject *parent) : + QDesignerTaskMenu(textEdit, parent), + m_format(Qt::PlainText), + m_property(QLatin1String("plainText")), + m_windowTitle(tr("Edit Text")), + m_editTextAction(new QAction(tr("Change Plain Text..."), this)) +{ + initialize(); +} + + +void TextEditTaskMenu::initialize() +{ + connect(m_editTextAction, SIGNAL(triggered()), this, SLOT(editText())); + m_taskActions.append(m_editTextAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + +TextEditTaskMenu::~TextEditTaskMenu() +{ +} + +QAction *TextEditTaskMenu::preferredEditAction() const +{ + return m_editTextAction; +} + +QList TextEditTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void TextEditTaskMenu::editText() +{ + changeTextProperty(m_property, m_windowTitle, MultiSelectionMode, m_format); +} + +} +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/textedit_taskmenu.h b/src/designer/components/taskmenu/textedit_taskmenu.h new file mode 100644 index 000000000..08fad6b0b --- /dev/null +++ b/src/designer/components/taskmenu/textedit_taskmenu.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTEDIT_TASKMENU_H +#define TEXTEDIT_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class TextEditTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit TextEditTaskMenu(QTextEdit *button, QObject *parent = 0); + explicit TextEditTaskMenu(QPlainTextEdit *button, QObject *parent = 0); + + virtual ~TextEditTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editText(); + +private: + void initialize(); + + const Qt::TextFormat m_format; + const QString m_property; + const QString m_windowTitle; + + mutable QList m_taskActions; + QAction *m_editTextAction; +}; + +typedef ExtensionFactory TextEditTaskMenuFactory; +typedef ExtensionFactory PlainTextEditTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TEXTEDIT_TASKMENU_H diff --git a/src/designer/components/taskmenu/toolbar_taskmenu.cpp b/src/designer/components/taskmenu/toolbar_taskmenu.cpp new file mode 100644 index 000000000..940ab3fea --- /dev/null +++ b/src/designer/components/taskmenu/toolbar_taskmenu.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "toolbar_taskmenu.h" +#include "qdesigner_toolbar_p.h" + +#include + +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + // ------------ ToolBarTaskMenu + ToolBarTaskMenu::ToolBarTaskMenu(QToolBar *tb, QObject *parent) : + QObject(parent), + m_toolBar(tb) + { + } + + QAction *ToolBarTaskMenu::preferredEditAction() const + { + return 0; + } + + QList ToolBarTaskMenu::taskActions() const + { + if (ToolBarEventFilter *ef = ToolBarEventFilter::eventFilterOf(m_toolBar)) + return ef->contextMenuActions(); + return QList(); + } + + // ------------ StatusBarTaskMenu + StatusBarTaskMenu::StatusBarTaskMenu(QStatusBar *sb, QObject *parent) : + QObject(parent), + m_statusBar(sb), + m_removeAction(new QAction(tr("Remove"), this)), + m_promotionTaskMenu(new PromotionTaskMenu(sb, PromotionTaskMenu::ModeSingleWidget, this)) + { + connect(m_removeAction, SIGNAL(triggered()), this, SLOT(removeStatusBar())); + } + + QAction *StatusBarTaskMenu::preferredEditAction() const + { + return 0; + } + + QList StatusBarTaskMenu::taskActions() const + { + QList rc; + rc.push_back(m_removeAction); + m_promotionTaskMenu->addActions(PromotionTaskMenu::LeadingSeparator, rc); + return rc; + } + + void StatusBarTaskMenu::removeStatusBar() + { + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_statusBar)) { + DeleteStatusBarCommand *cmd = new DeleteStatusBarCommand(fw); + cmd->init(m_statusBar); + fw->commandHistory()->push(cmd); + } + } +} + +QT_END_NAMESPACE + +#include diff --git a/src/designer/components/taskmenu/toolbar_taskmenu.h b/src/designer/components/taskmenu/toolbar_taskmenu.h new file mode 100644 index 000000000..5e24341e7 --- /dev/null +++ b/src/designer/components/taskmenu/toolbar_taskmenu.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOOLBAR_TASKMENU_H +#define TOOLBAR_TASKMENU_H + +#include + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + class PromotionTaskMenu; + +// ToolBarTaskMenu forwards the actions of ToolBarEventFilter +class ToolBarTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit ToolBarTaskMenu(QToolBar *tb, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private: + QToolBar *m_toolBar; +}; + +// StatusBarTaskMenu provides promotion and deletion +class StatusBarTaskMenu : public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + explicit StatusBarTaskMenu(QStatusBar *tb, QObject *parent = 0); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void removeStatusBar(); + +private: + QStatusBar *m_statusBar; + QAction *m_removeAction; + PromotionTaskMenu *m_promotionTaskMenu; +}; + +typedef ExtensionFactory ToolBarTaskMenuFactory; +typedef ExtensionFactory StatusBarTaskMenuFactory; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TOOLBAR_TASKMENU_H diff --git a/src/designer/components/taskmenu/treewidget_taskmenu.cpp b/src/designer/components/taskmenu/treewidget_taskmenu.cpp new file mode 100644 index 000000000..ebcd888fd --- /dev/null +++ b/src/designer/components/taskmenu/treewidget_taskmenu.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "treewidget_taskmenu.h" +#include "treewidgeteditor.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +TreeWidgetTaskMenu::TreeWidgetTaskMenu(QTreeWidget *button, QObject *parent) + : QDesignerTaskMenu(button, parent), + m_treeWidget(button), + m_editItemsAction(new QAction(tr("Edit Items..."), this)) +{ + connect(m_editItemsAction, SIGNAL(triggered()), this, SLOT(editItems())); + m_taskActions.append(m_editItemsAction); + + QAction *sep = new QAction(this); + sep->setSeparator(true); + m_taskActions.append(sep); +} + + +TreeWidgetTaskMenu::~TreeWidgetTaskMenu() +{ +} + +QAction *TreeWidgetTaskMenu::preferredEditAction() const +{ + return m_editItemsAction; +} + +QList TreeWidgetTaskMenu::taskActions() const +{ + return m_taskActions + QDesignerTaskMenu::taskActions(); +} + +void TreeWidgetTaskMenu::editItems() +{ + m_formWindow = QDesignerFormWindowInterface::findFormWindow(m_treeWidget); + if (m_formWindow.isNull()) + return; + + Q_ASSERT(m_treeWidget != 0); + + TreeWidgetEditorDialog dlg(m_formWindow, m_treeWidget->window()); + TreeWidgetContents oldCont = dlg.fillContentsFromTreeWidget(m_treeWidget); + if (dlg.exec() == QDialog::Accepted) { + TreeWidgetContents newCont = dlg.contents(); + if (newCont != oldCont) { + ChangeTreeContentsCommand *cmd = new ChangeTreeContentsCommand(m_formWindow); + cmd->init(m_treeWidget, oldCont, newCont); + m_formWindow->commandHistory()->push(cmd); + } + } +} + +void TreeWidgetTaskMenu::updateSelection() +{ + if (m_editor) + m_editor->deleteLater(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/treewidget_taskmenu.h b/src/designer/components/taskmenu/treewidget_taskmenu.h new file mode 100644 index 000000000..d8d3b3394 --- /dev/null +++ b/src/designer/components/taskmenu/treewidget_taskmenu.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TREEWIDGET_TASKMENU_H +#define TREEWIDGET_TASKMENU_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QLineEdit; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class TreeWidgetTaskMenu: public QDesignerTaskMenu +{ + Q_OBJECT +public: + explicit TreeWidgetTaskMenu(QTreeWidget *button, QObject *parent = 0); + virtual ~TreeWidgetTaskMenu(); + + virtual QAction *preferredEditAction() const; + virtual QList taskActions() const; + +private slots: + void editItems(); + void updateSelection(); + +private: + QTreeWidget *m_treeWidget; + QPointer m_formWindow; + QPointer m_editor; + mutable QList m_taskActions; + QAction *m_editItemsAction; +}; + +typedef ExtensionFactory TreeWidgetTaskMenuFactory; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TREEWIDGET_TASKMENU_H diff --git a/src/designer/components/taskmenu/treewidgeteditor.cpp b/src/designer/components/taskmenu/treewidgeteditor.cpp new file mode 100644 index 000000000..d3caba9f6 --- /dev/null +++ b/src/designer/components/taskmenu/treewidgeteditor.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "treewidgeteditor.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +TreeWidgetEditor::TreeWidgetEditor(QDesignerFormWindowInterface *form, QDialog *dialog) + : AbstractItemEditor(form, 0), m_updatingBrowser(false) +{ + m_columnEditor = new ItemListEditor(form, this); + m_columnEditor->setObjectName(QLatin1String("columnEditor")); + m_columnEditor->setNewItemText(tr("New Column")); + ui.setupUi(dialog); + + injectPropertyBrowser(ui.itemsTab, ui.widget); + connect(ui.showPropertiesButton, SIGNAL(clicked()), + this, SLOT(togglePropertyBrowser())); + setPropertyBrowserVisible(false); + + ui.tabWidget->insertTab(0, m_columnEditor, tr("&Columns")); + ui.tabWidget->setCurrentIndex(0); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + ui.newItemButton->setIcon(createIconSet(QString::fromUtf8("plus.png"))); + ui.newSubItemButton->setIcon(createIconSet(QString::fromUtf8("downplus.png"))); + ui.deleteItemButton->setIcon(createIconSet(QString::fromUtf8("minus.png"))); + ui.moveItemUpButton->setIcon(createIconSet(QString::fromUtf8("up.png"))); + ui.moveItemDownButton->setIcon(createIconSet(QString::fromUtf8("down.png"))); + ui.moveItemRightButton->setIcon(createIconSet(QString::fromUtf8("leveldown.png"))); + ui.moveItemLeftButton->setIcon(createIconSet(QString::fromUtf8("levelup.png"))); + + ui.treeWidget->header()->setMovable(false); + + connect(ui.newItemButton, SIGNAL(clicked()), this, SLOT(on_newItemButton_clicked())); + connect(ui.newSubItemButton, SIGNAL(clicked()), this, SLOT(on_newSubItemButton_clicked())); + connect(ui.moveItemUpButton, SIGNAL(clicked()), this, SLOT(on_moveItemUpButton_clicked())); + connect(ui.moveItemDownButton, SIGNAL(clicked()), this, SLOT(on_moveItemDownButton_clicked())); + connect(ui.moveItemRightButton, SIGNAL(clicked()), this, SLOT(on_moveItemRightButton_clicked())); + connect(ui.moveItemLeftButton, SIGNAL(clicked()), this, SLOT(on_moveItemLeftButton_clicked())); + connect(ui.deleteItemButton, SIGNAL(clicked()), this, SLOT(on_deleteItemButton_clicked())); + connect(ui.treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), + this, SLOT(on_treeWidget_currentItemChanged())); + connect(ui.treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), + this, SLOT(on_treeWidget_itemChanged(QTreeWidgetItem*,int))); + + connect(m_columnEditor, SIGNAL(indexChanged(int)), + this, SLOT(on_columnEditor_indexChanged(int))); + connect(m_columnEditor, SIGNAL(itemChanged(int,int,QVariant)), + this, SLOT(on_columnEditor_itemChanged(int,int,QVariant))); + connect(m_columnEditor, SIGNAL(itemInserted(int)), + this, SLOT(on_columnEditor_itemInserted(int))); + connect(m_columnEditor, SIGNAL(itemDeleted(int)), + this, SLOT(on_columnEditor_itemDeleted(int))); + connect(m_columnEditor, SIGNAL(itemMovedUp(int)), + this, SLOT(on_columnEditor_itemMovedUp(int))); + connect(m_columnEditor, SIGNAL(itemMovedDown(int)), + this, SLOT(on_columnEditor_itemMovedDown(int))); + + connect(iconCache(), SIGNAL(reloaded()), this, SLOT(cacheReloaded())); +} + +static AbstractItemEditor::PropertyDefinition treeHeaderPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" }, + { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" }, + { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" }, + { Qt::FontRole, QVariant::Font, 0, "font" }, + { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" }, + { Qt::BackgroundRole, QVariant::Color, 0, "background" }, + { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" }, + { 0, 0, 0, 0 } +}; + +static AbstractItemEditor::PropertyDefinition treeItemColumnPropList[] = { + { Qt::DisplayPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "text" }, + { Qt::DecorationPropertyRole, 0, DesignerPropertyManager::designerIconTypeId, "icon" }, + { Qt::ToolTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "toolTip" }, + { Qt::StatusTipPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "statusTip" }, + { Qt::WhatsThisPropertyRole, 0, DesignerPropertyManager::designerStringTypeId, "whatsThis" }, + { Qt::FontRole, QVariant::Font, 0, "font" }, + { Qt::TextAlignmentRole, 0, DesignerPropertyManager::designerAlignmentTypeId, "textAlignment" }, + { Qt::BackgroundRole, QVariant::Brush, 0, "background" }, + { Qt::ForegroundRole, QVariant::Brush, 0, "foreground" }, + { Qt::CheckStateRole, 0, QtVariantPropertyManager::enumTypeId, "checkState" }, + { 0, 0, 0, 0 } +}; + +static AbstractItemEditor::PropertyDefinition treeItemCommonPropList[] = { + { ItemFlagsShadowRole, 0, QtVariantPropertyManager::flagTypeId, "flags" }, + { 0, 0, 0, 0 } +}; + +QtVariantProperty *TreeWidgetEditor::setupPropertyGroup(const QString &title, PropertyDefinition *propDefs) +{ + setupProperties(propDefs); + QtVariantProperty *groupProp = m_propertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), title); + foreach (QtVariantProperty *prop, m_rootProperties) + groupProp->addSubProperty(prop); + m_rootProperties.clear(); + return groupProp; +} + +TreeWidgetContents TreeWidgetEditor::fillContentsFromTreeWidget(QTreeWidget *treeWidget) +{ + TreeWidgetContents treeCont; + treeCont.fromTreeWidget(treeWidget, false); + treeCont.applyToTreeWidget(ui.treeWidget, iconCache(), true); + + treeCont.m_headerItem.applyToListWidget(m_columnEditor->listWidget(), iconCache(), true); + m_columnEditor->setupEditor(treeWidget, treeHeaderPropList); + + QList rootProperties; + rootProperties.append(setupPropertyGroup(tr("Per column properties"), treeItemColumnPropList)); + rootProperties.append(setupPropertyGroup(tr("Common properties"), treeItemCommonPropList)); + m_rootProperties = rootProperties; + m_propertyBrowser->setPropertiesWithoutValueMarked(true); + m_propertyBrowser->setRootIsDecorated(false); + setupObject(treeWidget); + + if (ui.treeWidget->topLevelItemCount() > 0) + ui.treeWidget->setCurrentItem(ui.treeWidget->topLevelItem(0)); + + updateEditor(); + + return treeCont; +} + +TreeWidgetContents TreeWidgetEditor::contents() const +{ + TreeWidgetContents retVal; + retVal.fromTreeWidget(ui.treeWidget, true); + return retVal; +} + +void TreeWidgetEditor::setItemData(int role, const QVariant &v) +{ + const int col = (role == ItemFlagsShadowRole) ? 0 : ui.treeWidget->currentColumn(); + QVariant newValue = v; + BoolBlocker block(m_updatingBrowser); + if (role == Qt::FontRole && newValue.type() == QVariant::Font) { + QFont oldFont = ui.treeWidget->font(); + QFont newFont = qvariant_cast(newValue).resolve(oldFont); + newValue = QVariant::fromValue(newFont); + ui.treeWidget->currentItem()->setData(col, role, QVariant()); // force the right font with the current resolve mask is set (item view bug) + } + ui.treeWidget->currentItem()->setData(col, role, newValue); +} + +QVariant TreeWidgetEditor::getItemData(int role) const +{ + const int col = (role == ItemFlagsShadowRole) ? 0 : ui.treeWidget->currentColumn(); + return ui.treeWidget->currentItem()->data(col, role); +} + +void TreeWidgetEditor::on_newItemButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + QTreeWidgetItem *newItem = 0; + ui.treeWidget->blockSignals(true); + if (curItem) { + if (curItem->parent()) + newItem = new QTreeWidgetItem(curItem->parent(), curItem); + else + newItem = new QTreeWidgetItem(ui.treeWidget, curItem); + } else + newItem = new QTreeWidgetItem(ui.treeWidget); + const QString newItemText = tr("New Item"); + newItem->setText(0, newItemText); + newItem->setData(0, Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(newItemText))); + newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(newItem, qMax(ui.treeWidget->currentColumn(), 0)); + updateEditor(); + ui.treeWidget->editItem(newItem, ui.treeWidget->currentColumn()); +} + +void TreeWidgetEditor::on_newSubItemButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + ui.treeWidget->blockSignals(true); + QTreeWidgetItem *newItem = new QTreeWidgetItem(curItem); + const QString newItemText = tr("New Subitem"); + newItem->setText(0, newItemText); + newItem->setData(0, Qt::DisplayPropertyRole, QVariant::fromValue(PropertySheetStringValue(newItemText))); + newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(newItem, ui.treeWidget->currentColumn()); + updateEditor(); + ui.treeWidget->editItem(newItem, ui.treeWidget->currentColumn()); +} + +void TreeWidgetEditor::on_deleteItemButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + QTreeWidgetItem *nextCurrent = 0; + if (curItem->parent()) { + int idx = curItem->parent()->indexOfChild(curItem); + if (idx == curItem->parent()->childCount() - 1) + idx--; + else + idx++; + if (idx < 0) + nextCurrent = curItem->parent(); + else + nextCurrent = curItem->parent()->child(idx); + } else { + int idx = ui.treeWidget->indexOfTopLevelItem(curItem); + if (idx == ui.treeWidget->topLevelItemCount() - 1) + idx--; + else + idx++; + if (idx >= 0) + nextCurrent = ui.treeWidget->topLevelItem(idx); + } + closeEditors(); + ui.treeWidget->blockSignals(true); + delete curItem; + ui.treeWidget->blockSignals(false); + + if (nextCurrent) + ui.treeWidget->setCurrentItem(nextCurrent, ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::on_moveItemUpButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + int idx; + if (curItem->parent()) + idx = curItem->parent()->indexOfChild(curItem); + else + idx = ui.treeWidget->indexOfTopLevelItem(curItem); + if (idx == 0) + return; + + QTreeWidgetItem *takenItem; + ui.treeWidget->blockSignals(true); + if (curItem->parent()) { + QTreeWidgetItem *parentItem = curItem->parent(); + takenItem = parentItem->takeChild(idx); + parentItem->insertChild(idx - 1, takenItem); + } else { + takenItem = ui.treeWidget->takeTopLevelItem(idx); + ui.treeWidget->insertTopLevelItem(idx - 1, takenItem); + } + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::on_moveItemDownButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + int idx, idxCount; + if (curItem->parent()) { + idx = curItem->parent()->indexOfChild(curItem); + idxCount = curItem->parent()->childCount(); + } else { + idx = ui.treeWidget->indexOfTopLevelItem(curItem); + idxCount = ui.treeWidget->topLevelItemCount(); + } + if (idx == idxCount - 1) + return; + + QTreeWidgetItem *takenItem; + ui.treeWidget->blockSignals(true); + if (curItem->parent()) { + QTreeWidgetItem *parentItem = curItem->parent(); + takenItem = parentItem->takeChild(idx); + parentItem->insertChild(idx + 1, takenItem); + } else { + takenItem = ui.treeWidget->takeTopLevelItem(idx); + ui.treeWidget->insertTopLevelItem(idx + 1, takenItem); + } + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::on_moveItemLeftButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + QTreeWidgetItem *parentItem = curItem->parent(); + if (!parentItem) + return; + + ui.treeWidget->blockSignals(true); + QTreeWidgetItem *takenItem = parentItem->takeChild(parentItem->indexOfChild(curItem)); + if (parentItem->parent()) { + int idx = parentItem->parent()->indexOfChild(parentItem); + parentItem->parent()->insertChild(idx, takenItem); + } else { + int idx = ui.treeWidget->indexOfTopLevelItem(parentItem); + ui.treeWidget->insertTopLevelItem(idx, takenItem); + } + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::on_moveItemRightButton_clicked() +{ + QTreeWidgetItem *curItem = ui.treeWidget->currentItem(); + if (!curItem) + return; + + int idx, idxCount; + if (curItem->parent()) { + idx = curItem->parent()->indexOfChild(curItem); + idxCount = curItem->parent()->childCount(); + } else { + idx = ui.treeWidget->indexOfTopLevelItem(curItem); + idxCount = ui.treeWidget->topLevelItemCount(); + } + if (idx == idxCount - 1) + return; + + QTreeWidgetItem *takenItem; + ui.treeWidget->blockSignals(true); + if (curItem->parent()) { + QTreeWidgetItem *parentItem = curItem->parent()->child(idx + 1); + takenItem = curItem->parent()->takeChild(idx); + parentItem->insertChild(0, takenItem); + } else { + QTreeWidgetItem *parentItem = ui.treeWidget->topLevelItem(idx + 1); + takenItem = ui.treeWidget->takeTopLevelItem(idx); + parentItem->insertChild(0, takenItem); + } + ui.treeWidget->blockSignals(false); + + ui.treeWidget->setCurrentItem(takenItem, ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::togglePropertyBrowser() +{ + setPropertyBrowserVisible(!m_propertyBrowser->isVisible()); +} + +void TreeWidgetEditor::setPropertyBrowserVisible(bool v) +{ + ui.showPropertiesButton->setText(v ? tr("Properties &>>") : tr("Properties &<<")); + m_propertyBrowser->setVisible(v); +} + +void TreeWidgetEditor::on_treeWidget_currentItemChanged() +{ + m_columnEditor->setCurrentIndex(ui.treeWidget->currentColumn()); + updateEditor(); +} + +void TreeWidgetEditor::on_treeWidget_itemChanged(QTreeWidgetItem *item, int column) +{ + if (m_updatingBrowser) + return; + + PropertySheetStringValue val = qvariant_cast(item->data(column, Qt::DisplayPropertyRole)); + val.setValue(item->text(column)); + BoolBlocker block(m_updatingBrowser); + item->setData(column, Qt::DisplayPropertyRole, QVariant::fromValue(val)); + + updateBrowser(); +} + +void TreeWidgetEditor::on_columnEditor_indexChanged(int idx) +{ + if (QTreeWidgetItem *item = ui.treeWidget->currentItem()) + ui.treeWidget->setCurrentItem(item, idx); +} + +void TreeWidgetEditor::on_columnEditor_itemChanged(int idx, int role, const QVariant &v) +{ + if (role == Qt::DisplayPropertyRole) + ui.treeWidget->headerItem()->setData(idx, Qt::EditRole, qvariant_cast(v).value()); + ui.treeWidget->headerItem()->setData(idx, role, v); +} + +void TreeWidgetEditor::updateEditor() +{ + QTreeWidgetItem *current = ui.treeWidget->currentItem(); + + bool itemsEnabled = false; + bool currentItemEnabled = false; + bool moveItemUpEnabled = false; + bool moveItemDownEnabled = false; + bool moveItemRightEnabled = false; + bool moveItemLeftEnabled = false; + + if (ui.treeWidget->columnCount() > 0) { + itemsEnabled = true; + if (current) { + int idx; + int idxCount; + currentItemEnabled = true; + if (current->parent()) { + moveItemLeftEnabled = true; + idx = current->parent()->indexOfChild(current); + idxCount = current->parent()->childCount(); + } else { + idx = ui.treeWidget->indexOfTopLevelItem(current); + idxCount = ui.treeWidget->topLevelItemCount(); + } + if (idx > 0) + moveItemUpEnabled = true; + if (idx < idxCount - 1) { + moveItemDownEnabled = true; + moveItemRightEnabled = true; + } + } + } + ui.tabWidget->setTabEnabled(1, itemsEnabled); + ui.newSubItemButton->setEnabled(currentItemEnabled); + ui.deleteItemButton->setEnabled(currentItemEnabled); + + ui.moveItemUpButton->setEnabled(moveItemUpEnabled); + ui.moveItemDownButton->setEnabled(moveItemDownEnabled); + ui.moveItemRightButton->setEnabled(moveItemRightEnabled); + ui.moveItemLeftButton->setEnabled(moveItemLeftEnabled); + + if (current) + updateBrowser(); + else + m_propertyBrowser->clear(); +} + +void TreeWidgetEditor::moveColumnItems(const PropertyDefinition *propList, + QTreeWidgetItem *item, int fromColumn, int toColumn, int step) +{ + BoolBlocker block(m_updatingBrowser); + + QList saveCol; + for (int j = 0; propList[j].name; j++) + saveCol.append(item->data(toColumn, propList[j].role)); + QVariant editVariant = item->data(toColumn, Qt::EditRole); + QVariant toolTipVariant = item->data(toColumn, Qt::ToolTipRole); + QVariant statusTipVariant = item->data(toColumn, Qt::StatusTipRole); + QVariant whatsThisVariant = item->data(toColumn, Qt::WhatsThisRole); + QVariant decorationVariant = item->data(toColumn, Qt::DecorationRole); + for (int i = toColumn; i != fromColumn; i += step) { + for (int j = 0; propList[j].name; j++) + item->setData(i, propList[j].role, item->data(i + step, propList[j].role)); + item->setData(i, Qt::EditRole, item->data(i + step, Qt::EditRole)); + item->setData(i, Qt::ToolTipRole, item->data(i + step, Qt::ToolTipRole)); + item->setData(i, Qt::StatusTipRole, item->data(i + step, Qt::StatusTipRole)); + item->setData(i, Qt::WhatsThisRole, item->data(i + step, Qt::WhatsThisRole)); + item->setData(i, Qt::DecorationRole, item->data(i + step, Qt::DecorationRole)); + } + for (int j = 0; propList[j].name; j++) + item->setData(fromColumn, propList[j].role, saveCol[j]); + item->setData(fromColumn, Qt::EditRole, editVariant); + item->setData(fromColumn, Qt::ToolTipRole, toolTipVariant); + item->setData(fromColumn, Qt::StatusTipRole, statusTipVariant); + item->setData(fromColumn, Qt::WhatsThisRole, whatsThisVariant); + item->setData(fromColumn, Qt::DecorationRole, decorationVariant); +} + +void TreeWidgetEditor::moveColumns(int fromColumn, int toColumn, int step) +{ + ui.treeWidget->blockSignals(true); + + moveColumnItems(treeHeaderPropList, ui.treeWidget->headerItem(), fromColumn, toColumn, step); + + QQueue pendingQueue; + for (int i = 0; i < ui.treeWidget->topLevelItemCount(); i++) + pendingQueue.enqueue(ui.treeWidget->topLevelItem(i)); + + while (!pendingQueue.isEmpty()) { + QTreeWidgetItem *item = pendingQueue.dequeue(); + for (int i = 0; i < item->childCount(); i++) + pendingQueue.enqueue(item->child(i)); + + moveColumnItems(treeItemColumnPropList, item, fromColumn, toColumn, step); + } + + ui.treeWidget->blockSignals(false); +} + +void TreeWidgetEditor::moveColumnsLeft(int fromColumn, int toColumn) +{ + if (fromColumn >= toColumn) + return; + + moveColumns(fromColumn, toColumn, -1); +} + +void TreeWidgetEditor::moveColumnsRight(int fromColumn, int toColumn) +{ + if (fromColumn >= toColumn) + return; + + moveColumns(toColumn, fromColumn, 1); +} + +void TreeWidgetEditor::on_columnEditor_itemInserted(int idx) +{ + int columnCount = ui.treeWidget->columnCount(); + ui.treeWidget->setColumnCount(columnCount + 1); + ui.treeWidget->headerItem()->setText(columnCount, m_columnEditor->newItemText()); + moveColumnsLeft(idx, columnCount); + + updateEditor(); +} + +void TreeWidgetEditor::on_columnEditor_itemDeleted(int idx) +{ + closeEditors(); + + int columnCount = ui.treeWidget->columnCount() - 1; + if (!columnCount) + ui.treeWidget->clear(); + else + moveColumnsRight(idx, columnCount); + ui.treeWidget->setColumnCount(columnCount); + + updateEditor(); +} + +void TreeWidgetEditor::on_columnEditor_itemMovedUp(int idx) +{ + moveColumnsRight(idx - 1, idx); + + ui.treeWidget->setCurrentItem(ui.treeWidget->currentItem(), idx - 1); + updateEditor(); +} + +void TreeWidgetEditor::on_columnEditor_itemMovedDown(int idx) +{ + moveColumnsLeft(idx, idx + 1); + + ui.treeWidget->setCurrentItem(ui.treeWidget->currentItem(), idx + 1); + updateEditor(); +} + +void TreeWidgetEditor::closeEditors() +{ + if (QTreeWidgetItem *cur = ui.treeWidget->currentItem() ) { + const int numCols = cur->columnCount (); + for (int i = 0; i < numCols; i++) + ui.treeWidget->closePersistentEditor (cur, i); + } +} + +void TreeWidgetEditor::cacheReloaded() +{ + reloadIconResources(iconCache(), ui.treeWidget); +} + +TreeWidgetEditorDialog::TreeWidgetEditorDialog(QDesignerFormWindowInterface *form, QWidget *parent) : + QDialog(parent), m_editor(form, this) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); +} + +TreeWidgetContents TreeWidgetEditorDialog::fillContentsFromTreeWidget(QTreeWidget *treeWidget) +{ + return m_editor.fillContentsFromTreeWidget(treeWidget); +} + +TreeWidgetContents TreeWidgetEditorDialog::contents() const +{ + return m_editor.contents(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/taskmenu/treewidgeteditor.h b/src/designer/components/taskmenu/treewidgeteditor.h new file mode 100644 index 000000000..2f069102b --- /dev/null +++ b/src/designer/components/taskmenu/treewidgeteditor.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TREEWIDGETEDITOR_H +#define TREEWIDGETEDITOR_H + +#include "ui_treewidgeteditor.h" + +#include "listwidgeteditor.h" + +#include + +QT_BEGIN_NAMESPACE + +class QTreeWidget; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class FormWindowBase; +class PropertySheetIconValue; + +class TreeWidgetEditor: public AbstractItemEditor +{ + Q_OBJECT +public: + explicit TreeWidgetEditor(QDesignerFormWindowInterface *form, QDialog *dialog); + + TreeWidgetContents fillContentsFromTreeWidget(QTreeWidget *treeWidget); + TreeWidgetContents contents() const; + +private slots: + void on_newItemButton_clicked(); + void on_newSubItemButton_clicked(); + void on_deleteItemButton_clicked(); + void on_moveItemUpButton_clicked(); + void on_moveItemDownButton_clicked(); + void on_moveItemRightButton_clicked(); + void on_moveItemLeftButton_clicked(); + + void on_treeWidget_currentItemChanged(); + void on_treeWidget_itemChanged(QTreeWidgetItem *item, int column); + + void on_columnEditor_indexChanged(int idx); + void on_columnEditor_itemChanged(int idx, int role, const QVariant &v); + + void on_columnEditor_itemInserted(int idx); + void on_columnEditor_itemDeleted(int idx); + void on_columnEditor_itemMovedUp(int idx); + void on_columnEditor_itemMovedDown(int idx); + + void togglePropertyBrowser(); + void cacheReloaded(); + +protected: + virtual void setItemData(int role, const QVariant &v); + virtual QVariant getItemData(int role) const; + +private: + void setPropertyBrowserVisible(bool v); + QtVariantProperty *setupPropertyGroup(const QString &title, PropertyDefinition *propDefs); + void updateEditor(); + void moveColumnItems(const PropertyDefinition *propList, QTreeWidgetItem *item, int fromColumn, int toColumn, int step); + void moveColumns(int fromColumn, int toColumn, int step); + void moveColumnsLeft(int fromColumn, int toColumn); + void moveColumnsRight(int fromColumn, int toColumn); + void closeEditors(); + + Ui::TreeWidgetEditor ui; + ItemListEditor *m_columnEditor; + bool m_updatingBrowser; +}; + +class TreeWidgetEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit TreeWidgetEditorDialog(QDesignerFormWindowInterface *form, QWidget *parent); + + TreeWidgetContents fillContentsFromTreeWidget(QTreeWidget *treeWidget); + TreeWidgetContents contents() const; + +private: + TreeWidgetEditor m_editor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // TREEWIDGETEDITOR_H diff --git a/src/designer/components/taskmenu/treewidgeteditor.ui b/src/designer/components/taskmenu/treewidgeteditor.ui new file mode 100644 index 000000000..54b0e49c2 --- /dev/null +++ b/src/designer/components/taskmenu/treewidgeteditor.ui @@ -0,0 +1,257 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +********************************************************************* + qdesigner_internal::TreeWidgetEditor + + + + 0 + 0 + 700 + 360 + + + + Edit Tree Widget + + + + + + 0 + + + + &Items + + + + 9 + + + 9 + + + 9 + + + + + + 0 + + + + + Qt::WheelFocus + + + Tree Items + + + + 1 + + + + + + + + + + New Item + + + &New + + + + + + + New Subitem + + + New &Subitem + + + + + + + Delete Item + + + &Delete + + + + + + + Qt::Horizontal + + + + 28 + 23 + + + + + + + + Move Item Left (before Parent Item) + + + L + + + + + + + Move Item Right (as a First Subitem of the Next Sibling Item) + + + R + + + + + + + Move Item Up + + + U + + + + + + + Move Item Down + + + D + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Properties &>> + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + qdesigner_internal::TreeWidgetEditor + accept() + + + 440 + 335 + + + 373 + 362 + + + + + buttonBox + rejected() + qdesigner_internal::TreeWidgetEditor + reject() + + + 556 + 335 + + + 562 + 362 + + + + + diff --git a/src/designer/components/widgetbox/widgetbox.cmake b/src/designer/components/widgetbox/widgetbox.cmake new file mode 100644 index 000000000..3638caba6 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox.cmake @@ -0,0 +1,18 @@ +set(DESIGNERCOMPONENTS_HEADERS + ${DESIGNERCOMPONENTS_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetboxcategorylistview.h + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetboxtreewidget.h + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox.h + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox_global.h + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox_dnditem.h +) + +set(DESIGNERCOMPONENTS_SOURCES + ${DESIGNERCOMPONENTS_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetboxcategorylistview.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetboxtreewidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox_dnditem.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/widgetbox/widgetbox.qrc +) + diff --git a/src/designer/components/widgetbox/widgetbox.cpp b/src/designer/components/widgetbox/widgetbox.cpp new file mode 100644 index 000000000..ca98cd4ad --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetbox.h" +#include "widgetboxtreewidget.h" +#include "widgetbox_dnditem.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +WidgetBox::WidgetBox(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) + : QDesignerWidgetBox(parent, flags), + m_core(core), + m_view(new WidgetBoxTreeWidget(m_core)) +{ + + QVBoxLayout *l = new QVBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + + // Prevent the filter from grabbing focus since Our view has Qt::NoFocus + FilterWidget *filterWidget = new FilterWidget(0, FilterWidget::LayoutAlignNone); + filterWidget->setRefuseFocus(true); + connect(filterWidget, SIGNAL(filterChanged(QString)), m_view, SLOT(filter(QString))); + + QToolBar *toolBar = new QToolBar(this); + toolBar->addWidget(filterWidget); + l->addWidget(toolBar); + + // View + connect(m_view, SIGNAL(pressed(QString,QString,QPoint)), + this, SLOT(handleMousePress(QString,QString,QPoint))); + l->addWidget(m_view); + + setAcceptDrops (true); +} + +WidgetBox::~WidgetBox() +{ +} + +QDesignerFormEditorInterface *WidgetBox::core() const +{ + return m_core; +} + +void WidgetBox::handleMousePress(const QString &name, const QString &xml, const QPoint &global_mouse_pos) +{ + if (QApplication::mouseButtons() != Qt::LeftButton) + return; + + DomUI *ui = xmlToUi(name, xml, true); + if (ui == 0) + return; + QList item_list; + item_list.append(new WidgetBoxDnDItem(core(), ui, global_mouse_pos)); + m_core->formWindowManager()->dragItems(item_list); +} + +int WidgetBox::categoryCount() const +{ + return m_view->categoryCount(); +} + +QDesignerWidgetBoxInterface::Category WidgetBox::category(int cat_idx) const +{ + return m_view->category(cat_idx); +} + +void WidgetBox::addCategory(const Category &cat) +{ + m_view->addCategory(cat); +} + +void WidgetBox::removeCategory(int cat_idx) +{ + m_view->removeCategory(cat_idx); +} + +int WidgetBox::widgetCount(int cat_idx) const +{ + return m_view->widgetCount(cat_idx); +} + +QDesignerWidgetBoxInterface::Widget WidgetBox::widget(int cat_idx, int wgt_idx) const +{ + return m_view->widget(cat_idx, wgt_idx); +} + +void WidgetBox::addWidget(int cat_idx, const Widget &wgt) +{ + m_view->addWidget(cat_idx, wgt); +} + +void WidgetBox::removeWidget(int cat_idx, int wgt_idx) +{ + m_view->removeWidget(cat_idx, wgt_idx); +} + +void WidgetBox::dropWidgets(const QList &item_list, const QPoint&) +{ + m_view->dropWidgets(item_list); +} + +void WidgetBox::setFileName(const QString &file_name) +{ + m_view->setFileName(file_name); +} + +QString WidgetBox::fileName() const +{ + return m_view->fileName(); +} + +bool WidgetBox::load() +{ + return m_view->load(loadMode()); +} + +bool WidgetBox::loadContents(const QString &contents) +{ + return m_view->loadContents(contents); +} + +bool WidgetBox::save() +{ + return m_view->save(); +} + +static const QDesignerMimeData *checkDragEvent(QDropEvent * event, + bool acceptEventsFromWidgetBox) +{ + const QDesignerMimeData *mimeData = qobject_cast(event->mimeData()); + if (!mimeData) { + event->ignore(); + return 0; + } + // If desired, ignore a widget box drag and drop, where widget==0. + if (!acceptEventsFromWidgetBox) { + const bool fromWidgetBox = !mimeData->items().first()->widget(); + if (fromWidgetBox) { + event->ignore(); + return 0; + } + } + + mimeData->acceptEvent(event); + return mimeData; +} + +void WidgetBox::dragEnterEvent (QDragEnterEvent * event) +{ + // We accept event originating from the widget box also here, + // because otherwise Windows will not show the DnD pixmap. + checkDragEvent(event, true); +} + +void WidgetBox::dragMoveEvent(QDragMoveEvent * event) +{ + checkDragEvent(event, true); +} + +void WidgetBox::dropEvent(QDropEvent * event) +{ + const QDesignerMimeData *mimeData = checkDragEvent(event, false); + if (!mimeData) + return; + + dropWidgets(mimeData->items(), event->pos()); + QDesignerMimeData::removeMovedWidgetsFromSourceForm(mimeData->items()); +} + +QIcon WidgetBox::iconForWidget(const QString &className, const QString &category) const +{ + Widget widgetData; + if (!findWidget(this, className, category, &widgetData)) + return QIcon(); + return m_view->iconForWidget(widgetData.iconName()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include +#include diff --git a/src/designer/components/widgetbox/widgetbox.h b/src/designer/components/widgetbox/widgetbox.h new file mode 100644 index 000000000..ccae614e1 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETBOX_H +#define WIDGETBOX_H + +#include "widgetbox_global.h" +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class WidgetBoxTreeWidget; + +class QT_WIDGETBOX_EXPORT WidgetBox : public QDesignerWidgetBox +{ + Q_OBJECT +public: + explicit WidgetBox(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~WidgetBox(); + + QDesignerFormEditorInterface *core() const; + + virtual int categoryCount() const; + virtual Category category(int cat_idx) const; + virtual void addCategory(const Category &cat); + virtual void removeCategory(int cat_idx); + + virtual int widgetCount(int cat_idx) const; + virtual Widget widget(int cat_idx, int wgt_idx) const; + virtual void addWidget(int cat_idx, const Widget &wgt); + virtual void removeWidget(int cat_idx, int wgt_idx); + + void dropWidgets(const QList &item_list, const QPoint &global_mouse_pos); + + virtual void setFileName(const QString &file_name); + virtual QString fileName() const; + virtual bool load(); + virtual bool save(); + + virtual bool loadContents(const QString &contents); + virtual QIcon iconForWidget(const QString &className, const QString &category = QString()) const; + +protected: + virtual void dragEnterEvent (QDragEnterEvent * event); + virtual void dragMoveEvent(QDragMoveEvent * event); + virtual void dropEvent (QDropEvent * event); + +private slots: + void handleMousePress(const QString &name, const QString &xml, const QPoint &global_mouse_pos); + +private: + QDesignerFormEditorInterface *m_core; + WidgetBoxTreeWidget *m_view; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETBOX_H diff --git a/src/designer/components/widgetbox/widgetbox.qrc b/src/designer/components/widgetbox/widgetbox.qrc new file mode 100644 index 000000000..7ecf78c85 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox.qrc @@ -0,0 +1,5 @@ + + + widgetbox.xml + + diff --git a/src/designer/components/widgetbox/widgetbox.xml b/src/designer/components/widgetbox/widgetbox.xml new file mode 100644 index 000000000..8b71b3d92 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox.xml @@ -0,0 +1,932 @@ + + + + + + + + + + verticalLayoutWidget + + + + 0 + 0 + 160 + 80 + + + + + + + + + + + + + horizontalLayoutWidget + + + + 0 + 0 + 160 + 80 + + + + + + + + + + + + + gridLayoutWidget + + + + 0 + 0 + 160 + 80 + + + + + + + + + + + + + formLayoutWidget + + + + 0 + 0 + 160 + 80 + + + + + + + + + + + + + + + + + Qt::Horizontal + + + horizontalSpacer + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + verticalSpacer + + + + 20 + 40 + + + + + + + + + + + + + + + PushButton + + + pushButton + + + + + + + + + + toolButton + + + ... + + + + + + + + + + RadioButton + + + radioButton + + + + + + + + + + CheckBox + + + checkBox + + + + + + + + + + CommandLinkButton + + + commandLinkButton + + + + + + + + + + QDialogButtonBox::Ok|QDialogButtonBox::Cancel + + + buttonBox + + + + + + + + + + + + + + listView + + + + + + + + + + treeView + + + + + + + + + + tableView + + + + + + + + + + columnView + + + + + + + + + + + + + listWidget + + + + + + + + + + treeWidget + + + + + + + + + + tableWidget + + + + + + + + + + + + + + GroupBox + + + + 0 + 0 + 120 + 80 + + + + groupBox + + + + + + + + + + scrollArea + + + true + + + + 0 + 0 + 120 + 80 + + + + + + + + + + + + + 0 + + + toolBox + + + + Page 1 + + + + + Page 2 + + + + + + + + + + + + 0 + 0 + 120 + 80 + + + + tabWidget + + + + Tab 1 + + + + + Tab 2 + + + + + + + + + + + + 0 + 0 + 120 + 80 + + + + stackedWidget + + + + + + + + + + + + QFrame::Raised + + + + 0 + 0 + 120 + 80 + + + + QFrame::StyledPanel + + + frame + + + + + + + + + + + 0 + 0 + 120 + 80 + + + + widget + + + + + + + + + + + 0 + 0 + 200 + 160 + + + + mdiArea + + + + + + + + + + + 0 + 0 + 120 + 80 + + + + dockWidget + + + + + + + + + + + + + + + + 119 + 28 + 41 + 22 + + + + comboBox + + + + + + + + + + + 119 + 28 + 41 + 22 + + + + fontComboBox + + + + + + + + + + + 0 + 1 + 113 + 20 + + + + lineEdit + + + + + + + + + + textEdit + + + + 0 + 0 + 104 + 64 + + + + + + + + + + + plainTextEdit + + + + 0 + 0 + 104 + 64 + + + + + + + + + + + + 119 + 0 + 42 + 22 + + + + spinBox + + + + + + + + + + + 119 + 0 + 62 + 22 + + + + doubleSpinBox + + + + + + + + + + + 0 + 28 + 118 + 22 + + + + timeEdit + + + + + + + + + + + 0 + 28 + 110 + 22 + + + + dateEdit + + + + + + + + + + + 0 + 28 + 194 + 22 + + + + dateTimeEdit + + + + + + + + + + + 110 + 0 + 50 + 64 + + + + dial + + + + + + + + + + Qt::Horizontal + + + + 0 + 126 + 160 + 16 + + + + horizontalScrollBar + + + + + + + + + + Qt::Vertical + + + + 0 + 126 + 16 + 160 + + + + verticalScrollBar + + + + + + + + + + Qt::Horizontal + + + + 0 + 126 + 160 + 16 + + + + horizontalSlider + + + + + + + + + + Qt::Vertical + + + + 0 + 126 + 16 + 160 + + + + verticalSlider + + + + + + + + + + + + + + + TextLabel + + + label + + + + + + + + + + textBrowser + + + + + + + + + + graphicsView + + + + + + + + + + calendarWidget + + + + + + + + + + lcdNumber + + + + + + + + + + 24 + + + + 9 + 38 + 118 + 23 + + + + progressBar + + + + + + + + + + Qt::Horizontal + + + line + + + + 9 + 67 + 118 + 3 + + + + + + + + + + + Qt::Vertical + + + line + + + + 133 + 9 + 3 + 61 + + + + + + + + diff --git a/src/designer/components/widgetbox/widgetbox_dnditem.cpp b/src/designer/components/widgetbox/widgetbox_dnditem.cpp new file mode 100644 index 000000000..14ea8c243 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox_dnditem.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetbox_dnditem.h" +#include "ui4_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +/******************************************************************************* +** WidgetBoxResource +*/ + +static inline DeviceProfile currentDeviceProfile(const QDesignerFormEditorInterface *core) +{ + if (QDesignerFormWindowInterface *cfw = core->formWindowManager()->activeFormWindow()) + if (const FormWindowBase *fwb = qobject_cast(cfw)) + return fwb->deviceProfile(); + return DeviceProfile(); +} + +class WidgetBoxResource : public QDesignerFormBuilder +{ +public: + WidgetBoxResource(QDesignerFormEditorInterface *core); + + // protected->public + QWidget *createUI(DomUI *ui, QWidget *parents) { return QDesignerFormBuilder::create(ui, parents); } + +protected: + + virtual QWidget *create(DomWidget *ui_widget, QWidget *parents); + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual void createCustomWidgets(DomCustomWidgets *); +}; + +WidgetBoxResource::WidgetBoxResource(QDesignerFormEditorInterface *core) : + QDesignerFormBuilder(core, DisableScripts, currentDeviceProfile(core)) +{ +} + + +QWidget *WidgetBoxResource::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + if (widgetName == QLatin1String("Spacer")) { + Spacer *spacer = new Spacer(parentWidget); + spacer->setObjectName(name); + return spacer; + } + + return QDesignerFormBuilder::createWidget(widgetName, parentWidget, name); +} + +QWidget *WidgetBoxResource::create(DomWidget *ui_widget, QWidget *parent) +{ + QWidget *result = QDesignerFormBuilder::create(ui_widget, parent); + // It is possible to have a syntax error or something in custom + // widget XML, so, try to recover here by creating an artificial + // top level + widget. + if (!result) { + const QString msg = QApplication::translate("qdesigner_internal::WidgetBox", "Warning: Widget creation failed in the widget box. This could be caused by invalid custom widget XML."); + qdesigner_internal::designerWarning(msg); + result = new QWidget(parent); + new QWidget(result); + } + result->setFocusPolicy(Qt::NoFocus); + result->setObjectName(ui_widget->attributeName()); + return result; +} + +void WidgetBoxResource::createCustomWidgets(DomCustomWidgets *dc) +{ + // Make a promotion entry in case someone has a promoted widget + // in the scratchpad. + QSimpleResource::handleDomCustomWidgets(core(), dc); + +} + +/******************************************************************************* +** WidgetBoxResource +*/ + +static QSize geometryProp(const DomWidget *dw) +{ + const QList prop_list = dw->elementProperty(); + const QString geometry = QLatin1String("geometry"); + foreach (DomProperty *prop, prop_list) { + if (prop->attributeName() != geometry) + continue; + DomRect *dr = prop->elementRect(); + if (dr == 0) + continue; + return QSize(dr->elementWidth(), dr->elementHeight()); + } + return QSize(); +} + +static QSize domWidgetSize(const DomWidget *dw) +{ + QSize size = geometryProp(dw); + if (size.isValid()) + return size; + + foreach (const DomWidget *child, dw->elementWidget()) { + size = geometryProp(child); + if (size.isValid()) + return size; + } + + foreach (const DomLayout *dl, dw->elementLayout()) { + foreach (DomLayoutItem *item, dl->elementItem()) { + const DomWidget *child = item->elementWidget(); + if (child == 0) + continue; + size = geometryProp(child); + if (size.isValid()) + return size; + } + } + + return QSize(); +} + +static QWidget *decorationFromDomWidget(DomUI *dom_ui, QDesignerFormEditorInterface *core) +{ + WidgetBoxResource builder(core); + // We have the builder create the articial QWidget fake top level as a tooltip + // because the size algorithm works better at weird DPI settings + // if the actual widget is created as a child of a container + QWidget *fakeTopLevel = builder.createUI(dom_ui, static_cast(0)); + fakeTopLevel->setParent(0, Qt::ToolTip); // Container + // Actual widget + const DomWidget *domW = dom_ui->elementWidget()->elementWidget().front(); + QWidget *w = fakeTopLevel->findChildren().front(); + Q_ASSERT(w); + // hack begin; + // We set _q_dockDrag dynamic property which will be detected in drag enter event of form window. + // Dock drop is handled in special way (highlight goes to central widget of main window) + if (qobject_cast(w)) + fakeTopLevel->setProperty("_q_dockDrag", QVariant(true)); + // hack end; + w->setAutoFillBackground(true); // Different style for embedded + QSize size = domWidgetSize(domW); + const QSize minimumSize = w->minimumSizeHint(); + if (!size.isValid()) + size = w->sizeHint(); + if (size.width() < minimumSize.width()) + size.setWidth(minimumSize.width()); + if (size.height() < minimumSize.height()) + size.setHeight(minimumSize.height()); + // A QWidget might have size -1,-1 if no geometry property is specified in the widget box. + if (size.isEmpty()) + size = size.expandedTo(QSize(16, 16)); + w->setGeometry(QRect(QPoint(0, 0), size)); + fakeTopLevel->resize(size); + return fakeTopLevel; +} + +WidgetBoxDnDItem::WidgetBoxDnDItem(QDesignerFormEditorInterface *core, + DomUI *dom_ui, + const QPoint &global_mouse_pos) : + QDesignerDnDItem(CopyDrop) +{ + QWidget *decoration = decorationFromDomWidget(dom_ui, core); + decoration->move(global_mouse_pos - QPoint(5, 5)); + + init(dom_ui, 0, decoration, global_mouse_pos); +} +} + +QT_END_NAMESPACE diff --git a/src/designer/components/widgetbox/widgetbox_dnditem.h b/src/designer/components/widgetbox/widgetbox_dnditem.h new file mode 100644 index 000000000..085b48ed0 --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox_dnditem.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETBOX_DNDITEM_H +#define WIDGETBOX_DNDITEM_H + +#include +#include "widgetbox_global.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class DomUI; + +namespace qdesigner_internal { + +class QT_WIDGETBOX_EXPORT WidgetBoxDnDItem : public QDesignerDnDItem +{ +public: + WidgetBoxDnDItem(QDesignerFormEditorInterface *core, + DomUI *dom_ui, + const QPoint &global_mouse_pos); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETBOX_DNDITEM_H diff --git a/src/designer/components/widgetbox/widgetbox_global.h b/src/designer/components/widgetbox/widgetbox_global.h new file mode 100644 index 000000000..87804611e --- /dev/null +++ b/src/designer/components/widgetbox/widgetbox_global.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETBOX_GLOBAL_H +#define WIDGETBOX_GLOBAL_H + +#include + +#ifdef Q_OS_WIN +#ifdef QT_WIDGETBOX_LIBRARY +# define QT_WIDGETBOX_EXPORT +#else +# define QT_WIDGETBOX_EXPORT +#endif +#else +#define QT_WIDGETBOX_EXPORT +#endif + +#endif // WIDGETBOX_GLOBAL_H diff --git a/src/designer/components/widgetbox/widgetboxcategorylistview.cpp b/src/designer/components/widgetbox/widgetboxcategorylistview.cpp new file mode 100644 index 000000000..a4af69401 --- /dev/null +++ b/src/designer/components/widgetbox/widgetboxcategorylistview.cpp @@ -0,0 +1,511 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetboxcategorylistview.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const char *widgetElementC = "widget"; +static const char *nameAttributeC = "name"; +static const char *uiOpeningTagC = ""; +static const char *uiClosingTagC = ""; + +QT_BEGIN_NAMESPACE + +enum { FilterRole = Qt::UserRole + 11 }; + +static QString domToString(const QDomElement &elt) +{ + QString result; + QTextStream stream(&result, QIODevice::WriteOnly); + elt.save(stream, 2); + stream.flush(); + return result; +} + +static QDomDocument stringToDom(const QString &xml) +{ + QDomDocument result; + result.setContent(xml); + return result; +} + +namespace qdesigner_internal { + +// Entry of the model list + +struct WidgetBoxCategoryEntry { + WidgetBoxCategoryEntry(); + explicit WidgetBoxCategoryEntry(const QDesignerWidgetBoxInterface::Widget &widget, + const QString &filter, + const QIcon &icon, + bool editable); + + QDesignerWidgetBoxInterface::Widget widget; + QString toolTip; + QString whatsThis; + QString filter; + QIcon icon; + bool editable; +}; + + +WidgetBoxCategoryEntry::WidgetBoxCategoryEntry() : + editable(false) +{ +} + +WidgetBoxCategoryEntry::WidgetBoxCategoryEntry(const QDesignerWidgetBoxInterface::Widget &w, + const QString &filterIn, + const QIcon &i, bool e) : + widget(w), + filter(filterIn), + icon(i), + editable(e) +{ +} + +/* WidgetBoxCategoryModel, representing a list of category entries. Uses a + * QAbstractListModel since the behaviour depends on the view mode of the list + * view, it does not return text in the case of IconMode. */ + +class WidgetBoxCategoryModel : public QAbstractListModel { +public: + explicit WidgetBoxCategoryModel(QDesignerFormEditorInterface *core, QObject *parent = 0); + + // QAbstractListModel + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + virtual Qt::ItemFlags flags (const QModelIndex & index ) const; + virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + + // The model returns no text in icon mode, so, it also needs to know it + QListView::ViewMode viewMode() const; + void setViewMode(QListView::ViewMode vm); + + void addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon, bool editable); + + QDesignerWidgetBoxInterface::Widget widgetAt(const QModelIndex & index) const; + QDesignerWidgetBoxInterface::Widget widgetAt(int row) const; + + int indexOfWidget(const QString &name); + + QDesignerWidgetBoxInterface::Category category() const; + bool removeCustomWidgets(); + +private: + typedef QList WidgetBoxCategoryEntrys; + + QRegExp m_classNameRegExp; + QDesignerFormEditorInterface *m_core; + WidgetBoxCategoryEntrys m_items; + QListView::ViewMode m_viewMode; +}; + +WidgetBoxCategoryModel::WidgetBoxCategoryModel(QDesignerFormEditorInterface *core, QObject *parent) : + QAbstractListModel(parent), + m_classNameRegExp(QLatin1String("widget); + return rc; +} + +bool WidgetBoxCategoryModel::removeCustomWidgets() +{ + // Typically, we are a whole category of custom widgets, so, remove all + // and do reset. + bool changed = false; + for (WidgetBoxCategoryEntrys::iterator it = m_items.begin(); it != m_items.end(); ) + if (it->widget.type() == QDesignerWidgetBoxInterface::Widget::Custom) { + it = m_items.erase(it); + changed = true; + } else { + ++it; + } + if (changed) + reset(); + return changed; +} + +void WidgetBoxCategoryModel::addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon,bool editable) +{ + // build item. Filter on name + class name if it is different and not a layout. + QString filter = widget.name(); + if (!filter.contains(QLatin1String("Layout")) && m_classNameRegExp.indexIn(widget.domXml()) != -1) { + const QString className = m_classNameRegExp.cap(1); + if (!filter.contains(className)) + filter += className; + } + WidgetBoxCategoryEntry item(widget, filter, icon, editable); + const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase(); + const int dbIndex = db->indexOfClassName(widget.name()); + if (dbIndex != -1) { + const QDesignerWidgetDataBaseItemInterface *dbItem = db->item(dbIndex); + const QString toolTip = dbItem->toolTip(); + if (!toolTip.isEmpty()) + item.toolTip = toolTip; + const QString whatsThis = dbItem->whatsThis(); + if (!whatsThis.isEmpty()) + item.whatsThis = whatsThis; + } + // insert + const int row = m_items.size(); + beginInsertRows(QModelIndex(), row, row); + m_items.push_back(item); + endInsertRows(); +} + +QVariant WidgetBoxCategoryModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (row < 0 || row >= m_items.size()) + return QVariant(); + + const WidgetBoxCategoryEntry &item = m_items.at(row); + switch (role) { + case Qt::DisplayRole: + // No text in icon mode + return QVariant(m_viewMode == QListView::ListMode ? item.widget.name() : QString()); + case Qt::DecorationRole: + return QVariant(item.icon); + case Qt::EditRole: + return QVariant(item.widget.name()); + case Qt::ToolTipRole: { + if (m_viewMode == QListView::ListMode) + return QVariant(item.toolTip); + // Icon mode tooltip should contain the class name + QString tt = item.widget.name(); + if (!item.toolTip.isEmpty()) { + tt += QLatin1Char('\n'); + tt += item.toolTip; + } + return QVariant(tt); + + } + case Qt::WhatsThisRole: + return QVariant(item.whatsThis); + case FilterRole: + return item.filter; + } + return QVariant(); +} + +bool WidgetBoxCategoryModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + const int row = index.row(); + if (role != Qt::EditRole || row < 0 || row >= m_items.size() || value.type() != QVariant::String) + return false; + // Set name and adapt Xml + WidgetBoxCategoryEntry &item = m_items[row]; + const QString newName = value.toString(); + item.widget.setName(newName); + + const QDomDocument doc = stringToDom(WidgetBoxCategoryListView::widgetDomXml(item.widget)); + QDomElement widget_elt = doc.firstChildElement(QLatin1String(widgetElementC)); + if (!widget_elt.isNull()) { + widget_elt.setAttribute(QLatin1String(nameAttributeC), newName); + item.widget.setDomXml(domToString(widget_elt)); + } + emit dataChanged(index, index); + return true; +} + +Qt::ItemFlags WidgetBoxCategoryModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags rc = Qt::ItemIsEnabled; + const int row = index.row(); + if (row >= 0 && row < m_items.size()) + if (m_items.at(row).editable) { + rc |= Qt::ItemIsSelectable; + // Can change name in list mode only + if (m_viewMode == QListView::ListMode) + rc |= Qt::ItemIsEditable; + } + return rc; +} + +int WidgetBoxCategoryModel::rowCount(const QModelIndex & /*parent*/) const +{ + return m_items.size(); +} + +bool WidgetBoxCategoryModel::removeRows(int row, int count, const QModelIndex & parent) +{ + if (row < 0 || count < 1) + return false; + const int size = m_items.size(); + const int last = row + count - 1; + if (row >= size || last >= size) + return false; + beginRemoveRows(parent, row, last); + for (int r = last; r >= row; r--) + m_items.removeAt(r); + endRemoveRows(); + return true; +} + +QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryModel::widgetAt(const QModelIndex & index) const +{ + return widgetAt(index.row()); +} + +QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryModel::widgetAt(int row) const +{ + if (row < 0 || row >= m_items.size()) + return QDesignerWidgetBoxInterface::Widget(); + return m_items.at(row).widget; +} + +/* WidgetSubBoxItemDelegate, ensures a valid name using a regexp validator */ + +class WidgetBoxCategoryEntryDelegate : public QItemDelegate +{ +public: + explicit WidgetBoxCategoryEntryDelegate(QWidget *parent = 0) : QItemDelegate(parent) {} + QWidget *createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +QWidget *WidgetBoxCategoryEntryDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QWidget *result = QItemDelegate::createEditor(parent, option, index); + if (QLineEdit *line_edit = qobject_cast(result)) { + const QRegExp re = QRegExp(QLatin1String("[_a-zA-Z][_a-zA-Z0-9]*")); + Q_ASSERT(re.isValid()); + line_edit->setValidator(new QRegExpValidator(re, line_edit)); + } + return result; +} + +// ---------------------- WidgetBoxCategoryListView + +WidgetBoxCategoryListView::WidgetBoxCategoryListView(QDesignerFormEditorInterface *core, QWidget *parent) : + QListView(parent), + m_proxyModel(new QSortFilterProxyModel(this)), + m_model(new WidgetBoxCategoryModel(core, this)) +{ + setFocusPolicy(Qt::NoFocus); + setFrameShape(QFrame::NoFrame); + setIconSize(QSize(22, 22)); + setSpacing(1); + setTextElideMode(Qt::ElideMiddle); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setResizeMode(QListView::Adjust); + setUniformItemSizes(true); + + setItemDelegate(new WidgetBoxCategoryEntryDelegate(this)); + + connect(this, SIGNAL(pressed(QModelIndex)), this, SLOT(slotPressed(QModelIndex))); + setEditTriggers(QAbstractItemView::AnyKeyPressed); + + m_proxyModel->setSourceModel(m_model); + m_proxyModel->setFilterRole(FilterRole); + setModel(m_proxyModel); + connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SIGNAL(scratchPadChanged())); +} + +void WidgetBoxCategoryListView::setViewMode(ViewMode vm) +{ + QListView::setViewMode(vm); + m_model->setViewMode(vm); +} + +void WidgetBoxCategoryListView::setCurrentItem(AccessMode am, int row) +{ + const QModelIndex index = am == FilteredAccess ? + m_proxyModel->index(row, 0) : + m_proxyModel->mapFromSource(m_model->index(row, 0)); + + if (index.isValid()) + setCurrentIndex(index); +} + +void WidgetBoxCategoryListView::slotPressed(const QModelIndex &index) +{ + const QDesignerWidgetBoxInterface::Widget wgt = m_model->widgetAt(m_proxyModel->mapToSource(index)); + if (wgt.isNull()) + return; + emit pressed(wgt.name(), widgetDomXml(wgt), QCursor::pos()); +} + +void WidgetBoxCategoryListView::removeCurrentItem() +{ + const QModelIndex index = currentIndex(); + if (!index.isValid() || !m_proxyModel->removeRow(index.row())) + return; + + // We check the unfiltered item count here, we don't want to get removed if the + // filtered view is empty + if (m_model->rowCount()) { + emit itemRemoved(); + } else { + emit lastItemRemoved(); + } +} + +void WidgetBoxCategoryListView::editCurrentItem() +{ + const QModelIndex index = currentIndex(); + if (index.isValid()) + edit(index); +} + +int WidgetBoxCategoryListView::count(AccessMode am) const +{ + return am == FilteredAccess ? m_proxyModel->rowCount() : m_model->rowCount(); +} + +int WidgetBoxCategoryListView::mapRowToSource(int filterRow) const +{ + const QModelIndex filterIndex = m_proxyModel->index(filterRow, 0); + return m_proxyModel->mapToSource(filterIndex).row(); +} + +QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryListView::widgetAt(AccessMode am, const QModelIndex & index) const +{ + const QModelIndex unfilteredIndex = am == FilteredAccess ? m_proxyModel->mapToSource(index) : index; + return m_model->widgetAt(unfilteredIndex); +} + +QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryListView::widgetAt(AccessMode am, int row) const +{ + return m_model->widgetAt(am == UnfilteredAccess ? row : mapRowToSource(row)); +} + +void WidgetBoxCategoryListView::removeRow(AccessMode am, int row) +{ + m_model->removeRow(am == UnfilteredAccess ? row : mapRowToSource(row)); +} + +bool WidgetBoxCategoryListView::containsWidget(const QString &name) +{ + return m_model->indexOfWidget(name) != -1; +} + +void WidgetBoxCategoryListView::addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon, bool editable) +{ + m_model->addWidget(widget, icon, editable); +} + +QString WidgetBoxCategoryListView::widgetDomXml(const QDesignerWidgetBoxInterface::Widget &widget) +{ + QString domXml = widget.domXml(); + + if (domXml.isEmpty()) { + domXml = QLatin1String(uiOpeningTagC); + domXml += QLatin1String(""); + domXml += QLatin1String(uiClosingTagC); + } + return domXml; +} + +void WidgetBoxCategoryListView::filter(const QRegExp &re) +{ + m_proxyModel->setFilterRegExp(re); +} + +QDesignerWidgetBoxInterface::Category WidgetBoxCategoryListView::category() const +{ + return m_model->category(); +} + +bool WidgetBoxCategoryListView::removeCustomWidgets() +{ + return m_model->removeCustomWidgets(); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/widgetbox/widgetboxcategorylistview.h b/src/designer/components/widgetbox/widgetboxcategorylistview.h new file mode 100644 index 000000000..350f5450d --- /dev/null +++ b/src/designer/components/widgetbox/widgetboxcategorylistview.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETBOXCATEGORYLISTVIEW_H +#define WIDGETBOXCATEGORYLISTVIEW_H + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerDnDItemInterface; + +class QSortFilterProxyModel; +class QRegExp; + +namespace qdesigner_internal { + +class WidgetBoxCategoryModel; + +// List view of a category, switchable between icon and list mode. +// Provides a filtered view. +class WidgetBoxCategoryListView : public QListView +{ + Q_OBJECT +public: + // Whether to access the filtered or unfiltered view + enum AccessMode { FilteredAccess, UnfilteredAccess }; + + explicit WidgetBoxCategoryListView(QDesignerFormEditorInterface *core, QWidget *parent = 0); + void setViewMode(ViewMode vm); + + void dropWidgets(const QList &item_list); + + using QListView::contentsSize; + + // These methods operate on the filtered/unfiltered model according to accessmode + int count(AccessMode am) const; + QDesignerWidgetBoxInterface::Widget widgetAt(AccessMode am, const QModelIndex &index) const; + QDesignerWidgetBoxInterface::Widget widgetAt(AccessMode am, int row) const; + void removeRow(AccessMode am, int row); + void setCurrentItem(AccessMode am, int row); + + // These methods operate on the unfiltered model and are used for serialization + void addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon, bool editable); + bool containsWidget(const QString &name); + QDesignerWidgetBoxInterface::Category category() const; + bool removeCustomWidgets(); + + // Helper: Ensure a tag in the case of empty XML + static QString widgetDomXml(const QDesignerWidgetBoxInterface::Widget &widget); + +signals: + void scratchPadChanged(); + void pressed(const QString &name, const QString &xml, const QPoint &globalPos); + void itemRemoved(); + void lastItemRemoved(); + +public slots: + void filter(const QRegExp &re); + +private slots: + void slotPressed(const QModelIndex &index); + void removeCurrentItem(); + void editCurrentItem(); + +private: + int mapRowToSource(int filterRow) const; + QSortFilterProxyModel *m_proxyModel; + WidgetBoxCategoryModel *m_model; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETBOXCATEGORYLISTVIEW_H diff --git a/src/designer/components/widgetbox/widgetboxtreewidget.cpp b/src/designer/components/widgetbox/widgetboxtreewidget.cpp new file mode 100644 index 000000000..94433d022 --- /dev/null +++ b/src/designer/components/widgetbox/widgetboxtreewidget.cpp @@ -0,0 +1,1002 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetboxtreewidget.h" +#include "widgetboxcategorylistview.h" + +// shared +#include +#include +#include +#include +#include +#include + +// sdk +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const char *widgetBoxRootElementC = "widgetbox"; +static const char *widgetElementC = "widget"; +static const char *uiElementC = "ui"; +static const char *categoryElementC = "category"; +static const char *categoryEntryElementC = "categoryentry"; +static const char *nameAttributeC = "name"; +static const char *typeAttributeC = "type"; +static const char *iconAttributeC = "icon"; +static const char *defaultTypeValueC = "default"; +static const char *customValueC = "custom"; +static const char *iconPrefixC = "__qt_icon__"; +static const char *scratchPadValueC = "scratchpad"; +static const char *qtLogoC = "qtlogo.png"; +static const char *invisibleNameC = "[invisible]"; + +enum TopLevelRole { NORMAL_ITEM, SCRATCHPAD_ITEM, CUSTOM_ITEM }; + +QT_BEGIN_NAMESPACE + +static void setTopLevelRole(TopLevelRole tlr, QTreeWidgetItem *item) +{ + item->setData(0, Qt::UserRole, QVariant(tlr)); +} + +static TopLevelRole topLevelRole(const QTreeWidgetItem *item) +{ + return static_cast(item->data(0, Qt::UserRole).toInt()); +} + +namespace qdesigner_internal { + +WidgetBoxTreeWidget::WidgetBoxTreeWidget(QDesignerFormEditorInterface *core, QWidget *parent) : + QTreeWidget(parent), + m_core(core), + m_iconMode(false), + m_scratchPadDeleteTimer(0) +{ + setFocusPolicy(Qt::NoFocus); + setIndentation(0); + setRootIsDecorated(false); + setColumnCount(1); + header()->hide(); + header()->setResizeMode(QHeaderView::Stretch); + setTextElideMode(Qt::ElideMiddle); + setVerticalScrollMode(ScrollPerPixel); + + setItemDelegate(new SheetDelegate(this, this)); + + connect(this, SIGNAL(itemPressed(QTreeWidgetItem*,int)), + this, SLOT(handleMousePress(QTreeWidgetItem*))); +} + +QIcon WidgetBoxTreeWidget::iconForWidget(QString iconName) const +{ + if (iconName.isEmpty()) + iconName = QLatin1String(qtLogoC); + + if (iconName.startsWith(QLatin1String(iconPrefixC))) { + const IconCache::const_iterator it = m_pluginIcons.constFind(iconName); + if (it != m_pluginIcons.constEnd()) + return it.value(); + } + return createIconSet(iconName); +} + +WidgetBoxCategoryListView *WidgetBoxTreeWidget::categoryViewAt(int idx) const +{ + WidgetBoxCategoryListView *rc = 0; + if (QTreeWidgetItem *cat_item = topLevelItem(idx)) + if (QTreeWidgetItem *embedItem = cat_item->child(0)) + rc = qobject_cast(itemWidget(embedItem, 0)); + Q_ASSERT(rc); + return rc; +} + +void WidgetBoxTreeWidget::saveExpandedState() const +{ + QStringList closedCategories; + if (const int numCategories = categoryCount()) { + for (int i = 0; i < numCategories; ++i) { + const QTreeWidgetItem *cat_item = topLevelItem(i); + if (!isItemExpanded(cat_item)) + closedCategories.append(cat_item->text(0)); + } + } + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(widgetBoxRootElementC)); + settings->setValue(QLatin1String("Closed categories"), closedCategories); + settings->setValue(QLatin1String("View mode"), m_iconMode); + settings->endGroup(); +} + +void WidgetBoxTreeWidget::restoreExpandedState() +{ + typedef QSet StringSet; + QDesignerSettingsInterface *settings = m_core->settingsManager(); + m_iconMode = settings->value(QLatin1String("WidgetBox/View mode")).toBool(); + updateViewMode(); + const StringSet closedCategories = settings->value(QLatin1String("WidgetBox/Closed categories"), QStringList()).toStringList().toSet(); + expandAll(); + if (closedCategories.empty()) + return; + + if (const int numCategories = categoryCount()) { + for (int i = 0; i < numCategories; ++i) { + QTreeWidgetItem *item = topLevelItem(i); + if (closedCategories.contains(item->text(0))) + item->setExpanded(false); + } + } +} + +WidgetBoxTreeWidget::~WidgetBoxTreeWidget() +{ + saveExpandedState(); +} + +void WidgetBoxTreeWidget::setFileName(const QString &file_name) +{ + m_file_name = file_name; +} + +QString WidgetBoxTreeWidget::fileName() const +{ + return m_file_name; +} + +bool WidgetBoxTreeWidget::save() +{ + if (fileName().isEmpty()) + return false; + + QFile file(fileName()); + if (!file.open(QIODevice::WriteOnly)) + return false; + + CategoryList cat_list; + const int count = categoryCount(); + for (int i = 0; i < count; ++i) + cat_list.append(category(i)); + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + writeCategories(writer, cat_list); + writer.writeEndDocument(); + + return true; +} + +void WidgetBoxTreeWidget::slotSave() +{ + save(); +} + +void WidgetBoxTreeWidget::handleMousePress(QTreeWidgetItem *item) +{ + if (item == 0) + return; + + if (QApplication::mouseButtons() != Qt::LeftButton) + return; + + if (item->parent() == 0) { + setItemExpanded(item, !isItemExpanded(item)); + return; + } +} + +int WidgetBoxTreeWidget::ensureScratchpad() +{ + const int existingIndex = indexOfScratchpad(); + if (existingIndex != -1) + return existingIndex; + + QTreeWidgetItem *scratch_item = new QTreeWidgetItem(this); + scratch_item->setText(0, tr("Scratchpad")); + setTopLevelRole(SCRATCHPAD_ITEM, scratch_item); + addCategoryView(scratch_item, false); // Scratchpad in list mode. + return categoryCount() - 1; +} + +WidgetBoxCategoryListView *WidgetBoxTreeWidget::addCategoryView(QTreeWidgetItem *parent, bool iconMode) +{ + QTreeWidgetItem *embed_item = new QTreeWidgetItem(parent); + embed_item->setFlags(Qt::ItemIsEnabled); + WidgetBoxCategoryListView *categoryView = new WidgetBoxCategoryListView(m_core, this); + categoryView->setViewMode(iconMode ? QListView::IconMode : QListView::ListMode); + connect(categoryView, SIGNAL(scratchPadChanged()), this, SLOT(slotSave())); + connect(categoryView, SIGNAL(pressed(QString,QString,QPoint)), this, SIGNAL(pressed(QString,QString,QPoint))); + connect(categoryView, SIGNAL(itemRemoved()), this, SLOT(slotScratchPadItemDeleted())); + connect(categoryView, SIGNAL(lastItemRemoved()), this, SLOT(slotLastScratchPadItemDeleted())); + setItemWidget(embed_item, 0, categoryView); + return categoryView; +} + +int WidgetBoxTreeWidget::indexOfScratchpad() const +{ + if (const int numTopLevels = topLevelItemCount()) { + for (int i = numTopLevels - 1; i >= 0; --i) { + if (topLevelRole(topLevelItem(i)) == SCRATCHPAD_ITEM) + return i; + } + } + return -1; +} + +int WidgetBoxTreeWidget::indexOfCategory(const QString &name) const +{ + const int topLevelCount = topLevelItemCount(); + for (int i = 0; i < topLevelCount; ++i) { + if (topLevelItem(i)->text(0) == name) + return i; + } + return -1; +} + +bool WidgetBoxTreeWidget::load(QDesignerWidgetBox::LoadMode loadMode) +{ + switch (loadMode) { + case QDesignerWidgetBox::LoadReplace: + clear(); + break; + case QDesignerWidgetBox::LoadCustomWidgetsOnly: + addCustomCategories(true); + updateGeometries(); + return true; + default: + break; + } + + const QString name = fileName(); + + QFile f(name); + if (!f.open(QIODevice::ReadOnly)) // Might not exist at first startup + return false; + + const QString contents = QString::fromUtf8(f.readAll()); + return loadContents(contents); +} + +bool WidgetBoxTreeWidget::loadContents(const QString &contents) +{ + QString errorMessage; + CategoryList cat_list; + if (!readCategories(m_file_name, contents, &cat_list, &errorMessage)) { + qdesigner_internal::designerWarning(errorMessage); + return false; + } + + foreach(const Category &cat, cat_list) + addCategory(cat); + + addCustomCategories(false); + // Restore which items are expanded + restoreExpandedState(); + return true; +} + +void WidgetBoxTreeWidget::addCustomCategories(bool replace) +{ + if (replace) { + // clear out all existing custom widgets + if (const int numTopLevels = topLevelItemCount()) { + for (int t = 0; t < numTopLevels ; ++t) + categoryViewAt(t)->removeCustomWidgets(); + } + } + // re-add + const CategoryList customList = loadCustomCategoryList(); + const CategoryList::const_iterator cend = customList.constEnd(); + for (CategoryList::const_iterator it = customList.constBegin(); it != cend; ++it) + addCategory(*it); +} + +static inline QString msgXmlError(const QString &fileName, const QXmlStreamReader &r) +{ + return QDesignerWidgetBox::tr("An error has been encountered at line %1 of %2: %3") + .arg(r.lineNumber()).arg(fileName, r.errorString()); +} + +bool WidgetBoxTreeWidget::readCategories(const QString &fileName, const QString &contents, + CategoryList *cats, QString *errorMessage) +{ + // Read widget box XML: + // + // + // + // + // + // ... + + QXmlStreamReader reader(contents); + + + // Entries of category with name="invisible" should be ignored + bool ignoreEntries = false; + + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement: { + const QStringRef tag = reader.name(); + if (tag == QLatin1String(widgetBoxRootElementC)) { + // + continue; + } + if (tag == QLatin1String(categoryElementC)) { + // + const QXmlStreamAttributes attributes = reader.attributes(); + const QString categoryName = attributes.value(QLatin1String(nameAttributeC)).toString(); + if (categoryName == QLatin1String(invisibleNameC)) { + ignoreEntries = true; + } else { + Category category(categoryName); + if (attributes.value(QLatin1String(typeAttributeC)) == QLatin1String(scratchPadValueC)) + category.setType(Category::Scratchpad); + cats->push_back(category); + } + continue; + } + if (tag == QLatin1String(categoryEntryElementC)) { + // + if (!ignoreEntries) { + QXmlStreamAttributes attr = reader.attributes(); + const QString widgetName = attr.value(QLatin1String(nameAttributeC)).toString(); + const QString widgetIcon = attr.value(QLatin1String(iconAttributeC)).toString(); + const WidgetBoxTreeWidget::Widget::Type widgetType = + attr.value(QLatin1String(typeAttributeC)).toString() + == QLatin1String(customValueC) ? + WidgetBoxTreeWidget::Widget::Custom : + WidgetBoxTreeWidget::Widget::Default; + + Widget w; + w.setName(widgetName); + w.setIconName(widgetIcon); + w.setType(widgetType); + if (!readWidget(&w, contents, reader)) + continue; + + cats->back().addWidget(w); + } // ignoreEntries + continue; + } + break; + } + case QXmlStreamReader::EndElement: { + const QStringRef tag = reader.name(); + if (tag == QLatin1String(widgetBoxRootElementC)) { + continue; + } + if (tag == QLatin1String(categoryElementC)) { + ignoreEntries = false; + continue; + } + if (tag == QLatin1String(categoryEntryElementC)) { + continue; + } + break; + } + default: break; + } + } + + if (reader.hasError()) { + *errorMessage = msgXmlError(fileName, reader); + return false; + } + + return true; +} + +/*! + * Read out a widget within a category. This can either be + * enclosed in a element or a (legacy) element which may + * contain nested elements. + * + * Examples: + * + * + * ... + * ... + * + * + * or + * + * + * ... + * ... + * + * + * Returns true on success, false if end was reached or an error has been encountered + * in which case the reader has its error flag set. If successful, the current item + * of the reader will be the closing element ( or ) + */ +bool WidgetBoxTreeWidget::readWidget(Widget *w, const QString &xml, QXmlStreamReader &r) +{ + qint64 startTagPosition =0, endTagPosition = 0; + + int nesting = 0; + bool endEncountered = false; + bool parsedWidgetTag = false; + QString outmostElement; + while (!endEncountered) { + const qint64 currentPosition = r.characterOffset(); + switch(r.readNext()) { + case QXmlStreamReader::StartElement: + if (nesting++ == 0) { + // First element must be or (legacy) + const QStringRef name = r.name(); + if (name == QLatin1String(uiElementC)) { + startTagPosition = currentPosition; + } else { + if (name == QLatin1String(widgetElementC)) { + startTagPosition = currentPosition; + parsedWidgetTag = true; + } else { + r.raiseError(QDesignerWidgetBox::tr("Unexpected element <%1> encountered when parsing for or ").arg(name.toString())); + return false; + } + } + } else { + // We are within looking for the first tag + if (!parsedWidgetTag && r.name() == QLatin1String(widgetElementC)) { + parsedWidgetTag = true; + } + } + break; + case QXmlStreamReader::EndElement: + // Reached end of widget? + if (--nesting == 0) { + endTagPosition = r.characterOffset(); + endEncountered = true; + } + break; + case QXmlStreamReader::EndDocument: + r.raiseError(QDesignerWidgetBox::tr("Unexpected end of file encountered when parsing widgets.")); + return false; + case QXmlStreamReader::Invalid: + return false; + default: + break; + } + } + if (!parsedWidgetTag) { + r.raiseError(QDesignerWidgetBox::tr("A widget element could not be found.")); + return false; + } + // Oddity: Startposition is 1 off + QString widgetXml = xml.mid(startTagPosition, endTagPosition - startTagPosition); + const QChar lessThan = QLatin1Char('<'); + if (!widgetXml.startsWith(lessThan)) + widgetXml.prepend(lessThan); + w->setDomXml(widgetXml); + return true; +} + +void WidgetBoxTreeWidget::writeCategories(QXmlStreamWriter &writer, const CategoryList &cat_list) const +{ + const QString widgetbox = QLatin1String(widgetBoxRootElementC); + const QString name = QLatin1String(nameAttributeC); + const QString type = QLatin1String(typeAttributeC); + const QString icon = QLatin1String(iconAttributeC); + const QString defaultType = QLatin1String(defaultTypeValueC); + const QString category = QLatin1String(categoryElementC); + const QString categoryEntry = QLatin1String(categoryEntryElementC); + const QString iconPrefix = QLatin1String(iconPrefixC); + const QString widgetTag = QLatin1String(widgetElementC); + + // + // + // + // + // + // ... + // + // + // ... + // + // ... + // + // + + writer.writeStartElement(widgetbox); + + foreach (const Category &cat, cat_list) { + writer.writeStartElement(category); + writer.writeAttribute(name, cat.name()); + if (cat.type() == Category::Scratchpad) + writer.writeAttribute(type, QLatin1String(scratchPadValueC)); + + const int widgetCount = cat.widgetCount(); + for (int i = 0; i < widgetCount; ++i) { + const Widget wgt = cat.widget(i); + if (wgt.type() == Widget::Custom) + continue; + + writer.writeStartElement(categoryEntry); + writer.writeAttribute(name, wgt.name()); + if (!wgt.iconName().startsWith(iconPrefix)) + writer.writeAttribute(icon, wgt.iconName()); + writer.writeAttribute(type, defaultType); + + const DomUI *domUI = QDesignerWidgetBox::xmlToUi(wgt.name(), WidgetBoxCategoryListView::widgetDomXml(wgt), false); + if (domUI) { + domUI->write(writer); + delete domUI; + } + + writer.writeEndElement(); // categoryEntry + } + writer.writeEndElement(); // categoryEntry + } + + writer.writeEndElement(); // widgetBox +} + +static int findCategory(const QString &name, const WidgetBoxTreeWidget::CategoryList &list) +{ + int idx = 0; + foreach (const WidgetBoxTreeWidget::Category &cat, list) { + if (cat.name() == name) + return idx; + ++idx; + } + return -1; +} + +static inline bool isValidIcon(const QIcon &icon) +{ + if (!icon.isNull()) { + const QList availableSizes = icon.availableSizes(); + if (!availableSizes.empty()) + return !availableSizes.front().isEmpty(); + } + return false; +} + +WidgetBoxTreeWidget::CategoryList WidgetBoxTreeWidget::loadCustomCategoryList() const +{ + CategoryList result; + + const QDesignerPluginManager *pm = m_core->pluginManager(); + const QDesignerPluginManager::CustomWidgetList customWidgets = pm->registeredCustomWidgets(); + if (customWidgets.empty()) + return result; + + static const QString customCatName = tr("Custom Widgets"); + + const QString invisible = QLatin1String(invisibleNameC); + const QString iconPrefix = QLatin1String(iconPrefixC); + + foreach(QDesignerCustomWidgetInterface *c, customWidgets) { + const QString dom_xml = c->domXml(); + if (dom_xml.isEmpty()) + continue; + + const QString pluginName = c->name(); + const QDesignerCustomWidgetData data = pm->customWidgetData(c); + QString displayName = data.xmlDisplayName(); + if (displayName.isEmpty()) + displayName = pluginName; + + QString cat_name = c->group(); + if (cat_name.isEmpty()) + cat_name = customCatName; + else if (cat_name == invisible) + continue; + + int idx = findCategory(cat_name, result); + if (idx == -1) { + result.append(Category(cat_name)); + idx = result.size() - 1; + } + Category &cat = result[idx]; + + const QIcon icon = c->icon(); + + QString icon_name; + if (isValidIcon(icon)) { + icon_name = iconPrefix; + icon_name += pluginName; + m_pluginIcons.insert(icon_name, icon); + } else { + icon_name = QLatin1String(qtLogoC); + } + + cat.addWidget(Widget(displayName, dom_xml, icon_name, Widget::Custom)); + } + + return result; +} + +void WidgetBoxTreeWidget::adjustSubListSize(QTreeWidgetItem *cat_item) +{ + QTreeWidgetItem *embedItem = cat_item->child(0); + if (embedItem == 0) + return; + + WidgetBoxCategoryListView *list_widget = static_cast(itemWidget(embedItem, 0)); + list_widget->setFixedWidth(header()->width()); + list_widget->doItemsLayout(); + const int height = qMax(list_widget->contentsSize().height() ,1); + list_widget->setFixedHeight(height); + embedItem->setSizeHint(0, QSize(-1, height - 1)); +} + +int WidgetBoxTreeWidget::categoryCount() const +{ + return topLevelItemCount(); +} + +WidgetBoxTreeWidget::Category WidgetBoxTreeWidget::category(int cat_idx) const +{ + if (cat_idx >= topLevelItemCount()) + return Category(); + + QTreeWidgetItem *cat_item = topLevelItem(cat_idx); + + QTreeWidgetItem *embedItem = cat_item->child(0); + WidgetBoxCategoryListView *categoryView = static_cast(itemWidget(embedItem, 0)); + + Category result = categoryView->category(); + result.setName(cat_item->text(0)); + + switch (topLevelRole(cat_item)) { + case SCRATCHPAD_ITEM: + result.setType(Category::Scratchpad); + break; + default: + result.setType(Category::Default); + break; + } + return result; +} + +void WidgetBoxTreeWidget::addCategory(const Category &cat) +{ + if (cat.widgetCount() == 0) + return; + + const bool isScratchPad = cat.type() == Category::Scratchpad; + WidgetBoxCategoryListView *categoryView; + QTreeWidgetItem *cat_item; + + if (isScratchPad) { + const int idx = ensureScratchpad(); + categoryView = categoryViewAt(idx); + cat_item = topLevelItem(idx); + } else { + const int existingIndex = indexOfCategory(cat.name()); + if (existingIndex == -1) { + cat_item = new QTreeWidgetItem(); + cat_item->setText(0, cat.name()); + setTopLevelRole(NORMAL_ITEM, cat_item); + // insert before scratchpad + const int scratchPadIndex = indexOfScratchpad(); + if (scratchPadIndex == -1) { + addTopLevelItem(cat_item); + } else { + insertTopLevelItem(scratchPadIndex, cat_item); + } + setItemExpanded(cat_item, true); + categoryView = addCategoryView(cat_item, m_iconMode); + } else { + categoryView = categoryViewAt(existingIndex); + cat_item = topLevelItem(existingIndex); + } + } + // The same categories are read from the file $HOME, avoid duplicates + const int widgetCount = cat.widgetCount(); + for (int i = 0; i < widgetCount; ++i) { + const Widget w = cat.widget(i); + if (!categoryView->containsWidget(w.name())) + categoryView->addWidget(w, iconForWidget(w.iconName()), isScratchPad); + } + adjustSubListSize(cat_item); +} + +void WidgetBoxTreeWidget::removeCategory(int cat_idx) +{ + if (cat_idx >= topLevelItemCount()) + return; + delete takeTopLevelItem(cat_idx); +} + +int WidgetBoxTreeWidget::widgetCount(int cat_idx) const +{ + if (cat_idx >= topLevelItemCount()) + return 0; + // SDK functions want unfiltered access + return categoryViewAt(cat_idx)->count(WidgetBoxCategoryListView::UnfilteredAccess); +} + +WidgetBoxTreeWidget::Widget WidgetBoxTreeWidget::widget(int cat_idx, int wgt_idx) const +{ + if (cat_idx >= topLevelItemCount()) + return Widget(); + // SDK functions want unfiltered access + WidgetBoxCategoryListView *categoryView = categoryViewAt(cat_idx); + return categoryView->widgetAt(WidgetBoxCategoryListView::UnfilteredAccess, wgt_idx); +} + +void WidgetBoxTreeWidget::addWidget(int cat_idx, const Widget &wgt) +{ + if (cat_idx >= topLevelItemCount()) + return; + + QTreeWidgetItem *cat_item = topLevelItem(cat_idx); + WidgetBoxCategoryListView *categoryView = categoryViewAt(cat_idx); + + const bool scratch = topLevelRole(cat_item) == SCRATCHPAD_ITEM; + categoryView->addWidget(wgt, iconForWidget(wgt.iconName()), scratch); + adjustSubListSize(cat_item); +} + +void WidgetBoxTreeWidget::removeWidget(int cat_idx, int wgt_idx) +{ + if (cat_idx >= topLevelItemCount()) + return; + + WidgetBoxCategoryListView *categoryView = categoryViewAt(cat_idx); + + // SDK functions want unfiltered access + const WidgetBoxCategoryListView::AccessMode am = WidgetBoxCategoryListView::UnfilteredAccess; + if (wgt_idx >= categoryView->count(am)) + return; + + categoryView->removeRow(am, wgt_idx); +} + +void WidgetBoxTreeWidget::slotScratchPadItemDeleted() +{ + const int scratch_idx = indexOfScratchpad(); + QTreeWidgetItem *scratch_item = topLevelItem(scratch_idx); + adjustSubListSize(scratch_item); + save(); +} + +void WidgetBoxTreeWidget::slotLastScratchPadItemDeleted() +{ + // Remove the scratchpad in the next idle loop + if (!m_scratchPadDeleteTimer) { + m_scratchPadDeleteTimer = new QTimer(this); + m_scratchPadDeleteTimer->setSingleShot(true); + m_scratchPadDeleteTimer->setInterval(0); + connect(m_scratchPadDeleteTimer, SIGNAL(timeout()), this, SLOT(deleteScratchpad())); + } + if (!m_scratchPadDeleteTimer->isActive()) + m_scratchPadDeleteTimer->start(); +} + +void WidgetBoxTreeWidget::deleteScratchpad() +{ + const int idx = indexOfScratchpad(); + if (idx == -1) + return; + delete takeTopLevelItem(idx); + save(); +} + + +void WidgetBoxTreeWidget::slotListMode() +{ + m_iconMode = false; + updateViewMode(); +} + +void WidgetBoxTreeWidget::slotIconMode() +{ + m_iconMode = true; + updateViewMode(); +} + +void WidgetBoxTreeWidget::updateViewMode() +{ + if (const int numTopLevels = topLevelItemCount()) { + for (int i = numTopLevels - 1; i >= 0; --i) { + QTreeWidgetItem *topLevel = topLevelItem(i); + // Scratch pad stays in list mode. + const QListView::ViewMode viewMode = m_iconMode && (topLevelRole(topLevel) != SCRATCHPAD_ITEM) ? QListView::IconMode : QListView::ListMode; + WidgetBoxCategoryListView *categoryView = categoryViewAt(i); + if (viewMode != categoryView->viewMode()) { + categoryView->setViewMode(viewMode); + adjustSubListSize(topLevelItem(i)); + } + } + } + + updateGeometries(); +} + +void WidgetBoxTreeWidget::resizeEvent(QResizeEvent *e) +{ + QTreeWidget::resizeEvent(e); + if (const int numTopLevels = topLevelItemCount()) { + for (int i = numTopLevels - 1; i >= 0; --i) + adjustSubListSize(topLevelItem(i)); + } +} + +void WidgetBoxTreeWidget::contextMenuEvent(QContextMenuEvent *e) +{ + QTreeWidgetItem *item = itemAt(e->pos()); + + const bool scratchpad_menu = item != 0 + && item->parent() != 0 + && topLevelRole(item->parent()) == SCRATCHPAD_ITEM; + + QMenu menu; + menu.addAction(tr("Expand all"), this, SLOT(expandAll())); + menu.addAction(tr("Collapse all"), this, SLOT(collapseAll())); + menu.addSeparator(); + + QAction *listModeAction = menu.addAction(tr("List View")); + QAction *iconModeAction = menu.addAction(tr("Icon View")); + listModeAction->setCheckable(true); + iconModeAction->setCheckable(true); + QActionGroup *viewModeGroup = new QActionGroup(&menu); + viewModeGroup->addAction(listModeAction); + viewModeGroup->addAction(iconModeAction); + if (m_iconMode) + iconModeAction->setChecked(true); + else + listModeAction->setChecked(true); + connect(listModeAction, SIGNAL(triggered()), SLOT(slotListMode())); + connect(iconModeAction, SIGNAL(triggered()), SLOT(slotIconMode())); + + if (scratchpad_menu) { + menu.addSeparator(); + menu.addAction(tr("Remove"), itemWidget(item, 0), SLOT(removeCurrentItem())); + if (!m_iconMode) + menu.addAction(tr("Edit name"), itemWidget(item, 0), SLOT(editCurrentItem())); + } + e->accept(); + menu.exec(mapToGlobal(e->pos())); +} + +void WidgetBoxTreeWidget::dropWidgets(const QList &item_list) +{ + QTreeWidgetItem *scratch_item = 0; + WidgetBoxCategoryListView *categoryView = 0; + bool added = false; + + foreach (QDesignerDnDItemInterface *item, item_list) { + QWidget *w = item->widget(); + if (w == 0) + continue; + + DomUI *dom_ui = item->domUi(); + if (dom_ui == 0) + continue; + + const int scratch_idx = ensureScratchpad(); + scratch_item = topLevelItem(scratch_idx); + categoryView = categoryViewAt(scratch_idx); + + // Temporarily remove the fake toplevel in-between + DomWidget *fakeTopLevel = dom_ui->takeElementWidget(); + DomWidget *firstWidget = 0; + if (fakeTopLevel && !fakeTopLevel->elementWidget().isEmpty()) { + firstWidget = fakeTopLevel->elementWidget().first(); + dom_ui->setElementWidget(firstWidget); + } else { + dom_ui->setElementWidget(fakeTopLevel); + continue; + } + + // Serialize to XML + QString xml; + { + QXmlStreamWriter writer(&xml); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + dom_ui->write(writer); + writer.writeEndDocument(); + } + + // Insert fake toplevel again + dom_ui->takeElementWidget(); + dom_ui->setElementWidget(fakeTopLevel); + + const Widget wgt = Widget(w->objectName(), xml); + categoryView->addWidget(wgt, iconForWidget(wgt.iconName()), true); + setItemExpanded(scratch_item, true); + added = true; + } + + if (added) { + save(); + QApplication::setActiveWindow(this); + // Is the new item visible in filtered mode? + const WidgetBoxCategoryListView::AccessMode am = WidgetBoxCategoryListView::FilteredAccess; + if (const int count = categoryView->count(am)) + categoryView->setCurrentItem(am, count - 1); + categoryView->adjustSize(); // XXX + adjustSubListSize(scratch_item); + } +} + +void WidgetBoxTreeWidget::filter(const QString &f) +{ + const bool empty = f.isEmpty(); + const QRegExp re = empty ? QRegExp() : QRegExp(f, Qt::CaseInsensitive, QRegExp::FixedString); + const int numTopLevels = topLevelItemCount(); + bool changed = false; + for (int i = 0; i < numTopLevels; i++) { + QTreeWidgetItem *tl = topLevelItem(i); + WidgetBoxCategoryListView *categoryView = categoryViewAt(i); + // Anything changed? -> Enable the category + const int oldCount = categoryView->count(WidgetBoxCategoryListView::FilteredAccess); + categoryView->filter(re); + const int newCount = categoryView->count(WidgetBoxCategoryListView::FilteredAccess); + if (oldCount != newCount) { + changed = true; + const bool categoryEnabled = newCount > 0 || empty; + if (categoryEnabled) { + categoryView->adjustSize(); + adjustSubListSize(tl); + } + setRowHidden (i, QModelIndex(), !categoryEnabled); + } + } + if (changed) + updateGeometries(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE +#include diff --git a/src/designer/components/widgetbox/widgetboxtreewidget.h b/src/designer/components/widgetbox/widgetboxtreewidget.h new file mode 100644 index 000000000..491ceef94 --- /dev/null +++ b/src/designer/components/widgetbox/widgetboxtreewidget.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WIDGETBOXTREEWIDGET_H +#define WIDGETBOXTREEWIDGET_H + +#include + +#include +#include +#include +#include +#include // Cannot forward declare them on Mac +#include + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerDnDItemInterface; + +class QTimer; + +namespace qdesigner_internal { + +class WidgetBoxCategoryListView; + +// WidgetBoxTreeWidget: A tree of categories + +class WidgetBoxTreeWidget : public QTreeWidget +{ + Q_OBJECT + +public: + typedef QDesignerWidgetBoxInterface::Widget Widget; + typedef QDesignerWidgetBoxInterface::Category Category; + typedef QDesignerWidgetBoxInterface::CategoryList CategoryList; + + explicit WidgetBoxTreeWidget(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~WidgetBoxTreeWidget(); + + int categoryCount() const; + Category category(int cat_idx) const; + void addCategory(const Category &cat); + void removeCategory(int cat_idx); + + int widgetCount(int cat_idx) const; + Widget widget(int cat_idx, int wgt_idx) const; + void addWidget(int cat_idx, const Widget &wgt); + void removeWidget(int cat_idx, int wgt_idx); + + void dropWidgets(const QList &item_list); + + void setFileName(const QString &file_name); + QString fileName() const; + bool load(QDesignerWidgetBox::LoadMode loadMode); + bool loadContents(const QString &contents); + bool save(); + QIcon iconForWidget(QString iconName) const; + +signals: + void pressed(const QString name, const QString dom_xml, const QPoint &global_mouse_pos); + +public slots: + void filter(const QString &); + +protected: + void contextMenuEvent(QContextMenuEvent *e); + void resizeEvent(QResizeEvent *e); + +private slots: + void slotSave(); + void slotScratchPadItemDeleted(); + void slotLastScratchPadItemDeleted(); + + void handleMousePress(QTreeWidgetItem *item); + void deleteScratchpad(); + void slotListMode(); + void slotIconMode(); + +private: + WidgetBoxCategoryListView *addCategoryView(QTreeWidgetItem *parent, bool iconMode); + WidgetBoxCategoryListView *categoryViewAt(int idx) const; + void adjustSubListSize(QTreeWidgetItem *cat_item); + + static bool readCategories(const QString &fileName, const QString &xml, CategoryList *cats, QString *errorMessage); + static bool readWidget(Widget *w, const QString &xml, QXmlStreamReader &r); + + CategoryList loadCustomCategoryList() const; + void writeCategories(QXmlStreamWriter &writer, const CategoryList &cat_list) const; + + int indexOfCategory(const QString &name) const; + int indexOfScratchpad() const; + int ensureScratchpad(); + void addCustomCategories(bool replace); + + void saveExpandedState() const; + void restoreExpandedState(); + void updateViewMode(); + + QDesignerFormEditorInterface *m_core; + QString m_file_name; + typedef QHash IconCache; + mutable IconCache m_pluginIcons; + bool m_iconMode; + QTimer *m_scratchPadDeleteTimer; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETBOXTREEWIDGET_H diff --git a/src/designer/data/generate_header.xsl b/src/designer/data/generate_header.xsl new file mode 100644 index 000000000..fa61d55cf --- /dev/null +++ b/src/designer/data/generate_header.xsl @@ -0,0 +1,465 @@ + +]> + + + + + + + + + + + + + class + + ;&endl; + + + + + + + + + + + enum Kind { Unknown = 0 + + + + + + + + + + + + + , + + + };&endl; + inline Kind kind() const { return m_kind; }&endl;&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inline + + element + + () const { return m_ + + ; }&endl; + + + + + takeElement + + ();&endl; + + + void setElement + + ( + + a);&endl; + + + inline bool hasElement + + () const { return m_children & + + ; }&endl; + void clearElement + + ();&endl; + + &endl; + + + + + + + + + + + + + + + + + + + + + + + + + + m_ + + ;&endl; + + + + enum Child {&endl; + + + + + + + + + + + + = + + + + + , + + &endl; + + + };&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inline bool hasAttribute + + () const { return m_has_attr_ + + ; }&endl; + + inline + + attribute + + () const { return m_attr_ + + ; }&endl; + + inline void setAttribute + + ( + + a) { m_attr_ + + = a; m_has_attr_ + + = true; }&endl; + + inline void clearAttribute + + () { m_has_attr_ + + = false; }&endl;&endl; + + + + + + + + + + + + class QDESIGNER_UILIB_EXPORT + + {&endl; + public:&endl; + + + ();&endl; + ~ + + ();&endl;&endl; + + void read(QXmlStreamReader &reader);&endl; + #ifdef QUILOADER_QDOM_READ&endl; + void read(const QDomElement &node);&endl; + #endif&endl; + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const;&endl; + + + inline QString text() const { return m_text; }&endl; + inline void setText(const QString &s) { m_text = s; }&endl; + + + &endl; + + // attribute accessors&endl; + + + + + // child element accessors&endl; + + + + + + + + private:&endl; + + + QString m_text;&endl; + + + void clear(bool clear_all = true);&endl;&endl; + + // attribute data&endl; + + + + + + + + + + + + + + m_attr_ + + ;&endl; + bool m_has_attr_ + + ;&endl;&endl; + + + // child element data&endl; + + Kind m_kind;&endl; + + + + uint m_children;&endl; + + + + + + + + &endl; + + + (const + + &other);&endl; + void operator = (const + + &other);&endl; + + };&endl;&endl; + + + + + + +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $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 <QtCore/QList> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamWriter> +#include <QtCore/qglobal.h> + +#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 + + + + &endl; + /*******************************************************************************&endl; + ** Forward declarations&endl; + */&endl;&endl; + + + + + + + + &endl; + /*******************************************************************************&endl; + ** Declarations&endl; + */&endl;&endl; + + + + + + + +#ifdef QFORMINTERNAL_NAMESPACE +} +#endif + +QT_END_NAMESPACE + +#endif // UI4_H + + + diff --git a/src/designer/data/generate_impl.xsl b/src/designer/data/generate_impl.xsl new file mode 100644 index 000000000..f01b4bf90 --- /dev/null +++ b/src/designer/data/generate_impl.xsl @@ -0,0 +1,1161 @@ + +]> + + + + + + + + + + + + + + + + + m_has_attr_ + + = false;&endl; + + + m_attr_ + + = 0;&endl; + + + m_attr_ + + = 0.0;&endl; + + + m_attr_ + + = 0.0;&endl; + + + m_attr_ + + = false;&endl; + + + + + + + + + + + + + + + + + + + + + + + + m_ + + = 0;&endl; + + + m_ + + = 0.0;&endl; + + + m_ + + = false;&endl; + + + + m_ + + = 0;&endl; + + + + + + + + + + + m_kind = Unknown;&endl;&endl; + + + + m_children = 0;&endl; + + + + + + + + m_text = QLatin1String("");&endl; + + + + + + + + + + + + + + + :: + + ()&endl; + {&endl; + + + + }&endl;&endl; + + + + + + + + + + + + + + + + + + + + + + qDeleteAll(m_ + + );&endl; + + m_ + + .clear();&endl; + + + + delete m_ + + ;&endl; + + + + + + + + + + + + ::~ + + ()&endl; + {&endl; + + + + + + + + }&endl;&endl; + + + + + + + + + void + ::clear(bool clear_all)&endl; + {&endl; + + + + + + + + &endl; if (clear_all) {&endl; + + + + m_text = QLatin1String("");&endl; + + + m_text.clear();&endl; + + + + + + + }&endl;&endl; + + + m_kind = Unknown;&endl;&endl; + + + + m_children = 0;&endl; + + + + + + + + + }&endl;&endl; + + + + + + + + + QString(QLatin1Char(' + + ')) + + + QLatin1String(" + + ") + + + + + + + + + + + &endl; + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) {&endl; + QStringRef name = attribute.name();&endl; + + + + + + + + + + + + + + + + + attribute.value().toString() + + + + + if (name == + + + + ) {&endl; + setAttribute + + ( + + );&endl; + continue;&endl; + }&endl; + + + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString());&endl; + }&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if (tag == + + + + ) {&endl; + + + + + + + + + + + setElement + + ( + + );&endl; + + + + + + + + + + m_ + + .append( + + );&endl; + + + Dom + + *v = new Dom + + ();&endl; + v->read(reader);&endl; + setElement + + (v);&endl; + + + Dom + + *v = new Dom + + ();&endl; + v->read(reader);&endl; + m_ + + .append(v);&endl; + + + continue;&endl; + }&endl; + + + + + + + + void + + ::read(QXmlStreamReader &reader)&endl; + + {&endl; + + + + + + &endl; + + for (bool finished = false; !finished && !reader.hasError();) {&endl; + switch (reader.readNext()) {&endl; + case QXmlStreamReader::StartElement : {&endl; + const QString tag = reader.name().toString().toLower();&endl; + + + + + + + + reader.raiseError(QLatin1String("Unexpected element ") + tag);&endl; + }&endl; + break;&endl; + case QXmlStreamReader::EndElement :&endl; + finished = true;&endl; + break;&endl; + case QXmlStreamReader::Characters :&endl; + if (!reader.isWhitespace())&endl; + m_text.append(reader.text().toString());&endl; + break;&endl; + default :&endl; + break;&endl; + + }&endl; + }&endl; + }&endl;&endl; + + + + + + + + + &endl; + + + + + + + + + + + + + + + + + node.attribute( + + + + ) + + + + + if (node.hasAttribute( + + + + ))&endl; + setAttribute + + ( + + );&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if (tag == + + + + ) {&endl; + + + + + + + + + + + setElement + + ( + + );&endl; + + + + + + + + + + m_ + + .append( + + );&endl; + + + Dom + + *v = new Dom + + ();&endl; + v->read(e);&endl; + setElement + + (v);&endl; + + + Dom + + *v = new Dom + + ();&endl; + v->read(e);&endl; + m_ + + .append(v);&endl; + + + continue;&endl; + }&endl; + + + + + + + + #ifdef QUILOADER_QDOM_READ&endl; + + void + + ::read(const QDomElement &node)&endl; + + { + + + + + + &endl; + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {&endl; + if (!n.isElement())&endl; + continue;&endl; + QDomElement e = n.toElement();&endl; + QString tag = e.tagName().toLower();&endl; + + + + + + + + }&endl; + + + + m_text = QLatin1String("");&endl; + + + m_text.clear();&endl; + + + + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) {&endl; + if (child.isText())&endl; + m_text.append(child.nodeValue());&endl; + }&endl; + + }&endl; + #endif&endl; + &endl; + + + + + + + + + + + + + + + + + + + + + + + + if (hasAttribute + + ())&endl; + writer.writeAttribute( + + + + + , + + + + + + + );&endl;&endl; + + + + + + + + switch (kind()) {&endl; + + + + + + + + + + + + + + + + + + + + + + + + case + + : {&endl; + + + + + + + + + + writer.writeTextElement( + + + + , + + );&endl; + + + + + + + + + + + v = element + + ();&endl; + if (v != 0) {&endl; + v->write(writer, + + + + );&endl; + }&endl; + + + break;&endl; + }&endl; + + + default:&endl; + break;&endl; + }&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for (int i = 0; i < m_ + + .size(); ++i) {&endl; + + + v = m_ + + [i];&endl; + + + v->write(writer, + + + + );&endl; + + + + + + + + + + writer.writeTextElement( + + + + , + + );&endl; + + + }&endl; + + + if (m_children & + + ) {&endl; + + + m_ + + ->write(writer, + + + + );&endl; + + + + + + + + + writer.writeTextElement( + + + + , + + );&endl; + + + }&endl;&endl; + + + + + + + + + + + + + + + void + + ::write(QXmlStreamWriter &writer, const QString &tagName) const&endl; + {&endl; + + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8(" + + ") : tagName.toLower());&endl;&endl; + + + + + + + + + + + + + + + + + + if (!m_text.isEmpty())&endl; + writer.writeCharacters(m_text);&endl;&endl; + + writer.writeEndElement();&endl; + }&endl;&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ::takeElement + + () &endl;{&endl; + + + a = m_ + + ;&endl; + m_ + + = 0;&endl; + + m_children ^= + + ;&endl; + + return a;&endl; + }&endl;&endl; + + + void + + ::setElement + + ( + + a)&endl; + {&endl; + + + clear(false);&endl; + m_kind = + + ;&endl; + + + delete + m_ + + ;&endl; + + + + m_children |= + + ;&endl; + + m_ + + = a;&endl; + }&endl;&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + void + + ::clearElement + + ()&endl; + {&endl; + + delete m_ + + ;&endl; + m_ + + = 0;&endl; + + m_children &= ~ + + ;&endl; + }&endl;&endl; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + #include "ui4_p.h"&endl; + &endl; + #ifdef QUILOADER_QDOM_READ&endl; + #include <QtXml/QDomElement>&endl; + #endif&endl; + &endl; + QT_BEGIN_NAMESPACE&endl; + + #ifdef QFORMINTERNAL_NAMESPACE&endl; + using namespace QFormInternal;&endl; + #endif&endl; + &endl; + + /*******************************************************************************&endl; + ** Implementations&endl; + */&endl;&endl; + + + + + + + QT_END_NAMESPACE&endl; + + &endl; + + + diff --git a/src/designer/data/generate_shared.xsl b/src/designer/data/generate_shared.xsl new file mode 100644 index 000000000..ec95fe2b8 --- /dev/null +++ b/src/designer/data/generate_shared.xsl @@ -0,0 +1,331 @@ + +]> + + + + + + + exportMacro + layoutDefault + layoutFunction + pixmapFunction + customWidgets + tabStops + tabStop + buttonGroups + exportMacro + actionGroup + buttonGroup + customWidget + sizeHint + addPageMethod + sizePolicy + horData + verData + rowSpan + colSpan + addAction + zOrder + startX + startY + endX + endY + centralX + centralY + focalX + focalY + widgetData + coordinateMode + brushStyle + colorRole + pointSize + strikeOut + styleStrategy + hSizeType + vSizeType + horStretch + verStretch + normalOff + normalOn + disabledOff + disabledOn + activeOff + activeOn + selectedOff + selectedOn + cursorShape + iconSet + stringList + dateTime + pointF + rectF + sizeF + longLong + UInt + uLongLong + rowStretch + columnStretch + rowMinimumHeight + columnMinimumWidth + extraComment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + .toInt() + + + + .toFloat() + + + + .toDouble() + + + ( + + == QLatin1String("true") ? true : false) + + + + .toLongLong() + + + + .toUInt() + + + + .toULongLong() + + ### BZZZZT! ### + + + + + + + + + + + + QString::number( + + ) + + + QString::number( + + ) + + + QString::number( + + ) + + + QString::number( + + ) + + + QString::number( + + , 'f', 8) + + + QString::number( + + , 'f', 15) + + + ( + + ? QLatin1String("true") : QLatin1String("false")) + + ### BZZZZT! ### + + + + + + + + value + + + value + value + value + value + value + value + value + value + pointer + + + + + + + + + + + + QStringList + QList<int> + QList<float> + QList<double> + QList<bool> + QList<qlonglong> + QList<uint> + QList<qulonglong> + QList<Dom*> + + + + + QString + int + float + double + bool + qlonglong + uint + qulonglong + Dom + + + + + + + + + + + + QStringList + QList<int> + QList<float> + QList<double> + QList<bool> + QList<qlonglong> + QList<uint> + QList<qulonglong> + QList<Dom*> + + + + + QString + int + float + double + bool + qlonglong + uint + qulonglong + Dom* + + + + + + + + + + + + const QStringList& + const QList<int>& + const QList<float>& + const QList<double>& + const QList<bool>& + const QList<qlonglong>& + const QList<uint>& + const QList<qulonglong>& + const QList<Dom*>& + + + + + const QString& + int + float + double + bool + qlonglong + uint + qulonglong + Dom* + + + + + + + diff --git a/src/designer/data/ui3.xsd b/src/designer/data/ui3.xsd new file mode 100644 index 000000000..06f325ef3 --- /dev/null +++ b/src/designer/data/ui3.xsd @@ -0,0 +1,353 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/designer/data/ui4.xsd b/src/designer/data/ui4.xsd new file mode 100644 index 000000000..53bae62e2 --- /dev/null +++ b/src/designer/data/ui4.xsd @@ -0,0 +1,589 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/designer/designer/CMakeLists.txt b/src/designer/designer/CMakeLists.txt new file mode 100644 index 000000000..381839062 --- /dev/null +++ b/src/designer/designer/CMakeLists.txt @@ -0,0 +1,104 @@ +add_definitions( + ${SHAREDDEVICESKIN_DEFINITIONS} +) +set(EXTRA_DESIGNERBIN_LIBS KtCore KtGui KtNetwork KtDesigner KtDesignerComponents) + +include(fontpanel/fontpanel.cmake) +include(qttoolbardialog/qttoolbardialog.cmake) + +include_directories( + ${CMAKE_BINARY_DIR}/include + ${CMAKE_BINARY_DIR}/privateinclude + ${CMAKE_BINARY_DIR}/include/QtCore + ${CMAKE_BINARY_DIR}/privateinclude/QtCore + ${CMAKE_BINARY_DIR}/include/QtGui + ${CMAKE_BINARY_DIR}/privateinclude/QtGui + ${CMAKE_BINARY_DIR}/include/QtNetwork + ${CMAKE_BINARY_DIR}/privateinclude/QtNetwork + ${CMAKE_BINARY_DIR}/include/QtDesigner + ${CMAKE_BINARY_DIR}/privateinclude/QtDesigner + ${CMAKE_BINARY_DIR}/include/QtDesignerComponents + ${CMAKE_BINARY_DIR}/privateinclude/QtDesignerComponents + ${CMAKE_BINARY_DIR}/include/QtUiTools + ${CMAKE_BINARY_DIR}/privateinclude/QtUiTools + ${CMAKE_CURRENT_SOURCE_DIR}/fontpanel + ${CMAKE_CURRENT_SOURCE_DIR}/qttoolbardialog + ${CMAKE_CURRENT_BINARY_DIR}/fontpanel + ${CMAKE_CURRENT_BINARY_DIR}/qttoolbardialog + ${CMAKE_SOURCE_DIR}/src/designer/sdk + ${CMAKE_SOURCE_DIR}/src/designer/extension + ${CMAKE_SOURCE_DIR}/src/designer/shared + ${CMAKE_SOURCE_DIR}/src/designer + ${SHAREDDEVICESKIN_INCLUDES} +) + +set(DESIGNERBIN_HEADERS + ${DESIGNERBIN_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_toolwindow.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_formwindow.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_workbench.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_settings.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_actions.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_server.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_appearanceoptions.h + ${CMAKE_CURRENT_SOURCE_DIR}/saveformastemplate.h + ${CMAKE_CURRENT_SOURCE_DIR}/newform.h + ${CMAKE_CURRENT_SOURCE_DIR}/versiondialog.h + ${CMAKE_CURRENT_SOURCE_DIR}/designer_enums.h + ${CMAKE_CURRENT_SOURCE_DIR}/appfontdialog.h + ${CMAKE_CURRENT_SOURCE_DIR}/preferencesdialog.h + ${CMAKE_CURRENT_SOURCE_DIR}/assistantclient.h + ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.h + ${SHAREDDEVICESKIN_HEADERS} +) + +set(DESIGNERBIN_SOURCES + ${DESIGNERBIN_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_toolwindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_formwindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_workbench.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_settings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_server.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_actions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_appearanceoptions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/saveformastemplate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/newform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/versiondialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/appfontdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/preferencesdialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/assistantclient.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mainwindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/saveformastemplate.ui + ${CMAKE_CURRENT_SOURCE_DIR}/preferencesdialog.ui + ${CMAKE_CURRENT_SOURCE_DIR}/qdesigner_appearanceoptions.ui + ${CMAKE_CURRENT_SOURCE_DIR}/designer.qrc +) + +if(NOT ${KATIE_TYPE} STREQUAL SHARED) + add_definitions(-DQT_DESIGNER_STATIC) +endif() + +if(UNIX AND NOT ${KATIE_PLATFORM} STREQUAL "mac") + set(EXTRA_DESIGNERBIN_LIBS + ${EXTRA_DESIGNERBIN_LIBS} + m + ) +endif() + +katie_resources(${DESIGNERBIN_SOURCES} ${DESIGNERBIN_HEADERS}) +katie_setup_flags() + +add_executable(designer + $ + ${DESIGNERBIN_SOURCES} ${DESIGNERBIN_HEADERS} +) +target_link_libraries(designer ${EXTRA_DESIGNERBIN_LIBS}) + +install( + TARGETS designer + RUNTIME DESTINATION ${QT_BINARIES_PATH_INST} + COMPONENT Devel +) diff --git a/src/designer/designer/Info_mac.plist b/src/designer/designer/Info_mac.plist new file mode 100644 index 000000000..b3549329c --- /dev/null +++ b/src/designer/designer/Info_mac.plist @@ -0,0 +1,35 @@ + + + + + CFBundleIconFile + @ICON@ + CFBundlePackageType + APPL + CFBundleGetInfoString + Created by Qt/QMake + CFBundleIdentifier + com.trolltech.Designer + CFBundleSignature + ttxt + CFBundleExecutable + @EXECUTABLE@ + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + ui + + CFBundleTypeIconFile + uifile.icns + CFBundleTypeRole + Editor + LSIsAppleDefaultForType + + + + NOTE + Qt/Designer by The Qt Company Ltd + + diff --git a/src/designer/designer/appfontdialog.cpp b/src/designer/designer/appfontdialog.cpp new file mode 100644 index 000000000..aa3b178b9 --- /dev/null +++ b/src/designer/designer/appfontdialog.cpp @@ -0,0 +1,430 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/appfontdialog.h b/src/designer/designer/appfontdialog.h new file mode 100644 index 000000000..c4b8674bd --- /dev/null +++ b/src/designer/designer/appfontdialog.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/assistantclient.cpp b/src/designer/designer/assistantclient.cpp new file mode 100644 index 000000000..c6ae30e79 --- /dev/null +++ b/src/designer/designer/assistantclient.cpp @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/assistantclient.h b/src/designer/designer/assistantclient.h new file mode 100644 index 000000000..2b753a715 --- /dev/null +++ b/src/designer/designer/assistantclient.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/designer.icns b/src/designer/designer/designer.icns new file mode 100644 index 0000000000000000000000000000000000000000..9940214fc29c3e9143672547c122ab76199fa1a4 GIT binary patch literal 154893 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 F`X5Yy_Ok#0 literal 0 HcmV?d00001 diff --git a/src/designer/designer/designer.pro b/src/designer/designer/designer.pro new file mode 100644 index 000000000..9c59b6756 --- /dev/null +++ b/src/designer/designer/designer.pro @@ -0,0 +1,13 @@ +win32 { + RC_FILE = designer.rc +} + +mac { + ICON = designer.icns + QMAKE_INFO_PLIST = Info_mac.plist + TARGET = Designer + FILETYPES.files = uifile.icns + FILETYPES.path = Contents/Resources + QMAKE_BUNDLE_DATA += FILETYPES +} + diff --git a/src/designer/designer/designer.qrc b/src/designer/designer/designer.qrc new file mode 100644 index 000000000..fac8120d0 --- /dev/null +++ b/src/designer/designer/designer.qrc @@ -0,0 +1,5 @@ + + + images/designer.png + + diff --git a/src/designer/designer/designer.rc b/src/designer/designer/designer.rc new file mode 100644 index 000000000..e43943a4f --- /dev/null +++ b/src/designer/designer/designer.rc @@ -0,0 +1,32 @@ +#include "winver.h" + +IDI_ICON1 ICON DISCARDABLE "designer.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "The Qt Company Ltd" + VALUE "FileDescription", "Qt Designer" + VALUE "FileVersion", "1.0.0.0" + VALUE "LegalCopyright", "Copyright (C) 2015 The Qt Company Ltd." + VALUE "InternalName", "designer" + VALUE "OriginalFilename", "designer.exe" + VALUE "ProductName", "Qt Designer" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/src/designer/designer/designer_enums.h b/src/designer/designer/designer_enums.h new file mode 100644 index 000000000..7908af0f9 --- /dev/null +++ b/src/designer/designer/designer_enums.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DESIGNERENUMS_H +#define DESIGNERENUMS_H + +enum UIMode +{ + NeutralMode, + TopLevelMode, + DockedMode +}; + +#endif // DESIGNERENUMS_H diff --git a/src/designer/designer/fontpanel/fontpanel.cmake b/src/designer/designer/fontpanel/fontpanel.cmake new file mode 100644 index 000000000..07937c08f --- /dev/null +++ b/src/designer/designer/fontpanel/fontpanel.cmake @@ -0,0 +1,9 @@ +set(DESIGNERBIN_SOURCES + ${DESIGNERBIN_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/fontpanel/fontpanel.cpp +) + +set(DESIGNERBIN_HEADERS + ${DESIGNERBIN_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/fontpanel/fontpanel.h +) diff --git a/src/designer/designer/fontpanel/fontpanel.cpp b/src/designer/designer/fontpanel/fontpanel.cpp new file mode 100644 index 000000000..f1663c1ae --- /dev/null +++ b/src/designer/designer/fontpanel/fontpanel.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/fontpanel/fontpanel.h b/src/designer/designer/fontpanel/fontpanel.h new file mode 100644 index 000000000..7d178b797 --- /dev/null +++ b/src/designer/designer/fontpanel/fontpanel.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/images/designer.png b/src/designer/designer/images/designer.png new file mode 100644 index 0000000000000000000000000000000000000000..0988fcee3f28eacfbd0cb0722b34093e8eace9a8 GIT binary patch literal 4205 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 z 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 jA{BrQEND-$o5j|9sL;<%2fB-`1Uk0=+( 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 +#include + +#include + +QT_USE_NAMESPACE + +int main(int argc, char *argv[]) +{ + Q_INIT_RESOURCE(designer); + + QDesigner app(argc, argv); + app.setQuitOnLastWindowClosed(false); + + return app.exec(); +} + +#include \ No newline at end of file diff --git a/src/designer/designer/mainwindow.cpp b/src/designer/designer/mainwindow.cpp new file mode 100644 index 000000000..2ae225976 --- /dev/null +++ b/src/designer/designer/mainwindow.cpp @@ -0,0 +1,420 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/mainwindow.h b/src/designer/designer/mainwindow.h new file mode 100644 index 000000000..105ddcc14 --- /dev/null +++ b/src/designer/designer/mainwindow.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/newform.cpp b/src/designer/designer/newform.cpp new file mode 100644 index 000000000..aef992a5b --- /dev/null +++ b/src/designer/designer/newform.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/newform.h b/src/designer/designer/newform.h new file mode 100644 index 000000000..f0508d7aa --- /dev/null +++ b/src/designer/designer/newform.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/preferencesdialog.cpp b/src/designer/designer/preferencesdialog.cpp new file mode 100644 index 000000000..8c93daae1 --- /dev/null +++ b/src/designer/designer/preferencesdialog.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/preferencesdialog.h b/src/designer/designer/preferencesdialog.h new file mode 100644 index 000000000..cd9c16b1a --- /dev/null +++ b/src/designer/designer/preferencesdialog.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/preferencesdialog.ui b/src/designer/designer/preferencesdialog.ui new file mode 100644 index 000000000..28d14bb83 --- /dev/null +++ b/src/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/src/designer/designer/qdesigner.cpp b/src/designer/designer/qdesigner.cpp new file mode 100644 index 000000000..1d5b10dfc --- /dev/null +++ b/src/designer/designer/qdesigner.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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(); + +#ifndef Q_WS_MAC + setWindowIcon(QIcon(QLatin1String(":/trolltech/designer/images/designer.png"))); +#endif + 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); + QTranslator *qtTranslator = new QTranslator(this); + + const QString localSysName = QLocale::system().name(); + QString translatorFileName = QLatin1String("designer_"); + translatorFileName += localSysName; + translator->load(translatorFileName, resourceDir); + + translatorFileName = QLatin1String("qt_"); + translatorFileName += localSysName; + qtTranslator->load(translatorFileName, resourceDir); + installTranslator(translator); + installTranslator(qtTranslator); + + 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) + m_workbench->actionManager()->createForm(); +} + +QT_END_NAMESPACE +#include diff --git a/src/designer/designer/qdesigner.h b/src/designer/designer/qdesigner.h new file mode 100644 index 000000000..37bf8728a --- /dev/null +++ b/src/designer/designer/qdesigner.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_actions.cpp b/src/designer/designer/qdesigner_actions.cpp new file mode 100644 index 000000000..c1dc4a5ed --- /dev/null +++ b/src/designer/designer/qdesigner_actions.cpp @@ -0,0 +1,1438 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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::toNativeSeparators(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::toNativeSeparators(m_backupPath ); + + m_backupTmpPath = m_backupPath; + m_backupTmpPath += QDir::separator(); + m_backupTmpPath += QLatin1String("tmp"); + m_backupTmpPath = QDir::toNativeSeparators(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 +#include diff --git a/src/designer/designer/qdesigner_actions.h b/src/designer/designer/qdesigner_actions.h new file mode 100644 index 000000000..e659a5ebb --- /dev/null +++ b/src/designer/designer/qdesigner_actions.h @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_appearanceoptions.cpp b/src/designer/designer/qdesigner_appearanceoptions.cpp new file mode 100644 index 000000000..05f89e0ac --- /dev/null +++ b/src/designer/designer/qdesigner_appearanceoptions.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 + +#include diff --git a/src/designer/designer/qdesigner_appearanceoptions.h b/src/designer/designer/qdesigner_appearanceoptions.h new file mode 100644 index 000000000..69c78bbf7 --- /dev/null +++ b/src/designer/designer/qdesigner_appearanceoptions.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_appearanceoptions.ui b/src/designer/designer/qdesigner_appearanceoptions.ui new file mode 100644 index 000000000..a5582f2ab --- /dev/null +++ b/src/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/src/designer/designer/qdesigner_formwindow.cpp b/src/designer/designer/qdesigner_formwindow.cpp new file mode 100644 index 000000000..f4338f1f7 --- /dev/null +++ b/src/designer/designer/qdesigner_formwindow.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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()); + updateChanged(); + } + } + 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 +#include diff --git a/src/designer/designer/qdesigner_formwindow.h b/src/designer/designer/qdesigner_formwindow.h new file mode 100644 index 000000000..0ef63ac99 --- /dev/null +++ b/src/designer/designer/qdesigner_formwindow.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_pch.h b/src/designer/designer/qdesigner_pch.h new file mode 100644 index 000000000..3935aacf2 --- /dev/null +++ b/src/designer/designer/qdesigner_pch.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_server.cpp b/src/designer/designer/qdesigner_server.cpp new file mode 100644 index 000000000..52a6107d3 --- /dev/null +++ b/src/designer/designer/qdesigner_server.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/qdesigner_server.h b/src/designer/designer/qdesigner_server.h new file mode 100644 index 000000000..8b9ec5177 --- /dev/null +++ b/src/designer/designer/qdesigner_server.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_settings.cpp b/src/designer/designer/qdesigner_settings.cpp new file mode 100644 index 000000000..8e8df24b9 --- /dev/null +++ b/src/designer/designer/qdesigner_settings.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 = qvariant_cast(value(QLatin1String("UI/font"))); + fontSettings.m_useFont = + settings()->value(QLatin1String("UI/useFont"), QVariant(false)).toBool(); + return fontSettings; +} + +QT_END_NAMESPACE diff --git a/src/designer/designer/qdesigner_settings.h b/src/designer/designer/qdesigner_settings.h new file mode 100644 index 000000000..b4c4c0253 --- /dev/null +++ b/src/designer/designer/qdesigner_settings.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_toolwindow.cpp b/src/designer/designer/qdesigner_toolwindow.cpp new file mode 100644 index 000000000..86e55dfa0 --- /dev/null +++ b/src/designer/designer/qdesigner_toolwindow.cpp @@ -0,0 +1,439 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/qdesigner_toolwindow.h b/src/designer/designer/qdesigner_toolwindow.h new file mode 100644 index 000000000..a26192642 --- /dev/null +++ b/src/designer/designer/qdesigner_toolwindow.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qdesigner_workbench.cpp b/src/designer/designer/qdesigner_workbench.cpp new file mode 100644 index 000000000..3364cfa57 --- /dev/null +++ b/src/designer/designer/qdesigner_workbench.cpp @@ -0,0 +1,1111 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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(); + +#ifdef Q_WS_X11 + QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false); +#endif +#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); +#ifdef Q_WS_X11 + // For now the appmenu protocol does not make it possible to associate a + // menubar with all application windows. This means in top level mode you + // can only reach the menubar when the widgetbox window is active. Since + // this is quite inconvenient, better not use the native menubar in this + // configuration and keep the menubar in the widgetbox window. + QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true); +#endif +#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::toNativeSeparators(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 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(); + // Did user specify another (missing) resource path -> set dirty. + const bool dirty = editor->property("_q_resourcepathchanged").toBool(); + editor->setDirty(dirty); + 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 +#include diff --git a/src/designer/designer/qdesigner_workbench.h b/src/designer/designer/qdesigner_workbench.h new file mode 100644 index 000000000..9d0861e47 --- /dev/null +++ b/src/designer/designer/qdesigner_workbench.h @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qttoolbardialog/images/back.png b/src/designer/designer/qttoolbardialog/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/src/designer/designer/qttoolbardialog/images/down.png b/src/designer/designer/qttoolbardialog/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/src/designer/designer/qttoolbardialog/images/minus.png b/src/designer/designer/qttoolbardialog/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/src/designer/designer/qttoolbardialog/images/up.png b/src/designer/designer/qttoolbardialog/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 +#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 "moc_qttoolbardialog.h" diff --git a/src/designer/designer/qttoolbardialog/qttoolbardialog.h b/src/designer/designer/qttoolbardialog/qttoolbardialog.h new file mode 100644 index 000000000..3c6affdf5 --- /dev/null +++ b/src/designer/designer/qttoolbardialog/qttoolbardialog.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/qttoolbardialog/qttoolbardialog.qrc b/src/designer/designer/qttoolbardialog/qttoolbardialog.qrc new file mode 100644 index 000000000..ce0336682 --- /dev/null +++ b/src/designer/designer/qttoolbardialog/qttoolbardialog.qrc @@ -0,0 +1,10 @@ + + + images/up.png + images/down.png + images/forward.png + images/back.png + images/plus.png + images/minus.png + + diff --git a/src/designer/designer/qttoolbardialog/qttoolbardialog.ui b/src/designer/designer/qttoolbardialog/qttoolbardialog.ui new file mode 100644 index 000000000..c4ad934f8 --- /dev/null +++ b/src/designer/designer/qttoolbardialog/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/src/designer/designer/saveformastemplate.cpp b/src/designer/designer/saveformastemplate.cpp new file mode 100644 index 000000000..96142090d --- /dev/null +++ b/src/designer/designer/saveformastemplate.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 +#include diff --git a/src/designer/designer/saveformastemplate.h b/src/designer/designer/saveformastemplate.h new file mode 100644 index 000000000..25c957ebc --- /dev/null +++ b/src/designer/designer/saveformastemplate.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/saveformastemplate.ui b/src/designer/designer/saveformastemplate.ui new file mode 100644 index 000000000..9be3c8031 --- /dev/null +++ b/src/designer/designer/saveformastemplate.ui @@ -0,0 +1,166 @@ + + ********************************************************************* +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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/src/designer/designer/uifile.icns b/src/designer/designer/uifile.icns new file mode 100644 index 0000000000000000000000000000000000000000..2473ea4dc9f0531cdc64203e4a269763b170ee72 GIT binary patch literal 123696 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≶(LAWv}iP^toXC`7e(R(09aW5$MF8Yp-BJ0 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�twUWYcYN8Ys$`!}*JQ(7Wu1jAu?vp7gjz zSzY0*rMhLeQtuj3=hxUWq74GymJNC5QC-*0!`14 z1^-E6q}|83^p``VyaB?=Im~3k!rdW6_KFe#xB4uzGxu*a$JPlz3?vlMpg|`k)&!i& z2SvIVpV$~e<&+x=FrcT(I#b(0^_tD~pRO3V!sjYre6$vL%?151)ym?|XHk8F`_%-y z$)%0MfdIq$!tOCQcou!tR%ME4;*TA9WuD{c!a;k_#+BddhzCWME^d`+B=*6SiG#0Y z%5v(!Dzu>I_=f2-l}7X>lL}9A8_^|j>v~BRmdL^DuZw;r1-%s z`2K)J!1&g}LVqx*VZDIf_oHxW$`VUges-M|k_#Awhm5WiRUxro1;Ni#P*|J=KE_(cXO2S$(th=d(@;QC=yxKFa)L4n>a2V<_oG z$JRQEfAYcsY~3zE1PPA^p%>hA(ywOze@9U|d7Mivgcd7Fwv&psPne@r1~i0#?y++e zcIY)qr`wem@TM2zyvmznpo@;vMf$hOV_t&g=(A=%R0t2zX$O&~m1(4^|`!TQZ|z>lvzQt@D3{qXqH5uc=4%F%}<79BqlakU{}?BFZ609Z&gs_ic0s(;4N zG`C-Yl@WGiHb{ zA0FIVToC6lq*vH|G0`Q9@JH?Aqms*(;s-KW%XUvS81vq{BT5JvoOfA430kWs(VTq5 zMHr(D^8S6%Lx>FLJEs@YIz-jDh+6l>3zX};0={e`s5@i0H|;?dCVuloy1})McELuN zE>abfc5ZBKrD921vnLG_UHxW9geLZWDL1^y@d?nphXXBe7pJ}qM1koE^pLK5T5ek< zUQvU9aiwM|wX+^v!jM&jkca2=YB*{HgyhRT41Noze@N(xIQ95^c3nOt$-e^*q{3uF zp>k7Oh)723Hɽ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 Gfc$?%1304q literal 0 HcmV?d00001 diff --git a/src/designer/designer/versiondialog.cpp b/src/designer/designer/versiondialog.cpp new file mode 100644 index 000000000..8b1c89ead --- /dev/null +++ b/src/designer/designer/versiondialog.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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) 2015 The Qt Company Ltd." + ).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 "moc_versiondialog.cpp" +#include "moc_versiondialog.h" diff --git a/src/designer/designer/versiondialog.h b/src/designer/designer/versiondialog.h new file mode 100644 index 000000000..6ac781d40 --- /dev/null +++ b/src/designer/designer/versiondialog.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $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 -- 2.11.0