diff options
Diffstat (limited to 'tools')
116 files changed, 5044 insertions, 1967 deletions
diff --git a/tools/designer/data/ui4.xsd b/tools/designer/data/ui4.xsd index 703e497..de4253c 100644 --- a/tools/designer/data/ui4.xsd +++ b/tools/designer/data/ui4.xsd @@ -143,6 +143,7 @@ <xs:element name="script" type="Script" minOccurs="0" /> <xs:element name="properties" type="Properties" minOccurs="0" /> <xs:element name="slots" type="Slots" minOccurs="0" /> + <xs:element name="propertyspecifications" type="PropertySpecifications" minOccurs="0" /> </xs:all> </xs:complexType> @@ -571,4 +572,16 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="PropertySpecifications"> + <xs:sequence maxOccurs="unbounded"> + <xs:element name="stringpropertyspecification" type="StringPropertySpecification" minOccurs="0" maxOccurs="unbounded" /> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="StringPropertySpecification"> + <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required" /> + <xs:attribute name="notr" type="xs:string"/> + </xs:complexType> + </xs:schema> diff --git a/tools/designer/src/components/buddyeditor/buddyeditor.cpp b/tools/designer/src/components/buddyeditor/buddyeditor.cpp index f5c93fa..43348d2 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditor -*/ - #include "buddyeditor.h" #include <QtDesigner/QDesignerFormWindowInterface> diff --git a/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp b/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp index 98fff8c..9319916 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditorPlugin -*/ - #include <QtGui/QAction> #include "buddyeditor_plugin.h" diff --git a/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp b/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp index 68a6030..cf7f250 100644 --- a/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp +++ b/tools/designer/src/components/buddyeditor/buddyeditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::BuddyEditorTool -*/ - #include "buddyeditor_tool.h" #include "buddyeditor.h" diff --git a/tools/designer/src/components/formeditor/embeddedoptionspage.cpp b/tools/designer/src/components/formeditor/embeddedoptionspage.cpp index bf3f3f1..1a41985 100644 --- a/tools/designer/src/components/formeditor/embeddedoptionspage.cpp +++ b/tools/designer/src/components/formeditor/embeddedoptionspage.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::EmbeddedOptionsControl -*/ - #include "embeddedoptionspage.h" #include "deviceprofiledialog.h" #include "widgetfactory_p.h" diff --git a/tools/designer/src/components/formeditor/formwindow.cpp b/tools/designer/src/components/formeditor/formwindow.cpp index 48efcde..07d785a 100644 --- a/tools/designer/src/components/formeditor/formwindow.cpp +++ b/tools/designer/src/components/formeditor/formwindow.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindow -*/ - #include "formwindow.h" #include "formeditor.h" #include "formwindow_dnditem.h" diff --git a/tools/designer/src/components/formeditor/formwindowcursor.cpp b/tools/designer/src/components/formeditor/formwindowcursor.cpp index 5b4aee1..fb30885 100644 --- a/tools/designer/src/components/formeditor/formwindowcursor.cpp +++ b/tools/designer/src/components/formeditor/formwindowcursor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowCursor -*/ - #include "formwindowcursor.h" #include "formwindow.h" diff --git a/tools/designer/src/components/formeditor/formwindowmanager.cpp b/tools/designer/src/components/formeditor/formwindowmanager.cpp index 69fb4a3..ea8d128 100644 --- a/tools/designer/src/components/formeditor/formwindowmanager.cpp +++ b/tools/designer/src/components/formeditor/formwindowmanager.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowManager -*/ - // components/formeditor #include "formwindowmanager.h" #include "formwindow_dnditem.h" diff --git a/tools/designer/src/components/formeditor/tool_widgeteditor.cpp b/tools/designer/src/components/formeditor/tool_widgeteditor.cpp index 3a59543..aed7e80 100644 --- a/tools/designer/src/components/formeditor/tool_widgeteditor.cpp +++ b/tools/designer/src/components/formeditor/tool_widgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::WidgetEditorTool -*/ - #include "tool_widgeteditor.h" #include "formwindow.h" diff --git a/tools/designer/src/components/objectinspector/objectinspector.cpp b/tools/designer/src/components/objectinspector/objectinspector.cpp index 4e515be..cb90d2a 100644 --- a/tools/designer/src/components/objectinspector/objectinspector.cpp +++ b/tools/designer/src/components/objectinspector/objectinspector.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ObjectInspector -*/ - #include "objectinspector.h" #include "objectinspectormodel_p.h" #include "formwindow.h" diff --git a/tools/designer/src/components/objectinspector/objectinspectormodel.cpp b/tools/designer/src/components/objectinspector/objectinspectormodel.cpp index bc1ac0c..1e20ec3 100644 --- a/tools/designer/src/components/objectinspector/objectinspectormodel.cpp +++ b/tools/designer/src/components/objectinspector/objectinspectormodel.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ObjectInspector -*/ - #include "objectinspectormodel_p.h" #include <qlayout_widget_p.h> diff --git a/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp b/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp index 346da18..1dd5bd6 100644 --- a/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp +++ b/tools/designer/src/components/propertyeditor/designerpropertymanager.cpp @@ -2455,6 +2455,9 @@ void DesignerEditorFactory::slotStringTextChanged(const QString &value) if (val.userType() == DesignerPropertyManager::designerStringTypeId()) { PropertySheetStringValue strVal = qVariantValue<PropertySheetStringValue>(val); strVal.setValue(value); + // Disable translation if no translation subproperties exist. + if (varProp->subProperties().empty()) + strVal.setTranslatable(false); val = qVariantFromValue(strVal); } else { val = QVariant(value); diff --git a/tools/designer/src/components/propertyeditor/paletteeditor.cpp b/tools/designer/src/components/propertyeditor/paletteeditor.cpp index b0dc82b..4c60941 100644 --- a/tools/designer/src/components/propertyeditor/paletteeditor.cpp +++ b/tools/designer/src/components/propertyeditor/paletteeditor.cpp @@ -39,13 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PaletteEditor -*/ -/* -TRANSLATOR qdesigner_internal::PaletteModel -*/ - #include "paletteeditor.h" #include <iconloader_p.h> diff --git a/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp b/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp index d7a76b9..c425e18 100644 --- a/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp +++ b/tools/designer/src/components/propertyeditor/paletteeditorbutton.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PaletteEditorButton -*/ - #include "paletteeditorbutton.h" #include "paletteeditor.h" diff --git a/tools/designer/src/components/propertyeditor/previewframe.cpp b/tools/designer/src/components/propertyeditor/previewframe.cpp index 95d533d..e94ace0 100644 --- a/tools/designer/src/components/propertyeditor/previewframe.cpp +++ b/tools/designer/src/components/propertyeditor/previewframe.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PreviewWorkspace -*/ - #include "previewframe.h" #include "previewwidget.h" diff --git a/tools/designer/src/components/propertyeditor/propertyeditor.cpp b/tools/designer/src/components/propertyeditor/propertyeditor.cpp index 63b57d8..ead404b 100644 --- a/tools/designer/src/components/propertyeditor/propertyeditor.cpp +++ b/tools/designer/src/components/propertyeditor/propertyeditor.cpp @@ -137,13 +137,8 @@ void PropertyEditor::setupStringProperty(QtVariantProperty *property, bool isMai 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) { - QList<QtProperty *> commentProperties = property->subProperties(); - if (commentProperties.count() > 0) - delete commentProperties.at(0); - if (commentProperties.count() > 1) - delete commentProperties.at(1); - } + if (!hasComment) + qDeleteAll(property->subProperties()); } void PropertyEditor::setupPaletteProperty(QtVariantProperty *property) diff --git a/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp b/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp index 440015c..32a8c78 100644 --- a/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp +++ b/tools/designer/src/components/propertyeditor/stringlisteditorbutton.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StringListEditorButton -*/ - #include "stringlisteditorbutton.h" #include "stringlisteditor.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp index 311c843..a6f4969 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::SignalSlotEditorPlugin -*/ - #include "signalsloteditor_plugin.h" #include "signalsloteditor_tool.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp index 973105c..311ec36 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::SignalSlotEditorTool -*/ - #include "signalsloteditor_tool.h" #include "signalsloteditor.h" #include "ui4_p.h" diff --git a/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp b/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp index dc6b3c3..c916af8 100644 --- a/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp +++ b/tools/designer/src/components/signalsloteditor/signalsloteditorwindow.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ConnectionModel -*/ - #include "signalsloteditorwindow.h" #include "signalsloteditor_p.h" #include "signalsloteditor.h" diff --git a/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp b/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp index 46bf378..abe882b 100644 --- a/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp +++ b/tools/designer/src/components/tabordereditor/tabordereditor_plugin.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TabOrderEditorPlugin -*/ - #include <QtGui/QAction> #include "tabordereditor_plugin.h" diff --git a/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp b/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp index 4f291a1..75304ee 100644 --- a/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp +++ b/tools/designer/src/components/tabordereditor/tabordereditor_tool.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TabOrderEditorTool -*/ - #include "tabordereditor_tool.h" #include "tabordereditor.h" diff --git a/tools/designer/src/components/taskmenu/button_taskmenu.cpp b/tools/designer/src/components/taskmenu/button_taskmenu.cpp index ced32ce..c2816a2 100644 --- a/tools/designer/src/components/taskmenu/button_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/button_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ButtonTaskMenu -*/ - #include "button_taskmenu.h" #include "inplace_editor.h" #include <qdesigner_formwindowcommand_p.h> diff --git a/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp b/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp index e3364cc..908cb61 100644 --- a/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/combobox_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ComboBoxTaskMenu -*/ - #include "combobox_taskmenu.h" #include "listwidgeteditor.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp index b182ddf..c6673e3 100644 --- a/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/containerwidget_taskmenu.cpp @@ -39,13 +39,8 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ContainerWidgetTaskMenu -*/ - #include "containerwidget_taskmenu.h" - #include <QtDesigner/QDesignerFormEditorInterface> #include <QtDesigner/QDesignerFormWindowInterface> #include <QtDesigner/QExtensionManager> diff --git a/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp b/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp index bb97342..18b6612 100644 --- a/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/groupbox_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::GroupBoxTaskMenu -*/ - #include "groupbox_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/label_taskmenu.cpp b/tools/designer/src/components/taskmenu/label_taskmenu.cpp index 77e5ed9..f85df33 100644 --- a/tools/designer/src/components/taskmenu/label_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/label_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LabelTaskMenu -*/ - #include "label_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/layouttaskmenu.cpp b/tools/designer/src/components/taskmenu/layouttaskmenu.cpp index 4a3aeec..0bfe607 100644 --- a/tools/designer/src/components/taskmenu/layouttaskmenu.cpp +++ b/tools/designer/src/components/taskmenu/layouttaskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LayoutWidgetTaskMenu -*/ - #include "layouttaskmenu.h" #include <formlayoutmenu_p.h> #include <morphmenu_p.h> diff --git a/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp b/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp index baf4785..a88246a 100644 --- a/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/lineedit_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::LineEditTaskMenu -*/ - #include "lineedit_taskmenu.h" #include "inplace_editor.h" diff --git a/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp index 1a9ba36..10e004d 100644 --- a/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/listwidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ListWidgetTaskMenu -*/ - #include "listwidget_taskmenu.h" #include "listwidgeteditor.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/components/taskmenu/listwidgeteditor.cpp b/tools/designer/src/components/taskmenu/listwidgeteditor.cpp index 2a8de52..4c3ede9 100644 --- a/tools/designer/src/components/taskmenu/listwidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/listwidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ListWidgetEditor -*/ - #include "listwidgeteditor.h" #include <designerpropertymanager.h> #include <abstractformbuilder.h> diff --git a/tools/designer/src/components/taskmenu/menutaskmenu.cpp b/tools/designer/src/components/taskmenu/menutaskmenu.cpp index 52320ac..093a1fe 100644 --- a/tools/designer/src/components/taskmenu/menutaskmenu.cpp +++ b/tools/designer/src/components/taskmenu/menutaskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::MenuTaskMenu -*/ - #include "menutaskmenu.h" #include <promotiontaskmenu_p.h> diff --git a/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp index 5ef1f9c..dce9f97 100644 --- a/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/tablewidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TableWidgetTaskMenu -*/ - #include "tablewidget_taskmenu.h" #include "tablewidgeteditor.h" diff --git a/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp b/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp index 0863847..be1f89a 100644 --- a/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/tablewidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TableWidgetEditor -*/ - #include "tablewidgeteditor.h" #include <abstractformbuilder.h> #include <iconloader_p.h> diff --git a/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp b/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp index feaec2c..a9a2a96 100644 --- a/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/textedit_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TextEditTaskMenu -*/ - #include "textedit_taskmenu.h" #include <QtDesigner/QDesignerFormWindowInterface> diff --git a/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp b/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp index a15cd8a..d841b87 100644 --- a/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/toolbar_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ToolBarTaskMenu -*/ - #include "toolbar_taskmenu.h" #include "qdesigner_toolbar_p.h" diff --git a/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp b/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp index b1c8c28..34d883a 100644 --- a/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp +++ b/tools/designer/src/components/taskmenu/treewidget_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TreeWidgetTaskMenu -*/ - #include "treewidget_taskmenu.h" #include "treewidgeteditor.h" diff --git a/tools/designer/src/components/taskmenu/treewidgeteditor.cpp b/tools/designer/src/components/taskmenu/treewidgeteditor.cpp index faf00f2..e97ebde 100644 --- a/tools/designer/src/components/taskmenu/treewidgeteditor.cpp +++ b/tools/designer/src/components/taskmenu/treewidgeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::TreeWidgetEditor -*/ - #include "treewidgeteditor.h" #include <formwindowbase_p.h> #include <iconloader_p.h> diff --git a/tools/designer/src/lib/sdk/abstractformeditor.cpp b/tools/designer/src/lib/sdk/abstractformeditor.cpp index 17e93e7..e6debd5 100644 --- a/tools/designer/src/lib/sdk/abstractformeditor.cpp +++ b/tools/designer/src/lib/sdk/abstractformeditor.cpp @@ -66,9 +66,22 @@ #include <grid_p.h> #include <QtDesigner/QDesignerPromotionInterface> +// Must be done outside of the Qt namespace static void initResources() { Q_INIT_RESOURCE(shared); + Q_INIT_RESOURCE(ClamshellPhone); + Q_INIT_RESOURCE(PDAPhone); + Q_INIT_RESOURCE(pda); + Q_INIT_RESOURCE(PortableMedia); + Q_INIT_RESOURCE(S60_nHD_Touchscreen); + Q_INIT_RESOURCE(S60_QVGA_Candybar); + Q_INIT_RESOURCE(SmartPhone2); + Q_INIT_RESOURCE(SmartPhone); + Q_INIT_RESOURCE(SmartPhoneWithButtons); + Q_INIT_RESOURCE(TouchscreenPhone); + Q_INIT_RESOURCE(Trolltech_Keypad); + Q_INIT_RESOURCE(Trolltech_Touchscreen); } QT_BEGIN_NAMESPACE diff --git a/tools/designer/src/lib/shared/actioneditor.cpp b/tools/designer/src/lib/shared/actioneditor.cpp index 7f96a15..e60b212 100644 --- a/tools/designer/src/lib/shared/actioneditor.cpp +++ b/tools/designer/src/lib/shared/actioneditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ActionEditor -*/ - #include "actioneditor_p.h" #include "filterwidget_p.h" #include "actionrepository_p.h" diff --git a/tools/designer/src/lib/shared/codedialog.cpp b/tools/designer/src/lib/shared/codedialog.cpp index 5a2db33..0b18f5e 100644 --- a/tools/designer/src/lib/shared/codedialog.cpp +++ b/tools/designer/src/lib/shared/codedialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::CodeDialog -*/ - #include "codedialog_p.h" #include "qdesigner_utils_p.h" #include "iconloader_p.h" diff --git a/tools/designer/src/lib/shared/csshighlighter.cpp b/tools/designer/src/lib/shared/csshighlighter.cpp index d5e045b..f7144c5 100644 --- a/tools/designer/src/lib/shared/csshighlighter.cpp +++ b/tools/designer/src/lib/shared/csshighlighter.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StyleSheetEditorDialog -*/ - #include "csshighlighter_p.h" QT_BEGIN_NAMESPACE diff --git a/tools/designer/src/lib/shared/formwindowbase.cpp b/tools/designer/src/lib/shared/formwindowbase.cpp index 3e7e17b..5be907b 100644 --- a/tools/designer/src/lib/shared/formwindowbase.cpp +++ b/tools/designer/src/lib/shared/formwindowbase.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::FormWindowBase -*/ - #include "formwindowbase_p.h" #include "connectionedit_p.h" #include "qdesigner_command_p.h" diff --git a/tools/designer/src/lib/shared/orderdialog.cpp b/tools/designer/src/lib/shared/orderdialog.cpp index 3f14ca6..0df3866 100644 --- a/tools/designer/src/lib/shared/orderdialog.cpp +++ b/tools/designer/src/lib/shared/orderdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::OrderDialog -*/ - #include "orderdialog_p.h" #include "iconloader_p.h" #include "ui_orderdialog.h" diff --git a/tools/designer/src/lib/shared/plaintexteditor.cpp b/tools/designer/src/lib/shared/plaintexteditor.cpp index ce5cd5b..f30df3d 100644 --- a/tools/designer/src/lib/shared/plaintexteditor.cpp +++ b/tools/designer/src/lib/shared/plaintexteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PlainTextEditorDialog -*/ - #include "plaintexteditor_p.h" #include "abstractsettings_p.h" diff --git a/tools/designer/src/lib/shared/pluginmanager.cpp b/tools/designer/src/lib/shared/pluginmanager.cpp index 9dc8c7b..100a088 100644 --- a/tools/designer/src/lib/shared/pluginmanager.cpp +++ b/tools/designer/src/lib/shared/pluginmanager.cpp @@ -16,6 +16,8 @@ ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the +What usually causes this mess is specifying a Z-order (raise/lower) widget or changing the tab order and then deleting the widget. Designer did not delete the widget from those settings, causing uic to report this when applying them. I believe we fixed that 4.5. + ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. @@ -72,6 +74,11 @@ static const char *classAttributeC = "class"; static const char *customwidgetElementC = "customwidget"; static const char *extendsElementC = "extends"; static const char *addPageMethodC = "addpagemethod"; +static const char *propertySpecsC = "propertyspecifications"; +static const char *stringPropertySpecC = "stringpropertyspecification"; +static const char *stringPropertyNameAttrC = "name"; +static const char *stringPropertyTypeAttrC = "type"; +static const char *stringPropertyNoTrAttrC = "notr"; static const char *jambiLanguageC = "jambi"; enum { debugPluginManager = 0 }; @@ -141,6 +148,10 @@ static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core) class QDesignerCustomWidgetSharedData : public QSharedData { public: + // Type of a string property + typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType; + typedef QHash<QString, StringPropertyType> StringPropertyTypeMap; + explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} void clearXML(); @@ -152,6 +163,7 @@ public: QString xmlAddPageMethod; QString xmlExtends; + StringPropertyTypeMap xmlStringPropertyTypeMap; }; void QDesignerCustomWidgetSharedData::clearXML() @@ -161,6 +173,7 @@ void QDesignerCustomWidgetSharedData::clearXML() xmlLanguage.clear(); xmlAddPageMethod.clear(); xmlExtends.clear(); + xmlStringPropertyTypeMap.clear(); } // ---------------- QDesignerCustomWidgetData @@ -220,6 +233,17 @@ QString QDesignerCustomWidgetData::pluginPath() const return m_d->pluginPath; } +bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, StringPropertyType *type) const +{ + QDesignerCustomWidgetSharedData::StringPropertyTypeMap::const_iterator it = m_d->xmlStringPropertyTypeMap.constFind(name); + if (it == m_d->xmlStringPropertyTypeMap.constEnd()) { + *type = StringPropertyType(qdesigner_internal::ValidationRichText, true); + return false; + } + *type = it.value(); + return true; +} + // Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult enum FindResult { FindError = -2, ElementNotFound = -1 }; @@ -249,6 +273,82 @@ static inline QString msgXmlError(const QString &name, const QString &errorMessa return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage); } +static inline QString msgAttributeMissing(const QString &name) +{ + return QDesignerPluginManager::tr("A required attribute ('%1') is missing.").arg(name); +} + +static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QString &v, bool *ok) +{ + *ok = true; + if (v == QLatin1String("multiline")) + return qdesigner_internal::ValidationMultiLine; + if (v == QLatin1String("richtext")) + return qdesigner_internal::ValidationRichText; + if (v == QLatin1String("stylesheet")) + return qdesigner_internal::ValidationStyleSheet; + if (v == QLatin1String("singleline")) + return qdesigner_internal::ValidationSingleLine; + if (v == QLatin1String("objectname")) + return qdesigner_internal::ValidationObjectName; + if (v == QLatin1String("objectnamescope")) + return qdesigner_internal::ValidationObjectNameScope; + if (v == QLatin1String("url")) + return qdesigner_internal::ValidationURL; + *ok = false; + return qdesigner_internal::ValidationRichText; +} + +static bool parsePropertySpecs(QXmlStreamReader &sr, + QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc, + QString *errorMessage) +{ + const QString propertySpecs = QLatin1String(propertySpecsC); + const QString stringPropertySpec = QLatin1String(stringPropertySpecC); + const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC); + const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC); + const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC); + + while (!sr.atEnd()) { + switch(sr.readNext()) { + case QXmlStreamReader::StartElement: { + if (sr.name() != stringPropertySpec) { + *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec); + return false; + } + const QXmlStreamAttributes atts = sr.attributes(); + const QString name = atts.value(stringPropertyNameAttr).toString(); + const QString type = atts.value(stringPropertyTypeAttr).toString(); + const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional + + if (type.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); + return false; + } + if (name.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyNameAttr); + return false; + } + bool typeOk; + const bool noTr = notrS == QLatin1String("true") || notrS == QLatin1String("1"); + QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); + if (!typeOk) { + *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification!").arg(type); + return false; + } + rc->insert(name, v); + } + break; + case QXmlStreamReader::EndElement: // Outer </stringproperties> + if (sr.name() == propertySpecs) + return true; + default: + break; + } + } + return true; +} + QDesignerCustomWidgetData::ParseResult QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage) { @@ -311,10 +411,11 @@ QDesignerCustomWidgetData::ParseResult default: break; } - // Find <extends>, <addPageMethod> + // Find <extends>, <addPageMethod>, <stringproperties> elements.clear(); elements.push_back(QLatin1String(extendsElementC)); elements.push_back(QLatin1String(addPageMethodC)); + elements.push_back(QLatin1String(propertySpecsC)); while (true) { switch (findElement(elements, sr)) { case FindError: @@ -336,6 +437,12 @@ QDesignerCustomWidgetData::ParseResult return ParseError; } break; + case 2: // <stringproperties> + if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) { + *errorMessage = msgXmlError(name, *errorMessage); + return ParseError; + } + break; } } return rc; @@ -345,6 +452,8 @@ QDesignerCustomWidgetData::ParseResult class QDesignerPluginManagerPrivate { public: + typedef QPair<QString, QString> ClassNamePropertyNameKey; + QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core); void clearCustomWidgets(); @@ -562,7 +671,7 @@ bool QDesignerPluginManager::registerNewPlugins() const int before = m_d->m_registeredPlugins.size(); foreach (const QString &path, m_d->m_pluginPaths) - registerPath(path); + registerPath(path); const bool newPluginsFound = m_d->m_registeredPlugins.size() > before; // We force a re-initialize as Jambi collection might return // different widget lists when switching projects. @@ -654,6 +763,15 @@ QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCust return m_d->m_customWidgetData.at(index); } +QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(const QString &name) const +{ + const int count = m_d->m_customWidgets.size(); + for (int i = 0; i < count; i++) + if (m_d->m_customWidgets.at(i)->name() == name) + return m_d->m_customWidgetData.at(i); + return QDesignerCustomWidgetData(); +} + QObjectList QDesignerPluginManager::instances() const { QStringList plugins = registeredPlugins(); diff --git a/tools/designer/src/lib/shared/pluginmanager_p.h b/tools/designer/src/lib/shared/pluginmanager_p.h index e374f42..f73bc74 100644 --- a/tools/designer/src/lib/shared/pluginmanager_p.h +++ b/tools/designer/src/lib/shared/pluginmanager_p.h @@ -54,9 +54,11 @@ #define PLUGINMANAGER_H #include "shared_global_p.h" +#include "shared_enums_p.h" #include <QtCore/QSharedDataPointer> #include <QtCore/QMap> +#include <QtCore/QPair> #include <QtCore/QStringList> QT_BEGIN_NAMESPACE @@ -70,6 +72,9 @@ class QDesignerCustomWidgetSharedData; /* Information contained in the Dom XML of a custom widget. */ class QDESIGNER_SHARED_EXPORT QDesignerCustomWidgetData { public: + // StringPropertyType: validation mode and translatable flag. + typedef QPair<qdesigner_internal::TextPropertyValidationMode, bool> StringPropertyType; + explicit QDesignerCustomWidgetData(const QString &pluginPath = QString()); enum ParseResult { ParseOk, ParseWarning, ParseError }; @@ -93,6 +98,8 @@ public: QString xmlExtends() const; // Optional. The name to be used in the widget box. QString xmlDisplayName() const; + // Type of a string property + bool xmlStringPropertyType(const QString &name, StringPropertyType *type) const; private: QSharedDataPointer<QDesignerCustomWidgetSharedData> m_d; @@ -128,6 +135,7 @@ public: CustomWidgetList registeredCustomWidgets() const; QDesignerCustomWidgetData customWidgetData(QDesignerCustomWidgetInterface *w) const; + QDesignerCustomWidgetData customWidgetData(const QString &className) const; bool registerNewPlugins(); diff --git a/tools/designer/src/lib/shared/previewconfigurationwidget.cpp b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp index 2cf362f..2b45759 100644 --- a/tools/designer/src/lib/shared/previewconfigurationwidget.cpp +++ b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp @@ -65,43 +65,27 @@ #include <QtCore/QFileInfo> #include <QtCore/QSharedData> -// #define DEFAULT_SKINS_FROM_RESOURCE -#ifdef DEFAULT_SKINS_FROM_RESOURCE -QT_BEGIN_NAMESPACE + static const char *skinResourcePathC = ":/skins/"; -QT_END_NAMESPACE -#else -# include <QtCore/QLibraryInfo> -#endif QT_BEGIN_NAMESPACE static const char *skinExtensionC = "skin"; -namespace { - // Pair of skin name, path - typedef QPair<QString, QString> SkinNamePath; - typedef QList<SkinNamePath> Skins; - enum { SkinComboNoneIndex = 0 }; -} +// Pair of skin name, path +typedef QPair<QString, QString> SkinNamePath; +typedef QList<SkinNamePath> Skins; +enum { SkinComboNoneIndex = 0 }; // find default skins (resources) static const Skins &defaultSkins() { static Skins rc; if (rc.empty()) { -#ifdef DEFAULT_SKINS_FROM_RESOURCE const QString skinPath = QLatin1String(skinResourcePathC); -#else - QString skinPath = QLibraryInfo::location(QLibraryInfo::PrefixPath); - skinPath += QDir::separator(); - skinPath += QLatin1String("tools"); - skinPath += QDir::separator(); - skinPath += QLatin1String("qvfb"); -#endif QString pattern = QLatin1String("*."); pattern += QLatin1String(skinExtensionC); const QDir dir(skinPath, pattern); - const QFileInfoList list = dir.entryInfoList(); + const QFileInfoList list = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot, QDir::Name); if (list.empty()) return rc; const QFileInfoList::const_iterator lcend = list.constEnd(); diff --git a/tools/designer/src/lib/shared/previewmanager.cpp b/tools/designer/src/lib/shared/previewmanager.cpp index 8ed1772..ac16741 100644 --- a/tools/designer/src/lib/shared/previewmanager.cpp +++ b/tools/designer/src/lib/shared/previewmanager.cpp @@ -67,6 +67,7 @@ #include <QtGui/QAction> #include <QtGui/QActionGroup> #include <QtGui/QCursor> +#include <QtGui/QMatrix> #include <QtCore/QMap> #include <QtCore/QDebug> @@ -149,11 +150,16 @@ QGraphicsProxyWidget *DesignerZoomWidget::createProxyWidget(QGraphicsItem *paren return new DesignerZoomProxyWidget(parent, wFlags); } -// --------- Widget Preview skin: Forward the key events to the window +// PreviewDeviceSkin: Forwards the key events to the window and +// provides context menu with rotation options. Derived class +// can apply additional transformations to the skin. + class PreviewDeviceSkin : public DeviceSkin { Q_OBJECT public: + enum Direction { DirectionUp, DirectionLeft, DirectionRight }; + explicit PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); virtual void setPreview(QWidget *w); QSize screenSize() const { return m_screenSize; } @@ -164,15 +170,36 @@ private slots: void slotPopupMenu(); protected: - virtual void populateContextMenu(QMenu *m); + virtual void populateContextMenu(QMenu *) {} + +private slots: + void slotDirection(QAction *); + +protected: + // Fit the widget in case the orientation changes (transposing screensize) + virtual void fitWidget(const QSize &size); + // Calculate the complete transformation for the skin + // (base class implementation provides rotation). + virtual QMatrix skinTransform() const; private: const QSize m_screenSize; + Direction m_direction; + + QAction *m_directionUpAction; + QAction *m_directionLeftAction; + QAction *m_directionRightAction; + QAction *m_closeAction; }; PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : - DeviceSkin(parameters, parent), - m_screenSize(parameters.screenSize()) + DeviceSkin(parameters, parent), + m_screenSize(parameters.screenSize()), + m_direction(DirectionUp), + m_directionUpAction(0), + m_directionLeftAction(0), + m_directionRightAction(0), + m_closeAction(0) { connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)), this, SLOT(slotSkinKeyPressEvent(int,QString,bool))); @@ -195,7 +222,6 @@ void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, boo QKeyEvent e(QEvent::KeyPress,code,0,text,autorep); QApplication::sendEvent(focusWidget, &e); } - } void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep) @@ -206,16 +232,83 @@ void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, b } } +// Create a checkable action with integer data and +// set it checked if it matches the currentState. +static inline QAction + *createCheckableActionIntData(const QString &label, + int actionValue, int currentState, + QActionGroup *ag, QObject *parent) +{ + QAction *a = new QAction(label, parent); + a->setData(actionValue); + a->setCheckable(true); + if (actionValue == currentState) + a->setChecked(true); + ag->addAction(a); + return a; +} + void PreviewDeviceSkin::slotPopupMenu() { QMenu menu(this); + // Create actions + if (!m_directionUpAction) { + QActionGroup *directionGroup = new QActionGroup(this); + connect(directionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotDirection(QAction*))); + directionGroup->setExclusive(true); + m_directionUpAction = createCheckableActionIntData(tr("&Portrait"), DirectionUp, m_direction, directionGroup, this); + m_directionLeftAction = createCheckableActionIntData(tr("Landscape (&CCW)"), DirectionLeft, m_direction, directionGroup, this); + m_directionRightAction = createCheckableActionIntData(tr("&Landscape (CW)"), DirectionRight, m_direction, directionGroup, this); + m_closeAction = new QAction(tr("&Close"), this); + connect(m_closeAction, SIGNAL(triggered()), parentWidget(), SLOT(close())); + } + menu.addAction(m_directionUpAction); + menu.addAction(m_directionLeftAction); + menu.addAction(m_directionRightAction); + menu.addSeparator(); populateContextMenu(&menu); + menu.addAction(m_closeAction); menu.exec(QCursor::pos()); } -void PreviewDeviceSkin::populateContextMenu(QMenu *menu) +void PreviewDeviceSkin::slotDirection(QAction *a) +{ + const Direction newDirection = static_cast<Direction>(a->data().toInt()); + if (m_direction == newDirection) + return; + const Qt::Orientation newOrientation = newDirection == DirectionUp ? Qt::Vertical : Qt::Horizontal; + const Qt::Orientation oldOrientation = m_direction == DirectionUp ? Qt::Vertical : Qt::Horizontal; + m_direction = newDirection; + QApplication::setOverrideCursor(Qt::WaitCursor); + if (oldOrientation != newOrientation) { + QSize size = screenSize(); + if (newOrientation == Qt::Horizontal) + size.transpose(); + fitWidget(size); + } + setTransform(skinTransform()); + QApplication::restoreOverrideCursor(); +} + +void PreviewDeviceSkin::fitWidget(const QSize &size) +{ + view()->setFixedSize(size); +} + +QMatrix PreviewDeviceSkin::skinTransform() const { - connect(menu->addAction(tr("&Close")), SIGNAL(triggered()), parentWidget(), SLOT(close())); + QMatrix newTransform; + switch (m_direction) { + case DirectionUp: + break; + case DirectionLeft: + newTransform.rotate(270.0); + break; + case DirectionRight: + newTransform.rotate(90.0); + break; + } + return newTransform; } // ------------ PreviewConfigurationPrivate @@ -257,16 +350,20 @@ signals: void zoomPercentChanged(int); protected: - virtual void populateContextMenu(QMenu *m); + virtual void populateContextMenu(QMenu *m); + virtual QMatrix skinTransform() const; + virtual void fitWidget(const QSize &size); private: ZoomMenu *m_zoomMenu; + QAction *m_zoomSubMenuAction; ZoomWidget *m_zoomWidget; }; ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : PreviewDeviceSkin(parameters, parent), m_zoomMenu(new ZoomMenu(this)), + m_zoomSubMenuAction(0), m_zoomWidget(new DesignerZoomWidget) { connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int))); @@ -279,10 +376,20 @@ ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters setView(m_zoomWidget); } -void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +static inline qreal zoomFactor(int percent) { - formWidget->setFixedSize(screenSize()); + return qreal(percent) / 100.0; +} + +static inline QSize scaleSize(int zoomPercent, const QSize &size) +{ + return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize(); +} + +void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +{ m_zoomWidget->setWidget(formWidget); + m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize())); } int ZoomablePreviewDeviceSkin::zoomPercent() const @@ -290,32 +397,50 @@ int ZoomablePreviewDeviceSkin::zoomPercent() const return m_zoomWidget->zoom(); } -void ZoomablePreviewDeviceSkin::setZoomPercent(int z) +void ZoomablePreviewDeviceSkin::setZoomPercent(int zp) { - if (z == zoomPercent()) + if (zp == zoomPercent()) return; // If not triggered by the menu itself: Update it - if (m_zoomMenu->zoom() != z) - m_zoomMenu->setZoom(z); + if (m_zoomMenu->zoom() != zp) + m_zoomMenu->setZoom(zp); - const QCursor oldCursor = cursor(); - QApplication::setOverrideCursor(Qt::WaitCursor); - // DeviceSkin has double, not qreal. - const double hundred = 100.0; - setZoom(static_cast<double>(z) / hundred); - m_zoomWidget->setZoom(z); + QApplication::setOverrideCursor(Qt::WaitCursor); + m_zoomWidget->setZoom(zp); + setTransform(skinTransform()); QApplication::restoreOverrideCursor(); } void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu) { - m_zoomMenu->addActions(menu); - menu->addSeparator(); - PreviewDeviceSkin::populateContextMenu(menu); + if (!m_zoomSubMenuAction) { + m_zoomSubMenuAction = new QAction(tr("&Zoom"), this); + QMenu *zoomSubMenu = new QMenu; + m_zoomSubMenuAction->setMenu(zoomSubMenu); + m_zoomMenu->addActions(zoomSubMenu); + } + menu->addAction(m_zoomSubMenuAction); menu->addSeparator(); } +QMatrix ZoomablePreviewDeviceSkin::skinTransform() const +{ + // Complete transformation consisting of base class rotation and zoom. + QMatrix rc = PreviewDeviceSkin::skinTransform(); + const int zp = zoomPercent(); + if (zp != 100) { + const qreal factor = zoomFactor(zp); + rc.scale(factor, factor); + } + return rc; +} + +void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size) +{ + m_zoomWidget->resize(scaleSize(zoomPercent(), size)); +} + // ------------- PreviewConfiguration static const char *styleKey = "Style"; diff --git a/tools/designer/src/lib/shared/promotionmodel.cpp b/tools/designer/src/lib/shared/promotionmodel.cpp index 77d0bbb..1df9cbe 100644 --- a/tools/designer/src/lib/shared/promotionmodel.cpp +++ b/tools/designer/src/lib/shared/promotionmodel.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::PromotionModel -*/ - #include "promotionmodel_p.h" #include "widgetdatabase_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp index 8453c16..a1c47d2 100644 --- a/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp +++ b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerPromotionDialog -*/ - #include "qdesigner_promotiondialog_p.h" #include "promotionmodel_p.h" #include "iconloader_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp index 7c7d9d7..3875d0e 100644 --- a/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp +++ b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp @@ -40,16 +40,56 @@ ****************************************************************************/ #include "qdesigner_propertyeditor_p.h" -#ifdef Q_OS_WIN -# include <widgetfactory_p.h> -#endif -#include <QAction> -#include <QLineEdit> -#include <QAbstractButton> +#include "pluginmanager_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <widgetfactory_p.h> +#include <QtGui/QAction> +#include <QtGui/QLineEdit> +#include <QtGui/QAbstractButton> QT_BEGIN_NAMESPACE namespace qdesigner_internal { +typedef QDesignerPropertyEditor::StringPropertyParameters StringPropertyParameters; +// A map of property name to type +typedef QHash<QString, StringPropertyParameters> PropertyNameTypeMap; + +// Compile a map of hard-coded string property types +static const PropertyNameTypeMap &stringPropertyTypes() +{ + static PropertyNameTypeMap propertyNameTypeMap; + if (propertyNameTypeMap.empty()) { + const StringPropertyParameters richtext(ValidationRichText, true); + // Accessibility. Both are texts the narrator reads + propertyNameTypeMap.insert(QLatin1String("accessibleDescription"), richtext); + propertyNameTypeMap.insert(QLatin1String("accessibleName"), richtext); + // object names + const StringPropertyParameters objectName(ValidationObjectName, false); + propertyNameTypeMap.insert(QLatin1String("buddy"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentItemName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentPageName"), objectName); + propertyNameTypeMap.insert(QLatin1String("currentTabName"), objectName); + propertyNameTypeMap.insert(QLatin1String("layoutName"), objectName); + propertyNameTypeMap.insert(QLatin1String("spacerName"), objectName); + // Style sheet + propertyNameTypeMap.insert(QLatin1String("styleSheet"), StringPropertyParameters(ValidationStyleSheet, false)); + // Buttons/ QCommandLinkButton + const StringPropertyParameters multiline(ValidationMultiLine, true); + propertyNameTypeMap.insert(QLatin1String("description"), multiline); + propertyNameTypeMap.insert(QLatin1String("iconText"), multiline); + // Tooltips, etc. + propertyNameTypeMap.insert(QLatin1String("toolTip"), richtext); + propertyNameTypeMap.insert(QLatin1String("whatsThis"), richtext); + propertyNameTypeMap.insert(QLatin1String("windowIconText"), richtext); + propertyNameTypeMap.insert(QLatin1String("html"), richtext); + // A QWizard page id + propertyNameTypeMap.insert(QLatin1String("pageId"), StringPropertyParameters(ValidationSingleLine, false)); + // QPlainTextEdit + propertyNameTypeMap.insert(QLatin1String("plainText"), StringPropertyParameters(ValidationMultiLine, true)); + } + return propertyNameTypeMap; +} QDesignerPropertyEditor::QDesignerPropertyEditor(QWidget *parent, Qt::WindowFlags flags) : QDesignerPropertyEditorInterface(parent, flags) @@ -68,33 +108,19 @@ QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textP return StringPropertyParameters(vm, false); } - // Accessibility. Both are texts the narrator reads - if (propertyName == QLatin1String("accessibleDescription") || propertyName == QLatin1String("accessibleName")) - return StringPropertyParameters(ValidationRichText, true); - - // Names - if (propertyName == QLatin1String("buddy") - || propertyName == QLatin1String("currentItemName") - || propertyName == QLatin1String("currentPageName") - || propertyName == QLatin1String("currentTabName") - || propertyName == QLatin1String("layoutName") - || propertyName == QLatin1String("spacerName")) - return StringPropertyParameters(ValidationObjectName, false); - - if (propertyName.endsWith(QLatin1String("Name"))) - return StringPropertyParameters(ValidationSingleLine, true); - - // Multi line? - if (propertyName == QLatin1String("styleSheet")) - return StringPropertyParameters(ValidationStyleSheet, false); - - if (propertyName == QLatin1String("description") || propertyName == QLatin1String("iconText")) // QCommandLinkButton - return StringPropertyParameters(ValidationMultiLine, true); + // Check custom widgets by class. + const QString className = WidgetFactory::classNameOf(core, object); + const QDesignerCustomWidgetData customData = core->pluginManager()->customWidgetData(className); + if (!customData.isNull()) { + StringPropertyParameters customType; + if (customData.xmlStringPropertyType(propertyName, &customType)) + return customType; + } - if (propertyName == QLatin1String("toolTip") || propertyName.endsWith(QLatin1String("ToolTip")) || - propertyName == QLatin1String("whatsThis") || - propertyName == QLatin1String("windowIconText") || propertyName == QLatin1String("html")) - return StringPropertyParameters(ValidationRichText, true); + // Check hardcoded property ames + const PropertyNameTypeMap::const_iterator hit = stringPropertyTypes().constFind(propertyName); + if (hit != stringPropertyTypes().constEnd()) + return hit.value(); // text: Check according to widget type. if (propertyName == QLatin1String("text")) { @@ -104,17 +130,17 @@ QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textP return StringPropertyParameters(ValidationMultiLine, true); return StringPropertyParameters(ValidationRichText, true); } - if (propertyName == QLatin1String("pageId")) // A QWizard page id - return StringPropertyParameters(ValidationSingleLine, false); - if (propertyName == QLatin1String("plainText")) // QPlainTextEdit - return StringPropertyParameters(ValidationMultiLine, true); + // Fuzzy matching + if (propertyName.endsWith(QLatin1String("Name"))) + return StringPropertyParameters(ValidationSingleLine, true); + + if (propertyName.endsWith(QLatin1String("ToolTip"))) + return StringPropertyParameters(ValidationRichText, true); #ifdef Q_OS_WIN // No translation for the active X "control" property - if (propertyName == QLatin1String("control") && WidgetFactory::classNameOf(core, object) == QLatin1String("QAxWidget")) + if (propertyName == QLatin1String("control") && className == QLatin1String("QAxWidget")) return StringPropertyParameters(ValidationSingleLine, false); -#else - Q_UNUSED(core); #endif // default to single diff --git a/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp index cce0528..d85e18f 100644 --- a/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp +++ b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerTaskMenu -*/ - #include "qdesigner_taskmenu_p.h" #include "qdesigner_command_p.h" #include "richtexteditor_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp index cb77801..1c465da 100644 --- a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp +++ b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::Sentinel -*/ - #include "qdesigner_toolbar_p.h" #include "qdesigner_command_p.h" #include "actionrepository_p.h" diff --git a/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp index 5ba8ad7..d94a397 100644 --- a/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp +++ b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QDesignerWidgetBox -*/ - #include "qdesigner_widgetbox_p.h" #include "qdesigner_utils_p.h" diff --git a/tools/designer/src/lib/shared/qtresourceview.cpp b/tools/designer/src/lib/shared/qtresourceview.cpp index d956491..2b59605 100644 --- a/tools/designer/src/lib/shared/qtresourceview.cpp +++ b/tools/designer/src/lib/shared/qtresourceview.cpp @@ -44,6 +44,7 @@ #include "qtresourcemodel_p.h" #include "qtresourceeditordialog_p.h" #include "iconloader_p.h" +#include "filterwidget_p.h" // For FilterWidget #include <QtDesigner/QDesignerFormEditorInterface> @@ -144,6 +145,7 @@ public: void slotReloadResources(); void slotCopyResourcePath(); void slotListWidgetContextMenuRequested(const QPoint &pos); + void slotFilterChanged(const QString &pattern); void createPaths(); QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent); void createResources(const QString &path); @@ -152,16 +154,20 @@ public: void restoreSettings(); void saveSettings(); void updateActions(); + void filterOutResources(); QPixmap makeThumbnail(const QPixmap &pix) const; QDesignerFormEditorInterface *m_core; QtResourceModel *m_resourceModel; QToolBar *m_toolBar; + qdesigner_internal::FilterWidget *m_filterWidget; QTreeWidget *m_treeWidget; QListWidget *m_listWidget; QSplitter *m_splitter; - QMap<QString, QStringList> m_pathToContents; // full path to contents file names + QMap<QString, QStringList> m_pathToContents; // full path to contents file names (full path to its resource filenames) + QMap<QString, QString> m_pathToParentPath; // full path to full parent path + QMap<QString, QStringList> m_pathToSubPaths; // full path to full sub paths QMap<QString, QTreeWidgetItem *> m_pathToItem; QMap<QTreeWidgetItem *, QString> m_itemToPath; QMap<QString, QListWidgetItem *> m_resourceToItem; @@ -175,6 +181,7 @@ public: bool m_ignoreGuiSignals; QString m_settingsKey; bool m_resourceEditingEnabled; + QString m_filterPattern; }; QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) : @@ -251,6 +258,12 @@ void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos menu.exec(m_listWidget->mapToGlobal(pos)); } +void QtResourceViewPrivate::slotFilterChanged(const QString &pattern) +{ + m_filterPattern = pattern; + filterOutResources(); +} + void QtResourceViewPrivate::storeExpansionState() { QMapIterator<QString, QTreeWidgetItem *> it(m_pathToItem); @@ -294,6 +307,7 @@ void QtResourceViewPrivate::updateActions() m_editResourcesAction->setVisible(m_resourceEditingEnabled); m_editResourcesAction->setEnabled(resourceActive); m_reloadResourcesAction->setEnabled(resourceActive); + m_filterWidget->setEnabled(resourceActive); } void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) @@ -307,6 +321,8 @@ void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) const QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); m_treeWidget->clear(); m_pathToContents.clear(); + m_pathToParentPath.clear(); + m_pathToSubPaths.clear(); m_pathToItem.clear(); m_itemToPath.clear(); m_listWidget->clear(); @@ -320,6 +336,7 @@ void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) q_ptr->selectResource(currentResource); else if (!currentPath.isEmpty()) q_ptr->selectResource(currentPath); + filterOutResources(); } void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item) @@ -362,9 +379,6 @@ void QtResourceViewPrivate::createPaths() const QString root(QLatin1Char(':')); - QMap<QString, QString> pathToParentPath; // full path to full parent path - QMap<QString, QStringList> pathToSubPaths; // full path to full sub paths - QMap<QString, QString> contents = m_resourceModel->contents(); QMapIterator<QString, QString> itContents(contents); while (itContents.hasNext()) { @@ -372,11 +386,11 @@ void QtResourceViewPrivate::createPaths() const QFileInfo fi(filePath); QString dirPath = fi.absolutePath(); m_pathToContents[dirPath].append(fi.fileName()); - while (!pathToParentPath.contains(dirPath) && dirPath != root) { + while (!m_pathToParentPath.contains(dirPath) && dirPath != root) { // create all parent paths const QFileInfo fd(dirPath); const QString parentDirPath = fd.absolutePath(); - pathToParentPath[dirPath] = parentDirPath; - pathToSubPaths[parentDirPath].append(dirPath); + m_pathToParentPath[dirPath] = parentDirPath; + m_pathToSubPaths[parentDirPath].append(dirPath); dirPath = parentDirPath; } } @@ -387,13 +401,126 @@ void QtResourceViewPrivate::createPaths() QPair<QString, QTreeWidgetItem *> pathToParentItem = pathToParentItemQueue.dequeue(); const QString path = pathToParentItem.first; QTreeWidgetItem *item = createPath(path, pathToParentItem.second); - QStringList subPaths = pathToSubPaths.value(path); + QStringList subPaths = m_pathToSubPaths.value(path); QStringListIterator itSubPaths(subPaths); while (itSubPaths.hasNext()) pathToParentItemQueue.enqueue(qMakePair(itSubPaths.next(), item)); } } +void QtResourceViewPrivate::filterOutResources() +{ + QMap<QString, bool> pathToMatchingContents; // true means the path has any matching contents + QMap<QString, bool> pathToVisible; // true means the path has to be shown + + // 1) we go from root path recursively. + // 2) we check every path if it contains at least one matching resource - if empty we add it + // to pathToMatchingContents and pathToVisible with false, if non empty + // we add it with true and change every parent path in pathToVisible to true. + // 3) we hide these items which has pathToVisible value false. + + const bool matchAll = m_filterPattern.isEmpty(); + const QString root(QLatin1Char(':')); + + QQueue<QString> pathQueue; + pathQueue.enqueue(root); + while (!pathQueue.isEmpty()) { + const QString path = pathQueue.dequeue(); + + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); + bool hasContents = matchAll; + if (!matchAll) { // the case filter is not empty - we check if the path contains anything + while (it.hasNext()) { + QString fileName = it.next(); + hasContents = fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (hasContents) // the path contains at least one resource which matches the filter + break; + } + } + + pathToMatchingContents[path] = hasContents; + pathToVisible[path] = hasContents; + + if (hasContents) { // if the path is going to be shown we need to show all its parent paths + QString parentPath = m_pathToParentPath.value(path); + while (!parentPath.isEmpty()) { + QString p = parentPath; + if (pathToVisible.value(p)) // parent path is already shown, we break the loop + break; + pathToVisible[p] = true; + parentPath = m_pathToParentPath.value(p); + } + } + + QStringList subPaths = m_pathToSubPaths.value(path); // we do the same for children paths + QStringListIterator itSubPaths(subPaths); + while (itSubPaths.hasNext()) + pathQueue.enqueue(itSubPaths.next()); + } + + // we setup here new path and resource to be activated + const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem()); + QString newCurrentPath = currentPath; + QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); + if (!matchAll) { + bool searchForNewPathWithContents = true; + + if (!currentPath.isEmpty()) { // if the currentPath is empty we will search for a new path too + QMap<QString, bool>::ConstIterator it = pathToMatchingContents.constFind(currentPath); + if (it != pathToMatchingContents.constEnd() && it.value()) // the current item has contents, we don't need to search for another path + searchForNewPathWithContents = false; + } + + if (searchForNewPathWithContents) { + // we find the first path with the matching contents + QMap<QString, bool>::ConstIterator itContents = pathToMatchingContents.constBegin(); + while (itContents != pathToMatchingContents.constEnd()) { + if (itContents.value()) { + newCurrentPath = itContents.key(); // the new path will be activated + break; + } + + itContents++; + } + } + + QFileInfo fi(currentResource); + if (!fi.fileName().contains(m_filterPattern, Qt::CaseInsensitive)) { // the case when the current resource is filtered out + const QStringList fileNames = m_pathToContents.value(newCurrentPath); + QStringListIterator it(fileNames); + while (it.hasNext()) { // we try to select the first matching resource from the newCurrentPath + QString fileName = it.next(); + if (fileName.contains(m_filterPattern, Qt::CaseInsensitive)) { + QDir dirPath(newCurrentPath); + currentResource = dirPath.absoluteFilePath(fileName); // the new resource inside newCurrentPath will be activated + break; + } + } + } + } + + QTreeWidgetItem *newCurrentItem = m_pathToItem.value(newCurrentPath); + if (currentPath != newCurrentPath) + m_treeWidget->setCurrentItem(newCurrentItem); + else + slotCurrentPathChanged(newCurrentItem); // trigger filtering on the current path + + QListWidgetItem *currentResourceItem = m_resourceToItem.value(currentResource); + if (currentResourceItem) { + m_listWidget->setCurrentItem(currentResourceItem); + m_listWidget->scrollToItem(currentResourceItem); + } + + QMapIterator<QString, bool> it(pathToVisible); // hide all paths filtered out + while (it.hasNext()) { + const QString path = it.next().key(); + QTreeWidgetItem *item = m_pathToItem.value(path); + if (item) + item->setHidden(!it.value()); + } +} + QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent) { QTreeWidgetItem *item = 0; @@ -417,27 +544,32 @@ QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWid void QtResourceViewPrivate::createResources(const QString &path) { + const bool matchAll = m_filterPattern.isEmpty(); + QDir dir(path); - QStringList files = m_pathToContents.value(path); - QStringListIterator it(files); + QStringList fileNames = m_pathToContents.value(path); + QStringListIterator it(fileNames); while (it.hasNext()) { - QString file = it.next(); - QString filePath = dir.absoluteFilePath(file); - QFileInfo fi(filePath); - if (fi.isFile()) { - QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); - const QPixmap pix = QPixmap(filePath); - if (pix.isNull()) { - item->setToolTip(filePath); - } else { - item->setIcon(QIcon(makeThumbnail(pix))); - const QSize size = pix.size(); - item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + QString fileName = it.next(); + const bool showProperty = matchAll || fileName.contains(m_filterPattern, Qt::CaseInsensitive); + if (showProperty) { + QString filePath = dir.absoluteFilePath(fileName); + QFileInfo fi(filePath); + if (fi.isFile()) { + QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); + const QPixmap pix = QPixmap(filePath); + if (pix.isNull()) { + item->setToolTip(filePath); + } else { + item->setIcon(QIcon(makeThumbnail(pix))); + const QSize size = pix.size(); + item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + } + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + item->setData(Qt::UserRole, filePath); + m_itemToResource[item] = filePath; + m_resourceToItem[filePath] = item; } - item->setFlags(item->flags() | Qt::ItemIsDragEnabled); - item->setData(Qt::UserRole, filePath); - m_itemToResource[item] = filePath; - m_resourceToItem[filePath] = item; } } } @@ -464,6 +596,11 @@ QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *pare connect(d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), this, SLOT(slotCopyResourcePath())); d_ptr->m_copyResourcePathAction->setEnabled(false); + //d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(0, qdesigner_internal::FilterWidget::LayoutAlignNone); + d_ptr->m_filterWidget = new qdesigner_internal::FilterWidget(d_ptr->m_toolBar); + d_ptr->m_toolBar->addWidget(d_ptr->m_filterWidget); + connect(d_ptr->m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(slotFilterChanged(QString))); + d_ptr->m_splitter = new QSplitter; d_ptr->m_splitter->setChildrenCollapsible(false); d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget); diff --git a/tools/designer/src/lib/shared/qtresourceview_p.h b/tools/designer/src/lib/shared/qtresourceview_p.h index 33162c5..f78c5af 100644 --- a/tools/designer/src/lib/shared/qtresourceview_p.h +++ b/tools/designer/src/lib/shared/qtresourceview_p.h @@ -113,6 +113,7 @@ private: Q_PRIVATE_SLOT(d_func(), void slotReloadResources()) Q_PRIVATE_SLOT(d_func(), void slotCopyResourcePath()) Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &pos)) + Q_PRIVATE_SLOT(d_func(), void slotFilterChanged(const QString &pattern)) }; class QDESIGNER_SHARED_EXPORT QtResourceViewDialog : public QDialog diff --git a/tools/designer/src/lib/shared/richtexteditor.cpp b/tools/designer/src/lib/shared/richtexteditor.cpp index 8aa036b..42cf449 100644 --- a/tools/designer/src/lib/shared/richtexteditor.cpp +++ b/tools/designer/src/lib/shared/richtexteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::RichTextEditorDialog -*/ - #include "richtexteditor_p.h" #include "htmlhighlighter_p.h" #include "iconselector_p.h" diff --git a/tools/designer/src/lib/shared/scriptdialog.cpp b/tools/designer/src/lib/shared/scriptdialog.cpp index 616f0c9..ef4d08f 100644 --- a/tools/designer/src/lib/shared/scriptdialog.cpp +++ b/tools/designer/src/lib/shared/scriptdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ScriptDialog -*/ - #include "scriptdialog_p.h" #include "qscripthighlighter_p.h" diff --git a/tools/designer/src/lib/shared/scripterrordialog.cpp b/tools/designer/src/lib/shared/scripterrordialog.cpp index c4a7e07..326cba2 100644 --- a/tools/designer/src/lib/shared/scripterrordialog.cpp +++ b/tools/designer/src/lib/shared/scripterrordialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::ScriptErrorDialog -*/ - #include "scripterrordialog_p.h" #include <QtGui/QTextEdit> diff --git a/tools/designer/src/lib/shared/shared.pri b/tools/designer/src/lib/shared/shared.pri index 8ed051a..0424a41 100644 --- a/tools/designer/src/lib/shared/shared.pri +++ b/tools/designer/src/lib/shared/shared.pri @@ -186,4 +186,17 @@ SOURCES += \ $$PWD/filterwidget.cpp \ $$PWD/plugindialog.cpp -RESOURCES += $$PWD/shared.qrc +RESOURCES += $$PWD/shared.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/ClamshellPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/PDAPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/pda.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/PortableMedia.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/qvfb.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/S60-nHD-Touchscreen.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/S60-QVGA-Candybar.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhone2.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/SmartPhoneWithButtons.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/TouchscreenPhone.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/Trolltech-Keypad.qrc \ +$$QT_SOURCE_TREE/tools/qvfb/Trolltech-Touchscreen.qrc diff --git a/tools/designer/src/lib/shared/stylesheeteditor.cpp b/tools/designer/src/lib/shared/stylesheeteditor.cpp index 2056fcf..de64135 100644 --- a/tools/designer/src/lib/shared/stylesheeteditor.cpp +++ b/tools/designer/src/lib/shared/stylesheeteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::StyleSheetEditorDialog -*/ - #include "stylesheeteditor_p.h" #include "csshighlighter_p.h" #include "iconselector_p.h" diff --git a/tools/designer/src/lib/shared/widgetfactory.cpp b/tools/designer/src/lib/shared/widgetfactory.cpp index 3822fec..56a1744 100644 --- a/tools/designer/src/lib/shared/widgetfactory.cpp +++ b/tools/designer/src/lib/shared/widgetfactory.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::WidgetFactory -*/ - #include "widgetfactory_p.h" #include "widgetdatabase_p.h" #include "metadatabase_p.h" diff --git a/tools/designer/src/lib/shared/zoomwidget.cpp b/tools/designer/src/lib/shared/zoomwidget.cpp index f4ab48e..a404dcb 100644 --- a/tools/designer/src/lib/shared/zoomwidget.cpp +++ b/tools/designer/src/lib/shared/zoomwidget.cpp @@ -143,9 +143,10 @@ ZoomView::ZoomView(QWidget *parent) : m_zoom(100), m_zoomFactor(1.0), m_zoomContextMenuEnabled(false), - m_autoScrollSuppressed(true), m_zoomMenu(0) { + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setFrameShape(QFrame::NoFrame); setScene(m_scene); if (debugZoomWidget) @@ -187,8 +188,6 @@ void ZoomView::setZoom(int percent) resetTransform(); scale(m_zoomFactor, m_zoomFactor); - if (m_autoScrollSuppressed) - scrollToOrigin(); } void ZoomView::applyZoom() @@ -210,16 +209,6 @@ void ZoomView::setZoomContextMenuEnabled(bool e) m_zoomContextMenuEnabled = e; } -bool ZoomView::isAutoScrollSuppressed() const -{ - return m_autoScrollSuppressed; -} - -void ZoomView::setAutoScrollSuppressed(bool s) -{ - m_autoScrollSuppressed = s; -} - ZoomMenu *ZoomView::zoomMenu() { if (!m_zoomMenu) { @@ -469,13 +458,16 @@ void ZoomWidget::resizeEvent(QResizeEvent *event) * and the use the size ZoomView::resizeEvent(event); */ if (m_proxy && !m_viewResizeBlocked) { if (debugZoomWidget > 1) - qDebug() << ">ZoomWidget (" << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); + qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); const QSizeF newViewPortSize = size() - viewPortMargin(); const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF(); m_widgetResizeBlocked = true; m_proxy->widget()->resize(widgetSizeF.toSize()); + setSceneRect(QRectF(QPointF(0, 0), widgetSizeF)); scrollToOrigin(); m_widgetResizeBlocked = false; + if (debugZoomWidget > 1) + qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size(); } } @@ -559,7 +551,7 @@ QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::W void ZoomWidget::dump() const { - qDebug() << "ZoomWidget " << geometry() << " Viewport " << viewport()->geometry() + qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry() << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect(); if (m_proxy) { qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size() diff --git a/tools/designer/src/lib/shared/zoomwidget_p.h b/tools/designer/src/lib/shared/zoomwidget_p.h index fe850f7..92c857e 100644 --- a/tools/designer/src/lib/shared/zoomwidget_p.h +++ b/tools/designer/src/lib/shared/zoomwidget_p.h @@ -104,8 +104,6 @@ class QDESIGNER_SHARED_EXPORT ZoomView : public QGraphicsView { Q_PROPERTY(int zoom READ zoom WRITE setZoom DESIGNABLE true SCRIPTABLE true) Q_PROPERTY(bool zoomContextMenuEnabled READ isZoomContextMenuEnabled WRITE setZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) - Q_PROPERTY(bool autoScrollSuppressed READ isAutoScrollSuppressed WRITE setAutoScrollSuppressed DESIGNABLE true SCRIPTABLE true) - Q_OBJECT Q_DISABLE_COPY(ZoomView) public: @@ -120,10 +118,6 @@ public: bool isZoomContextMenuEnabled() const; void setZoomContextMenuEnabled(bool e); - // Suppress scrolling when changing zoom. Default: on - bool isAutoScrollSuppressed() const; - void setAutoScrollSuppressed(bool s); - QGraphicsScene &scene() { return *m_scene; } const QGraphicsScene &scene() const { return *m_scene; } @@ -149,8 +143,7 @@ private: int m_zoom; qreal m_zoomFactor; - bool m_zoomContextMenuEnabled; - bool m_autoScrollSuppressed; + bool m_zoomContextMenuEnabled; bool m_resizeBlocked; ZoomMenu *m_zoomMenu; }; diff --git a/tools/designer/src/lib/uilib/ui4.cpp b/tools/designer/src/lib/uilib/ui4.cpp index 2047739..72c7f50 100644 --- a/tools/designer/src/lib/uilib/ui4.cpp +++ b/tools/designer/src/lib/uilib/ui4.cpp @@ -2368,6 +2368,7 @@ void DomCustomWidget::clear(bool clear_all) delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; if (clear_all) { m_text.clear(); @@ -2381,6 +2382,7 @@ void DomCustomWidget::clear(bool clear_all) m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::DomCustomWidget() @@ -2393,6 +2395,7 @@ DomCustomWidget::DomCustomWidget() m_script = 0; m_properties = 0; m_slots = 0; + m_propertyspecifications = 0; } DomCustomWidget::~DomCustomWidget() @@ -2403,6 +2406,7 @@ DomCustomWidget::~DomCustomWidget() delete m_script; delete m_properties; delete m_slots; + delete m_propertyspecifications; } void DomCustomWidget::read(QXmlStreamReader &reader) @@ -2468,6 +2472,12 @@ void DomCustomWidget::read(QXmlStreamReader &reader) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(reader); + setElementPropertyspecifications(v); + continue; + } reader.raiseError(QLatin1String("Unexpected element ") + tag); } break; @@ -2548,6 +2558,12 @@ void DomCustomWidget::read(const QDomElement &node) setElementSlots(v); continue; } + if (tag == QLatin1String("propertyspecifications")) { + DomPropertySpecifications *v = new DomPropertySpecifications(); + v->read(e); + setElementPropertyspecifications(v); + continue; + } } m_text.clear(); for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { @@ -2605,6 +2621,10 @@ void DomCustomWidget::write(QXmlStreamWriter &writer, const QString &tagName) co m_slots->write(writer, QLatin1String("slots")); } + if (m_children & Propertyspecifications) { + m_propertyspecifications->write(writer, QLatin1String("propertyspecifications")); + } + if (!m_text.isEmpty()) writer.writeCharacters(m_text); @@ -2731,6 +2751,21 @@ void DomCustomWidget::setElementSlots(DomSlots* a) m_slots = a; } +DomPropertySpecifications* DomCustomWidget::takeElementPropertyspecifications() +{ + DomPropertySpecifications* a = m_propertyspecifications; + m_propertyspecifications = 0; + m_children ^= Propertyspecifications; + return a; +} + +void DomCustomWidget::setElementPropertyspecifications(DomPropertySpecifications* a) +{ + delete m_propertyspecifications; + m_children |= Propertyspecifications; + m_propertyspecifications = a; +} + void DomCustomWidget::clearElementClass() { m_children &= ~Class; @@ -2798,6 +2833,13 @@ void DomCustomWidget::clearElementSlots() m_children &= ~Slots; } +void DomCustomWidget::clearElementPropertyspecifications() +{ + delete m_propertyspecifications; + m_propertyspecifications = 0; + m_children &= ~Propertyspecifications; +} + void DomProperties::clear(bool clear_all) { qDeleteAll(m_property); @@ -10883,5 +10925,208 @@ void DomSlots::setElementSlot(const QStringList& a) m_slot = a; } +void DomPropertySpecifications::clear(bool clear_all) +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); + + if (clear_all) { + m_text.clear(); + } + + m_children = 0; +} + +DomPropertySpecifications::DomPropertySpecifications() +{ + m_children = 0; +} + +DomPropertySpecifications::~DomPropertySpecifications() +{ + qDeleteAll(m_stringpropertyspecification); + m_stringpropertyspecification.clear(); +} + +void DomPropertySpecifications::read(QXmlStreamReader &reader) +{ + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(reader); + m_stringpropertyspecification.append(v); + continue; + } + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomPropertySpecifications::read(const QDomElement &node) +{ + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + if (tag == QLatin1String("stringpropertyspecification")) { + DomStringPropertySpecification *v = new DomStringPropertySpecification(); + v->read(e); + m_stringpropertyspecification.append(v); + continue; + } + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertyspecifications") : tagName.toLower()); + + for (int i = 0; i < m_stringpropertyspecification.size(); ++i) { + DomStringPropertySpecification* v = m_stringpropertyspecification[i]; + v->write(writer, QLatin1String("stringpropertyspecification")); + } + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + +void DomPropertySpecifications::setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a) +{ + m_children |= Stringpropertyspecification; + m_stringpropertyspecification = a; +} + +void DomStringPropertySpecification::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; + } + + m_children = 0; +} + +DomStringPropertySpecification::DomStringPropertySpecification() +{ + m_children = 0; + m_has_attr_name = false; + m_has_attr_type = false; + m_has_attr_notr = false; +} + +DomStringPropertySpecification::~DomStringPropertySpecification() +{ +} + +void DomStringPropertySpecification::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + if (name == QLatin1String("type")) { + setAttributeType(attribute.value().toString()); + continue; + } + if (name == QLatin1String("notr")) { + setAttributeNotr(attribute.value().toString()); + continue; + } + reader.raiseError(QLatin1String("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QLatin1String("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +#ifdef QUILOADER_QDOM_READ +void DomStringPropertySpecification::read(const QDomElement &node) +{ + if (node.hasAttribute(QLatin1String("name"))) + setAttributeName(node.attribute(QLatin1String("name"))); + if (node.hasAttribute(QLatin1String("type"))) + setAttributeType(node.attribute(QLatin1String("type"))); + if (node.hasAttribute(QLatin1String("notr"))) + setAttributeNotr(node.attribute(QLatin1String("notr"))); + + for (QDomNode n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { + if (!n.isElement()) + continue; + QDomElement e = n.toElement(); + QString tag = e.tagName().toLower(); + } + m_text.clear(); + for (QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling()) { + if (child.isText()) + m_text.append(child.nodeValue()); + } +} +#endif + +void DomStringPropertySpecification::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("stringpropertyspecification") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QLatin1String("name"), attributeName()); + + if (hasAttributeType()) + writer.writeAttribute(QLatin1String("type"), attributeType()); + + if (hasAttributeNotr()) + writer.writeAttribute(QLatin1String("notr"), attributeNotr()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + QT_END_NAMESPACE diff --git a/tools/designer/src/lib/uilib/ui4_p.h b/tools/designer/src/lib/uilib/ui4_p.h index df02a39..fa70573 100644 --- a/tools/designer/src/lib/uilib/ui4_p.h +++ b/tools/designer/src/lib/uilib/ui4_p.h @@ -161,6 +161,8 @@ class DomScript; class DomWidgetData; class DomDesignerData; class DomSlots; +class DomPropertySpecifications; +class DomStringPropertySpecification; /******************************************************************************* ** Declarations @@ -1015,6 +1017,12 @@ public: inline bool hasElementSlots() const { return m_children & Slots; } void clearElementSlots(); + inline DomPropertySpecifications* elementPropertyspecifications() const { return m_propertyspecifications; } + DomPropertySpecifications* takeElementPropertyspecifications(); + void setElementPropertyspecifications(DomPropertySpecifications* a); + inline bool hasElementPropertyspecifications() const { return m_children & Propertyspecifications; } + void clearElementPropertyspecifications(); + private: QString m_text; void clear(bool clear_all = true); @@ -1033,6 +1041,7 @@ private: DomScript* m_script; DomProperties* m_properties; DomSlots* m_slots; + DomPropertySpecifications* m_propertyspecifications; enum Child { Class = 1, Extends = 2, @@ -1044,7 +1053,8 @@ private: Pixmap = 128, Script = 256, Properties = 512, - Slots = 1024 + Slots = 1024, + Propertyspecifications = 2048 }; DomCustomWidget(const DomCustomWidget &other); @@ -3686,6 +3696,91 @@ private: void operator = (const DomSlots&other); }; +class QDESIGNER_UILIB_EXPORT DomPropertySpecifications { +public: + DomPropertySpecifications(); + ~DomPropertySpecifications(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + // child element accessors + inline QList<DomStringPropertySpecification*> elementStringpropertyspecification() const { return m_stringpropertyspecification; } + void setElementStringpropertyspecification(const QList<DomStringPropertySpecification*>& a); + +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + // child element data + uint m_children; + QList<DomStringPropertySpecification*> m_stringpropertyspecification; + enum Child { + Stringpropertyspecification = 1 + }; + + DomPropertySpecifications(const DomPropertySpecifications &other); + void operator = (const DomPropertySpecifications&other); +}; + +class QDESIGNER_UILIB_EXPORT DomStringPropertySpecification { +public: + DomStringPropertySpecification(); + ~DomStringPropertySpecification(); + + void read(QXmlStreamReader &reader); +#ifdef QUILOADER_QDOM_READ + void read(const QDomElement &node); +#endif + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + inline bool hasAttributeType() const { return m_has_attr_type; } + inline QString attributeType() const { return m_attr_type; } + inline void setAttributeType(const QString& a) { m_attr_type = a; m_has_attr_type = true; } + inline void clearAttributeType() { m_has_attr_type = false; } + + inline bool hasAttributeNotr() const { return m_has_attr_notr; } + inline QString attributeNotr() const { return m_attr_notr; } + inline void setAttributeNotr(const QString& a) { m_attr_notr = a; m_has_attr_notr = true; } + inline void clearAttributeNotr() { m_has_attr_notr = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + QString m_attr_type; + bool m_has_attr_type; + + QString m_attr_notr; + bool m_has_attr_notr; + + // child element data + uint m_children; + + DomStringPropertySpecification(const DomStringPropertySpecification &other); + void operator = (const DomStringPropertySpecification&other); +}; + #ifdef QFORMINTERNAL_NAMESPACE } diff --git a/tools/kmap2qmap/kmap2qmap.pro b/tools/kmap2qmap/kmap2qmap.pro new file mode 100644 index 0000000..cc8200b --- /dev/null +++ b/tools/kmap2qmap/kmap2qmap.pro @@ -0,0 +1,12 @@ + +TEMPLATE = app +DESTDIR = ../../bin +QT = core +CONFIG += console +CONFIG -= app_bundle + +DEPENDPATH += $$QT_SOURCE_TREE/src/gui/embedded +INCLUDEPATH += $$QT_SOURCE_TREE/src/gui/embedded + +# Input +SOURCES += main.cpp diff --git a/tools/kmap2qmap/main.cpp b/tools/kmap2qmap/main.cpp new file mode 100644 index 0000000..b518392 --- /dev/null +++ b/tools/kmap2qmap/main.cpp @@ -0,0 +1,989 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <cstdio> + +#include <QFile> +#include <QFileInfo> +#include <QDir> +#include <QTextCodec> +#include <QList> +#include <QVector> +#include <QByteArray> +#include <QStringList> +#include <QTextStream> + +#include "qkbd_qws_p.h" + +using namespace std; + + +struct modifier_map_t { + const char *symbol; + quint8 modifier; + quint32 qtmodifier; +}; + +static const struct modifier_map_t modifier_map[] = { + { "plain", QWSKeyboard::ModPlain, Qt::NoModifier }, + { "shift", QWSKeyboard::ModShift, Qt::ShiftModifier }, + { "altgr", QWSKeyboard::ModAltGr, Qt::AltModifier }, + { "control", QWSKeyboard::ModControl, Qt::ControlModifier }, + { "alt", QWSKeyboard::ModAlt, Qt::AltModifier }, + { "meta", QWSKeyboard::ModAlt, Qt::AltModifier }, + { "shiftl", QWSKeyboard::ModShiftL, Qt::ShiftModifier }, + { "shiftr", QWSKeyboard::ModShiftR, Qt::ShiftModifier }, + { "ctrll", QWSKeyboard::ModCtrlL, Qt::ControlModifier }, + { "ctrlr", QWSKeyboard::ModCtrlR, Qt::ControlModifier }, +}; + +static const int modifier_map_size = sizeof(modifier_map)/sizeof(modifier_map_t); + + +struct symbol_map_t { + const char *symbol; + quint32 qtcode; +}; + +static const struct symbol_map_t symbol_map[] = { + { "space", Qt::Key_Space }, + { "exclam", Qt::Key_Exclam }, + { "quotedbl", Qt::Key_QuoteDbl }, + { "numbersign", Qt::Key_NumberSign }, + { "dollar", Qt::Key_Dollar }, + { "percent", Qt::Key_Percent }, + { "ampersand", Qt::Key_Ampersand }, + { "apostrophe", Qt::Key_Apostrophe }, + { "parenleft", Qt::Key_ParenLeft }, + { "parenright", Qt::Key_ParenRight }, + { "asterisk", Qt::Key_Asterisk }, + { "plus", Qt::Key_Plus }, + { "comma", Qt::Key_Comma }, + { "minus", Qt::Key_Minus }, + { "period", Qt::Key_Period }, + { "slash", Qt::Key_Slash }, + { "zero", Qt::Key_0 }, + { "one", Qt::Key_1 }, + { "two", Qt::Key_2 }, + { "three", Qt::Key_3 }, + { "four", Qt::Key_4 }, + { "five", Qt::Key_5 }, + { "six", Qt::Key_6 }, + { "seven", Qt::Key_7 }, + { "eight", Qt::Key_8 }, + { "nine", Qt::Key_9 }, + { "colon", Qt::Key_Colon }, + { "semicolon", Qt::Key_Semicolon }, + { "less", Qt::Key_Less }, + { "equal", Qt::Key_Equal }, + { "greater", Qt::Key_Greater }, + { "question", Qt::Key_Question }, + { "at", Qt::Key_At }, + { "bracketleft", Qt::Key_BracketLeft }, + { "backslash", Qt::Key_Backslash }, + { "bracketright", Qt::Key_BracketRight }, + { "asciicircum", Qt::Key_AsciiCircum }, + { "underscore", Qt::Key_Underscore }, + { "grave", Qt::Key_QuoteLeft }, + { "braceleft", Qt::Key_BraceLeft }, + { "bar", Qt::Key_Bar }, + { "braceright", Qt::Key_BraceRight }, + { "asciitilde", Qt::Key_AsciiTilde }, + { "nobreakspace", Qt::Key_nobreakspace }, + { "exclamdown", Qt::Key_exclamdown }, + { "cent", Qt::Key_cent }, + { "sterling", Qt::Key_sterling }, + { "currency", Qt::Key_currency }, + { "yen", Qt::Key_yen }, + { "brokenbar", Qt::Key_brokenbar }, + { "section", Qt::Key_section }, + { "diaeresis", Qt::Key_diaeresis }, + { "copyright", Qt::Key_copyright }, + { "ordfeminine", Qt::Key_ordfeminine }, + { "guillemotleft", Qt::Key_guillemotleft }, + { "notsign", Qt::Key_notsign }, + { "hyphen", Qt::Key_hyphen }, + { "registered", Qt::Key_registered }, + { "macron", Qt::Key_macron }, + { "degree", Qt::Key_degree }, + { "plusminus", Qt::Key_plusminus }, + { "twosuperior", Qt::Key_twosuperior }, + { "threesuperior", Qt::Key_threesuperior }, + { "acute", Qt::Key_acute }, + { "mu", Qt::Key_mu }, + { "paragraph", Qt::Key_paragraph }, + { "periodcentered", Qt::Key_periodcentered }, + { "cedilla", Qt::Key_cedilla }, + { "onesuperior", Qt::Key_onesuperior }, + { "masculine", Qt::Key_masculine }, + { "guillemotright", Qt::Key_guillemotright }, + { "onequarter", Qt::Key_onequarter }, + { "onehalf", Qt::Key_onehalf }, + { "threequarters", Qt::Key_threequarters }, + { "questiondown", Qt::Key_questiondown }, + { "Agrave", Qt::Key_Agrave }, + { "Aacute", Qt::Key_Aacute }, + { "Acircumflex", Qt::Key_Acircumflex }, + { "Atilde", Qt::Key_Atilde }, + { "Adiaeresis", Qt::Key_Adiaeresis }, + { "Aring", Qt::Key_Aring }, + { "AE", Qt::Key_AE }, + { "Ccedilla", Qt::Key_Ccedilla }, + { "Egrave", Qt::Key_Egrave }, + { "Eacute", Qt::Key_Eacute }, + { "Ecircumflex", Qt::Key_Ecircumflex }, + { "Ediaeresis", Qt::Key_Ediaeresis }, + { "Igrave", Qt::Key_Igrave }, + { "Iacute", Qt::Key_Iacute }, + { "Icircumflex", Qt::Key_Icircumflex }, + { "Idiaeresis", Qt::Key_Idiaeresis }, + { "ETH", Qt::Key_ETH }, + { "Ntilde", Qt::Key_Ntilde }, + { "Ograve", Qt::Key_Ograve }, + { "Oacute", Qt::Key_Oacute }, + { "Ocircumflex", Qt::Key_Ocircumflex }, + { "Otilde", Qt::Key_Otilde }, + { "Odiaeresis", Qt::Key_Odiaeresis }, + { "multiply", Qt::Key_multiply }, + { "Ooblique", Qt::Key_Ooblique }, + { "Ugrave", Qt::Key_Ugrave }, + { "Uacute", Qt::Key_Uacute }, + { "Ucircumflex", Qt::Key_Ucircumflex }, + { "Udiaeresis", Qt::Key_Udiaeresis }, + { "Yacute", Qt::Key_Yacute }, + { "THORN", Qt::Key_THORN }, + { "ssharp", Qt::Key_ssharp }, + + { "agrave", 0xe0 /*Qt::Key_agrave*/ }, + { "aacute", 0xe1 /*Qt::Key_aacute*/ }, + { "acircumflex", 0xe2 /*Qt::Key_acircumflex*/ }, + { "atilde", 0xe3 /*Qt::Key_atilde*/ }, + { "adiaeresis", 0xe4 /*Qt::Key_adiaeresis*/ }, + { "aring", 0xe5 /*Qt::Key_aring*/ }, + { "ae", 0xe6 /*Qt::Key_ae*/ }, + { "ccedilla", 0xe7 /*Qt::Key_ccedilla*/ }, + { "egrave", 0xe8 /*Qt::Key_egrave*/ }, + { "eacute", 0xe9 /*Qt::Key_eacute*/ }, + { "ecircumflex", 0xea /*Qt::Key_ecircumflex*/ }, + { "ediaeresis", 0xeb /*Qt::Key_ediaeresis*/ }, + { "igrave", 0xec /*Qt::Key_igrave*/ }, + { "iacute", 0xed /*Qt::Key_iacute*/ }, + { "icircumflex", 0xee /*Qt::Key_icircumflex*/ }, + { "idiaeresis", 0xef /*Qt::Key_idiaeresis*/ }, + { "eth", 0xf0 /*Qt::Key_eth*/ }, + { "ntilde", 0xf1 /*Qt::Key_ntilde*/ }, + { "ograve", 0xf2 /*Qt::Key_ograve*/ }, + { "oacute", 0xf3 /*Qt::Key_oacute*/ }, + { "ocircumflex", 0xf4 /*Qt::Key_ocircumflex*/ }, + { "otilde", 0xf5 /*Qt::Key_otilde*/ }, + { "odiaeresis", 0xf6 /*Qt::Key_odiaeresis*/ }, + { "division", Qt::Key_division }, + { "oslash", 0xf8 /*Qt::Key_oslash*/ }, + { "ugrave", 0xf9 /*Qt::Key_ugrave*/ }, + { "uacute", 0xfa /*Qt::Key_uacute*/ }, + { "ucircumflex", 0xfb /*Qt::Key_ucircumflex*/ }, + { "udiaeresis", 0xfc /*Qt::Key_udiaeresis*/ }, + { "yacute", 0xfd /*Qt::Key_yacute*/ }, + { "thorn", 0xfe /*Qt::Key_thorn*/ }, + { "ydiaeresis", Qt::Key_ydiaeresis }, + + { "F1", Qt::Key_F1 }, + { "F2", Qt::Key_F2 }, + { "F3", Qt::Key_F3 }, + { "F4", Qt::Key_F4 }, + { "F5", Qt::Key_F5 }, + { "F6", Qt::Key_F6 }, + { "F7", Qt::Key_F7 }, + { "F8", Qt::Key_F8 }, + { "F9", Qt::Key_F9 }, + { "F10", Qt::Key_F10 }, + { "F11", Qt::Key_F11 }, + { "F12", Qt::Key_F12 }, + { "F13", Qt::Key_F13 }, + { "F14", Qt::Key_F14 }, + { "F15", Qt::Key_F15 }, + { "F16", Qt::Key_F16 }, + { "F17", Qt::Key_F17 }, + { "F18", Qt::Key_F18 }, + { "F19", Qt::Key_F19 }, + { "F20", Qt::Key_F20 }, + { "F21", Qt::Key_F21 }, + { "F22", Qt::Key_F22 }, + { "F23", Qt::Key_F23 }, + { "F24", Qt::Key_F24 }, + { "F25", Qt::Key_F25 }, + { "F26", Qt::Key_F26 }, + { "F27", Qt::Key_F27 }, + { "F28", Qt::Key_F28 }, + { "F29", Qt::Key_F29 }, + { "F30", Qt::Key_F30 }, + { "F31", Qt::Key_F31 }, + { "F32", Qt::Key_F32 }, + { "F33", Qt::Key_F33 }, + { "F34", Qt::Key_F34 }, + { "F35", Qt::Key_F35 }, + + { "BackSpace", Qt::Key_Backspace }, + { "Tab", Qt::Key_Tab }, + { "Escape", Qt::Key_Escape }, + { "Delete", Qt::Key_Backspace }, // what's the difference between "Delete" and "BackSpace"?? + { "Return", Qt::Key_Return }, + { "Break", Qt::Key_unknown }, //TODO: why doesn't Qt support the 'Break' key? + { "Caps_Lock", Qt::Key_CapsLock }, + { "Num_Lock", Qt::Key_NumLock }, + { "Scroll_Lock", Qt::Key_ScrollLock }, + { "Caps_On", Qt::Key_CapsLock }, + { "Compose", Qt::Key_Multi_key }, + { "Bare_Num_Lock", Qt::Key_NumLock }, + { "Find", Qt::Key_Home }, + { "Insert", Qt::Key_Insert }, + { "Remove", Qt::Key_Delete }, + { "Select", Qt::Key_End }, + { "Prior", Qt::Key_PageUp }, + { "Next", Qt::Key_PageDown }, + { "Help", Qt::Key_Help }, + { "Pause", Qt::Key_Pause }, + + { "KP_0", Qt::Key_0 | Qt::KeypadModifier }, + { "KP_1", Qt::Key_1 | Qt::KeypadModifier }, + { "KP_2", Qt::Key_2 | Qt::KeypadModifier }, + { "KP_3", Qt::Key_3 | Qt::KeypadModifier }, + { "KP_4", Qt::Key_4 | Qt::KeypadModifier }, + { "KP_5", Qt::Key_5 | Qt::KeypadModifier }, + { "KP_6", Qt::Key_6 | Qt::KeypadModifier }, + { "KP_7", Qt::Key_7 | Qt::KeypadModifier }, + { "KP_8", Qt::Key_8 | Qt::KeypadModifier }, + { "KP_9", Qt::Key_9 | Qt::KeypadModifier }, + { "KP_Add", Qt::Key_Plus | Qt::KeypadModifier }, + { "KP_Subtract", Qt::Key_Minus | Qt::KeypadModifier }, + { "KP_Multiply", Qt::Key_Asterisk | Qt::KeypadModifier }, + { "KP_Divide", Qt::Key_Slash | Qt::KeypadModifier }, + { "KP_Enter", Qt::Key_Enter | Qt::KeypadModifier }, + { "KP_Comma", Qt::Key_Comma | Qt::KeypadModifier }, + { "KP_Period", Qt::Key_Period | Qt::KeypadModifier }, + { "KP_MinPlus", Qt::Key_plusminus | Qt::KeypadModifier }, + + { "dead_grave", Qt::Key_Dead_Grave }, + { "dead_acute", Qt::Key_Dead_Acute }, + { "dead_circumflex", Qt::Key_Dead_Circumflex }, + { "dead_tilde", Qt::Key_Dead_Tilde }, + { "dead_diaeresis", Qt::Key_Dead_Diaeresis }, + { "dead_cedilla", Qt::Key_Dead_Cedilla }, + + { "Down", Qt::Key_Down }, + { "Left", Qt::Key_Left }, + { "Right", Qt::Key_Right }, + { "Up", Qt::Key_Up }, + { "Shift", Qt::Key_Shift }, + { "AltGr", Qt::Key_AltGr }, + { "Control", Qt::Key_Control }, + { "Alt", Qt::Key_Alt }, + { "ShiftL", Qt::Key_Shift }, + { "ShiftR", Qt::Key_Shift }, + { "CtrlL", Qt::Key_Control }, + { "CtrlR", Qt::Key_Control }, +}; + +static const int symbol_map_size = sizeof(symbol_map)/sizeof(symbol_map_t); + + +struct symbol_dead_unicode_t { + quint32 dead; + quint16 unicode; +}; + +static const symbol_dead_unicode_t symbol_dead_unicode[] = { + { Qt::Key_Dead_Grave, '`' }, + { Qt::Key_Dead_Acute, '\'' }, + { Qt::Key_Dead_Circumflex, '^' }, + { Qt::Key_Dead_Tilde, '~' }, + { Qt::Key_Dead_Diaeresis, '"' }, + { Qt::Key_Dead_Cedilla, ',' }, +}; + +static const int symbol_dead_unicode_size = sizeof(symbol_dead_unicode)/sizeof(symbol_dead_unicode_t); + + +struct symbol_synonyms_t { + const char *from; + const char *to; +}; + +static const symbol_synonyms_t symbol_synonyms[] = { + { "Control_h", "BackSpace" }, + { "Control_i", "Tab" }, + { "Control_j", "Linefeed" }, + { "Home", "Find" }, + { "End", "Select" }, + { "PageUp", "Prior" }, + { "PageDown", "Next" }, + { "multiplication", "multiply" }, + { "pound", "sterling" }, + { "pilcrow", "paragraph" }, + { "Oslash", "Ooblique" }, + { "Shift_L", "ShiftL" }, + { "Shift_R", "ShiftR" }, + { "Control_L", "CtrlL" }, + { "Control_R", "CtrlR" }, + { "AltL", "Alt" }, + { "AltR", "AltGr" }, + { "Alt_L", "Alt" }, + { "Alt_R", "AltGr" }, + { "AltGr_L", "Alt" }, + { "AltGr_R", "AltGr" }, + { "tilde", "asciitilde" }, + { "circumflex", "asciicircum" }, + { "dead_ogonek", "dead_cedilla" }, + { "dead_caron", "dead_circumflex" }, + { "dead_breve", "dead_tilde" }, + { "dead_doubleacute", "dead_tilde" }, + { "no-break_space", "nobreakspace" }, + { "paragraph_sign", "section" }, + { "soft_hyphen", "hyphen" }, + { "rightanglequote", "guillemotright" }, +}; + +static const int symbol_synonyms_size = sizeof(symbol_synonyms)/sizeof(symbol_synonyms_t); + +// makes the generated array in --header mode a bit more human readable +static bool operator<(const QWSKeyboard::Mapping &m1, const QWSKeyboard::Mapping &m2) +{ + return m1.keycode != m2.keycode ? m1.keycode < m2.keycode : m1.modifiers < m2.modifiers; +} + +class KeymapParser { +public: + KeymapParser(); + ~KeymapParser(); + + bool parseKmap(QFile *kmap); + bool generateQmap(QFile *qmap); + bool generateHeader(QFile *qmap); + + int parseWarningCount() const { return m_warning_count; } + +private: + bool parseSymbol(const QByteArray &str, const QTextCodec *codec, quint16 &unicode, quint32 &qtcode, quint8 &flags, quint16 &special); + bool parseCompose(const QByteArray &str, const QTextCodec *codec, quint16 &unicode); + bool parseModifier(const QByteArray &str, quint8 &modifier); + + void updateMapping(quint16 keycode = 0, quint8 modifiers = 0, quint16 unicode = 0xffff, quint32 qtcode = Qt::Key_unknown, quint8 flags = 0, quint16 = 0); + + static quint32 toQtModifiers(quint8 modifiers); + static QList<QByteArray> tokenize(const QByteArray &line); + + +private: + QList<QWSKeyboard::Mapping> m_keymap; + QList<QWSKeyboard::Composing> m_keycompose; + + int m_warning_count; +}; + + + +int main(int argc, char **argv) +{ + int header = 0; + if (argc >= 2 && !qstrcmp(argv[1], "--header")) + header = 1; + + if (argc < (3 + header)) { + fprintf(stderr, "Usage: kmap2qmap [--header] <kmap> [<additional kmaps> ...] <qmap>\n"); + fprintf(stderr, " --header can be used to generate Qt's default compiled in qmap.\n"); + return 1; + } + + QVector<QFile *> kmaps(argc - header - 2); + for (int i = 0; i < kmaps.size(); ++i) { + kmaps [i] = new QFile(QString::fromLocal8Bit(argv[i + 1 + header])); + + if (!kmaps[i]->open(QIODevice::ReadOnly)) { + fprintf(stderr, "Could not read from '%s'.\n", argv[i + 1 + header]); + return 2; + } + } + QFile *qmap = new QFile(QString::fromLocal8Bit(argv[argc - 1])); + + if (!qmap->open(QIODevice::WriteOnly)) { + fprintf(stderr, "Could not write to '%s'.\n", argv[argc - 1]); + return 3; + } + + KeymapParser p; + + for (int i = 0; i < kmaps.size(); ++i) { + if (!p.parseKmap(kmaps[i])) { + fprintf(stderr, "Parsing kmap '%s' failed.\n", qPrintable(kmaps[i]->fileName())); + return 4; + } + } + + if (p.parseWarningCount()) { + fprintf(stderr, "\nParsing the specified keymap(s) produced %d warning(s).\n" \ + "Your generated qmap might not be complete.\n", \ + p.parseWarningCount()); + } + if (!(header ? p.generateHeader(qmap) : p.generateQmap(qmap))) { + fprintf(stderr, "Generating the qmap failed.\n"); + return 5; + } + + qDeleteAll(kmaps); + delete qmap; + + return 0; +} + + +KeymapParser::KeymapParser() + : m_warning_count(0) +{ } + + +KeymapParser::~KeymapParser() +{ } + + +bool KeymapParser::generateHeader(QFile *f) +{ + QTextStream ts(f); + + ts << "#ifndef QWSKEYBOARDHANDLER_DEFAULTMAP_H" << endl; + ts << "#define QWSKEYBOARDHANDLER_DEFAULTMAP_H" << endl << endl; + + ts << "const QWSKeyboard::Mapping QWSKbPrivate::s_keymap_default[] = {" << endl; + + for (int i = 0; i < m_keymap.size(); ++i) { + const QWSKeyboard::Mapping &m = m_keymap.at(i); + QString s; + s.sprintf(" { %3d, 0x%04x, 0x%08x, 0x%02x, 0x%02x, 0x%04x },\n", m.keycode, m.unicode, m.qtcode, m.modifiers, m.flags, m.special); + ts << s; + } + + ts << "};" << endl << endl; + + ts << "const QWSKeyboard::Composing QWSKbPrivate::s_keycompose_default[] = {" << endl; + + for (int i = 0; i < m_keycompose.size(); ++i) { + const QWSKeyboard::Composing &c = m_keycompose.at(i); + QString s; + s.sprintf(" { 0x%04x, 0x%04x, 0x%04x },\n", c.first, c.second, c.result); + ts << s; + } + ts << "};" << endl << endl; + + ts << "#endif" << endl; + + return (ts.status() == QTextStream::Ok); +} + + +bool KeymapParser::generateQmap(QFile *f) +{ + QDataStream ds(f); + + ds << quint32(QWSKeyboard::FileMagic) << quint32(1 /* version */) << quint32(m_keymap.size()) << quint32(m_keycompose.size()); + + if (ds.status() != QDataStream::Ok) + return false; + + for (int i = 0; i < m_keymap.size(); ++i) + ds << m_keymap[i]; + + for (int i = 0; i < m_keycompose.size(); ++i) + ds << m_keycompose[i]; + + return (ds.status() == QDataStream::Ok); +} + + +QList<QByteArray> KeymapParser::tokenize(const QByteArray &line) +{ + bool quoted = false, separator = true; + QList<QByteArray> result; + QByteArray token; + + for (int i = 0; i < line.length(); ++i) { + QChar c = line.at(i); + + if (!quoted && c == '#' && separator) + break; + else if (!quoted && c == '"' && separator) + quoted = true; + else if (quoted && c == '"') + quoted = false; + else if (!quoted && c.isSpace()) { + separator = true; + if (!token.isEmpty()) { + result.append(token); + token.truncate(0); + } + } + else { + separator = false; + token.append(c); + } + } + if (!token.isEmpty()) + result.append(token); + return result; +} + + +#define parseWarning(s) do { qWarning("Warning: keymap file '%s', line %d: %s", qPrintable(f->fileName()), lineno, s); ++m_warning_count; } while (false) + +bool KeymapParser::parseKmap(QFile *f) +{ + QByteArray line; + int lineno = 0; + QList<int> keymaps; + QTextCodec *codec = QTextCodec::codecForName("iso8859-1"); + + for (int i = 0; i <= 256; ++i) + keymaps << i; + + while (!f->atEnd() && !f->error()) { + line = f->readLine(); + lineno++; + + QList<QByteArray> tokens = tokenize(line); + + if (tokens.isEmpty()) + continue; + + if (tokens[0] == "keymaps") { + keymaps.clear(); + + if (tokens.count() > 1) { + foreach (const QByteArray §ion, tokens[1].split(',')) { + int dashpos = section.indexOf('-'); + + //qWarning("Section %s", section.constData()); + int end = section.mid(dashpos + 1).toInt(); + int start = end; + if (dashpos > 0) + start = section.left(dashpos).toInt(); + + if (start <= end && start >=0 && end <= 256) { + for (int i = start; i <= end; ++i) { + //qWarning("appending keymap %d", i); + keymaps.append(i); + } + } + else + parseWarning("keymaps has an invalid range"); + } + qSort(keymaps); + } + else + parseWarning("keymaps with more than one argument"); + } + else if (tokens[0] == "alt_is_meta") { + // simply ignore it for now + } + else if (tokens[0] == "include") { + if (tokens.count() == 2) { + QString incname = QString::fromLocal8Bit(tokens[1]); + bool found = false; + QList<QDir> searchpath; + QFileInfo fi(*f); + + if (!incname.endsWith(QLatin1String(".kmap")) && !incname.endsWith(QLatin1String(".inc"))) + incname.append(QLatin1String(".inc")); + + QDir d = fi.dir(); + searchpath << d; + if (d.cdUp() && d.cd(QLatin1String("include"))) + searchpath << d; + searchpath << QDir::current(); + + foreach (const QDir &path, searchpath) { + QFile f2(path.filePath(incname)); + //qWarning(" -- trying to include %s", qPrintable(f2.fileName())); + if (f2.open(QIODevice::ReadOnly)) { + if (!parseKmap(&f2)) + parseWarning("could not parse keymap include"); + found = true; + } + } + + if (!found) + parseWarning("could not locate keymap include"); + } else + parseWarning("include doesn't have exactly one argument"); + } + else if (tokens[0] == "charset") { + if (tokens.count() == 2) { + codec = QTextCodec::codecForName(tokens[1]); + if (!codec) { + parseWarning("could not parse codec definition"); + codec = QTextCodec::codecForName("iso8859-1"); + } + } else + parseWarning("codec doesn't habe exactly one argument"); + } + else if (tokens[0] == "strings") { + // simply ignore those - they have no meaning for QWS + } + else if (tokens[0] == "compose") { + if (tokens.count() == 5 && tokens[3] == "to") { + QWSKeyboard::Composing c = { 0xffff, 0xffff, 0xffff }; + + if (!parseCompose(tokens[1], codec, c.first)) + parseWarning("could not parse first compose symbol"); + if (!parseCompose(tokens[2], codec, c.second)) + parseWarning("could not parse second compose symbol"); + if (!parseCompose(tokens[4], codec, c.result)) + parseWarning("could not parse resulting compose symbol"); + + if (c.first != 0xffff && c.second != 0xffff && c.result != 0xffff) { + m_keycompose << c; + } + } else + parseWarning("non-standard compose line"); + } + else { + int kcpos = tokens.indexOf("keycode"); + + if (kcpos >= 0 && kcpos < (tokens.count()-3) && tokens[kcpos+2] == "=") { + quint16 keycode = tokens[kcpos+1].toInt(); + + if (keycode <= 0 || keycode > 0x1ff /* KEY_MAX */) { + parseWarning("keycode out of range [0..0x1ff]"); + break; + } + + bool line_modifiers = (kcpos > 0); + + quint8 modifiers = 0; //, modifiers_mask = 0xff; + for (int i = 0; i < kcpos; ++i) { + quint8 mod; + if (!parseModifier(tokens[i], mod)) { + parseWarning("unknown modifier prefix for keycode"); + continue; + } + modifiers |= mod; + } + + int kccount = tokens.count() - kcpos - 3; // 3 : 'keycode' 'X' '=' + + if (line_modifiers && kccount > 1) { + parseWarning("line has modifiers, but more than one keycode"); + break; + } + + // only process one symbol when a prefix modifer was specified + for (int i = 0; i < (line_modifiers ? 1 : kccount); ++i) { + if (!line_modifiers) + modifiers = keymaps[i]; + + quint32 qtcode; + quint16 unicode; + quint16 special; + quint8 flags; + if (!parseSymbol(tokens[i + kcpos + 3], codec, unicode, qtcode, flags, special)) { + parseWarning((QByteArray("symbol could not be parsed: ") + tokens[i + kcpos + 3]).constData()); + break; + } + + if (qtcode == Qt::Key_unknown && unicode == 0xffff) // VoidSymbol + continue; + + if (!line_modifiers && kccount == 1) { + if ((unicode >= 'A' && unicode <= 'Z') || (unicode >= 'a' && unicode <= 'z')) { + quint16 other_unicode = (unicode >= 'A' && unicode <= 'Z') ? unicode - 'A' + 'a' : unicode - 'a' + 'A'; + quint16 lower_unicode = (unicode >= 'A' && unicode <= 'Z') ? unicode - 'A' + 'a' : unicode; + + // a single a-z|A-Z value results in a very flags mapping: see below + + updateMapping(keycode, QWSKeyboard::ModPlain, unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModShift, other_unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAltGr, unicode, qtcode, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, other_unicode, qtcode, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModControl, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModAltGr, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModControl | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAlt, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModShift, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModAltGr, unicode, qtcode | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, unicode, qtcode | Qt::AltModifier, flags, 0); + + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModAltGr, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + updateMapping(keycode, QWSKeyboard::ModAlt | QWSKeyboard::ModControl | QWSKeyboard::ModAltGr | QWSKeyboard::ModShift, lower_unicode, qtcode | Qt::ControlModifier | Qt::AltModifier, flags, 0); + } + else { + // a single value results in that mapping regardless of the modifier + //for (int mod = 0; mod <= 255; ++mod) + // updateMapping(keycode, quint8(mod), unicode, qtcode | toQtModifiers(mod), flags, special); + + // we can save a lot of space in the qmap, since we do that anyway in the kbd handler: + updateMapping(keycode, QWSKeyboard::ModPlain, unicode, qtcode, flags, special); + } + } + else { + // "normal" mapping + updateMapping(keycode, modifiers, unicode, qtcode, flags, special); + } + } + } + } + } + qSort(m_keymap); + return !m_keymap.isEmpty(); +} + + + +void KeymapParser::updateMapping(quint16 keycode, quint8 modifiers, quint16 unicode, quint32 qtcode, quint8 flags, quint16 special) +{ + for (int i = 0; i < m_keymap.size(); ++i) { + QWSKeyboard::Mapping &m = m_keymap[i]; + + if (m.keycode == keycode && m.modifiers == modifiers) { + m.unicode = unicode; + m.qtcode = qtcode; + m.flags = flags; + m.special = special; + return; + } + } + QWSKeyboard::Mapping m = { keycode, unicode, qtcode, modifiers, flags, special }; + m_keymap << m; +} + + +quint32 KeymapParser::toQtModifiers(quint8 modifiers) +{ + quint32 qtmodifiers = Qt::NoModifier; + + for (int i = 0; i < modifier_map_size; ++i) { + if (modifiers & modifier_map[i].modifier) + qtmodifiers |= modifier_map[i].qtmodifier; + } + return qtmodifiers; +} + + +bool KeymapParser::parseModifier(const QByteArray &str, quint8 &modifier) +{ + QByteArray lstr = str.toLower(); + + for (int i = 0; i < modifier_map_size; ++i) { + if (lstr == modifier_map[i].symbol) { + modifier = modifier_map[i].modifier; + return true; + } + } + return false; +} + + +bool KeymapParser::parseCompose(const QByteArray &str, const QTextCodec *codec, quint16 &unicode) +{ + if (str == "'\\''") { + unicode = '\''; + return true; + } else if (str.length() == 3 && str.startsWith('\'') && str.endsWith('\'')) { + QString temp = codec->toUnicode(str.constData() + 1, str.length() - 2); + if (temp.length() != 1) + return false; + unicode = temp[0].unicode(); + return true; + } else { + quint32 code = str.toUInt(); + if (code > 255) + return false; + char c[2]; + c[0] = char(code); + c[1] = 0; + QString temp = codec->toUnicode(c); + if (temp.length() != 1) + return false; + unicode = temp[0].unicode(); + return true; + } +} + + +bool KeymapParser::parseSymbol(const QByteArray &str, const QTextCodec * /*codec*/, quint16 &unicode, quint32 &qtcode, quint8 &flags, quint16 &special) +{ + flags = (str[0] == '+') ? QWSKeyboard::IsLetter : 0; + QByteArray sym = (flags & QWSKeyboard::IsLetter) ? str.right(str.length() - 1) : str; + + special = 0; + qtcode = Qt::Key_unknown; + unicode = 0xffff; + + if (sym == "VoidSymbol" || sym == "nul") + return true; + + bool try_to_find_qtcode = false; + + if (sym[0] >= '0' && sym[0] <= '9') { // kernel internal action number + return false; + } else if (sym.length() == 6 && sym[1] == '+' && (sym[0] == 'U' || sym[0] == 'u')) { // unicode + bool ok; + unicode = sym.mid(2).toUInt(&ok, 16); + if (!ok) + return false; + try_to_find_qtcode = true; + } else { // symbolic + for (int i = 0; i < symbol_synonyms_size; ++i) { + if (sym == symbol_synonyms[i].from) { + sym = symbol_synonyms[i].to; + break; + } + } + + quint32 qtmod = 0; + + // parse prepended modifiers + forever { + int underpos = sym.indexOf('_'); + + if (underpos <= 0) + break; + QByteArray modsym = sym.left(underpos); + QByteArray nomodsym = sym.mid(underpos + 1); + quint8 modifier = 0; + + if (!parseModifier(modsym, modifier)) + break; + + qtmod |= toQtModifiers(modifier); + sym = nomodsym; + } + + if (qtcode == Qt::Key_unknown) { + quint8 modcode; + // check if symbol is a modifier + if (parseModifier(sym, modcode)) { + special = modcode; + flags |= QWSKeyboard::IsModifier; + } + + // map symbol to Qt key code + for (int i = 0; i < symbol_map_size; ++i) { + if (sym == symbol_map[i].symbol) { + qtcode = symbol_map[i].qtcode; + break; + } + } + + // a-zA-Z is not in the table to save space + if (qtcode == Qt::Key_unknown && sym.length() == 1) { + char letter = sym.at(0); + + if (letter >= 'a' && letter <= 'z') { + qtcode = Qt::Key_A + letter - 'a'; + unicode = letter; + } + else if (letter >= 'A' && letter <= 'Z') { + qtcode = Qt::Key_A + letter - 'A'; + unicode = letter; + } + } + // System keys + if (qtcode == Qt::Key_unknown) { + quint16 sys = 0; + + if (sym == "Decr_Console") { + sys = QWSKeyboard::SystemConsolePrevious; + } else if (sym == "Incr_Console") { + sys = QWSKeyboard::SystemConsoleNext; + } else if (sym.startsWith("Console_")) { + int console = sym.mid(8).toInt() - 1; + if (console >= 0 && console <= (QWSKeyboard::SystemConsoleLast - QWSKeyboard::SystemConsoleFirst)) { + sys = QWSKeyboard::SystemConsoleFirst + console; + } + } else if (sym == "Boot") { + sys = QWSKeyboard::SystemReboot; + } else if (sym == "QtZap") { + sys = QWSKeyboard::SystemZap; + } + + if (sys) { + flags |= QWSKeyboard::IsSystem; + special = sys; + qtcode = Qt::Key_Escape; // just a dummy + } + } + + // map Qt key codes in the iso-8859-1 range to unicode + if (qtcode != Qt::Key_unknown && unicode == 0xffff) { + quint32 qtcode_no_mod = qtcode & ~(Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier); + if (qtcode_no_mod <= 0x000000ff) // iso-8859-1 + unicode = quint16(qtcode_no_mod); + } + + // flag dead keys + if (qtcode >= Qt::Key_Dead_Grave && qtcode <= Qt::Key_Dead_Horn) { + flags = QWSKeyboard::IsDead; + + for (int i = 0; i < symbol_dead_unicode_size; ++i) { + if (symbol_dead_unicode[i].dead == qtcode) { + unicode = symbol_dead_unicode[i].unicode; + break; + } + } + } + } + if ((qtcode == Qt::Key_unknown) && (unicode == 0xffff)) + return false; + + qtcode |= qtmod; + } + + // map unicode in the iso-8859-1 range to Qt key codes + if (unicode >= 0x0020 && unicode <= 0x00ff && qtcode == Qt::Key_unknown) + qtcode = unicode; // iso-8859-1 + + return true; +} + diff --git a/tools/linguist/lconvert/main.cpp b/tools/linguist/lconvert/main.cpp index 9ccc60e..bdc6c9a 100644 --- a/tools/linguist/lconvert/main.cpp +++ b/tools/linguist/lconvert/main.cpp @@ -51,21 +51,16 @@ static int usage(const QStringList &args) Q_UNUSED(args); QString loaders; - QString savers; - QString line = QString(QLatin1String(" %1 - %2\n")); - foreach (Translator::FileFormat format, Translator::registeredFileFormats()) { + QString line(QLatin1String(" %1 - %2\n")); + foreach (Translator::FileFormat format, Translator::registeredFileFormats()) loaders += line.arg(format.extension, -5).arg(format.description); - if (format.fileType != Translator::FileFormat::SourceCode) - savers += line.arg(format.extension, -5).arg(format.description); - } qWarning("%s", qPrintable(QString(QLatin1String("\nUsage:\n" " lconvert [options] <infile> [<infile>...]\n\n" "lconvert is part of Qt's Linguist tool chain. It can be used as a\n" - "stand-alone tool to convert translation data files from one of the\n" - "following input formats\n\n%1\n" - "to one of the following output formats\n\n%2\n" - "If multiple input files are specified the translations are merged with\n" + "stand-alone tool to convert and filter translation data files.\n" + "The following file formats are supported:\n\n%1\n" + "If multiple input files are specified, they are merged with\n" "translations from later files taking precedence.\n\n" "Options:\n" " -h\n" @@ -93,7 +88,7 @@ static int usage(const QStringList &args) " Note: this implies --no-obsolete.\n\n" " --source-language <language>[_<region>]\n" " Specify/override the language of the source strings. Defaults to\n" - " POSIX if not specified and the file does not name it yet.\n" + " POSIX if not specified and the file does not name it yet.\n\n" " --target-language <language>[_<region>]\n" " Specify/override the language of the translation.\n" " The target language is guessed from the file name if this option\n" @@ -109,7 +104,7 @@ static int usage(const QStringList &args) " 0 on success\n" " 1 on command line parse failures\n" " 2 on read failures\n" - " 3 on write failures\n")).arg(loaders).arg(savers))); + " 3 on write failures\n")).arg(loaders))); return 1; } diff --git a/tools/linguist/linguist/main.cpp b/tools/linguist/linguist/main.cpp index 018fbc5..a6a0d27 100644 --- a/tools/linguist/linguist/main.cpp +++ b/tools/linguist/linguist/main.cpp @@ -80,12 +80,15 @@ int main(int argc, char **argv) } QTranslator translator; - translator.load(QLatin1String("linguist_") + QLocale::system().name(), resourceDir); - app.installTranslator(&translator); - QTranslator qtTranslator; - qtTranslator.load(QLatin1String("qt_") + QLocale::system().name(), resourceDir); - app.installTranslator(&qtTranslator); + QString sysLocale = QLocale::system().name(); + if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)) { + app.installTranslator(&translator); + if (qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) + app.installTranslator(&qtTranslator); + else + app.removeTranslator(&translator); + } app.setOrganizationName(QLatin1String("Trolltech")); app.setApplicationName(QLatin1String("Linguist")); diff --git a/tools/linguist/linguist/messageeditor.cpp b/tools/linguist/linguist/messageeditor.cpp index dc8b8e4..3fc91e3 100644 --- a/tools/linguist/linguist/messageeditor.cpp +++ b/tools/linguist/linguist/messageeditor.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -/* TRANSLATOR MsgEdit +/* TRANSLATOR MessageEditor This is the right panel of the main window. */ diff --git a/tools/linguist/linguist/phrasebookbox.cpp b/tools/linguist/linguist/phrasebookbox.cpp index 50749d7..d3bb937 100644 --- a/tools/linguist/linguist/phrasebookbox.cpp +++ b/tools/linguist/linguist/phrasebookbox.cpp @@ -56,13 +56,15 @@ QT_BEGIN_NAMESPACE -#define NewPhrase tr("(New Entry)") - PhraseBookBox::PhraseBookBox(PhraseBook *phraseBook, QWidget *parent) : QDialog(parent), m_phraseBook(phraseBook), m_translationSettingsDialog(0) { + +// This definition needs to be within class context for lupdate to find it +#define NewPhrase tr("(New Entry)") + setupUi(this); setWindowTitle(tr("%1[*] - Qt Linguist").arg(m_phraseBook->friendlyPhraseBookName())); setWindowModified(m_phraseBook->isModified()); diff --git a/tools/linguist/lrelease/main.cpp b/tools/linguist/lrelease/main.cpp index 3bcc998..7d0452f 100644 --- a/tools/linguist/lrelease/main.cpp +++ b/tools/linguist/lrelease/main.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "translator.h" -#include "translatortools.h" #include "profileevaluator.h" #include <QtCore/QCoreApplication> diff --git a/tools/linguist/lupdate/cpp.cpp b/tools/linguist/lupdate/cpp.cpp new file mode 100644 index 0000000..42aa2f0 --- /dev/null +++ b/tools/linguist/lupdate/cpp.cpp @@ -0,0 +1,1818 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Linguist of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lupdate.h" + +#include <translator.h> + +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QStack> +#include <QtCore/QString> +#include <QtCore/QTextCodec> +#include <QtCore/QTextStream> + +#include <ctype.h> // for isXXX() + +QT_BEGIN_NAMESPACE + +/* qmake ignore Q_OBJECT */ + +static const char MagicComment[] = "TRANSLATOR "; + +static const int yyIdentMaxLen = 128; +static const int yyCommentMaxLen = 65536; +static const int yyStringMaxLen = 65536; + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) +#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) + +//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this + +uint qHash(const QStringList &qsl) +{ + uint hash = 0; + foreach (const QString &qs, qsl) { + hash ^= qHash(qs) ^ 0xa09df22f; + hash = (hash << 13) | (hash >> 19); + } + return hash; +} + +struct Namespace { + + Namespace() : + isClass(false), + hasTrFunctions(false), needsTrFunctions(false), complained(false) + {} + + QString name; + QMap<QString, Namespace *> children; + QMap<QString, QStringList> aliases; + QSet<QStringList> usings; + + int fileId; + + bool isClass; + + bool hasTrFunctions; + bool needsTrFunctions; + bool complained; // ... that tr functions are missing. +}; + +typedef QList<Namespace *> NamespaceList; + +struct ParseResults { + + ParseResults() + { + static int nextFileId; + rootNamespace.fileId = nextFileId++; + tor = 0; + } + bool detachNamespace(Namespace **that); + Namespace *include(Namespace *that, const Namespace *other); + void unite(const ParseResults *other); + + Namespace rootNamespace; + Translator *tor; + QSet<QString> allIncludes; +}; + +typedef QHash<QString, const ParseResults *> ParseResultHash; + +class CppFiles { + +public: + static const ParseResults *getResults(const QString &cleanFile); + static void setResults(const QString &cleanFile, const ParseResults *results); + static bool isBlacklisted(const QString &cleanFile); + static void setBlacklisted(const QString &cleanFile); + +private: + static ParseResultHash &parsedFiles(); + static QSet<QString> &blacklistedFiles(); +}; + +class CppParser { + +public: + CppParser(ParseResults *results = 0); + void setInput(const QString &in); + void setInput(QTextStream &ts, const QString &fileName); + void setTranslator(Translator *tor) { results->tor = tor; } + void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions); + void parseInternal(ConversionData &cd, QSet<QString> &inclusions); + const ParseResults *getResults() const { return results; } + void deleteResults() { delete results; } + +private: + struct SavedState { + QStringList namespaces; + QStack<int> namespaceDepths; + QStringList functionContext; + QString functionContextUnresolved; + QString pendingContext; + }; + + struct IfdefState { + IfdefState() {} + IfdefState(int _braceDepth, int _parenDepth) : + braceDepth(_braceDepth), + parenDepth(_parenDepth), + elseLine(-1) + {} + + SavedState state; + int braceDepth, braceDepth1st; + int parenDepth, parenDepth1st; + int elseLine; + }; + + uint getChar(); + uint getToken(); + bool match(uint t); + bool matchString(QString *s); + bool matchEncoding(bool *utf8); + bool matchInteger(qlonglong *number); + bool matchStringOrNull(QString *s); + bool matchExpression(); + + QString transcode(const QString &str, bool utf8); + void recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool utf8, bool plural); + + void processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions); + + void saveState(SavedState *state); + void loadState(const SavedState *state); + + static QString stringifyNamespace(const NamespaceList &namespaces); + static QStringList stringListifyNamespace(const NamespaceList &namespaces); + void modifyNamespace(NamespaceList *namespaces); + NamespaceList resolveNamespaces(const QStringList &segments); + bool qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved); + bool fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved); + void enterNamespace(NamespaceList *namespaces, const QString &name); + void truncateNamespaces(NamespaceList *namespaces, int lenght); + + enum { + Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return, + Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, + Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, + Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, + Tok_Equals, + Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, + Tok_Integer = 40, + Tok_QuotedInclude = 50, Tok_AngledInclude, + Tok_Other = 99 + }; + + // Tokenizer state + QString yyFileName; + int yyCh; + bool yyAtNewline; + bool yyCodecIsUtf8; + bool yyForceUtf8; + QString yyIdent; + QString yyComment; + QString yyString; + qlonglong yyInteger; + QStack<IfdefState> yyIfdefStack; + int yyBraceDepth; + int yyParenDepth; + int yyLineNo; + int yyCurLineNo; + int yyBraceLineNo; + int yyParenLineNo; + + // the string to read from and current position in the string + QTextCodec *yySourceCodec; + bool yySourceIsUnicode; + QString yyInStr; + int yyInPos; + + // Parser state + uint yyTok; + + NamespaceList namespaces; + QStack<int> namespaceDepths; + NamespaceList functionContext; + QString functionContextUnresolved; + QString prospectiveContext; + QString pendingContext; + ParseResults *results; + bool directInclude; + + SavedState savedState; + int yyMinBraceDepth; + bool inDefine; +}; + +CppParser::CppParser(ParseResults *_results) +{ + if (_results) { + results = _results; + directInclude = true; + } else { + results = new ParseResults; + directInclude = false; + } + yyInPos = 0; + yyBraceDepth = 0; + yyParenDepth = 0; + yyCurLineNo = 1; + yyBraceLineNo = 1; + yyParenLineNo = 1; + yyAtNewline = true; + yyMinBraceDepth = 0; + inDefine = false; +} + +void CppParser::setInput(const QString &in) +{ + yyInStr = in; + yyFileName = QString(); + yySourceCodec = 0; + yySourceIsUnicode = true; + yyForceUtf8 = true; +} + +void CppParser::setInput(QTextStream &ts, const QString &fileName) +{ + yyInStr = ts.readAll(); + yyFileName = fileName; + yySourceCodec = ts.codec(); + yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); + yyForceUtf8 = false; +} + +/* + The first part of this source file is the C++ tokenizer. We skip + most of C++; the only tokens that interest us are defined here. + Thus, the code fragment + + int main() + { + printf("Hello, world!\n"); + return 0; + } + + is broken down into the following tokens (Tok_ omitted): + + Ident Ident LeftParen RightParen + LeftBrace + Ident LeftParen String RightParen Semicolon + return Semicolon + RightBrace. + + The 0 doesn't produce any token. +*/ + +uint CppParser::getChar() +{ + forever { + if (yyInPos >= yyInStr.size()) + return EOF; + uint c = yyInStr[yyInPos++].unicode(); + if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { + ++yyCurLineNo; + ++yyInPos; + continue; + } + if (c == '\n') { + ++yyCurLineNo; + yyAtNewline = true; + } else if (c != ' ' && c != '\t' && c != '#') { + yyAtNewline = false; + } + return c; + } +} + +uint CppParser::getToken() +{ + restart: + yyIdent.clear(); + yyComment.clear(); + yyString.clear(); + + while (yyCh != EOF) { + yyLineNo = yyCurLineNo; + + if (yyCh == '#' && yyAtNewline) { + /* + Early versions of lupdate complained about + unbalanced braces in the following code: + + #ifdef ALPHA + while (beta) { + #else + while (gamma) { + #endif + delta; + } + + The code contains, indeed, two opening braces for + one closing brace; yet there's no reason to panic. + + The solution is to remember yyBraceDepth as it was + when #if, #ifdef or #ifndef was met, and to set + yyBraceDepth to that value when meeting #elif or + #else. + */ + do { + yyCh = getChar(); + } while (isspace(yyCh) && yyCh != '\n'); + + switch (yyCh) { + case 'd': // define + // Skip over the name of the define to avoid it being interpreted as c++ code + do { // Rest of "define" + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Space beween "define" and macro name + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + do { // Macro name + if (yyCh == '(') { + // Argument list. Follows the name without a space, and no + // paren nesting is possible. + do { + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (yyCh != ')'); + break; + } + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (!isspace(yyCh)); + do { // Shortcut the immediate newline case if no comments follow. + yyCh = getChar(); + if (yyCh == EOF) + return Tok_Eof; + if (yyCh == '\n') + goto restart; + } while (isspace(yyCh)); + + saveState(&savedState); + yyMinBraceDepth = yyBraceDepth; + inDefine = true; + goto restart; + case 'i': + yyCh = getChar(); + if (yyCh == 'f') { + // if, ifdef, ifndef + yyIfdefStack.push(IfdefState(yyBraceDepth, yyParenDepth)); + yyCh = getChar(); + } else if (yyCh == 'n') { + // include + do { + yyCh = getChar(); + } while (yyCh != EOF && !isspace(yyCh)); + do { + yyCh = getChar(); + } while (isspace(yyCh)); + int tChar; + if (yyCh == '"') + tChar = '"'; + else if (yyCh == '<') + tChar = '>'; + else + break; + forever { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyCh == tChar) { + yyCh = getChar(); + break; + } + yyString += yyCh; + } + return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude; + } + break; + case 'e': + yyCh = getChar(); + if (yyCh == 'l') { + // elif, else + if (!yyIfdefStack.isEmpty()) { + IfdefState &is = yyIfdefStack.top(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + } else { + is.braceDepth1st = yyBraceDepth; + is.parenDepth1st = yyParenDepth; + saveState(&is.state); + } + is.elseLine = yyLineNo; + yyBraceDepth = is.braceDepth; + yyParenDepth = is.parenDepth; + } + yyCh = getChar(); + } else if (yyCh == 'n') { + // endif + if (!yyIfdefStack.isEmpty()) { + IfdefState is = yyIfdefStack.pop(); + if (is.elseLine != -1) { + if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) + qWarning("%s:%d: Parenthesis/brace mismatch between " + "#if and #else branches; using #if branch\n", + qPrintable(yyFileName), is.elseLine); + yyBraceDepth = is.braceDepth1st; + yyParenDepth = is.parenDepth1st; + loadState(&is.state); + } + } + yyCh = getChar(); + } + break; + } + // Optimization: skip over rest of preprocessor directive + do { + if (yyCh == '/') { + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + } while (yyCh != EOF && yyCh != '\n'); + break; + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + break; + } + + if (yyCh == '*') { + metAster = true; + } else if (metAster && yyCh == '/') { + yyCh = getChar(); + break; + } else { + metAster = false; + } + } + } + } else { + yyCh = getChar(); + } + } while (yyCh != '\n' && yyCh != EOF); + yyCh = getChar(); + } else if (isalpha(yyCh) || yyCh == '_') { + do { + yyIdent += yyCh; + yyCh = getChar(); + } while (isalnum(yyCh) || yyCh == '_'); + + //qDebug() << "IDENT: " << yyIdent; + + switch (yyIdent.at(0).unicode()) { + case 'Q': + if (yyIdent == QLatin1String("Q_OBJECT")) + return Tok_Q_OBJECT; + if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) + return Tok_Q_DECLARE_TR_FUNCTIONS; + if (yyIdent == QLatin1String("QT_TR_NOOP")) + return Tok_tr; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) + return Tok_translate; + if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) + return Tok_trUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) + return Tok_translateUtf8; + if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) + return Tok_translateUtf8; + break; + case 'T': + // TR() for when all else fails + if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { + return Tok_tr; + } + break; + case 'c': + if (yyIdent == QLatin1String("class")) + return Tok_class; + break; + case 'f': + /* + QTranslator::findMessage() has the same parameters as + QApplication::translate(). + */ + if (yyIdent == QLatin1String("findMessage")) + return Tok_translate; + if (yyIdent == QLatin1String("friend")) + return Tok_friend; + break; + case 'n': + if (yyIdent == QLatin1String("namespace")) + return Tok_namespace; + break; + case 'r': + if (yyIdent == QLatin1String("return")) + return Tok_return; + break; + case 's': + if (yyIdent == QLatin1String("struct")) + return Tok_class; + break; + case 't': + if (yyIdent == QLatin1String("tr")) { + return Tok_tr; + } + if (yyIdent == QLatin1String("trUtf8")) { + return Tok_trUtf8; + } + if (yyIdent == QLatin1String("translate")) { + return Tok_translate; + } + break; + case 'u': + if (yyIdent == QLatin1String("using")) + return Tok_using; + break; + } + return Tok_Ident; + } else { + switch (yyCh) { + case '\n': + if (inDefine) { + loadState(&savedState); + prospectiveContext.clear(); + yyBraceDepth = yyMinBraceDepth; + yyMinBraceDepth = 0; + inDefine = false; + } + yyCh = getChar(); + break; + case '/': + yyCh = getChar(); + if (yyCh == '/') { + do { + yyCh = getChar(); + if (yyCh == EOF) + break; + yyComment.append(yyCh); + } while (yyCh != '\n'); + } else if (yyCh == '*') { + bool metAster = false; + + forever { + yyCh = getChar(); + if (yyCh == EOF) { + qWarning("%s:%d: Unterminated C++ comment\n", + qPrintable(yyFileName), yyLineNo); + return Tok_Comment; + } + yyComment.append(yyCh); + + if (yyCh == '*') + metAster = true; + else if (metAster && yyCh == '/') + break; + else + metAster = false; + } + yyCh = getChar(); + yyComment.chop(2); + } + return Tok_Comment; + case '"': + yyCh = getChar(); + while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { + if (yyCh == '\\') { + yyCh = getChar(); + if (yyCh == EOF || yyCh == '\n') + break; + if (yyString.size() < yyStringMaxLen) { + yyString.append(QLatin1Char('\\')); + yyString.append(yyCh); + } + } else { + if (yyString.size() < yyStringMaxLen) + yyString.append(yyCh); + } + yyCh = getChar(); + } + + if (yyCh != '"') + qWarning("%s:%d: Unterminated C++ string\n", + qPrintable(yyFileName), yyLineNo); + else + yyCh = getChar(); + return Tok_String; + case '-': + yyCh = getChar(); + if (yyCh == '>') { + yyCh = getChar(); + return Tok_Arrow; + } + break; + case ':': + yyCh = getChar(); + if (yyCh == ':') { + yyCh = getChar(); + return Tok_ColonColon; + } + return Tok_Colon; + // Incomplete: '<' might be part of '<=' or of template syntax. + // The main intent of not completely ignoring it is to break + // parsing of things like std::cout << QObject::tr() as + // context std::cout::QObject (see Task 161106) + case '=': + yyCh = getChar(); + return Tok_Equals; + case '>': + case '<': + yyCh = getChar(); + return Tok_Other; + case '\'': + yyCh = getChar(); + if (yyCh == '\\') + yyCh = getChar(); + + forever { + if (yyCh == EOF || yyCh == '\n') { + qWarning("%s:%d: Unterminated C++ character\n", + qPrintable(yyFileName), yyLineNo); + break; + } + yyCh = getChar(); + if (yyCh == '\'') { + yyCh = getChar(); + break; + } + } + break; + case '{': + if (yyBraceDepth == 0) + yyBraceLineNo = yyCurLineNo; + yyBraceDepth++; + yyCh = getChar(); + return Tok_LeftBrace; + case '}': + if (yyBraceDepth == yyMinBraceDepth) { + if (!inDefine) + qWarning("%s:%d: Excess closing brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + // Avoid things getting messed up even more + yyCh = getChar(); + return Tok_Semicolon; + } + yyBraceDepth--; + yyCh = getChar(); + return Tok_RightBrace; + case '(': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; + yyParenDepth++; + yyCh = getChar(); + return Tok_LeftParen; + case ')': + if (yyParenDepth == 0) + qWarning("%s:%d: Excess closing parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyCurLineNo); + else + yyParenDepth--; + yyCh = getChar(); + return Tok_RightParen; + case ',': + yyCh = getChar(); + return Tok_Comma; + case ';': + yyCh = getChar(); + return Tok_Semicolon; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + QByteArray ba; + ba += yyCh; + yyCh = getChar(); + bool hex = yyCh == 'x'; + if (hex) { + ba += yyCh; + yyCh = getChar(); + } + while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { + ba += yyCh; + yyCh = getChar(); + } + bool ok; + yyInteger = ba.toLongLong(&ok); + if (ok) + return Tok_Integer; + break; + } + default: + yyCh = getChar(); + break; + } + } + } + return Tok_Eof; +} + +/* + The second part of this source file are namespace/class related + utilities for the third part. +*/ + +void CppParser::saveState(SavedState *state) +{ + state->namespaces = stringListifyNamespace(namespaces); + state->namespaceDepths = namespaceDepths; + state->functionContext = stringListifyNamespace(functionContext); + state->functionContextUnresolved = functionContextUnresolved; + state->pendingContext = pendingContext; +} + +void CppParser::loadState(const SavedState *state) +{ + namespaces = resolveNamespaces(state->namespaces); + namespaceDepths = state->namespaceDepths; + functionContext = resolveNamespaces(state->functionContext); + functionContextUnresolved = state->functionContextUnresolved; + pendingContext = state->pendingContext; +} + +bool ParseResults::detachNamespace(Namespace **that) +{ + if ((*that)->fileId != rootNamespace.fileId) { + Namespace *newThat = new Namespace; + *newThat = **that; + newThat->fileId = rootNamespace.fileId; + *that = newThat; + return true; + } + return false; +} + +Namespace *ParseResults::include(Namespace *that, const Namespace *other) +{ + Namespace *origThat = that; + foreach (Namespace *otherSub, other->children) { + if (Namespace *thisSub = that->children.value(otherSub->name)) { + // Don't make these cause a detach - it's best + // (though not necessary) if they are shared + thisSub->isClass |= otherSub->isClass; + thisSub->hasTrFunctions |= otherSub->hasTrFunctions; + thisSub->needsTrFunctions |= otherSub->needsTrFunctions; + thisSub->complained |= otherSub->complained; + + if (Namespace *newSub = include(thisSub, otherSub)) { + thisSub = newSub; + detachNamespace(&that); + that->children[thisSub->name] = thisSub; + } + } else { + detachNamespace(&that); + that->children[otherSub->name] = otherSub; + } + } + if ((that->aliases != other->aliases && !other->aliases.isEmpty()) + || (that->usings != other->usings && !other->usings.isEmpty())) { + detachNamespace(&that); + that->aliases.unite(other->aliases); + that->usings.unite(other->usings); + } + return (that != origThat) ? that : 0; +} + +void ParseResults::unite(const ParseResults *other) +{ + allIncludes.unite(other->allIncludes); + include(&rootNamespace, &other->rootNamespace); +} + +void CppParser::modifyNamespace(NamespaceList *namespaces) +{ + Namespace *pns = 0; + int i = namespaces->count(); + forever { + --i; + Namespace *ns = namespaces->at(i); + bool detached = results->detachNamespace(&ns); + if (pns) + ns->children[pns->name] = pns; + if (!detached) // Known to be true for root namespace + return; + pns = ns; + namespaces->replace(i, ns); + } +} + +QString CppParser::stringifyNamespace(const NamespaceList &namespaces) +{ + QString ret; + for (int i = 1; i < namespaces.count(); ++i) { + if (i > 1) + ret += QLatin1String("::"); + ret += namespaces.at(i)->name; + } + return ret; +} + +QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces) +{ + QStringList ret; + for (int i = 1; i < namespaces.count(); ++i) + ret << namespaces.at(i)->name; + return ret; +} + +// This function is called only with known-existing namespaces +NamespaceList CppParser::resolveNamespaces(const QStringList &segments) +{ + NamespaceList ret; + Namespace *ns = &results->rootNamespace; + ret << ns; + foreach (const QString &seg, segments) { + ns = ns->children.value(seg); + ret << ns; + } + return ret; +} + +bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsIdx, const QString &segment, + NamespaceList *resolved) +{ + const Namespace *ns = namespaces.at(nsIdx); + QMap<QString, Namespace *>::ConstIterator cnsi = ns->children.constFind(segment); + if (cnsi != ns->children.constEnd()) { + *resolved = namespaces.mid(0, nsIdx + 1); + *resolved << *cnsi; + return true; + } + QMap<QString, QStringList>::ConstIterator nsai = ns->aliases.constFind(segment); + if (nsai != ns->aliases.constEnd()) { + *resolved = resolveNamespaces(*nsai); + return true; + } + foreach (const QStringList &use, ns->usings) { + NamespaceList usedNs = resolveNamespaces(use); + if (qualifyOne(usedNs, usedNs.count() - 1, segment, resolved)) + return true; + } + return false; +} + +bool CppParser::fullyQualify(const NamespaceList &namespaces, const QStringList &segments, + bool isDeclaration, + NamespaceList *resolved, QStringList *unresolved) +{ + int nsIdx; + int initSegIdx; + + if (segments.first().isEmpty()) { + // fully qualified + if (segments.count() == 1) { + resolved->clear(); + *resolved << &results->rootNamespace; + return true; + } + initSegIdx = 1; + nsIdx = 0; + } else { + initSegIdx = 0; + nsIdx = namespaces.count() - 1; + } + + do { + if (qualifyOne(namespaces, nsIdx, segments[initSegIdx], resolved)) { + int segIdx = initSegIdx; + while (++segIdx < segments.count()) { + if (!qualifyOne(*resolved, resolved->count() - 1, segments[segIdx], resolved)) { + if (unresolved) + *unresolved = segments.mid(segIdx); + return false; + } + } + return true; + } + } while (!isDeclaration && --nsIdx >= 0); + resolved->clear(); + *resolved << &results->rootNamespace; + if (unresolved) + *unresolved = segments.mid(initSegIdx); + return false; +} + +void CppParser::enterNamespace(NamespaceList *namespaces, const QString &name) +{ + Namespace *ns = namespaces->last()->children.value(name); + if (!ns) { + ns = new Namespace; + ns->fileId = results->rootNamespace.fileId; + ns->name = name; + modifyNamespace(namespaces); + namespaces->last()->children[name] = ns; + } + *namespaces << ns; +} + +void CppParser::truncateNamespaces(NamespaceList *namespaces, int length) +{ + if (namespaces->count() > length) + namespaces->erase(namespaces->begin() + length, namespaces->end()); +} + +/* + Functions for processing include files. +*/ + +ParseResultHash &CppFiles::parsedFiles() +{ + static ParseResultHash parsed; + + return parsed; +} + +QSet<QString> &CppFiles::blacklistedFiles() +{ + static QSet<QString> blacklisted; + + return blacklisted; +} + +const ParseResults *CppFiles::getResults(const QString &cleanFile) +{ + ParseResultHash::ConstIterator it = parsedFiles().find(cleanFile); + if (it == parsedFiles().constEnd()) + return 0; + return *it; +} + +void CppFiles::setResults(const QString &cleanFile, const ParseResults *results) +{ + parsedFiles().insert(cleanFile, results); +} + +bool CppFiles::isBlacklisted(const QString &cleanFile) +{ + return blacklistedFiles().contains(cleanFile); +} + +void CppFiles::setBlacklisted(const QString &cleanFile) +{ + blacklistedFiles().insert(cleanFile); +} + +void CppParser::processInclude(const QString &file, ConversionData &cd, + QSet<QString> &inclusions) +{ + QString cleanFile = QDir::cleanPath(file); + + if (inclusions.contains(cleanFile)) { + qWarning("%s:%d: circular inclusion of %s\n", + qPrintable(yyFileName), yyLineNo, qPrintable(cleanFile)); + return; + } + + // If the #include is in any kind of namespace, has been blacklisted previously, + // or is not a header file (stdc++ extensionless or *.h*), then really include + // it. Otherwise it is safe to process it stand-alone and re-use the parsed + // namespace data for inclusion into other files. + bool isIndirect = false; + if (namespaces.count() == 1 && functionContext.count() == 1 + && functionContextUnresolved.isEmpty() && pendingContext.isEmpty() + && !CppFiles::isBlacklisted(cleanFile)) { + QString fileExt = QFileInfo(cleanFile).suffix(); + if (fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive)) { + + if (results->allIncludes.contains(cleanFile)) + return; + results->allIncludes.insert(cleanFile); + + if (const ParseResults *res = CppFiles::getResults(cleanFile)) { + results->unite(res); + return; + } + + isIndirect = true; + } + } + + QFile f(cleanFile); + if (!f.open(QIODevice::ReadOnly)) { + qWarning("%s:%d: Cannot open %s: %s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(cleanFile), qPrintable(f.errorString())); + return; + } + + QTextStream ts(&f); + ts.setCodec(yySourceCodec); + ts.setAutoDetectUnicode(true); + + inclusions.insert(cleanFile); + if (isIndirect) { + CppParser parser; + foreach (const QString &projectRoot, cd.m_projectRoots) + if (cleanFile.startsWith(projectRoot)) { + parser.setTranslator(new Translator); + break; + } + parser.setInput(ts, cleanFile); + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(cleanFile, parser.getResults()); + results->unite(parser.results); + } else { + CppParser parser(results); + parser.namespaces = namespaces; + parser.functionContext = functionContext; + parser.functionContextUnresolved = functionContextUnresolved; + parser.pendingContext = pendingContext; + parser.setInput(ts, cleanFile); + parser.parseInternal(cd, inclusions); + // Don't wreak havoc if not enough braces were found. + truncateNamespaces(&parser.namespaces, namespaces.count()); + truncateNamespaces(&parser.functionContext, functionContext.count()); + // Copy them back - the pointers might have changed. + namespaces = parser.namespaces; + functionContext = parser.functionContext; + // Avoid that messages obtained by direct scanning are used + CppFiles::setBlacklisted(cleanFile); + } + inclusions.remove(cleanFile); +} + +/* + The third part of this source file is the parser. It accomplishes + a very easy task: It finds all strings inside a tr() or translate() + call, and possibly finds out the context of the call. It supports + three cases: (1) the context is specified, as in + FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); + (2) the call appears within an inlined function; (3) the call + appears within a function defined outside the class definition. +*/ + +bool CppParser::match(uint t) +{ + bool matches = (yyTok == t); + if (matches) + yyTok = getToken(); + return matches; +} + +bool CppParser::matchString(QString *s) +{ + bool matches = (yyTok == Tok_String); + s->clear(); + while (yyTok == Tok_String) { + *s += yyString; + yyTok = getToken(); + } + return matches; +} + +bool CppParser::matchEncoding(bool *utf8) +{ + STRING(QApplication); + STRING(QCoreApplication); + STRING(UnicodeUTF8); + STRING(DefaultCodec); + STRING(CodecForTr); + + if (yyTok != Tok_Ident) + return false; + if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + yyTok = getToken(); + } + if (yyIdent == strUnicodeUTF8) { + *utf8 = true; + yyTok = getToken(); + return true; + } + if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { + *utf8 = false; + yyTok = getToken(); + return true; + } + return false; +} + +bool CppParser::matchInteger(qlonglong *number) +{ + bool matches = (yyTok == Tok_Integer); + if (matches) { + yyTok = getToken(); + *number = yyInteger; + } + return matches; +} + +bool CppParser::matchStringOrNull(QString *s) +{ + bool matches = matchString(s); + qlonglong num = 0; + if (!matches) + matches = matchInteger(&num); + return matches && num == 0; +} + +/* + * match any expression that can return a number, which can be + * 1. Literal number (e.g. '11') + * 2. simple identifier (e.g. 'm_count') + * 3. simple function call (e.g. 'size()' ) + * 4. function call on an object (e.g. 'list.size()') + * 5. function call on an object (e.g. 'list->size()') + * + * Other cases: + * size(2,4) + * list().size() + * list(a,b).size(2,4) + * etc... + */ +bool CppParser::matchExpression() +{ + if (match(Tok_Integer)) + return true; + + int parenlevel = 0; + while (match(Tok_Ident) || parenlevel > 0) { + if (yyTok == Tok_RightParen) { + if (parenlevel == 0) break; + --parenlevel; + yyTok = getToken(); + } else if (yyTok == Tok_LeftParen) { + yyTok = getToken(); + if (yyTok == Tok_RightParen) { + yyTok = getToken(); + } else { + ++parenlevel; + } + } else if (yyTok == Tok_Ident) { + continue; + } else if (yyTok == Tok_Arrow) { + yyTok = getToken(); + } else if (parenlevel == 0) { + return false; + } + } + return true; +} + +QString CppParser::transcode(const QString &str, bool utf8) +{ + static const char tab[] = "abfnrtv"; + static const char backTab[] = "\a\b\f\n\r\t\v"; + const QString in = (!utf8 || yySourceIsUnicode) + ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); + QString out; + + out.reserve(in.length()); + for (int i = 0; i < in.length();) { + ushort c = in[i++].unicode(); + if (c == '\\') { + if (i >= in.length()) + break; + c = in[i++].unicode(); + + if (c == '\n') + continue; + + if (c == 'x') { + QByteArray hex; + while (i < in.length() && isxdigit((c = in[i].unicode()))) { + hex += c; + i++; + } + out += hex.toUInt(0, 16); + } else if (c >= '0' && c < '8') { + QByteArray oct; + int n = 0; + oct += c; + while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') { + i++; + n++; + oct += c; + } + out += oct.toUInt(0, 8); + } else { + const char *p = strchr(tab, c); + out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); + } + } else { + out += c; + } + } + return out; +} + +void CppParser::recordMessage( + int line, const QString &context, const QString &text, const QString &comment, + const QString &extracomment, bool utf8, bool plural) +{ + TranslatorMessage msg( + transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), + yyFileName, line, QStringList(), + TranslatorMessage::Unfinished, plural); + msg.setExtraComment(transcode(extracomment.simplified(), utf8)); + if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) + msg.setUtf8(true); + results->tor->append(msg); +} + +void CppParser::parse(const QString &initialContext, ConversionData &cd, + QSet<QString> &inclusions) +{ + if (results->tor) + yyCodecIsUtf8 = (results->tor->codecName() == "UTF-8"); + + namespaces << &results->rootNamespace; + functionContext = namespaces; + functionContextUnresolved = initialContext; + + parseInternal(cd, inclusions); +} + +void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions) +{ + static QString strColons(QLatin1String("::")); + + QString context; + QString text; + QString comment; + QString extracomment; + QString prefix; +#ifdef DIAGNOSE_RETRANSLATABILITY + QString functionName; +#endif + int line; + bool utf8; + bool yyTokColonSeen = false; // Start of c'tor's initializer list + + yyCh = getChar(); + yyTok = getToken(); + while (yyTok != Tok_Eof) { + //qDebug() << "TOKEN: " << yyTok; + switch (yyTok) { + case Tok_QuotedInclude: { + text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + yyTok = getToken(); + break; + } + } + /* fall through */ + case Tok_AngledInclude: { + QStringList cSources = cd.m_allCSources.values(yyString); + if (!cSources.isEmpty()) { + foreach (const QString &cSource, cSources) + processInclude(cSource, cd, inclusions); + goto incOk; + } + foreach (const QString &incPath, cd.m_includePath) { + text = QDir(incPath).absoluteFilePath(yyString); + if (QFileInfo(text).isFile()) { + processInclude(text, cd, inclusions); + goto incOk; + } + } + incOk: + yyTok = getToken(); + break; + } + case Tok_friend: + yyTok = getToken(); + // Ensure that these don't end up being interpreted as forward declarations + // (they are forwards, but with different namespacing). + if (yyTok == Tok_class) + yyTok = getToken(); + break; + case Tok_class: + yyTokColonSeen = false; + /* + Partial support for inlined functions. + */ + yyTok = getToken(); + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) { + QStringList fct; + do { + /* + This code should execute only once, but we play + safe with impure definitions such as + 'class Q_EXPORT QMessageBox', in which case + 'QMessageBox' is the class name, not 'Q_EXPORT'. + */ + fct = QStringList(yyIdent); + yyTok = getToken(); + } while (yyTok == Tok_Ident); + while (yyTok == Tok_ColonColon) { + yyTok = getToken(); + if (yyTok != Tok_Ident) + break; // Oops ... + fct += yyIdent; + yyTok = getToken(); + } + if (fct.count() > 1) { + // Forward-declared class definitions can be namespaced + NamespaceList nsl; + if (!fullyQualify(namespaces, fct, true, &nsl, 0)) { + qWarning("%s:%d: Ignoring definition of undeclared qualified class\n", + qPrintable(yyFileName), yyLineNo); + break; + } + namespaceDepths.push(namespaces.count()); + namespaces = nsl; + } else { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, fct.first()); + } + namespaces.last()->isClass = true; + + while (yyTok == Tok_Comment) + yyTok = getToken(); + if (yyTok == Tok_Colon) { + // Skip any token until '{' since lupdate might do things wrong if it finds + // a '::' token here. + do { + yyTok = getToken(); + } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); + } else { + if (yyTok != Tok_LeftBrace) { + // Obviously a forward decl + truncateNamespaces(&namespaces, namespaceDepths.pop()); + break; + } + } + + functionContext = namespaces; + functionContextUnresolved.clear(); // Pointless + prospectiveContext.clear(); + pendingContext.clear(); + } + break; + case Tok_namespace: + yyTokColonSeen = false; + yyTok = getToken(); + if (yyTok == Tok_Ident) { + QString ns = yyIdent; + yyTok = getToken(); + if (yyTok == Tok_LeftBrace) { + namespaceDepths.push(namespaces.count()); + enterNamespace(&namespaces, ns); + yyTok = getToken(); + } else if (yyTok == Tok_Equals) { + // e.g. namespace Is = OuterSpace::InnerSpace; + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(ns, stringListifyNamespace(nsl)); + } + } + } else if (yyTok == Tok_LeftBrace) { + // Anonymous namespace + namespaceDepths.push(namespaces.count()); + yyTok = getToken(); + } + break; + case Tok_using: + yyTok = getToken(); + if (yyTok == Tok_namespace) { + QStringList fullName; + yyTok = getToken(); + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(namespaces, fullName, false, &nsl, &unresolved)) { + modifyNamespace(&namespaces); + namespaces.last()->usings.insert(stringListifyNamespace(nsl)); + } + } else { + QStringList fullName; + if (yyTok == Tok_ColonColon) + fullName.append(QString()); + while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { + if (yyTok == Tok_Ident) + fullName.append(yyIdent); + yyTok = getToken(); + } + if (fullName.isEmpty()) + break; + NamespaceList nsl; + if (fullyQualify(namespaces, fullName, false, &nsl, 0)) { + modifyNamespace(&namespaces); + namespaces.last()->aliases.insert(nsl.last()->name, stringListifyNamespace(nsl)); + } + } + break; + case Tok_tr: + case Tok_trUtf8: + if (!results->tor) + goto case_default; + utf8 = (yyTok == Tok_trUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { + comment.clear(); + bool plural = false; + + if (match(Tok_RightParen)) { + // no comment + } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment + if (match(Tok_RightParen)) { + // ok, + } else if (match(Tok_Comma)) { + plural = true; + } + } + if (!pendingContext.isEmpty()) { + QStringList unresolved; + if (!fullyQualify(namespaces, pendingContext.split(strColons), true, + &functionContext, &unresolved)) { + functionContextUnresolved = unresolved.join(strColons); + qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext)), + qPrintable(unresolved.first())); + } + pendingContext.clear(); + } + if (prefix.isEmpty()) { + if (functionContextUnresolved.isEmpty()) { + int idx = functionContext.length(); + if (idx < 2) { + qWarning("%s:%d: tr() cannot be called without context\n", + qPrintable(yyFileName), yyLineNo); + break; + } + while (!functionContext.at(idx - 1)->hasTrFunctions) { + if (idx == 1 || !functionContext.at(idx - 2)->isClass) { + idx = functionContext.length(); + if (!functionContext.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(functionContext))); + functionContext.last()->complained = true; + } + break; + } + --idx; + } + context.clear(); + for (int i = 1;;) { + context += functionContext.at(i)->name; + if (++i == idx) + break; + context += strColons; + } + } else { + context = (stringListifyNamespace(functionContext) + << functionContextUnresolved).join(strColons); + } + } else { +#ifdef DIAGNOSE_RETRANSLATABILITY + int last = prefix.lastIndexOf(strColons); + QString className = prefix.mid(last == -1 ? 0 : last + 2); + if (!className.isEmpty() && className == functionName) { + qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", + qPrintable(yyFileName), yyLineNo, + className.constData(), functionName.constData()); + } +#endif + prefix.chop(2); + NamespaceList nsl; + QStringList unresolved; + if (fullyQualify(functionContext, prefix.split(strColons), false, &nsl, &unresolved)) { + if (!nsl.last()->hasTrFunctions && !nsl.last()->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(nsl))); + nsl.last()->complained = true; + } + context = stringifyNamespace(nsl); + } else { + context = (stringListifyNamespace(nsl) + unresolved).join(strColons); + } + prefix.clear(); + } + + recordMessage(line, context, text, comment, extracomment, utf8, plural); + } + extracomment.clear(); + break; + case Tok_translateUtf8: + case Tok_translate: + if (!results->tor) + goto case_default; + utf8 = (yyTok == Tok_translateUtf8); + line = yyLineNo; + yyTok = getToken(); + if (match(Tok_LeftParen) + && matchString(&context) + && match(Tok_Comma) + && matchString(&text) && !text.isEmpty()) + { + comment.clear(); + bool plural = false; + if (!match(Tok_RightParen)) { + // look for comment + if (match(Tok_Comma) && matchStringOrNull(&comment)) { + if (!match(Tok_RightParen)) { + // look for encoding + if (match(Tok_Comma)) { + if (matchEncoding(&utf8)) { + if (!match(Tok_RightParen)) { + // look for the plural quantifier, + // this can be a number, an identifier or + // a function call, + // so for simplicity we mark it as plural if + // we know we have a comma instead of an + // right parentheses. + plural = match(Tok_Comma); + } + } else { + // This can be a QTranslator::translate("context", + // "source", "comment", n) plural translation + if (matchExpression() && match(Tok_RightParen)) { + plural = true; + } else { + break; + } + } + } else { + break; + } + } + } else { + break; + } + } + recordMessage(line, context, text, comment, extracomment, utf8, plural); + } + extracomment.clear(); + break; + case Tok_Q_DECLARE_TR_FUNCTIONS: + case Tok_Q_OBJECT: + namespaces.last()->hasTrFunctions = true; + yyTok = getToken(); + break; + case Tok_Ident: + prefix += yyIdent; + yyTok = getToken(); + if (yyTok != Tok_ColonColon) { + prefix.clear(); + if (yyTok == Tok_Ident && !yyParenDepth) + prospectiveContext.clear(); + } + break; + case Tok_Comment: + if (!results->tor) + goto case_default; + if (yyComment.startsWith(QLatin1Char(':'))) { + yyComment.remove(0, 1); + extracomment.append(yyComment); + } else { + comment = yyComment.simplified(); + if (comment.startsWith(QLatin1String(MagicComment))) { + comment.remove(0, sizeof(MagicComment) - 1); + int k = comment.indexOf(QLatin1Char(' ')); + if (k == -1) { + context = comment; + } else { + context = comment.left(k); + comment.remove(0, k + 1); + recordMessage(yyLineNo, context, QString(), comment, extracomment, false, false); + } + } + } + yyTok = getToken(); + break; + case Tok_Arrow: + yyTok = getToken(); + if (yyTok == Tok_tr || yyTok == Tok_trUtf8) + qWarning("%s:%d: Cannot invoke tr() like this\n", + qPrintable(yyFileName), yyLineNo); + break; + case Tok_ColonColon: + if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen) + prospectiveContext = prefix; + prefix += strColons; + yyTok = getToken(); +#ifdef DIAGNOSE_RETRANSLATABILITY + if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + functionName = yyIdent; +#endif + break; + case Tok_RightBrace: + if (yyBraceDepth + 1 == namespaceDepths.count()) { + // class or namespace + Namespace *ns = namespaces.last(); + if (ns->needsTrFunctions && !ns->hasTrFunctions && !ns->complained) { + qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", + qPrintable(yyFileName), yyLineNo, + qPrintable(stringifyNamespace(namespaces))); + ns->complained = true; + } + truncateNamespaces(&namespaces, namespaceDepths.pop()); + } + if (yyBraceDepth == namespaceDepths.count()) { + // function, class or namespace + if (!yyBraceDepth && !directInclude) { + truncateNamespaces(&functionContext, 1); + functionContextUnresolved = cd.m_defaultContext; + } else { + functionContext = namespaces; + functionContextUnresolved.clear(); + } + pendingContext.clear(); + } + // fallthrough + case Tok_Semicolon: + prospectiveContext.clear(); + prefix.clear(); + extracomment.clear(); + yyTokColonSeen = false; + yyTok = getToken(); + break; + case Tok_Colon: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) + pendingContext = prospectiveContext; + yyTokColonSeen = true; + yyTok = getToken(); + break; + case Tok_LeftBrace: + if (!prospectiveContext.isEmpty() + && yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0) + pendingContext = prospectiveContext; + // fallthrough + case Tok_LeftParen: + case Tok_RightParen: + yyTokColonSeen = false; + yyTok = getToken(); + break; + default: + if (!yyParenDepth) + prospectiveContext.clear(); + case_default: + yyTok = getToken(); + break; + } + } + + if (yyBraceDepth != 0) + qWarning("%s:%d: Unbalanced opening brace in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyBraceLineNo); + else if (yyParenDepth != 0) + qWarning("%s:%d: Unbalanced opening parenthesis in C++ code" + " (or abuse of the C++ preprocessor)\n", + qPrintable(yyFileName), yyParenLineNo); +} + +/* + Fetches tr() calls in C++ code in UI files (inside "<function>" + tag). This mechanism is obsolete. +*/ +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) +{ + CppParser parser; + parser.setInput(in); + ConversionData cd; + QSet<QString> inclusions; + parser.setTranslator(&translator); + parser.parse(context, cd, inclusions); + parser.deleteResults(); +} + +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd) +{ + QByteArray codecName = cd.m_codecForSource.isEmpty() + ? translator.codecName() : cd.m_codecForSource; + QTextCodec *codec = QTextCodec::codecForName(codecName); + + foreach (const QString filename, filenames) { + if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename)) + continue; + + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + continue; + } + + CppParser parser; + QTextStream ts(&file); + ts.setCodec(codec); + ts.setAutoDetectUnicode(true); + if (ts.codec()->name() == "UTF-16") + translator.setCodecName("System"); + parser.setInput(ts, filename); + Translator *tor = new Translator; + tor->setCodecName(translator.codecName()); + parser.setTranslator(tor); + QSet<QString> inclusions; + parser.parse(cd.m_defaultContext, cd, inclusions); + CppFiles::setResults(filename, parser.getResults()); + } + + foreach (const QString filename, filenames) + if (!CppFiles::isBlacklisted(filename)) + if (Translator *tor = CppFiles::getResults(filename)->tor) + foreach (const TranslatorMessage &msg, tor->messages()) + translator.extend(msg); +} + +QT_END_NAMESPACE diff --git a/tools/linguist/shared/java.cpp b/tools/linguist/lupdate/java.cpp index 912a8d7..c8dbe5b 100644 --- a/tools/linguist/shared/java.cpp +++ b/tools/linguist/lupdate/java.cpp @@ -39,7 +39,9 @@ ** ****************************************************************************/ -#include "translator.h" +#include "lupdate.h" + +#include <translator.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -600,14 +602,18 @@ static void parse( Translator *tor ) } -bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd) { - //void LupdateApplication::fetchtr_java( const QString &fileName, Translator *tor, - //const QString &defaultContext, bool mustExist, const QByteArray &codecForSource ) + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } yyDefaultContext = cd.m_defaultContext; yyInPos = -1; - yyFileName = cd.m_sourceFileName; + yyFileName = filename; yyPackage.clear(); yyScope.clear(); yyTok = -1; @@ -615,7 +621,7 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) yyCurLineNo = 0; yyParenLineNo = 1; - QTextStream ts(&dev); + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -625,7 +631,7 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) ts.setAutoDetectUnicode(true); yyInStr = ts.readAll(); yyInPos = 0; - yyFileName = cd.m_sourceFileName; + yyFileName = filename; yyCurLineNo = 1; yyParenLineNo = 1; yyCh = getChar(); @@ -637,19 +643,4 @@ bool loadJava(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -int initJava() -{ - Translator::FileFormat format; - format.extension = QLatin1String("java"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Java source files"); - format.loader = &loadJava; - format.saver = 0; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initJava) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/translatortools.h b/tools/linguist/lupdate/lupdate.h index 9eaf024..2f98643 100644 --- a/tools/linguist/shared/translatortools.h +++ b/tools/linguist/lupdate/lupdate.h @@ -48,7 +48,9 @@ QT_BEGIN_NAMESPACE +class ConversionData; class QString; +class QStringList; class Translator; class TranslatorMessage; @@ -72,6 +74,12 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateOptions) Translator merge(const Translator &tor, const Translator &virginTor, UpdateOptions options, QString &err); +void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context); +void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd); +bool loadJava(Translator &translator, const QString &filename, ConversionData &cd); +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd); +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd); + QT_END_NAMESPACE #endif diff --git a/tools/linguist/lupdate/lupdate.pro b/tools/linguist/lupdate/lupdate.pro index b05a4ef..ccc2d47 100644 --- a/tools/linguist/lupdate/lupdate.pro +++ b/tools/linguist/lupdate/lupdate.pro @@ -14,9 +14,20 @@ build_all:!build_pass { include(../shared/formats.pri) include(../shared/proparser.pri) -include(../shared/translatortools.pri) -SOURCES += main.cpp +SOURCES += \ + main.cpp \ + merge.cpp \ + ../shared/simtexth.cpp \ + \ + cpp.cpp \ + java.cpp \ + qscript.cpp \ + ui.cpp + +HEADERS += \ + lupdate.h \ + ../shared/simtexth.h DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII diff --git a/tools/linguist/lupdate/main.cpp b/tools/linguist/lupdate/main.cpp index b537b6e..8a70b55 100644 --- a/tools/linguist/lupdate/main.cpp +++ b/tools/linguist/lupdate/main.cpp @@ -39,9 +39,10 @@ ** ****************************************************************************/ -#include "translator.h" -#include "translatortools.h" -#include "profileevaluator.h" +#include "lupdate.h" + +#include <translator.h> +#include <profileevaluator.h> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> @@ -83,12 +84,12 @@ static void printUsage() { printOut(QObject::tr( "Usage:\n" - " lupdate [options] [project-file]\n" + " lupdate [options] [project-file]...\n" " lupdate [options] [source-file|path]... -ts ts-files\n\n" - "lupdate is part of Qt's Linguist tool chain. It can be used as a\n" - "stand-alone tool to create XML based translations files in the .ts\n" - "format from translatable messages in C++ and Java source code.\n\n" - "lupdate can also merge such messages into existing .ts files.\n\n" + "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n" + "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n" + "Extracted messages are stored in textual translation source files (typically\n" + "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n" "Options:\n" " -help Display this information and exit.\n" " -no-obsolete\n" @@ -106,7 +107,10 @@ static void printUsage() " -no-recursive\n" " Do not recursively scan the following directories.\n" " -recursive\n" - " Recursively scan the following directories.\n" + " Recursively scan the following directories (default).\n" + " -I <includepath> or -I<includepath>\n" + " Additional location to look for include files.\n" + " May be specified multiple times.\n" " -locations {absolute|relative|none}\n" " Specify/override how source code references are saved in ts files.\n" " Default is absolute.\n" @@ -216,7 +220,10 @@ int main(int argc, char **argv) QByteArray codecForSource; QStringList tsFileNames; QStringList proFiles; + QMultiHash<QString, QString> allCSources; + QSet<QString> projectRoots; QStringList sourceFiles; + QStringList includePath; QString targetLanguage; QString sourceLanguage; @@ -343,6 +350,18 @@ int main(int argc, char **argv) proFiles += args[i]; numFiles++; continue; + } else if (arg.startsWith(QLatin1String("-I"))) { + if (arg.length() == 2) { + ++i; + if (i == argc) { + qWarning("The -I option should be followed by a path."); + return 1; + } + includePath += args[i]; + } else { + includePath += args[i].mid(2); + } + continue; } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) { qWarning("Unrecognized option '%s'", qPrintable(arg)); return 1; @@ -387,33 +406,46 @@ int main(int argc, char **argv) if (options & Verbose) printOut(QObject::tr("Scanning directory '%1'...").arg(arg)); QDir dir = QDir(fi.filePath()); + projectRoots.insert(dir.absolutePath() + QLatin1Char('/')); if (extensionsNameFilters.isEmpty()) { - extensions = extensions.trimmed(); - // Remove the potential dot in front of each extension - if (extensions.startsWith(QLatin1Char('.'))) - extensions.remove(0,1); - extensions.replace(QLatin1String(",."), QLatin1String(",")); - - extensions.insert(0, QLatin1String("*.")); - extensions.replace(QLatin1Char(','), QLatin1String(",*.")); - extensionsNameFilters = extensions.split(QLatin1Char(',')); + foreach (QString ext, extensions.split(QLatin1Char(','))) { + ext = ext.trimmed(); + if (ext.startsWith(QLatin1Char('.'))) + ext.remove(0,1); + ext.insert(0, QLatin1String("*.")); + extensionsNameFilters << ext; + } } QDir::Filters filters = QDir::Files | QDir::NoSymLinks; QFileInfoList fileinfolist; recursiveFileInfoList(dir, extensionsNameFilters, filters, recursiveScan, &fileinfolist); - QFileInfoList::iterator ii; - QString fn; - for (ii = fileinfolist.begin(); ii != fileinfolist.end(); ++ii) { - // Make sure the path separator is stored with '/' in the ts file - sourceFiles << ii->canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + int scanRootLen = dir.absolutePath().length(); + foreach (const QFileInfo &fi, fileinfolist) { + QString fn = QDir::cleanPath(fi.absoluteFilePath()); + sourceFiles << fn; + + if (!fn.endsWith(QLatin1String(".java")) + && !fn.endsWith(QLatin1String(".ui")) + && !fn.endsWith(QLatin1String(".js")) + && !fn.endsWith(QLatin1String(".qs"))) { + int offset = 0; + int depth = 0; + do { + offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1); + QString ffn = fn.mid(offset + 1); + allCSources.insert(ffn, fn); + } while (++depth < 3 && offset > scanRootLen); + } } } else { - sourceFiles << fi.canonicalFilePath().replace(QLatin1Char('\\'), QLatin1Char('/')); + sourceFiles << QDir::cleanPath(fi.absoluteFilePath());; } } } // for args + foreach (const QString &proFile, proFiles) + projectRoots.insert(QDir::cleanPath(QFileInfo(proFile).absolutePath()) + QLatin1Char('/')); bool firstPass = true; bool fail = false; @@ -421,6 +453,9 @@ int main(int argc, char **argv) ConversionData cd; cd.m_defaultContext = defaultContext; cd.m_noUiLines = options & NoUiLines; + cd.m_projectRoots = projectRoots; + cd.m_includePath = includePath; + cd.m_allCSources = allCSources; QStringList tsFiles = tsFileNames; if (proFiles.count() > 0) { @@ -450,6 +485,8 @@ int main(int argc, char **argv) continue; } + cd.m_includePath += visitor.values(QLatin1String("INCLUDEPATH")); + evaluateProFile(visitor, &variables); sourceFiles = variables.value("SOURCES"); @@ -472,27 +509,19 @@ int main(int argc, char **argv) tsFiles += variables.value("TRANSLATIONS"); } + QStringList sourceFilesCpp; for (QStringList::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { - if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) { - cd.m_sourceFileName = *it; - fetchedTor.load(*it, cd, QLatin1String("java")); - //fetchtr_java(*it, &fetchedTor, defaultContext, true, codecForSource); - } - else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("ui")); - //fetchedTor.load(*it + QLatin1String(".h"), cd, QLatin1String("cpp")); - //fetchtr_ui(*it, &fetchedTor, defaultContext, true); - //fetchtr_cpp(*it + QLatin1String(".h"), &fetchedTor, - // defaultContext, false, codecForSource); - } + if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) + loadJava(fetchedTor, *it, cd); + else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)) + loadUI(fetchedTor, *it, cd); else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) - || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) { - fetchedTor.load(*it, cd, QLatin1String("js")); - } else { - fetchedTor.load(*it, cd, QLatin1String("cpp")); - //fetchtr_cpp(*it, &fetchedTor, defaultContext, true, codecForSource); - } + || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) + loadQScript(fetchedTor, *it, cd); + else + sourceFilesCpp << *it; } + loadCPP(fetchedTor, sourceFilesCpp, cd); if (!cd.error().isEmpty()) printOut(cd.error()); diff --git a/tools/linguist/shared/translatortools.cpp b/tools/linguist/lupdate/merge.cpp index 96301d5..c4f4448 100644 --- a/tools/linguist/shared/translatortools.cpp +++ b/tools/linguist/lupdate/merge.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "translatortools.h" +#include "lupdate.h" #include "simtexth.h" #include "translator.h" diff --git a/tools/linguist/shared/qscript.cpp b/tools/linguist/lupdate/qscript.cpp index 7377cba..64adde6 100644 --- a/tools/linguist/shared/qscript.cpp +++ b/tools/linguist/lupdate/qscript.cpp @@ -752,7 +752,7 @@ const int QScriptGrammar::action_check [] = { #define Q_SCRIPT_REGEXPLITERAL_RULE2 8 -#include "translator.h" +#include <translator.h> #include <QtCore/qdebug.h> #include <QtCore/qnumeric.h> @@ -2356,9 +2356,15 @@ case 94: { } -bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { - QTextStream ts(&dev); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -2371,8 +2377,8 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) QScript::Lexer lexer; lexer.setCode(code, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) { - qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(), + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), qPrintable(parser.errorMessage())); return false; } @@ -2382,27 +2388,4 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .js files")); - return false; -} - -int initQScript() -{ - Translator::FileFormat format; - format.extension = QLatin1String("js"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Qt Script source files"); - format.loader = &loadQScript; - format.saver = &saveQScript; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initQScript) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/qscript.g b/tools/linguist/lupdate/qscript.g index 8d33277..563974a 100644 --- a/tools/linguist/shared/qscript.g +++ b/tools/linguist/lupdate/qscript.g @@ -42,6 +42,10 @@ -- ---------------------------------------------------------------------------- +-------------------------------------------------------------------------------- +-- Process with "qlalr --no-debug --no-lines qscript.g" to update qscript.cpp -- +-------------------------------------------------------------------------------- + %parser QScriptGrammar %merged_output qscript.cpp %expect 3 @@ -81,7 +85,7 @@ %start Program /. -#include "translator.h" +#include <translator.h> #include <QtCore/qdebug.h> #include <QtCore/qnumeric.h> @@ -1986,9 +1990,15 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ; } -bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadQScript(Translator &translator, const QString &filename, ConversionData &cd) { - QTextStream ts(&dev); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QTextStream ts(&file); QByteArray codecName; if (!cd.m_codecForSource.isEmpty()) codecName = cd.m_codecForSource; @@ -2001,8 +2011,8 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) QScript::Lexer lexer; lexer.setCode(code, /*lineNumber=*/1); QScriptParser parser; - if (!parser.parse(&lexer, cd.m_sourceFileName, &translator)) { - qWarning("%s:%d: %s", qPrintable(cd.m_sourceFileName), parser.errorLineNumber(), + if (!parser.parse(&lexer, filename, &translator)) { + qWarning("%s:%d: %s", qPrintable(filename), parser.errorLineNumber(), qPrintable(parser.errorMessage())); return false; } @@ -2012,28 +2022,5 @@ bool loadQScript(Translator &translator, QIODevice &dev, ConversionData &cd) return true; } -bool saveQScript(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .js files")); - return false; -} - -int initQScript() -{ - Translator::FileFormat format; - format.extension = QLatin1String("js"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("Qt Script source files"); - format.loader = &loadQScript; - format.saver = &saveQScript; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initQScript) - QT_END_NAMESPACE ./ diff --git a/tools/linguist/shared/ui.cpp b/tools/linguist/lupdate/ui.cpp index ff98a90..935cac4 100644 --- a/tools/linguist/shared/ui.cpp +++ b/tools/linguist/lupdate/ui.cpp @@ -39,7 +39,9 @@ ** ****************************************************************************/ -#include "translator.h" +#include "lupdate.h" + +#include <translator.h> #include <QtCore/QDebug> #include <QtCore/QFile> @@ -53,9 +55,6 @@ QT_BEGIN_NAMESPACE -// in cpp.cpp -void fetchtrInlinedCpp(const QString &in, Translator &tor, const QString &context); - class UiReader : public QXmlDefaultHandler { public: @@ -157,7 +156,7 @@ bool UiReader::fatalError(const QXmlParseException &exception) msg.sprintf("XML error: Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().toLatin1().data()); - m_cd.appendError(msg); + m_cd.appendError(msg); return false; } @@ -177,9 +176,16 @@ void UiReader::flush() m_extracomment.clear(); } -bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) +bool loadUI(Translator &translator, const QString &filename, ConversionData &cd) { - QXmlInputSource in(&dev); + cd.m_sourceFileName = filename; + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) { + cd.appendError(QString::fromLatin1("Cannot open %1: %2") + .arg(filename, file.errorString())); + return false; + } + QXmlInputSource in(&file); QXmlSimpleReader reader; reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), false); reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), true); @@ -196,39 +202,4 @@ bool loadUI(Translator &translator, QIODevice &dev, ConversionData &cd) return result; } -bool saveUI(const Translator &translator, QIODevice &dev, ConversionData &cd) -{ - Q_UNUSED(dev); - Q_UNUSED(translator); - cd.appendError(QLatin1String("Cannot save .ui files")); - return false; -} - -int initUI() -{ - Translator::FileFormat format; - - // "real" Qt Designer - format.extension = QLatin1String("ui"); - format.description = QObject::tr("Qt Designer form files"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.loader = &loadUI; - format.saver = &saveUI; - Translator::registerFileFormat(format); - - // same for jambi - format.extension = QLatin1String("jui"); - format.description = QObject::tr("Qt Jambi form files"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.loader = &loadUI; - format.saver = &saveUI; - Translator::registerFileFormat(format); - - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initUI) - QT_END_NAMESPACE diff --git a/tools/linguist/shared/cpp.cpp b/tools/linguist/shared/cpp.cpp deleted file mode 100644 index 2e137cf..0000000 --- a/tools/linguist/shared/cpp.cpp +++ /dev/null @@ -1,1081 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the Qt Linguist of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "translator.h" - -#include <QtCore/QDebug> -#include <QtCore/QStack> -#include <QtCore/QString> -#include <QtCore/QTextCodec> -#include <QtCore/QTextStream> - -#include <ctype.h> // for isXXX() - -QT_BEGIN_NAMESPACE - -/* qmake ignore Q_OBJECT */ - -static const char MagicComment[] = "TRANSLATOR "; - -static QSet<QString> needs_Q_OBJECT; -static QSet<QString> lacks_Q_OBJECT; - -static const int yyIdentMaxLen = 128; -static const int yyCommentMaxLen = 65536; -static const int yyStringMaxLen = 65536; - -#define STRINGIFY_INTERNAL(x) #x -#define STRINGIFY(x) STRINGIFY_INTERNAL(x) -#define STRING(s) static QString str##s(QLatin1String(STRINGIFY(s))) - -//#define DIAGNOSE_RETRANSLATABILITY -/* - The first part of this source file is the C++ tokenizer. We skip - most of C++; the only tokens that interest us are defined here. - Thus, the code fragment - - int main() - { - printf("Hello, world!\n"); - return 0; - } - - is broken down into the following tokens (Tok_ omitted): - - Ident Ident LeftParen RightParen - LeftBrace - Ident LeftParen String RightParen Semicolon - return Semicolon - RightBrace. - - The 0 doesn't produce any token. -*/ - -enum { - Tok_Eof, Tok_class, Tok_namespace, Tok_return, - Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, - Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, - Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, - Tok_Equals, - Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, - Tok_Integer = 40, - Tok_Other -}; - -/* - The tokenizer maintains the following global variables. The names - should be self-explanatory. -*/ -static QString yyFileName; -static int yyCh; -static bool yyCodecIsUtf8; -static bool yyForceUtf8; -static QString yyIdent; -static QString yyComment; -static QString yyString; -static qlonglong yyInteger; -static QStack<int> yySavedBraceDepth; -static QStack<int> yySavedParenDepth; -static int yyBraceDepth; -static int yyParenDepth; -static int yyLineNo; -static int yyCurLineNo; -static int yyBraceLineNo; -static int yyParenLineNo; -static bool yyTokColonSeen = false; - -// the string to read from and current position in the string -static QTextCodec *yySourceCodec; -static bool yySourceIsUnicode; -static QString yyInStr; -static int yyInPos; - -static uint getChar() -{ - forever { - if (yyInPos >= yyInStr.size()) - return EOF; - uint c = yyInStr[yyInPos++].unicode(); - if (c == '\\' && yyInPos < yyInStr.size() && yyInStr[yyInPos].unicode() == '\n') { - ++yyCurLineNo; - ++yyInPos; - continue; - } - if (c == '\n') - ++yyCurLineNo; - return c; - } -} - -static uint getToken() -{ - yyIdent.clear(); - yyComment.clear(); - yyString.clear(); - - while (yyCh != EOF) { - yyLineNo = yyCurLineNo; - - if (isalpha(yyCh) || yyCh == '_') { - do { - yyIdent += yyCh; - yyCh = getChar(); - } while (isalnum(yyCh) || yyCh == '_'); - - //qDebug() << "IDENT: " << yyIdent; - - switch (yyIdent.at(0).unicode()) { - case 'Q': - if (yyIdent == QLatin1String("Q_OBJECT")) - return Tok_Q_OBJECT; - if (yyIdent == QLatin1String("Q_DECLARE_TR_FUNCTIONS")) - return Tok_Q_DECLARE_TR_FUNCTIONS; - if (yyIdent == QLatin1String("QT_TR_NOOP")) - return Tok_tr; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP")) - return Tok_translate; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3")) - return Tok_translate; - if (yyIdent == QLatin1String("QT_TR_NOOP_UTF8")) - return Tok_trUtf8; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP_UTF8")) - return Tok_translateUtf8; - if (yyIdent == QLatin1String("QT_TRANSLATE_NOOP3_UTF8")) - return Tok_translateUtf8; - break; - case 'T': - // TR() for when all else fails - if (yyIdent.compare(QLatin1String("TR"), Qt::CaseInsensitive) == 0) { - return Tok_tr; - } - break; - case 'c': - if (yyIdent == QLatin1String("class")) - return Tok_class; - break; - case 'f': - /* - QTranslator::findMessage() has the same parameters as - QApplication::translate(). - */ - if (yyIdent == QLatin1String("findMessage")) - return Tok_translate; - break; - case 'n': - if (yyIdent == QLatin1String("namespace")) - return Tok_namespace; - break; - case 'r': - if (yyIdent == QLatin1String("return")) - return Tok_return; - break; - case 's': - if (yyIdent == QLatin1String("struct")) - return Tok_class; - break; - case 't': - if (yyIdent == QLatin1String("tr")) { - return Tok_tr; - } - if (yyIdent == QLatin1String("trUtf8")) { - return Tok_trUtf8; - } - if (yyIdent == QLatin1String("translate")) { - return Tok_translate; - } - } - return Tok_Ident; - } else { - switch (yyCh) { - case '#': - /* - Early versions of lupdate complained about - unbalanced braces in the following code: - - #ifdef ALPHA - while (beta) { - #else - while (gamma) { - #endif - delta; - } - - The code contains, indeed, two opening braces for - one closing brace; yet there's no reason to panic. - - The solution is to remember yyBraceDepth as it was - when #if, #ifdef or #ifndef was met, and to set - yyBraceDepth to that value when meeting #elif or - #else. - */ - do { - yyCh = getChar(); - } while (isspace(yyCh) && yyCh != '\n'); - - switch (yyCh) { - case 'i': - yyCh = getChar(); - if (yyCh == 'f') { - // if, ifdef, ifndef - yySavedBraceDepth.push(yyBraceDepth); - yySavedParenDepth.push(yyParenDepth); - } - break; - case 'e': - yyCh = getChar(); - if (yyCh == 'l') { - // elif, else - if (!yySavedBraceDepth.isEmpty()) { - yyBraceDepth = yySavedBraceDepth.top(); - yyParenDepth = yySavedParenDepth.top(); - } - } else if (yyCh == 'n') { - // endif - if (!yySavedBraceDepth.isEmpty()) { - yySavedBraceDepth.pop(); - yySavedParenDepth.pop(); - } - } - } - while (isalnum(yyCh) || yyCh == '_') - yyCh = getChar(); - break; - case '/': - yyCh = getChar(); - if (yyCh == '/') { - do { - yyCh = getChar(); - if (yyCh == EOF) - break; - yyComment.append(yyCh); - } while (yyCh != '\n'); - } else if (yyCh == '*') { - bool metAster = false; - bool metAsterSlash = false; - - while (!metAsterSlash) { - yyCh = getChar(); - if (yyCh == EOF) { - qWarning("%s: Unterminated C++ comment starting at" - " line %d\n", - qPrintable(yyFileName), yyLineNo); - return Tok_Comment; - } - yyComment.append(yyCh); - - if (yyCh == '*') - metAster = true; - else if (metAster && yyCh == '/') - metAsterSlash = true; - else - metAster = false; - } - yyCh = getChar(); - yyComment.chop(2); - } - return Tok_Comment; - case '"': - yyCh = getChar(); - while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { - if (yyCh == '\\') { - yyCh = getChar(); - if (yyString.size() < yyStringMaxLen) { - yyString.append(QLatin1Char('\\')); - yyString.append(yyCh); - } - } else { - if (yyString.size() < yyStringMaxLen) - yyString.append(yyCh); - } - yyCh = getChar(); - } - - if (yyCh != '"') - qWarning("%s:%d: Unterminated C++ string", - qPrintable(yyFileName), yyLineNo); - - if (yyCh == EOF) - return Tok_Eof; - yyCh = getChar(); - return Tok_String; - case '-': - yyCh = getChar(); - if (yyCh == '>') { - yyCh = getChar(); - return Tok_Arrow; - } - break; - case ':': - yyCh = getChar(); - if (yyCh == ':') { - yyCh = getChar(); - return Tok_ColonColon; - } - return Tok_Colon; - // Incomplete: '<' might be part of '<=' or of template syntax. - // The main intent of not completely ignoring it is to break - // parsing of things like std::cout << QObject::tr() as - // context std::cout::QObject (see Task 161106) - case '=': - yyCh = getChar(); - return Tok_Equals; - case '>': - case '<': - yyCh = getChar(); - return Tok_Other; - case '\'': - yyCh = getChar(); - if (yyCh == '\\') - yyCh = getChar(); - - do { - yyCh = getChar(); - } while (yyCh != EOF && yyCh != '\''); - yyCh = getChar(); - break; - case '{': - if (yyBraceDepth == 0) - yyBraceLineNo = yyCurLineNo; - yyBraceDepth++; - yyCh = getChar(); - return Tok_LeftBrace; - case '}': - if (yyBraceDepth == 0) - yyBraceLineNo = yyCurLineNo; - yyBraceDepth--; - yyCh = getChar(); - return Tok_RightBrace; - case '(': - if (yyParenDepth == 0) - yyParenLineNo = yyCurLineNo; - yyParenDepth++; - yyCh = getChar(); - return Tok_LeftParen; - case ')': - if (yyParenDepth == 0) - yyParenLineNo = yyCurLineNo; - yyParenDepth--; - yyCh = getChar(); - return Tok_RightParen; - case ',': - yyCh = getChar(); - return Tok_Comma; - case ';': - yyCh = getChar(); - return Tok_Semicolon; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - QByteArray ba; - ba += yyCh; - yyCh = getChar(); - bool hex = yyCh == 'x'; - if (hex) { - ba += yyCh; - yyCh = getChar(); - } - while (hex ? isxdigit(yyCh) : isdigit(yyCh)) { - ba += yyCh; - yyCh = getChar(); - } - bool ok; - yyInteger = ba.toLongLong(&ok); - if (ok) - return Tok_Integer; - break; - } - default: - yyCh = getChar(); - break; - } - } - } - return Tok_Eof; -} - -/* - The second part of this source file is the parser. It accomplishes - a very easy task: It finds all strings inside a tr() or translate() - call, and possibly finds out the context of the call. It supports - three cases: (1) the context is specified, as in - FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); - (2) the call appears within an inlined function; (3) the call - appears within a function defined outside the class definition. -*/ - -static uint yyTok; - -static bool match(uint t) -{ - bool matches = (yyTok == t); - if (matches) - yyTok = getToken(); - return matches; -} - -static bool matchString(QString *s) -{ - bool matches = (yyTok == Tok_String); - s->clear(); - while (yyTok == Tok_String) { - *s += yyString; - yyTok = getToken(); - } - return matches; -} - -static bool matchEncoding(bool *utf8) -{ - STRING(QApplication); - STRING(QCoreApplication); - STRING(UnicodeUTF8); - STRING(DefaultCodec); - STRING(CodecForTr); - - if (yyTok != Tok_Ident) - return false; - if (yyIdent == strQApplication || yyIdent == strQCoreApplication) { - yyTok = getToken(); - if (yyTok == Tok_ColonColon) - yyTok = getToken(); - } - if (yyIdent == strUnicodeUTF8) { - *utf8 = true; - yyTok = getToken(); - return true; - } - if (yyIdent == strDefaultCodec || yyIdent == strCodecForTr) { - *utf8 = false; - yyTok = getToken(); - return true; - } - return false; -} - -static bool matchInteger(qlonglong *number) -{ - bool matches = (yyTok == Tok_Integer); - if (matches) { - yyTok = getToken(); - *number = yyInteger; - } - return matches; -} - -static bool matchStringOrNull(QString *s) -{ - bool matches = matchString(s); - qlonglong num = 0; - if (!matches) - matches = matchInteger(&num); - return matches && num == 0; -} - -/* - * match any expression that can return a number, which can be - * 1. Literal number (e.g. '11') - * 2. simple identifier (e.g. 'm_count') - * 3. simple function call (e.g. 'size()' ) - * 4. function call on an object (e.g. 'list.size()') - * 5. function call on an object (e.g. 'list->size()') - * - * Other cases: - * size(2,4) - * list().size() - * list(a,b).size(2,4) - * etc... - */ -static bool matchExpression() -{ - if (match(Tok_Integer)) - return true; - - int parenlevel = 0; - while (match(Tok_Ident) || parenlevel > 0) { - if (yyTok == Tok_RightParen) { - if (parenlevel == 0) break; - --parenlevel; - yyTok = getToken(); - } else if (yyTok == Tok_LeftParen) { - yyTok = getToken(); - if (yyTok == Tok_RightParen) { - yyTok = getToken(); - } else { - ++parenlevel; - } - } else if (yyTok == Tok_Ident) { - continue; - } else if (yyTok == Tok_Arrow) { - yyTok = getToken(); - } else if (parenlevel == 0) { - return false; - } - } - return true; -} - -static QStringList resolveNamespaces( - const QStringList &namespaces, const QHash<QString, QStringList> &namespaceAliases) -{ - static QString strColons(QLatin1String("::")); - - QStringList ns; - foreach (const QString &cns, namespaces) { - ns << cns; - ns = namespaceAliases.value(ns.join(strColons), ns); - } - return ns; -} - -static QStringList getFullyQualifiedNamespaceName( - const QSet<QString> &allNamespaces, const QStringList &namespaces, - const QHash<QString, QStringList> &namespaceAliases, - const QStringList &segments) -{ - static QString strColons(QLatin1String("::")); - - if (segments.first().isEmpty()) { - // fully qualified - QStringList segs = segments; - segs.removeFirst(); - return resolveNamespaces(segs, namespaceAliases); - } else { - for (int n = namespaces.count(); --n >= -1; ) { - QStringList ns; - for (int i = 0; i <= n; ++i) // Note: n == -1 possible - ns << namespaces[i]; - foreach (const QString &cns, segments) { - ns << cns; - ns = namespaceAliases.value(ns.join(strColons), ns); - } - if (allNamespaces.contains(ns.join(strColons))) - return ns; - } - - // Fallback when the namespace was declared in a header, etc. - QStringList ns = namespaces; - ns += segments; - return ns; - } -} - -static QString getFullyQualifiedClassName( - const QSet<QString> &allClasses, const QStringList &namespaces, - const QHash<QString, QStringList> &namespaceAliases, - const QString &ident, bool hasPrefix) -{ - static QString strColons(QLatin1String("::")); - - QString context = ident; - QStringList segments = context.split(strColons); - if (segments.first().isEmpty()) { - // fully qualified - segments.removeFirst(); - context = resolveNamespaces(segments, namespaceAliases).join(strColons); - } else { - for (int n = namespaces.count(); --n >= -1; ) { - QStringList ns; - for (int i = 0; i <= n; ++i) // Note: n == -1 possible - ns.append(namespaces[i]); - foreach (const QString &cns, segments) { - ns.append(cns); - ns = namespaceAliases.value(ns.join(strColons), ns); - } - QString nctx = ns.join(strColons); - if (allClasses.contains(nctx)) { - context = nctx; - goto gotit; - } - } - - if (!hasPrefix && namespaces.count()) - context = namespaces.join(strColons) + strColons + context; - } -gotit: - //qDebug() << "CLASSES:" << allClasses << "NAMEPACES:" << namespaces - // << "IDENT:" << ident << "CONTEXT:" << context; - return context; -} - - -static QString transcode(const QString &str, bool utf8) -{ - static const char tab[] = "abfnrtv"; - static const char backTab[] = "\a\b\f\n\r\t\v"; - const QString in = (!utf8 || yySourceIsUnicode) - ? str : QString::fromUtf8(yySourceCodec->fromUnicode(str).data()); - QString out; - - out.reserve(in.length()); - for (int i = 0; i < in.length();) { - ushort c = in[i++].unicode(); - if (c == '\\') { - if (i >= in.length()) - break; - c = in[i++].unicode(); - - if (c == '\n') - continue; - - if (c == 'x') { - QByteArray hex; - while (i < in.length() && isxdigit((c = in[i].unicode()))) { - hex += c; - i++; - } - out += hex.toUInt(0, 16); - } else if (c >= '0' && c < '8') { - QByteArray oct; - int n = 0; - oct += c; - while (n < 2 && i < in.length() && (c = in[i].unicode()) >= '0' && c < '8') { - i++; - n++; - oct += c; - } - out += oct.toUInt(0, 8); - } else { - const char *p = strchr(tab, c); - out += QChar(QLatin1Char(!p ? c : backTab[p - tab])); - } - } else { - out += c; - } - } - return out; -} - -static void recordMessage( - Translator *tor, int line, const QString &context, const QString &text, const QString &comment, - const QString &extracomment, bool utf8, bool plural) -{ - TranslatorMessage msg( - transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), - yyFileName, line, QStringList(), - TranslatorMessage::Unfinished, plural); - msg.setExtraComment(transcode(extracomment.simplified(), utf8)); - if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) - msg.setUtf8(true); - tor->extend(msg); -} - -static void parse(Translator *tor, const QString &initialContext, const QString &defaultContext) -{ - static QString strColons(QLatin1String("::")); - - QMap<QString, QString> qualifiedContexts; - QSet<QString> allClasses; - QSet<QString> allNamespaces; - QHash<QString, QStringList> namespaceAliases; - QStringList namespaces; - QString context; - QString text; - QString comment; - QString extracomment; - QString functionContext = initialContext; - QString prefix; -#ifdef DIAGNOSE_RETRANSLATABILITY - QString functionName; -#endif - int line; - bool utf8 = false; - bool missing_Q_OBJECT = false; - - yyTok = getToken(); - while (yyTok != Tok_Eof) { - //qDebug() << "TOKEN: " << yyTok; - switch (yyTok) { - case Tok_class: - yyTokColonSeen = false; - /* - Partial support for inlined functions. - */ - yyTok = getToken(); - if (yyBraceDepth == namespaces.count() && yyParenDepth == 0) { - QStringList fct; - do { - /* - This code should execute only once, but we play - safe with impure definitions such as - 'class Q_EXPORT QMessageBox', in which case - 'QMessageBox' is the class name, not 'Q_EXPORT'. - */ - fct = QStringList(yyIdent); - yyTok = getToken(); - } while (yyTok == Tok_Ident); - while (yyTok == Tok_ColonColon) { - yyTok = getToken(); - if (yyTok != Tok_Ident) - break; // Oops ... - fct += yyIdent; - yyTok = getToken(); - } - functionContext = resolveNamespaces(namespaces + fct, namespaceAliases).join(strColons); - allClasses.insert(functionContext); - - if (yyTok == Tok_Colon) { - missing_Q_OBJECT = true; - // Skip any token until '{' since lupdate might do things wrong if it finds - // a '::' token here. - do { - yyTok = getToken(); - } while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); - } else { - //functionContext = defaultContext; - } - } - break; - case Tok_namespace: - yyTokColonSeen = false; - yyTok = getToken(); - if (yyTok == Tok_Ident) { - QString ns = yyIdent; - yyTok = getToken(); - if (yyTok == Tok_LeftBrace) { - if (yyBraceDepth == namespaces.count() + 1) { - namespaces.append(ns); - allNamespaces.insert(namespaces.join(strColons)); - } - } else if (yyTok == Tok_Equals) { - // e.g. namespace Is = OuterSpace::InnerSpace; - QStringList alias = namespaces; - alias.append(ns); - QStringList fullName; - yyTok = getToken(); - if (yyTok == Tok_ColonColon) - fullName.append(QString()); - while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { - if (yyTok == Tok_Ident) - fullName.append(yyIdent); - yyTok = getToken(); - } - namespaceAliases[alias.join(strColons)] = - getFullyQualifiedNamespaceName(allNamespaces, namespaces, namespaceAliases, fullName); - } - } - break; - case Tok_tr: - case Tok_trUtf8: - utf8 = (yyTok == Tok_trUtf8); - line = yyLineNo; - yyTok = getToken(); - if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { - comment.clear(); - bool plural = false; - - if (match(Tok_RightParen)) { - // no comment - } else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment - if (match(Tok_RightParen)) { - // ok, - } else if (match(Tok_Comma)) { - plural = true; - } - } - if (prefix.isEmpty()) { - context = functionContext; - } else { -#ifdef DIAGNOSE_RETRANSLATABILITY - int last = prefix.lastIndexOf(strColons); - QString className = prefix.mid(last == -1 ? 0 : last + 2); - if (!className.isEmpty() && className == functionName) { - qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", - qPrintable(yyFileName), yyLineNo, - className.constData(), functionName.constData()); - } -#endif - prefix.chop(2); - context = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, true); - } - prefix.clear(); - if (qualifiedContexts.contains(context)) - context = qualifiedContexts[context]; - - if (!text.isEmpty()) - recordMessage(tor, line, context, text, comment, extracomment, utf8, plural); - - if (lacks_Q_OBJECT.contains(context)) { - qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro", - qPrintable(yyFileName), yyLineNo, - qPrintable(context)); - lacks_Q_OBJECT.remove(context); - } else { - needs_Q_OBJECT.insert(context); - } - } - extracomment.clear(); - break; - case Tok_translateUtf8: - case Tok_translate: - utf8 = (yyTok == Tok_translateUtf8); - line = yyLineNo; - yyTok = getToken(); - if (match(Tok_LeftParen) - && matchString(&context) - && match(Tok_Comma) - && matchString(&text)) - { - comment.clear(); - bool plural = false; - if (!match(Tok_RightParen)) { - // look for comment - if (match(Tok_Comma) && matchStringOrNull(&comment)) { - if (!match(Tok_RightParen)) { - // look for encoding - if (match(Tok_Comma)) { - if (matchEncoding(&utf8)) { - if (!match(Tok_RightParen)) { - // look for the plural quantifier, - // this can be a number, an identifier or - // a function call, - // so for simplicity we mark it as plural if - // we know we have a comma instead of an - // right parentheses. - plural = match(Tok_Comma); - } - } else { - // This can be a QTranslator::translate("context", - // "source", "comment", n) plural translation - if (matchExpression() && match(Tok_RightParen)) { - plural = true; - } else { - break; - } - } - } else { - break; - } - } - } else { - break; - } - } - if (!text.isEmpty()) - recordMessage(tor, line, context, text, comment, extracomment, utf8, plural); - } - extracomment.clear(); - break; - case Tok_Q_DECLARE_TR_FUNCTIONS: - case Tok_Q_OBJECT: - missing_Q_OBJECT = false; - yyTok = getToken(); - break; - case Tok_Ident: - prefix += yyIdent; - yyTok = getToken(); - if (yyTok != Tok_ColonColon) - prefix.clear(); - break; - case Tok_Comment: - if (yyComment.startsWith(QLatin1Char(':'))) { - yyComment.remove(0, 1); - extracomment.append(yyComment); - } else { - comment = yyComment.simplified(); - if (comment.startsWith(QLatin1String(MagicComment))) { - comment.remove(0, sizeof(MagicComment) - 1); - int k = comment.indexOf(QLatin1Char(' ')); - if (k == -1) { - context = comment; - } else { - context = comment.left(k); - comment.remove(0, k + 1); - recordMessage(tor, yyLineNo, context, QString(), comment, extracomment, false, false); - } - - /* - Provide a backdoor for people using "using - namespace". See the manual for details. - */ - k = 0; - while ((k = context.indexOf(strColons, k)) != -1) { - qualifiedContexts.insert(context.mid(k + 2), context); - k++; - } - } - } - yyTok = getToken(); - break; - case Tok_Arrow: - yyTok = getToken(); - if (yyTok == Tok_tr || yyTok == Tok_trUtf8) - qWarning("%s:%d: Cannot invoke tr() like this", - qPrintable(yyFileName), yyLineNo); - break; - case Tok_ColonColon: - if (yyBraceDepth == namespaces.count() && yyParenDepth == 0 && !yyTokColonSeen) - functionContext = getFullyQualifiedClassName(allClasses, namespaces, namespaceAliases, prefix, false); - prefix += strColons; - yyTok = getToken(); -#ifdef DIAGNOSE_RETRANSLATABILITY - if (yyTok == Tok_Ident && yyBraceDepth == namespaces.count() && yyParenDepth == 0) - functionName = yyIdent; -#endif - break; - case Tok_RightBrace: - case Tok_Semicolon: - prefix.clear(); - extracomment.clear(); - yyTokColonSeen = false; - if (yyBraceDepth >= 0 && yyBraceDepth + 1 == namespaces.count()) - namespaces.removeLast(); - if (yyBraceDepth == namespaces.count()) { - if (missing_Q_OBJECT) { - if (needs_Q_OBJECT.contains(functionContext)) { - qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro", - qPrintable(yyFileName), yyLineNo, - qPrintable(functionContext)); - } else { - lacks_Q_OBJECT.insert(functionContext); - } - } - functionContext = defaultContext; - missing_Q_OBJECT = false; - } - yyTok = getToken(); - break; - case Tok_Colon: - yyTokColonSeen = true; - yyTok = getToken(); - break; - case Tok_LeftParen: - case Tok_RightParen: - case Tok_LeftBrace: - yyTokColonSeen = false; - yyTok = getToken(); - break; - default: - yyTok = getToken(); - break; - } - } - - if (yyBraceDepth != 0) - qWarning("%s:%d: Unbalanced braces in C++ code (or abuse of the C++" - " preprocessor)\n", - qPrintable(yyFileName), yyBraceLineNo); - else if (yyParenDepth != 0) - qWarning("%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++" - " preprocessor)\n", - qPrintable(yyFileName), yyParenLineNo); -} - -/* - Fetches tr() calls in C++ code in UI files (inside "<function>" - tag). This mechanism is obsolete. -*/ -void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) -{ - yyInStr = in; - yyInPos = 0; - yyFileName = QString(); - yyCodecIsUtf8 = (translator.codecName() == "UTF-8"); - yyForceUtf8 = true; - yySourceIsUnicode = true; - yySavedBraceDepth.clear(); - yySavedParenDepth.clear(); - yyBraceDepth = 0; - yyParenDepth = 0; - yyCurLineNo = 1; - yyBraceLineNo = 1; - yyParenLineNo = 1; - yyCh = getChar(); - - parse(&translator, context, QString()); -} - - -bool loadCPP(Translator &translator, QIODevice &dev, ConversionData &cd) -{ - QString defaultContext = cd.m_defaultContext; - - yyCodecIsUtf8 = (translator.codecName() == "UTF-8"); - yyForceUtf8 = false; - QTextStream ts(&dev); - QByteArray codecName = cd.m_codecForSource.isEmpty() - ? translator.codecName() : cd.m_codecForSource; - ts.setCodec(QTextCodec::codecForName(codecName)); - ts.setAutoDetectUnicode(true); - yySourceCodec = ts.codec(); - if (yySourceCodec->name() == "UTF-16") - translator.setCodecName("System"); - yySourceIsUnicode = yySourceCodec->name().startsWith("UTF-"); - yyInStr = ts.readAll(); - yyInPos = 0; - yyFileName = cd.m_sourceFileName; - yySavedBraceDepth.clear(); - yySavedParenDepth.clear(); - yyBraceDepth = 0; - yyParenDepth = 0; - yyCurLineNo = 1; - yyBraceLineNo = 1; - yyParenLineNo = 1; - yyCh = getChar(); - - parse(&translator, defaultContext, defaultContext); - - return true; -} - -int initCPP() -{ - Translator::FileFormat format; - format.extension = QLatin1String("cpp"); - format.fileType = Translator::FileFormat::SourceCode; - format.priority = 0; - format.description = QObject::tr("C++ source files"); - format.loader = &loadCPP; - format.saver = 0; - Translator::registerFileFormat(format); - return 1; -} - -Q_CONSTRUCTOR_FUNCTION(initCPP) - -QT_END_NAMESPACE diff --git a/tools/linguist/shared/formats.pri b/tools/linguist/shared/formats.pri index 9c8072b..985f6db 100644 --- a/tools/linguist/shared/formats.pri +++ b/tools/linguist/shared/formats.pri @@ -19,8 +19,4 @@ SOURCES += \ $$PWD/qph.cpp \ $$PWD/po.cpp \ $$PWD/ts.cpp \ - $$PWD/ui.cpp \ - $$PWD/cpp.cpp \ - $$PWD/java.cpp \ - $$PWD/qscript.cpp \ $$PWD/xliff.cpp diff --git a/tools/linguist/shared/make-qscript.sh b/tools/linguist/shared/make-qscript.sh deleted file mode 100755 index 42cab7a..0000000 --- a/tools/linguist/shared/make-qscript.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -me=$(dirname $0) -mkdir -p $me/out -(cd $me/out && ${QLALR-qlalr} --no-debug --troll --no-lines ../qscript.g) - -for f in $me/out/*.{h,cpp}; do - n=$(basename $f) - p4 open $me/../$n - cp $f $me/../$n -done - -p4 revert -a $me/../... -p4 diff -du $me/../... diff --git a/tools/linguist/shared/profileevaluator.cpp b/tools/linguist/shared/profileevaluator.cpp index 5440752..5c83f7e 100644 --- a/tools/linguist/shared/profileevaluator.cpp +++ b/tools/linguist/shared/profileevaluator.cpp @@ -44,6 +44,7 @@ #include "proitems.h" #include <QtCore/QByteArray> +#include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QDir> #include <QtCore/QFile> @@ -56,6 +57,15 @@ #include <QtCore/QStringList> #include <QtCore/QTextStream> +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <sys/utsname.h> +#elif defined(Q_OS_WIN32) +#include <Windows.h> +#endif +#include <stdio.h> +#include <stdlib.h> + #ifdef Q_OS_WIN32 #define QT_POPEN _popen #else @@ -66,6 +76,58 @@ QT_BEGIN_NAMESPACE /////////////////////////////////////////////////////////////////////// // +// Option +// +/////////////////////////////////////////////////////////////////////// + +QString +Option::fixString(QString string, uchar flags) +{ + // XXX Ripped out caching, so this will be slow. Should not matter for current uses. + + //fix the environment variables + if (flags & Option::FixEnvVars) { + int rep; + QRegExp reg_variableName(QLatin1String("\\$\\(.*\\)")); + reg_variableName.setMinimal(true); + while ((rep = reg_variableName.indexIn(string)) != -1) + string.replace(rep, reg_variableName.matchedLength(), + QString::fromLocal8Bit(qgetenv(string.mid(rep + 2, reg_variableName.matchedLength() - 3).toLatin1().constData()).constData())); + } + + //canonicalize it (and treat as a path) + if (flags & Option::FixPathCanonicalize) { +#if 0 + string = QFileInfo(string).canonicalFilePath(); +#endif + string = QDir::cleanPath(string); + } + + if (string.length() > 2 && string[0].isLetter() && string[1] == QLatin1Char(':')) + string[0] = string[0].toLower(); + + //fix separators + Q_ASSERT(!((flags & Option::FixPathToLocalSeparators) && (flags & Option::FixPathToTargetSeparators))); + if (flags & Option::FixPathToLocalSeparators) { +#if defined(Q_OS_WIN32) + string = string.replace(QLatin1Char('/'), QLatin1Char('\\')); +#else + string = string.replace(QLatin1Char('\\'), QLatin1Char('/')); +#endif + } else if (flags & Option::FixPathToTargetSeparators) { + string = string.replace(QLatin1Char('/'), Option::dir_sep) + .replace(QLatin1Char('\\'), Option::dir_sep); + } + + if ((string.startsWith(QLatin1Char('"')) && string.endsWith(QLatin1Char('"'))) || + (string.startsWith(QLatin1Char('\'')) && string.endsWith(QLatin1Char('\'')))) + string = string.mid(1, string.length() - 2); + + return string; +} + +/////////////////////////////////////////////////////////////////////// +// // ProFileEvaluator::Private // /////////////////////////////////////////////////////////////////////// @@ -75,6 +137,12 @@ class ProFileEvaluator::Private : public AbstractProItemVisitor public: Private(ProFileEvaluator *q_); + ProFileEvaluator *q; + int m_lineNo; // Error reporting + bool m_verbose; + + /////////////// Reading pro file + bool read(ProFile *pro); ProBlock *currentBlock(); @@ -87,6 +155,17 @@ public: void leaveScope(); void finalizeBlock(); + QStack<ProBlock *> m_blockstack; + ProBlock *m_block; + + ProItem *m_commentItem; + QString m_proitem; + QString m_pendingComment; + bool m_syntaxError; + bool m_contNextLine; + + /////////////// Evaluating pro file contents + // implementation of AbstractProItemVisitor bool visitBeginProBlock(ProBlock *block); bool visitEndProBlock(ProBlock *block); @@ -102,6 +181,8 @@ public: QStringList valuesDirect(const QString &variableName) const { return m_valuemap[variableName]; } QStringList values(const QString &variableName) const; QStringList values(const QString &variableName, const ProFile *pro) const; + QStringList values(const QString &variableName, const QHash<QString, QStringList> &place, + const ProFile *pro) const; QString propertyValue(const QString &val) const; bool isActiveConfig(const QString &config, bool regex = false); @@ -113,7 +194,7 @@ public: QString format(const char *format) const; QString currentFileName() const; - QString getcwd() const; + QString currentDirectory() const; ProFile *currentProFile() const; bool evaluateConditionalFunction(const QString &function, const QString &arguments, bool *result); @@ -122,41 +203,51 @@ public: QStringList qmakeFeaturePaths(); - ProFileEvaluator *q; - - QStack<ProBlock *> m_blockstack; - ProBlock *m_block; - - ProItem *m_commentItem; - QString m_proitem; - QString m_pendingComment; - bool m_syntaxError; - bool m_contNextLine; - bool m_condition; + enum { ConditionTrue, ConditionFalse, ConditionElse }; + int m_condition; + int m_prevCondition; + bool m_updateCondition; bool m_invertNext; + int m_skipLevel; + bool m_cumulative; + bool m_isFirstVariableValue; QString m_lastVarName; ProVariable::VariableOperator m_variableOperator; - int m_lineNo; // Error reporting + QString m_origfile; QString m_oldPath; // To restore the current path to the path QStack<ProFile*> m_profileStack; // To handle 'include(a.pri), so we can track back to 'a.pro' when finished with 'a.pri' + // we need the following two variables for handling + // CONFIG = foo bar $$CONFIG + QHash<QString, QStringList> m_tempValuemap; // used while evaluating (variable operator value1 value2 ...) + QHash<const ProFile*, QHash<QString, QStringList> > m_tempFilevaluemap; // used while evaluating (variable operator value1 value2 ...) + QHash<QString, QStringList> m_valuemap; // VariableName must be us-ascii, the content however can be non-us-ascii. QHash<const ProFile*, QHash<QString, QStringList> > m_filevaluemap; // Variables per include file QHash<QString, QString> m_properties; - QString m_origfile; + QString m_outputDir; int m_prevLineNo; // Checking whether we're assigning the same TARGET ProFile *m_prevProFile; // See m_prevLineNo - - bool m_verbose; }; ProFileEvaluator::Private::Private(ProFileEvaluator *q_) : q(q_) { + // Global parser state m_prevLineNo = 0; m_prevProFile = 0; + + // Configuration, more or less m_verbose = true; + m_cumulative = true; + + // Evaluator state + m_updateCondition = false; + m_condition = ConditionFalse; + m_invertNext = false; + m_skipLevel = 0; + m_isFirstVariableValue = true; } bool ProFileEvaluator::Private::read(ProFile *pro) @@ -167,6 +258,7 @@ bool ProFileEvaluator::Private::read(ProFile *pro) return false; } + // Parser state m_block = 0; m_commentItem = 0; m_contNextLine = false; @@ -455,15 +547,35 @@ void ProFileEvaluator::Private::updateItem() bool ProFileEvaluator::Private::visitBeginProBlock(ProBlock *block) { if (block->blockKind() == ProBlock::ScopeKind) { - m_invertNext = false; - m_condition = false; + m_updateCondition = true; + if (!m_skipLevel) { + m_prevCondition = m_condition; + m_condition = ConditionFalse; + } else { + Q_ASSERT(m_condition != ConditionTrue); + } + } else if (block->blockKind() & ProBlock::ScopeContentsKind) { + m_updateCondition = false; + if (m_condition != ConditionTrue) + ++m_skipLevel; + else + Q_ASSERT(!m_skipLevel); } return true; } bool ProFileEvaluator::Private::visitEndProBlock(ProBlock *block) { - Q_UNUSED(block); + if (block->blockKind() & ProBlock::ScopeContentsKind) { + if (m_skipLevel) { + Q_ASSERT(m_condition != ConditionTrue); + --m_skipLevel; + } else { + // Conditionals contained inside this block may have changed the state. + // So we reset it here to make an else following us do the right thing. + m_condition = ConditionTrue; + } + } return true; } @@ -471,12 +583,17 @@ bool ProFileEvaluator::Private::visitBeginProVariable(ProVariable *variable) { m_lastVarName = variable->variable(); m_variableOperator = variable->variableOperator(); + m_isFirstVariableValue = true; + m_tempValuemap = m_valuemap; + m_tempFilevaluemap = m_filevaluemap; return true; } bool ProFileEvaluator::Private::visitEndProVariable(ProVariable *variable) { Q_UNUSED(variable); + m_valuemap = m_tempValuemap; + m_filevaluemap = m_tempFilevaluemap; m_lastVarName.clear(); return true; } @@ -489,12 +606,20 @@ bool ProFileEvaluator::Private::visitProOperator(ProOperator *oper) bool ProFileEvaluator::Private::visitProCondition(ProCondition *cond) { - if (!m_condition) { - if (m_invertNext) - m_condition |= !isActiveConfig(cond->text(), true); - else - m_condition |= isActiveConfig(cond->text(), true); + if (!m_skipLevel) { + if (cond->text().toLower() == QLatin1String("else")) { + // The state ConditionElse makes sure that subsequential elses are ignored. + // That's braindead, but qmake is like that. + if (m_prevCondition == ConditionTrue) + m_condition = ConditionElse; + else if (m_prevCondition == ConditionFalse) + m_condition = ConditionTrue; + } else if (m_condition == ConditionFalse) { + if (isActiveConfig(cond->text(), true) ^ m_invertNext) + m_condition = ConditionTrue; + } } + m_invertNext = false; return true; } @@ -503,18 +628,32 @@ bool ProFileEvaluator::Private::visitBeginProFile(ProFile * pro) PRE(pro); bool ok = true; m_lineNo = pro->lineNumber(); + + if (m_origfile.isEmpty()) + m_origfile = pro->fileName(); if (m_oldPath.isEmpty()) { // change the working directory for the initial profile we visit, since // that is *the* profile. All the other times we reach this function will be due to // include(file) or load(file) + m_oldPath = QDir::currentPath(); + m_profileStack.push(pro); + + const QString mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS")); + if (!mkspecDirectory.isEmpty()) { + bool cumulative = m_cumulative; + m_cumulative = false; + // This is what qmake does, everything set in the mkspec is also set + // But this also creates a lot of problems + evaluateFile(mkspecDirectory + QLatin1String("/default/qmake.conf"), &ok); + evaluateFile(mkspecDirectory + QLatin1String("/features/default_pre.prf"), &ok); + m_cumulative = cumulative; + } + ok = QDir::setCurrent(pro->directoryName()); } - if (m_origfile.isEmpty()) - m_origfile = pro->fileName(); - return ok; } @@ -524,12 +663,60 @@ bool ProFileEvaluator::Private::visitEndProFile(ProFile * pro) bool ok = true; m_lineNo = pro->lineNumber(); if (m_profileStack.count() == 1 && !m_oldPath.isEmpty()) { + const QString &mkspecDirectory = propertyValue(QLatin1String("QMAKE_MKSPECS")); + if (!mkspecDirectory.isEmpty()) { + bool cumulative = m_cumulative; + m_cumulative = false; + + evaluateFile(mkspecDirectory + QLatin1String("/features/default_post.prf"), &ok); + + QSet<QString> processed; + forever { + bool finished = true; + QStringList configs = valuesDirect(QLatin1String("CONFIG")); + for (int i = configs.size() - 1; i >= 0; --i) { + const QString config = configs[i].toLower(); + if (!processed.contains(config)) { + processed.insert(config); + evaluateFile(mkspecDirectory + QLatin1String("/features/") + + config + QLatin1String(".prf"), &ok); + if (ok) { + finished = false; + break; + } + } + } + if (finished) + break; + } + + m_cumulative = cumulative; + } + m_profileStack.pop(); ok = QDir::setCurrent(m_oldPath); } return ok; } +static void replaceInList(QStringList *varlist, + const QRegExp ®exp, const QString &replace, bool global) +{ + for (QStringList::Iterator varit = varlist->begin(); varit != varlist->end(); ) { + if ((*varit).contains(regexp)) { + (*varit).replace(regexp, replace); + if ((*varit).isEmpty()) + varit = varlist->erase(varit); + else + ++varit; + if(!global) + break; + } else { + ++varit; + } + } +} + bool ProFileEvaluator::Private::visitProValue(ProValue *value) { PRE(value); @@ -547,8 +734,8 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) if (varName == QLatin1String("TARGET") && m_lineNo == m_prevLineNo && currentProFile() == m_prevProFile) { - QStringList targets = m_valuemap.value(QLatin1String("TARGET")); - m_valuemap.remove(QLatin1String("TARGET")); + QStringList targets = m_tempValuemap.value(QLatin1String("TARGET")); + m_tempValuemap.remove(QLatin1String("TARGET")); QStringList lastTarget(targets.takeLast()); lastTarget << v.join(QLatin1String(" ")); targets.push_back(lastTarget.join(QLatin1String(" "))); @@ -581,37 +768,58 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) } switch (m_variableOperator) { - case ProVariable::UniqueAddOperator: // * - insertUnique(&m_valuemap, varName, v, true); - insertUnique(&m_filevaluemap[currentProFile()], varName, v, true); - break; case ProVariable::SetOperator: // = - case ProVariable::AddOperator: // + - insertUnique(&m_valuemap, varName, v, false); - insertUnique(&m_filevaluemap[currentProFile()], varName, v, false); + if (!m_cumulative) { + if (!m_skipLevel) { + if (m_isFirstVariableValue) { + m_tempValuemap[varName] = v; + m_tempFilevaluemap[currentProFile()][varName] = v; + } else { // handle lines "CONFIG = foo bar" + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } + } + } else { + // We are greedy for values. + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } break; - case ProVariable::RemoveOperator: // - - // fix me: interaction between AddOperator and RemoveOperator - insertUnique(&m_valuemap, varName.prepend(QLatin1Char('-')), v, false); - insertUnique(&m_filevaluemap[currentProFile()], - varName.prepend(QLatin1Char('-')), v, false); + case ProVariable::UniqueAddOperator: // *= + if (!m_skipLevel || m_cumulative) { + insertUnique(&m_tempValuemap, varName, v); + insertUnique(&m_tempFilevaluemap[currentProFile()], varName, v); + } break; - case ProVariable::ReplaceOperator: // ~ + case ProVariable::AddOperator: // += + if (!m_skipLevel || m_cumulative) { + m_tempValuemap[varName] += v; + m_tempFilevaluemap[currentProFile()][varName] += v; + } + break; + case ProVariable::RemoveOperator: // -= + if (!m_cumulative) { + if (!m_skipLevel) { + removeEach(&m_tempValuemap, varName, v); + removeEach(&m_tempFilevaluemap[currentProFile()], varName, v); + } + } else { + // We are stingy with our values, too. + } + break; + case ProVariable::ReplaceOperator: // ~= { // DEFINES ~= s/a/b/?[gqi] -/* Create a superset by executing replacement + adding items that have changed - to original list. We're not sure if this is really the right approach, so for - the time being we will just do nothing ... - + // FIXME: qmake variable-expands val first. + if (val.length() < 4 || val[0] != QLatin1Char('s')) { + q->logMessage(format("the ~= operator can handle only the s/// function.")); + return false; + } QChar sep = val.at(1); QStringList func = val.split(sep); if (func.count() < 3 || func.count() > 4) { - q->logMessage(format("'~= operator '(function s///) expects 3 or 4 arguments.")); - return false; - } - if (func[0] != QLatin1String("s")) { - q->logMessage(format("~= operator can only handle s/// function.")); + q->logMessage(format("the s/// function expects 3 or 4 arguments.")); return false; } @@ -628,40 +836,40 @@ bool ProFileEvaluator::Private::visitProValue(ProValue *value) QRegExp regexp(pattern, case_sense ? Qt::CaseSensitive : Qt::CaseInsensitive); - QStringList replaceList = replaceInList(m_valuemap.value(varName), regexp, replace, - global); - // Add changed entries to list - foreach (const QString &entry, replaceList) - if (!m_valuemap.value(varName).contains(entry)) - insertUnique(&m_valuemap, varName, QStringList() << entry, false); - - replaceList = replaceInList(m_filevaluemap[currentProFile()].value(varName), regexp, - replace, global); - foreach (const QString &entry, replaceList) - if (!m_filevaluemap[currentProFile()].value(varName).contains(entry)) - insertUnique(&m_filevaluemap[currentProFile()], varName, - QStringList() << entry, false); */ + if (!m_skipLevel || m_cumulative) { + // We could make a union of modified and unmodified values, + // but this will break just as much as it fixes, so leave it as is. + replaceInList(&m_tempValuemap[varName], regexp, replace, global); + replaceInList(&m_tempFilevaluemap[currentProFile()][varName], regexp, replace, global); + } } break; } + m_isFirstVariableValue = false; return true; } bool ProFileEvaluator::Private::visitProFunction(ProFunction *func) { - m_lineNo = func->lineNumber(); - bool result = true; - bool ok = true; - QString text = func->text(); - int lparen = text.indexOf(QLatin1Char('(')); - int rparen = text.lastIndexOf(QLatin1Char(')')); - Q_ASSERT(lparen < rparen); - - QString arguments = text.mid(lparen + 1, rparen - lparen - 1); - QString funcName = text.left(lparen); - ok &= evaluateConditionalFunction(funcName.trimmed(), arguments, &result); - return ok; + if (!m_updateCondition || m_condition == ConditionFalse) { + QString text = func->text(); + int lparen = text.indexOf(QLatin1Char('(')); + int rparen = text.lastIndexOf(QLatin1Char(')')); + Q_ASSERT(lparen < rparen); + QString arguments = text.mid(lparen + 1, rparen - lparen - 1); + QString funcName = text.left(lparen); + m_lineNo = func->lineNumber(); + bool result; + if (!evaluateConditionalFunction(funcName.trimmed(), arguments, &result)) { + m_invertNext = false; + return false; + } + if (!m_skipLevel && (result ^ m_invertNext)) + m_condition = ConditionTrue; + } + m_invertNext = false; + return true; } @@ -781,7 +989,7 @@ QString ProFileEvaluator::Private::currentFileName() const return QString(); } -QString ProFileEvaluator::Private::getcwd() const +QString ProFileEvaluator::Private::currentDirectory() const { ProFile *cur = m_profileStack.top(); return cur->directoryName(); @@ -1018,29 +1226,29 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun static QHash<QString, int> *expands = 0; if (!expands) { expands = new QHash<QString, int>; - expands->insert(QLatin1String("member"), E_MEMBER); //v (implemented) - expands->insert(QLatin1String("first"), E_FIRST); //v - expands->insert(QLatin1String("last"), E_LAST); //v + expands->insert(QLatin1String("member"), E_MEMBER); + expands->insert(QLatin1String("first"), E_FIRST); + expands->insert(QLatin1String("last"), E_LAST); expands->insert(QLatin1String("cat"), E_CAT); - expands->insert(QLatin1String("fromfile"), E_FROMFILE); + expands->insert(QLatin1String("fromfile"), E_FROMFILE); // implementation disabled (see comment below) expands->insert(QLatin1String("eval"), E_EVAL); expands->insert(QLatin1String("list"), E_LIST); expands->insert(QLatin1String("sprintf"), E_SPRINTF); - expands->insert(QLatin1String("join"), E_JOIN); //v - expands->insert(QLatin1String("split"), E_SPLIT); //v - expands->insert(QLatin1String("basename"), E_BASENAME); //v - expands->insert(QLatin1String("dirname"), E_DIRNAME); //v + expands->insert(QLatin1String("join"), E_JOIN); + expands->insert(QLatin1String("split"), E_SPLIT); + expands->insert(QLatin1String("basename"), E_BASENAME); + expands->insert(QLatin1String("dirname"), E_DIRNAME); expands->insert(QLatin1String("section"), E_SECTION); expands->insert(QLatin1String("find"), E_FIND); - expands->insert(QLatin1String("system"), E_SYSTEM); //v + expands->insert(QLatin1String("system"), E_SYSTEM); expands->insert(QLatin1String("unique"), E_UNIQUE); - expands->insert(QLatin1String("quote"), E_QUOTE); //v + expands->insert(QLatin1String("quote"), E_QUOTE); expands->insert(QLatin1String("escape_expand"), E_ESCAPE_EXPAND); expands->insert(QLatin1String("upper"), E_UPPER); expands->insert(QLatin1String("lower"), E_LOWER); expands->insert(QLatin1String("re_escape"), E_RE_ESCAPE); expands->insert(QLatin1String("files"), E_FILES); - expands->insert(QLatin1String("prompt"), E_PROMPT); + expands->insert(QLatin1String("prompt"), E_PROMPT); // interactive, so cannot be implemented expands->insert(QLatin1String("replace"), E_REPLACE); } ExpandFunc func_t = ExpandFunc(expands->value(func.toLower())); @@ -1089,6 +1297,16 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } break; } + case E_SPRINTF: + if(args.count() < 1) { + q->logMessage(format("sprintf(format, ...) requires at least one argument")); + } else { + QString tmp = args.at(0); + for (int i = 1; i < args.count(); ++i) + tmp = tmp.arg(args.at(i)); + ret = split_value_list(tmp); + } + break; case E_JOIN: { if (args.count() < 1 || args.count() > 4) { q->logMessage(format("join(var, glue, before, after) requires one to four arguments.")); @@ -1108,9 +1326,9 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } case E_SPLIT: { if (args.count() != 2) { - q->logMessage(format("split(var, sep) requires two arguments")); + q->logMessage(format("split(var, sep) requires one or two arguments")); } else { - QString sep = args.at(1); + const QString &sep = (args.count() == 2) ? args[1] : QString(Option::field_sep); foreach (const QString &var, values(args.first())) foreach (const QString &splt, var.split(sep)) ret.append(splt); @@ -1181,8 +1399,82 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun } break; } - case E_SYSTEM: { - if (m_condition) { + case E_CAT: + if (args.count() < 1 || args.count() > 2) { + q->logMessage(format("cat(file, singleline=true) requires one or two arguments.")); + } else { + QString file = args[0]; + file = Option::fixPathToLocalOS(file); + + bool singleLine = true; + if (args.count() > 1) + singleLine = (args[1].toLower() == QLatin1String("true")); + + QFile qfile(file); + if (qfile.open(QIODevice::ReadOnly)) { + QTextStream stream(&qfile); + while (!stream.atEnd()) { + ret += split_value_list(stream.readLine().trimmed()); + if (!singleLine) + ret += QLatin1String("\n"); + } + qfile.close(); + } + } + break; +#if 0 // Used only by Qt's configure for caching + case E_FROMFILE: + if (args.count() != 2) { + q->logMessage(format("fromfile(file, variable) requires two arguments.")); + } else { + QString file = args[0], seek_variableName = args[1]; + + ProFile pro(Option::fixPathToLocalOS(file)); + + ProFileEvaluator visitor; + visitor.setVerbose(m_verbose); + visitor.setCumulative(m_cumulative); + + if (!visitor.queryProFile(&pro)) + break; + + if (!visitor.accept(&pro)) + break; + + ret = visitor.values(seek_variableName); + } + break; +#endif + case E_EVAL: { + if (args.count() != 1) { + q->logMessage(format("eval(variable) requires one argument")); + + } else { + ret += values(args.at(0)); + } + break; } + case E_LIST: { + static int x = 0; + QString tmp; + tmp.sprintf(".QMAKE_INTERNAL_TMP_variableName_%d", x++); + ret = QStringList(tmp); + QStringList lst; + foreach (const QString &arg, args) + lst += split_value_list(arg); + m_valuemap[tmp] = lst; + break; } + case E_FIND: + if (args.count() != 2) { + q->logMessage(format("find(var, str) requires two arguments.")); + } else { + QRegExp regx(args[1]); + foreach (const QString &val, values(args.first())) + if (regx.indexIn(val) != -1) + ret += val; + } + break; + case E_SYSTEM: + if (!m_skipLevel) { if (args.count() < 1 || args.count() > 2) { q->logMessage(format("system(execute) requires one or two arguments.")); } else { @@ -1206,13 +1498,114 @@ QStringList ProFileEvaluator::Private::evaluateExpandFunction(const QString &fun ret += split_value_list(output); } } - break; } + break; + case E_UNIQUE: + if(args.count() != 1) { + q->logMessage(format("unique(var) requires one argument.")); + } else { + foreach (const QString &var, values(args.first())) + if (!ret.contains(var)) + ret.append(var); + } + break; case E_QUOTE: for (int i = 0; i < args.count(); ++i) ret += QStringList(args.at(i)); break; + case E_ESCAPE_EXPAND: + for (int i = 0; i < args.size(); ++i) { + QChar *i_data = args[i].data(); + int i_len = args[i].length(); + for (int x = 0; x < i_len; ++x) { + if (*(i_data+x) == QLatin1Char('\\') && x < i_len-1) { + if (*(i_data+x+1) == QLatin1Char('\\')) { + ++x; + } else { + struct { + char in, out; + } mapped_quotes[] = { + { 'n', '\n' }, + { 't', '\t' }, + { 'r', '\r' }, + { 0, 0 } + }; + for (int i = 0; mapped_quotes[i].in; ++i) { + if (*(i_data+x+1) == QLatin1Char(mapped_quotes[i].in)) { + *(i_data+x) = QLatin1Char(mapped_quotes[i].out); + if (x < i_len-2) + memmove(i_data+x+1, i_data+x+2, (i_len-x-2)*sizeof(QChar)); + --i_len; + break; + } + } + } + } + } + ret.append(QString(i_data, i_len)); + } + break; + case E_RE_ESCAPE: + for (int i = 0; i < args.size(); ++i) + ret += QRegExp::escape(args[i]); + break; + case E_UPPER: + case E_LOWER: + for (int i = 0; i < args.count(); ++i) + if (func_t == E_UPPER) + ret += args[i].toUpper(); + else + ret += args[i].toLower(); + break; + case E_FILES: + if (args.count() != 1 && args.count() != 2) { + q->logMessage(format("files(pattern, recursive=false) requires one or two arguments")); + } else { + bool recursive = false; + if (args.count() == 2) + recursive = (args[1].toLower() == QLatin1String("true") || args[1].toInt()); + QStringList dirs; + QString r = Option::fixPathToLocalOS(args[0]); + int slash = r.lastIndexOf(QDir::separator()); + if (slash != -1) { + dirs.append(r.left(slash)); + r = r.mid(slash+1); + } else { + dirs.append(QString()); + } + + const QRegExp regex(r, Qt::CaseSensitive, QRegExp::Wildcard); + for (int d = 0; d < dirs.count(); d++) { + QString dir = dirs[d]; + if (!dir.isEmpty() && !dir.endsWith(Option::dir_sep)) + dir += QLatin1Char('/'); + + QDir qdir(dir); + for (int i = 0; i < (int)qdir.count(); ++i) { + if (qdir[i] == QLatin1String(".") || qdir[i] == QLatin1String("..")) + continue; + QString fname = dir + qdir[i]; + if (QFileInfo(fname).isDir()) { + if (recursive) + dirs.append(fname); + } + if (regex.exactMatch(qdir[i])) + ret += fname; + } + } + } + break; + case E_REPLACE: + if(args.count() != 3 ) { + q->logMessage(format("replace(var, before, after) requires three arguments")); + } else { + const QRegExp before(args[1]); + const QString after(args[2]); + foreach (QString val, values(args.first())) + ret += val.replace(before, after); + } + break; case 0: - q->logMessage(format("'%1' is not a function").arg(func)); + q->logMessage(format("'%1' is not a recognized replace function").arg(func)); break; default: q->logMessage(format("Function '%1' is not implemented").arg(func)); @@ -1233,26 +1626,67 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct for (int i = 0; i < argumentsList.count(); ++i) args += expandVariableReferences(argumentsList[i]).join(sep); - enum ConditionFunc { CF_CONFIG = 1, CF_CONTAINS, CF_COUNT, CF_EXISTS, CF_INCLUDE, - CF_LOAD, CF_ISEMPTY, CF_SYSTEM, CF_MESSAGE}; + enum TestFunc { T_REQUIRES=1, T_GREATERTHAN, T_LESSTHAN, T_EQUALS, + T_EXISTS, T_EXPORT, T_CLEAR, T_UNSET, T_EVAL, T_CONFIG, T_SYSTEM, + T_RETURN, T_BREAK, T_NEXT, T_DEFINED, T_CONTAINS, T_INFILE, + T_COUNT, T_ISEMPTY, T_INCLUDE, T_LOAD, T_DEBUG, T_MESSAGE, T_IF }; static QHash<QString, int> *functions = 0; if (!functions) { functions = new QHash<QString, int>; - functions->insert(QLatin1String("load"), CF_LOAD); //v - functions->insert(QLatin1String("include"), CF_INCLUDE); //v - functions->insert(QLatin1String("message"), CF_MESSAGE); //v - functions->insert(QLatin1String("warning"), CF_MESSAGE); //v - functions->insert(QLatin1String("error"), CF_MESSAGE); //v + functions->insert(QLatin1String("requires"), T_REQUIRES); + functions->insert(QLatin1String("greaterThan"), T_GREATERTHAN); + functions->insert(QLatin1String("lessThan"), T_LESSTHAN); + functions->insert(QLatin1String("equals"), T_EQUALS); + functions->insert(QLatin1String("isEqual"), T_EQUALS); + functions->insert(QLatin1String("exists"), T_EXISTS); + functions->insert(QLatin1String("export"), T_EXPORT); + functions->insert(QLatin1String("clear"), T_CLEAR); + functions->insert(QLatin1String("unset"), T_UNSET); + functions->insert(QLatin1String("eval"), T_EVAL); + functions->insert(QLatin1String("CONFIG"), T_CONFIG); + functions->insert(QLatin1String("if"), T_IF); + functions->insert(QLatin1String("isActiveConfig"), T_CONFIG); + functions->insert(QLatin1String("system"), T_SYSTEM); + functions->insert(QLatin1String("return"), T_RETURN); + functions->insert(QLatin1String("break"), T_BREAK); + functions->insert(QLatin1String("next"), T_NEXT); + functions->insert(QLatin1String("defined"), T_DEFINED); + functions->insert(QLatin1String("contains"), T_CONTAINS); + functions->insert(QLatin1String("infile"), T_INFILE); + functions->insert(QLatin1String("count"), T_COUNT); + functions->insert(QLatin1String("isEmpty"), T_ISEMPTY); + functions->insert(QLatin1String("load"), T_LOAD); //v + functions->insert(QLatin1String("include"), T_INCLUDE); //v + functions->insert(QLatin1String("debug"), T_DEBUG); + functions->insert(QLatin1String("message"), T_MESSAGE); //v + functions->insert(QLatin1String("warning"), T_MESSAGE); //v + functions->insert(QLatin1String("error"), T_MESSAGE); //v } bool cond = false; bool ok = true; - ConditionFunc func_t = (ConditionFunc)functions->value(function); + TestFunc func_t = (TestFunc)functions->value(function); switch (func_t) { - case CF_CONFIG: { +#if 0 + case T_INFILE: + case T_REQUIRES: + case T_GREATERTHAN: + case T_LESSTHAN: + case T_EQUALS: + case T_EXPORT: + case T_CLEAR: + case T_UNSET: + case T_EVAL: + case T_IF: + case T_RETURN: + case T_BREAK: + case T_NEXT: + case T_DEFINED: +#endif + case T_CONFIG: { if (args.count() < 1 || args.count() > 2) { q->logMessage(format("CONFIG(config) requires one or two arguments.")); ok = false; @@ -1264,7 +1698,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } const QStringList mutuals = args[1].split(QLatin1Char('|')); const QStringList &configs = valuesDirect(QLatin1String("CONFIG")); - for (int i = configs.size() - 1 && ok; i >= 0; i--) { + for (int i = configs.size() - 1; i >= 0; i--) { for (int mut = 0; mut < mutuals.count(); mut++) { if (configs[i] == mutuals[mut].trimmed()) { cond = (configs[i] == args[0]); @@ -1275,7 +1709,7 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct done_T_CONFIG: break; } - case CF_CONTAINS: { + case T_CONTAINS: { if (args.count() < 2 || args.count() > 3) { q->logMessage(format("contains(var, val) requires two or three arguments.")); ok = false; @@ -1307,9 +1741,9 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct done_T_CONTAINS: break; } - case CF_COUNT: { + case T_COUNT: { if (args.count() != 2 && args.count() != 3) { - q->logMessage(format("count(var, count) requires two or three arguments.")); + q->logMessage(format("count(var, count, op=\"equals\") requires two or three arguments.")); ok = false; break; } @@ -1334,7 +1768,9 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct cond = values(args.first()).count() == args[1].toInt(); break; } - case CF_INCLUDE: { + case T_INCLUDE: { + if (m_skipLevel && !m_cumulative) + break; QString parseInto; if (args.count() == 2) { parseInto = args[1]; @@ -1345,12 +1781,14 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } QString fileName = args.first(); // ### this breaks if we have include(c:/reallystupid.pri) but IMHO that's really bad style. - QDir currentProPath(getcwd()); + QDir currentProPath(currentDirectory()); fileName = QDir::cleanPath(currentProPath.absoluteFilePath(fileName)); ok = evaluateFile(fileName, &ok); break; } - case CF_LOAD: { + case T_LOAD: { + if (m_skipLevel && !m_cumulative) + break; QString parseInto; bool ignore_error = false; if (args.count() == 2) { @@ -1364,13 +1802,16 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct ok = evaluateFeatureFile( args.first(), &cond); break; } - case CF_MESSAGE: { + case T_DEBUG: + // Yup - do nothing. Nothing is going to enable debug output anyway. + break; + case T_MESSAGE: { if (args.count() != 1) { q->logMessage(format("%1(message) requires one argument.").arg(function)); ok = false; break; } - QString msg = args.first(); + QString msg = fixEnvVariables(args.first()); if (function == QLatin1String("error")) { QStringList parents; foreach (ProFile *proFile, m_profileStack) @@ -1387,7 +1828,8 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } break; } - case CF_SYSTEM: { +#if 0 // Way too dangerous to enable. + case T_SYSTEM: { if (args.count() != 1) { q->logMessage(format("system(exec) requires one argument.")); ok = false; @@ -1396,7 +1838,8 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct ok = system(args.first().toLatin1().constData()) == 0; break; } - case CF_ISEMPTY: { +#endif + case T_ISEMPTY: { if (args.count() != 1) { q->logMessage(format("isEmpty(var) requires one argument.")); ok = false; @@ -1411,31 +1854,38 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct } break; } - case CF_EXISTS: { + case T_EXISTS: { if (args.count() != 1) { q->logMessage(format("exists(file) requires one argument.")); ok = false; break; } QString file = args.first(); - - file = QDir::cleanPath(file); + file = Option::fixPathToLocalOS(file); if (QFile::exists(file)) { cond = true; break; } //regular expression I guess - QString dirstr = getcwd(); + QString dirstr = currentDirectory(); int slsh = file.lastIndexOf(Option::dir_sep); if (slsh != -1) { dirstr = file.left(slsh+1); file = file.right(file.length() - slsh - 1); } - cond = QDir(dirstr).entryList(QStringList(file)).count(); + if (file.contains(QLatin1Char('*')) || file.contains(QLatin1Char('?'))) + cond = QDir(dirstr).entryList(QStringList(file)).count(); break; } + case 0: + // This is too chatty currently (missing defineTest and defineReplace) + //q->logMessage(format("'%1' is not a recognized test function").arg(function)); + break; + default: + q->logMessage(format("Function '%1' is not implemented").arg(function)); + break; } if (result) @@ -1444,32 +1894,131 @@ bool ProFileEvaluator::Private::evaluateConditionalFunction(const QString &funct return ok; } -QStringList ProFileEvaluator::Private::values(const QString &variableName) const +QStringList ProFileEvaluator::Private::values(const QString &variableName, + const QHash<QString, QStringList> &place, + const ProFile *pro) const { - if (variableName == QLatin1String("TARGET")) { - QStringList list = m_valuemap.value(variableName); - if (!m_origfile.isEmpty()) - list.append(QFileInfo(m_origfile).baseName()); - return list; + if (variableName == QLatin1String("LITERAL_WHITESPACE")) //a real space in a token + return QStringList(QLatin1String("\t")); + if (variableName == QLatin1String("LITERAL_DOLLAR")) //a real $ + return QStringList(QLatin1String("$")); + if (variableName == QLatin1String("LITERAL_HASH")) //a real # + return QStringList(QLatin1String("#")); + if (variableName == QLatin1String("OUT_PWD")) //the out going dir + return QStringList(m_outputDir); + if (variableName == QLatin1String("PWD") || //current working dir (of _FILE_) + variableName == QLatin1String("IN_PWD")) + return QStringList(currentDirectory()); + if (variableName == QLatin1String("DIR_SEPARATOR")) + return QStringList(Option::dir_sep); + if (variableName == QLatin1String("DIRLIST_SEPARATOR")) + return QStringList(Option::dirlist_sep); + if (variableName == QLatin1String("_LINE_")) //parser line number + return QStringList(QString::number(m_lineNo)); + if (variableName == QLatin1String("_FILE_")) //parser file; qmake is a bit weird here + return QStringList(m_profileStack.size() == 1 ? pro->fileName() : QFileInfo(pro->fileName()).fileName()); + if (variableName == QLatin1String("_DATE_")) //current date/time + return QStringList(QDateTime::currentDateTime().toString()); + if (variableName == QLatin1String("_PRO_FILE_")) + return QStringList(m_origfile); + if (variableName == QLatin1String("_PRO_FILE_PWD_")) + return QStringList(QFileInfo(m_origfile).absolutePath()); + if (variableName == QLatin1String("_QMAKE_CACHE_")) + return QStringList(); // FIXME? + if (variableName.startsWith(QLatin1String("QMAKE_HOST."))) { + QString ret, type = variableName.mid(11); +#if defined(Q_OS_WIN32) + if (type == QLatin1String("os")) { + ret = QLatin1String("Windows"); + } else if (type == QLatin1String("name")) { + DWORD name_length = 1024; + TCHAR name[1024]; + if (GetComputerName(name, &name_length)) + ret = QString::fromUtf16((ushort*)name, name_length); + } else if (type == QLatin1String("version") || type == QLatin1String("version_string")) { + QSysInfo::WinVersion ver = QSysInfo::WindowsVersion; + if (type == QLatin1String("version")) + ret = QString::number(ver); + else if (ver == QSysInfo::WV_Me) + ret = QLatin1String("WinMe"); + else if (ver == QSysInfo::WV_95) + ret = QLatin1String("Win95"); + else if (ver == QSysInfo::WV_98) + ret = QLatin1String("Win98"); + else if (ver == QSysInfo::WV_NT) + ret = QLatin1String("WinNT"); + else if (ver == QSysInfo::WV_2000) + ret = QLatin1String("Win2000"); + else if (ver == QSysInfo::WV_2000) + ret = QLatin1String("Win2003"); + else if (ver == QSysInfo::WV_XP) + ret = QLatin1String("WinXP"); + else if (ver == QSysInfo::WV_VISTA) + ret = QLatin1String("WinVista"); + else + ret = QLatin1String("Unknown"); + } else if (type == QLatin1String("arch")) { + SYSTEM_INFO info; + GetSystemInfo(&info); + switch(info.wProcessorArchitecture) { +#ifdef PROCESSOR_ARCHITECTURE_AMD64 + case PROCESSOR_ARCHITECTURE_AMD64: + ret = QLatin1String("x86_64"); + break; +#endif + case PROCESSOR_ARCHITECTURE_INTEL: + ret = QLatin1String("x86"); + break; + case PROCESSOR_ARCHITECTURE_IA64: +#ifdef PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: +#endif + ret = QLatin1String("IA64"); + break; + default: + ret = QLatin1String("Unknown"); + break; + } + } +#elif defined(Q_OS_UNIX) + struct utsname name; + if (!uname(&name)) { + if (type == QLatin1String("os")) + ret = QString::fromLatin1(name.sysname); + else if (type == QLatin1String("name")) + ret = QString::fromLatin1(name.nodename); + else if (type == QLatin1String("version")) + ret = QString::fromLatin1(name.release); + else if (type == QLatin1String("version_string")) + ret = QString::fromLatin1(name.version); + else if (type == QLatin1String("arch")) + ret = QString::fromLatin1(name.machine); + } +#endif + return QStringList(ret); } - if (variableName == QLatin1String("PWD")) { - return QStringList(getcwd()); + + QStringList result = place[variableName]; + if (result.isEmpty()) { + if (variableName == QLatin1String("TARGET")) { + result.append(QFileInfo(m_origfile).baseName()); + } else if (variableName == QLatin1String("TEMPLATE")) { + result.append(QLatin1String("app")); + } else if (variableName == QLatin1String("QMAKE_DIR_SEP")) { + result.append(Option::dirlist_sep); + } } - return m_valuemap.value(variableName); + return result; +} + +QStringList ProFileEvaluator::Private::values(const QString &variableName) const +{ + return values(variableName, m_valuemap, currentProFile()); } QStringList ProFileEvaluator::Private::values(const QString &variableName, const ProFile *pro) const { - if (variableName == QLatin1String("TARGET")) { - QStringList list = m_filevaluemap[pro].value(variableName); - if (!m_origfile.isEmpty()) - list.append(QFileInfo(m_origfile).baseName()); - return list; - } - if (variableName == QLatin1String("PWD")) { - return QStringList(QFileInfo(pro->fileName()).absoluteFilePath()); - } - return m_filevaluemap[pro].value(variableName); + return values(variableName, m_filevaluemap[pro], pro); } ProFile *ProFileEvaluator::parsedProFile(const QString &fileName) @@ -1540,7 +2089,13 @@ bool ProFileEvaluator::Private::evaluateFeatureFile(const QString &fileName, boo break; } } - return fn.isEmpty() ? false : evaluateFile(fn, result); + if (fn.isEmpty()) + return false; + bool cumulative = m_cumulative; + m_cumulative = false; + bool ok = evaluateFile(fn, result); + m_cumulative = cumulative; + return ok; } void ProFileEvaluator::Private::expandPatternHelper(const QString &relName, const QString &absName, @@ -1650,14 +2205,23 @@ bool ProFileEvaluator::contains(const QString &variableName) const return d->m_valuemap.contains(variableName); } +inline QStringList fixEnvVariables(const QStringList &x) +{ + QStringList ret; + foreach (const QString &str, x) + ret << Option::fixString(str, Option::FixEnvVars); + return ret; +} + + QStringList ProFileEvaluator::values(const QString &variableName) const { - return d->values(variableName); + return fixEnvVariables(d->values(variableName)); } QStringList ProFileEvaluator::values(const QString &variableName, const ProFile *pro) const { - return d->values(variableName, pro); + return fixEnvVariables(d->values(variableName, pro)); } ProFileEvaluator::TemplateType ProFileEvaluator::templateType() @@ -1669,6 +2233,8 @@ ProFileEvaluator::TemplateType ProFileEvaluator::templateType() return TT_Application; if (t == QLatin1String("lib")) return TT_Library; + if (t == QLatin1String("script")) + return TT_Script; if (t == QLatin1String("subdirs")) return TT_Subdirs; } @@ -1713,18 +2279,20 @@ void ProFileEvaluator::addProperties(const QHash<QString, QString> &properties) void ProFileEvaluator::logMessage(const QString &message) { - if (d->m_verbose) + if (d->m_verbose && !d->m_skipLevel) qWarning("%s", qPrintable(message)); } void ProFileEvaluator::fileMessage(const QString &message) { - qWarning("%s", qPrintable(message)); + if (!d->m_skipLevel) + qWarning("%s", qPrintable(message)); } void ProFileEvaluator::errorMessage(const QString &message) { - qWarning("%s", qPrintable(message)); + if (!d->m_skipLevel) + qWarning("%s", qPrintable(message)); } void ProFileEvaluator::setVerbose(bool on) @@ -1732,6 +2300,16 @@ void ProFileEvaluator::setVerbose(bool on) d->m_verbose = on; } +void ProFileEvaluator::setCumulative(bool on) +{ + d->m_cumulative = on; +} + +void ProFileEvaluator::setOutputDir(const QString &dir) +{ + d->m_outputDir = dir; +} + void evaluateProFile(const ProFileEvaluator &visitor, QHash<QByteArray, QStringList> *varMap) { QStringList sourceFiles; diff --git a/tools/linguist/shared/profileevaluator.h b/tools/linguist/shared/profileevaluator.h index beb16ea..ae1422a 100644 --- a/tools/linguist/shared/profileevaluator.h +++ b/tools/linguist/shared/profileevaluator.h @@ -65,6 +65,7 @@ public: TT_Unknown = 0, TT_Application, TT_Library, + TT_Script, TT_Subdirs }; @@ -73,7 +74,9 @@ public: ProFileEvaluator::TemplateType templateType(); virtual bool contains(const QString &variableName) const; - void setVerbose(bool on); + void setVerbose(bool on); // Default is false + void setCumulative(bool on); // Default is true! + void setOutputDir(const QString &dir); // Default is empty bool queryProFile(ProFile *pro); bool accept(ProFile *pro); diff --git a/tools/linguist/shared/proparserutils.h b/tools/linguist/shared/proparserutils.h index 8914a8e..c27c3c0 100644 --- a/tools/linguist/shared/proparserutils.h +++ b/tools/linguist/shared/proparserutils.h @@ -93,6 +93,25 @@ struct Option Option::qmakespec = QString::fromLatin1(qgetenv("QMAKESPEC").data()); Option::field_sep = QLatin1Char(' '); } + + enum StringFixFlags { + FixNone = 0x00, + FixEnvVars = 0x01, + FixPathCanonicalize = 0x02, + FixPathToLocalSeparators = 0x04, + FixPathToTargetSeparators = 0x08 + }; + static QString fixString(QString string, uchar flags); + + inline static QString fixPathToLocalOS(const QString &in, bool fix_env = true, bool canonical = true) + { + uchar flags = FixPathToLocalSeparators; + if (fix_env) + flags |= FixEnvVars; + if (canonical) + flags |= FixPathCanonicalize; + return fixString(in, flags); + } }; #if defined(Q_OS_WIN32) Option::TARG_MODE Option::target_mode = Option::TARG_WIN_MODE; @@ -110,17 +129,20 @@ QString Option::dir_sep; QChar Option::field_sep; static void insertUnique(QHash<QString, QStringList> *map, - const QString &key, const QStringList &value, bool unique = true) + const QString &key, const QStringList &value) { QStringList &sl = (*map)[key]; - if (!unique) { - sl += value; - } else { - for (int i = 0; i < value.count(); ++i) { - if (!sl.contains(value.at(i))) - sl.append(value.at(i)); - } - } + foreach (const QString &str, value) + if (!sl.contains(str)) + sl.append(str); +} + +static void removeEach(QHash<QString, QStringList> *map, + const QString &key, const QStringList &value) +{ + QStringList &sl = (*map)[key]; + foreach (const QString &str, value) + sl.removeAll(str); } /* @@ -148,7 +170,12 @@ static QStringList replaceInList(const QStringList &varList, const QRegExp ®e } */ -inline QStringList splitPathList(const QString paths) +inline QString fixEnvVariables(const QString &x) +{ + return Option::fixString(x, Option::FixEnvVars); +} + +inline QStringList splitPathList(const QString &paths) { return paths.split(Option::dirlist_sep); } diff --git a/tools/linguist/shared/translator.h b/tools/linguist/shared/translator.h index 6b88b23..4e97000 100644 --- a/tools/linguist/shared/translator.h +++ b/tools/linguist/shared/translator.h @@ -47,7 +47,9 @@ #include <QDir> #include <QList> #include <QLocale> +#include <QMultiHash> #include <QString> +#include <QSet> QT_BEGIN_NAMESPACE @@ -85,7 +87,10 @@ public: QString m_sourceFileName; QString m_targetFileName; QDir m_sourceDir; - QDir m_targetDir; // FIXME: TS spefic + QDir m_targetDir; // FIXME: TS specific + QSet<QString> m_projectRoots; + QMultiHash<QString, QString> m_allCSources; + QStringList m_includePath; QStringList m_dropTags; // tags to be dropped QStringList m_errors; bool m_verbose; @@ -178,7 +183,7 @@ public: QString description; // human-readable description LoadFunction loader; SaveFunction saver; - enum FileType { SourceCode, TranslationSource, TranslationBinary } fileType; + enum FileType { TranslationSource, TranslationBinary } fileType; int priority; // 0 = highest, -1 = invisible }; static void registerFileFormat(const FileFormat &format); diff --git a/tools/linguist/shared/translatortools.pri b/tools/linguist/shared/translatortools.pri deleted file mode 100644 index 2b6de8c..0000000 --- a/tools/linguist/shared/translatortools.pri +++ /dev/null @@ -1,11 +0,0 @@ - - -INCLUDEPATH *= $$PWD - -SOURCES += \ - $$PWD/translatortools.cpp \ - $$PWD/simtexth.cpp - -HEADERS += \ - $$PWD/translatortools.h - diff --git a/tools/macdeployqt/macchangeqt/main.cpp b/tools/macdeployqt/macchangeqt/main.cpp index e94e8a3..ebdfc14 100644 --- a/tools/macdeployqt/macchangeqt/main.cpp +++ b/tools/macdeployqt/macchangeqt/main.cpp @@ -42,13 +42,35 @@ int main(int argc, char **argv) { - if (argc != 3) { - qDebug() << "Changeqt changes witch qt frameworks an application links against."; - qDebug() << "Usage: changeqt app-bundle qt-dir"; + // useDebugLibs should always be false because even if set all Qt + // libraries inside a binary to point to debug versions, as soon as + // one of them loads a Qt plugin, the plugin itself will load the + // release version of Qt, and as such, the app will crash. + bool useDebugLibs = false; + + int optionsSpecified = 0; + for (int i = 2; i < argc; ++i) { + QByteArray argument = QByteArray(argv[i]); + if (argument.startsWith(QByteArray("-verbose="))) { + LogDebug() << "Argument found:" << argument; + optionsSpecified++; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; + } + } + + if (argc != (3 + optionsSpecified)) { + qDebug() << "Changeqt: changes witch Qt frameworks an application links against."; + qDebug() << "Usage: changeqt app-bundle qt-dir <-verbose=[0-3]>"; return 0; } - + const QString appPath = QString::fromLocal8Bit(argv[1]); const QString qtPath = QString::fromLocal8Bit(argv[2]); - changeQtFrameworks(appPath, qtPath); + changeQtFrameworks(appPath, qtPath, useDebugLibs); } diff --git a/tools/macdeployqt/macdeployqt/main.cpp b/tools/macdeployqt/macdeployqt/main.cpp index 0026c40..5353688 100644 --- a/tools/macdeployqt/macdeployqt/main.cpp +++ b/tools/macdeployqt/macdeployqt/main.cpp @@ -51,9 +51,11 @@ int main(int argc, char **argv) qDebug() << "Usage: macdeployqt app-bundle [options]"; qDebug() << ""; qDebug() << "Options:"; - qDebug() << " -no-plugins: Skip plugin deployment"; - qDebug() << " -dmg : Create a .dmg disk image"; - qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -verbose=<0-3> : 0 = no output, 1 = error/warning (default), 2 = normal, 3 = debug"; + qDebug() << " -no-plugins : Skip plugin deployment"; + qDebug() << " -dmg : Create a .dmg disk image"; + qDebug() << " -no-strip : Don't run 'strip' on the binaries"; + qDebug() << " -use-debug-libs : Deploy with debug versions of frameworks and plugins (implies -no-strip)"; qDebug() << ""; qDebug() << "macdeployqt takes an application bundle as input and makes it"; qDebug() << "self-contained by copying in the Qt frameworks and plugins that"; @@ -68,10 +70,10 @@ int main(int argc, char **argv) return 0; } - + if (appBundlePath.endsWith("/")) appBundlePath.chop(1); - + if (QDir().exists(appBundlePath) == false) { qDebug() << "Error: Could not find app bundle" << appBundlePath; return 0; @@ -79,23 +81,40 @@ int main(int argc, char **argv) bool plugins = true; bool dmg = false; + bool useDebugLibs = false; extern bool runStripEnabled; for (int i = 2; i < argc; ++i) { QByteArray argument = QByteArray(argv[i]); if (argument == QByteArray("-no-plugins")) { + LogDebug() << "Argument found:" << argument; plugins = false; } else if (argument == QByteArray("-dmg")) { + LogDebug() << "Argument found:" << argument; dmg = true; } else if (argument == QByteArray("-no-strip")) { + LogDebug() << "Argument found:" << argument; runStripEnabled = false; + } else if (argument == QByteArray("-use-debug-libs")) { + LogDebug() << "Argument found:" << argument; + useDebugLibs = true; + runStripEnabled = false; + } else if (argument.startsWith(QByteArray("-verbose"))) { + LogDebug() << "Argument found:" << argument; + int index = argument.indexOf("="); + bool ok = false; + int number = argument.mid(index+1).toInt(&ok); + if (!ok) + LogError() << "Could not parse verbose level"; + else + logLevel = number; } else if (argument.startsWith("-")) { - qDebug() << "Error: Unknown option" << argument << "\n"; + LogError() << "Unknown argument" << argument << "\n"; return 0; } } - DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath); + DeploymentInfo deploymentInfo = deployQtFrameworks(appBundlePath, useDebugLibs); if (plugins) { if (deploymentInfo.qtPath.isEmpty()) @@ -103,13 +122,13 @@ int main(int argc, char **argv) else deploymentInfo.pluginPath = deploymentInfo.qtPath + "/plugins"; - qDebug() << ""; - qDebug() << "Deploying plugins from" << deploymentInfo.pluginPath; - deployPlugins(appBundlePath, deploymentInfo); + LogNormal(); + deployPlugins(appBundlePath, deploymentInfo, useDebugLibs); createQtConf(appBundlePath); } if (dmg) { + LogNormal(); createDiskImage(appBundlePath); } } diff --git a/tools/macdeployqt/shared/shared.cpp b/tools/macdeployqt/shared/shared.cpp index db76ef2..a10e668 100644 --- a/tools/macdeployqt/shared/shared.cpp +++ b/tools/macdeployqt/shared/shared.cpp @@ -50,6 +50,7 @@ #include "shared.h" bool runStripEnabled = true; +int logLevel = 1; using std::cout; using std::endl; @@ -61,8 +62,8 @@ bool operator==(const FrameworkInfo &a, const FrameworkInfo &b) QDebug operator<<(QDebug debug, const FrameworkInfo &info) { - debug << "Framework directory" << info.frameworkDirectory << "\n"; debug << "Framework name" << info.frameworkName << "\n"; + debug << "Framework directory" << info.frameworkDirectory << "\n"; debug << "Framework path" << info.frameworkPath << "\n"; debug << "Binary directory" << info.binaryDirectory << "\n"; debug << "Binary name" << info.binaryName << "\n"; @@ -71,8 +72,8 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info) debug << "Install name" << info.installName << "\n"; debug << "Deployed install name" << info.deployedInstallName << "\n"; debug << "Source file Path" << info.sourceFilePath << "\n"; - debug << "Deployed Directtory (relative to bundle)" << info.destinationDirectory << "\n"; - + debug << "Deployed Directory (relative to bundle)" << info.destinationDirectory << "\n"; + return debug; } @@ -89,40 +90,41 @@ inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info) bool copyFilePrintStatus(const QString &from, const QString &to) { if (QFile::copy(from, to)) { - qDebug() << "copied" << from << "to" << to; + LogNormal() << " copied:" << from; + LogNormal() << " to" << to; return true; } else { - qDebug() << "ERROR: file copy failed from" << from << "to" << to; + LogError() << "file copy failed from" << from; + LogError() << " to" << to; return false; } } - -FrameworkInfo parseOtoolLibraryLine(const QString &line) +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs) { FrameworkInfo info; QString trimmed = line.trimmed(); if (trimmed.isEmpty()) return info; - + // Don't deploy system libraries. if (trimmed.startsWith("/System/Library/") || (trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene || trimmed.startsWith("@executable_path")) return info; - + enum State {QtPath, FrameworkName, DylibName, Version, End}; State state = QtPath; int part = 0; QString name; QString qtPath; + QString suffix = useDebugLibs ? "_debug" : ""; // Split the line into [Qt-path]/lib/qt[Module].framework/Versions/[Version]/ QStringList parts = trimmed.split("/"); while (part < parts.count()) { const QString currentPart = parts.at(part).simplified() ; -// qDebug() << "currentPart" << currentPart; ++part; if (currentPart == "") continue; @@ -148,13 +150,13 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line) info.frameworkDirectory = "/usr/lib/"; state = DylibName; } - + --part; continue; } qtPath += (currentPart + "/"); - - } if (state == FrameworkName) { + + } if (state == FrameworkName) { // remove ".framework" name = currentPart; name.chop(QString(".framework").length()); @@ -163,28 +165,29 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line) ++part; continue; } if (state == DylibName) { - name = currentPart.split(" (compatibility").at(0); + name = currentPart.split(" (compatibility").at(0); info.frameworkName = name; - info.installName += info.frameworkName; - info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName; - info.binaryName = name; - info.frameworkPath = info.frameworkDirectory + info.frameworkName; + info.binaryName = name.left(name.indexOf('.')) + suffix + name.mid(name.indexOf('.')); + info.installName += name; + info.deployedInstallName = "@executable_path/../Frameworks/" + info.binaryName; + info.frameworkPath = info.frameworkDirectory + info.binaryName; info.sourceFilePath = info.frameworkPath; info.destinationDirectory = bundleFrameworkDirectory + "/"; + info.binaryDirectory = info.frameworkDirectory; + info.binaryPath = info.frameworkPath; state = End; ++part; continue; } else if (state == Version) { info.version = currentPart; - info.binaryDirectory = "Versions/" + info.version; - info.binaryName = name; + info.binaryDirectory = "Versions/" + info.version; + info.binaryName = name + suffix; info.binaryPath = "/" + info.binaryDirectory + "/" + info.binaryName; - info.installName += info.frameworkName + info.binaryPath; + info.installName += info.frameworkName + "/" + info.binaryDirectory + "/" + name; info.deployedInstallName = "@executable_path/../Frameworks/" + info.frameworkName + info.binaryPath; info.frameworkPath = info.frameworkDirectory + info.frameworkName; info.sourceFilePath = info.frameworkPath + info.binaryPath; info.destinationDirectory = bundleFrameworkDirectory + "/" + info.frameworkName + "/" + info.binaryDirectory; - state = End; } else if (state == End) { break; @@ -198,42 +201,46 @@ QString findAppBinary(const QString &appBundlePath) { QString appName = QFileInfo(appBundlePath).completeBaseName(); QString binaryPath = appBundlePath + "/Contents/MacOS/" + appName; - + if (QFile::exists(binaryPath)) return binaryPath; - qDebug() << "Error: Could not find bundle binary for" << appBundlePath; + LogError() << "Could not find bundle binary for" << appBundlePath; return QString(); } -QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines) +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs) { - QList<FrameworkInfo> libraries; + QList<FrameworkInfo> libraries; foreach(const QString line, otoolLines) { - FrameworkInfo info = parseOtoolLibraryLine(line); + FrameworkInfo info = parseOtoolLibraryLine(line, useDebugLibs); if (info.frameworkName.isEmpty() == false) { + LogDebug() << "Adding framework:"; + LogDebug() << info; libraries.append(info); } } return libraries; } -QList<FrameworkInfo> getQtFrameworks(const QString &path) +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs) { + LogDebug() << "Using otool:"; + LogDebug() << " inspecting" << path; QProcess otool; otool.start("otool", QStringList() << "-L" << path); otool.waitForFinished(); - + if (otool.exitCode() != 0) { - qDebug() << otool.readAllStandardError(); + LogError() << otool.readAllStandardError(); } - + QString output = otool.readAllStandardOutput(); QStringList outputLines = output.split("\n"); outputLines.removeFirst(); // remove line containing the binary path if (path.contains(".framework") || path.contains(".dylib")) outputLines.removeFirst(); // frameworks and dylibs lists themselves as a dependency. - return getQtFrameworks(outputLines); + return getQtFrameworks(outputLines, useDebugLibs); } // copies everything _inside_ sourcePath to destinationPath @@ -256,32 +263,31 @@ void recursiveCopy(const QString &sourcePath, const QString &destinationPath) QString copyFramework(const FrameworkInfo &framework, const QString path) { - const QString from = framework.sourceFilePath; - const QString toDir = path + "/" + framework.destinationDirectory; - const QString to = toDir + "/" + framework.binaryName; + QString from = framework.sourceFilePath; + QString toDir = path + "/" + framework.destinationDirectory; + QString to = toDir + "/" + framework.binaryName; if (QFile::exists(from) == false) { - qDebug() << "ERROR: no file at" << from; + LogError() << "no file at" << from; return QString(); } QDir dir; if (dir.mkpath(toDir) == false) { - qDebug() << "ERROR: could not create destination directory" << to; + LogError() << "could not create destination directory" << to; return QString(); } - + if (QFile::exists(to)) { -// qDebug() << framework.frameworkName << "already deployed, skip"; return QString(); } - + copyFilePrintStatus(from, to); - const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; - const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; + const QString resourcesSourcePath = framework.frameworkPath + "/Resources"; + const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources"; recursiveCopy(resourcesSourcePath, resourcesDestianationPath); return to; @@ -293,20 +299,25 @@ void runInstallNameTool(QStringList options) installNametool.start("install_name_tool", options); installNametool.waitForFinished(); if (installNametool.exitCode() != 0) { - qDebug() << installNametool.readAllStandardError(); - qDebug() << installNametool.readAllStandardOutput(); + LogError() << installNametool.readAllStandardError(); + LogError() << installNametool.readAllStandardOutput(); } } void changeIdentification(const QString &id, const QString &binaryPath) { -// qDebug() << "change identification on" << binaryPath << id; + LogDebug() << "Using install_name_tool:"; + LogDebug() << " change identification in" << binaryPath; + LogDebug() << " to" << id; runInstallNameTool(QStringList() << "-id" << id << binaryPath); } void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath) { -// qDebug() << "change install name on" << binaryPath << oldName << newName; + LogDebug() << "Using install_name_tool:"; + LogDebug() << " in" << binaryPath; + LogDebug() << " change reference" << oldName; + LogDebug() << " to" << newName; runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath); } @@ -315,14 +326,14 @@ void runStrip(const QString &binaryPath) if (runStripEnabled == false) return; + LogDebug() << "Using strip:"; + LogDebug() << " stripped" << binaryPath; QProcess strip; strip.start("strip", QStringList() << "-x" << binaryPath); strip.waitForFinished(); if (strip.exitCode() != 0) { - qDebug() << strip.readAllStandardError(); - qDebug() << strip.readAllStandardOutput(); - } else { - qDebug() << "stripped" << binaryPath; + LogError() << strip.readAllStandardError(); + LogError() << strip.readAllStandardOutput(); } } @@ -333,31 +344,31 @@ void runStrip(const QString &binaryPath) Returns a DeploymentInfo structure containing the Qt path used and a a list of actually deployed frameworks. */ -DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString &bundlePath, const QString &binaryPath) +DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, + const QString &bundlePath, const QString &binaryPath, bool useDebugLibs) { + LogNormal(); + LogNormal() << "Deploying Qt frameworks found inside:" << binaryPath; QStringList copiedFrameworks; DeploymentInfo deploymenInfo; - + while (frameworks.isEmpty() == false) { const FrameworkInfo framework = frameworks.takeFirst(); copiedFrameworks.append(framework.frameworkName); - + // Get the qt path from one of the Qt frameworks; - if (deploymenInfo.qtPath == QString() && framework.frameworkName.contains("Qt") + if (deploymenInfo.qtPath == QString() && framework.frameworkName.contains("Qt") && framework.frameworkDirectory.contains("/lib")) { deploymenInfo.qtPath = framework.frameworkDirectory; deploymenInfo.qtPath.chop(5); // remove "/lib/" } -// qDebug() << ""; -// qDebug() << "deploy" << framework.frameworkName; - if (framework.installName.startsWith("/@executable_path/")) { - qDebug() << framework.frameworkName << "already deployed, skipping."; + LogError() << framework.frameworkName << "already deployed, skipping."; continue; } - + // Install_name_tool the new id into the binary changeInstallName(framework.installName, framework.deployedInstallName, binaryPath); @@ -366,18 +377,17 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString // Skip the rest if already was deployed. if (deployedBinaryPath == QString()) continue; - + runStrip(deployedBinaryPath); // Install_name_tool it a new id. changeIdentification(framework.deployedInstallName, deployedBinaryPath); // Check for framework dependencies - QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath); + QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs); foreach (FrameworkInfo dependency, dependencies) { -// qDebug() << "dependent framework" << dependency.installName << deployedBinaryPath; changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath); - + // Deploy framework if neccesary. if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) { frameworks.append(dependency); @@ -388,28 +398,39 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString return deploymenInfo; } -DeploymentInfo deployQtFrameworks(const QString &appBundlePath) +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs) { ApplicationBundleInfo applicationBundle; applicationBundle.path = appBundlePath; applicationBundle.binaryPath = findAppBinary(appBundlePath); - return deployQtFrameworks(getQtFrameworks(applicationBundle.binaryPath), applicationBundle.path, applicationBundle.binaryPath); + QList<FrameworkInfo> frameworks = getQtFrameworks(applicationBundle.binaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath; + LogWarning() << "Perhaps macdeployqt was already used on" << appBundlePath << "?"; + LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again."; + return DeploymentInfo(); + } else { + return deployQtFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, useDebugLibs); + } } -void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, const QString pluginDestinationPath, DeploymentInfo deploymentInfo) +void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pluginSourcePath, + const QString pluginDestinationPath, DeploymentInfo deploymentInfo, bool useDebugLibs) { + LogNormal() << "Deploying plugins from" << pluginSourcePath; QStringList plugins = QDir(pluginSourcePath).entryList(QStringList() << "*.dylib"); foreach (QString pluginName, plugins) { - - // Skip some Qt plugins based on what frameworks were deployed: - //qDebug() << pluginSourcePath << deploymentInfo.pluginPath; - if (pluginSourcePath.contains(deploymentInfo.pluginPath)) { QStringList deployedFrameworks = deploymentInfo.deployedFrameworks; - // Skip the debug versions of the plugins - if (pluginName.endsWith("_debug.dylib")) + // Skip the debug versions of the plugins, unless specified otherwise. + if (!useDebugLibs && pluginName.endsWith("_debug.dylib")) + continue; + + // Skip the release versions of the plugins, unless specified otherwise. + if (useDebugLibs && !pluginName.endsWith("_debug.dylib")) continue; // Skip the designer plugins @@ -420,7 +441,7 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl // SKip the opengl graphicssystem plugin when not in use. if (pluginName.contains("libqglgraphicssystem")) continue; -#endif +#endif // Deploy accessibility for Qt3Support only if the Qt3Support.framework is in use if (deployedFrameworks.indexOf("Qt3Support.framework") == -1 && pluginName.contains("accessiblecompatwidgets")) continue; @@ -448,28 +469,25 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl const QString sourcePath = pluginSourcePath + "/" + pluginName; const QString destinationPath = pluginDestinationPath + "/" + pluginName; if (copyFilePrintStatus(sourcePath, destinationPath)) { - - runStrip(destinationPath); - - // Special case for the phonon plugin: CoreVideo is not available as a separate framework - // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) - if (pluginName.contains("libphonon_qt7")) { - changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", - "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", - destinationPath); - } -// qDebug() << "deploy plugin depedencies:"; - QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath); -// qDebug() << frameworks; - deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath); -// qDebug() << "deploy plugin depedencies done"; + runStrip(destinationPath); + + // Special case for the phonon plugin: CoreVideo is not available as a separate framework + // on panther, link against the QuartzCore framework instead. (QuartzCore contians CoreVideo.) + if (pluginName.contains("libphonon_qt7")) { + changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo", + "/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", + destinationPath); + } + + QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs); + deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath, useDebugLibs); } } // foreach plugins QStringList subdirs = QDir(pluginSourcePath).entryList(QStringList() << "*", QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QString &subdir, subdirs) - deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo); + deployPlugins(appBundleInfo, pluginSourcePath + "/" + subdir, pluginDestinationPath + "/" + subdir, deploymentInfo, useDebugLibs); } void createQtConf(const QString &appBundlePath) @@ -479,63 +497,64 @@ void createQtConf(const QString &appBundlePath) QString fileName = filePath + "qt.conf"; QDir().mkpath(filePath); - + QFile qtconf(fileName); if (qtconf.exists()) { - qDebug() << ""; - qDebug() << "Warning:" << fileName << "already exists, will not overwrite."; - qDebug() << "To make sure the plugins are loaded from the correct location,"; - qDebug() << "please make sure qt.conf contains the following lines:"; - qDebug() << contents; - qDebug() << ""; + LogWarning(); + LogWarning() << fileName << "already exists, will not overwrite."; + LogWarning() << "To make sure the plugins are loaded from the correct location,"; + LogWarning() << "please make sure qt.conf contains the following lines:"; + LogWarning() << "[Paths]"; + LogWarning() << " Plugins = PlugIns"; return; } qtconf.open(QIODevice::WriteOnly); if (qtconf.write(contents) != -1) { - qDebug() << ""; - qDebug() << "Created configuration file:" << fileName; - qDebug() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; - qDebug() << ""; + LogNormal() << "Created configuration file:" << fileName; + LogNormal() << "This file sets the plugin search path to" << appBundlePath + "/Contents/PlugIns"; } } -void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo) +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs) { ApplicationBundleInfo applicationBundle; applicationBundle.path = appBundlePath; applicationBundle.binaryPath = findAppBinary(appBundlePath); - + const QString pluginDestinationPath = appBundlePath + "/" + "Contents/PlugIns"; - -// qDebug() << ""; -// qDebug() << "recursively copying plugins from" << deploymentInfo.pluginPath << "to" << pluginDestinationPath; - deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo); + deployPlugins(applicationBundle, deploymentInfo.pluginPath, pluginDestinationPath, deploymentInfo, useDebugLibs); } void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &absoluteQtPath) { - qDebug() << "Changing" << appBinaryPath << "to link against Qt in" << absoluteQtPath; + LogNormal() << "Changing" << appBinaryPath << "to link against"; + LogNormal() << "Qt in" << absoluteQtPath; QString finalQtPath = absoluteQtPath; - if (absoluteQtPath.startsWith("/Library/Frameworks") == false) + if (!absoluteQtPath.startsWith("/Library/Frameworks")) finalQtPath += "/lib/"; foreach (FrameworkInfo framework, frameworks) { const QString oldBinaryId = framework.installName; const QString newBinaryId = finalQtPath + framework.frameworkName + framework.binaryPath; - qDebug() << "Changing" << oldBinaryId << "to" << newBinaryId; changeInstallName(oldBinaryId, newBinaryId, appBinaryPath); } } -void changeQtFrameworks(const QString appPath, const QString &qtPath) +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs) { const QString appBinaryPath = findAppBinary(appPath); - const QList<FrameworkInfo> qtFrameworks = getQtFrameworks(appBinaryPath); - const QString absoluteQtPath = QDir(qtPath).absolutePath(); - changeQtFrameworks(qtFrameworks, appBinaryPath, absoluteQtPath); + const QList<FrameworkInfo> frameworks = getQtFrameworks(appBinaryPath, useDebugLibs); + if (frameworks.isEmpty()) { + LogWarning(); + LogWarning() << "Could not find any _external_ Qt frameworks to change in" << appPath; + return; + } else { + const QString absoluteQtPath = QDir(qtPath).absolutePath(); + changeQtFrameworks(frameworks, appBinaryPath, absoluteQtPath); + } } @@ -543,15 +562,15 @@ void createDiskImage(const QString &appBundlePath) { QString appBaseName = appBundlePath; appBaseName.chop(4); // remove ".app" from end - + QString dmgName = appBaseName + ".dmg"; QFile dmg(dmgName); if (dmg.exists()) { - qDebug() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); + LogNormal() << "Disk image already exists, skipping .dmg creation for" << dmg.fileName(); } else { - qDebug() << "Creating disk image (.dmg) for" << appBundlePath; + LogNormal() << "Creating disk image (.dmg) for" << appBundlePath; } // More dmg options can be found in the hdiutil man page. diff --git a/tools/macdeployqt/shared/shared.h b/tools/macdeployqt/shared/shared.h index 5f30dad..637873e 100644 --- a/tools/macdeployqt/shared/shared.h +++ b/tools/macdeployqt/shared/shared.h @@ -45,6 +45,12 @@ #include <QStringList> #include <QDebug> +extern int logLevel; +#define LogError() if (logLevel < 1) {} else qDebug() << "ERROR:" +#define LogWarning() if (logLevel < 1) {} else qDebug() << "WARNING:" +#define LogNormal() if (logLevel < 2) {} else qDebug() << "Log:" +#define LogDebug() if (logLevel < 3) {} else qDebug() << "Log:" + extern bool runStripEnabled; class FrameworkInfo @@ -68,7 +74,7 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info); class ApplicationBundleInfo { -public: + public: QString path; QString binaryPath; }; @@ -84,18 +90,18 @@ public: inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info); -void changeQtFrameworks(const QString appPath, const QString &qtPath); +void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs); void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &qtPath); -FrameworkInfo parseOtoolLibraryLine(const QString &line); +FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs); QString findAppBinary(const QString &appBundlePath); -QList<FrameworkInfo> getQtFrameworks(const QString &path); -QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines); +QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs); +QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs); QString copyFramework(const FrameworkInfo &framework, const QString path); -DeploymentInfo deployQtFrameworks(const QString &appBundlePath); +DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs); DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString &bundlePath, const QString &binaryPath); void createQtConf(const QString &appBundlePath); -void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo); +void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs); void changeIdentification(const QString &id, const QString &binaryPath); void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath); QString findAppBinary(const QString &appBundlePath); diff --git a/tools/qdoc3/test/assistant.qdocconf b/tools/qdoc3/test/assistant.qdocconf index 7bb6bbf..9ba1b14 100644 --- a/tools/qdoc3/test/assistant.qdocconf +++ b/tools/qdoc3/test/assistant.qdocconf @@ -17,7 +17,7 @@ qhp.Assistant.namespace = com.trolltech.assistant.450 qhp.Assistant.virtualFolder = qdoc qhp.Assistant.indexTitle = Qt Assistant Manual qhp.Assistant.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Assistant.filterAttributes = qt 4.5.0 tools assistant +qhp.Assistant.filterAttributes = qt 4.6.0 tools assistant qhp.Assistant.customFilters.Assistant.name = Qt Assistant Manual qhp.Assistant.customFilters.Assistant.filterAttributes = qt tools assistant qhp.Assistant.subprojects = manual examples diff --git a/tools/qdoc3/test/designer.qdocconf b/tools/qdoc3/test/designer.qdocconf index 26636cd..f67cccc 100644 --- a/tools/qdoc3/test/designer.qdocconf +++ b/tools/qdoc3/test/designer.qdocconf @@ -17,7 +17,7 @@ qhp.Designer.namespace = com.trolltech.designer.450 qhp.Designer.virtualFolder = qdoc qhp.Designer.indexTitle = Qt Designer Manual qhp.Designer.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Designer.filterAttributes = qt 4.5.0 tools designer +qhp.Designer.filterAttributes = qt 4.6.0 tools designer qhp.Designer.customFilters.Designer.name = Qt Designer Manual qhp.Designer.customFilters.Designer.filterAttributes = qt tools designer qhp.Designer.subprojects = manual examples diff --git a/tools/qdoc3/test/linguist.qdocconf b/tools/qdoc3/test/linguist.qdocconf index 5e889a8..beff2da 100644 --- a/tools/qdoc3/test/linguist.qdocconf +++ b/tools/qdoc3/test/linguist.qdocconf @@ -17,7 +17,7 @@ qhp.Linguist.namespace = com.trolltech.linguist.450 qhp.Linguist.virtualFolder = qdoc qhp.Linguist.indexTitle = Qt Linguist Manual qhp.Linguist.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.Linguist.filterAttributes = qt 4.5.0 tools linguist +qhp.Linguist.filterAttributes = qt 4.6.0 tools linguist qhp.Linguist.customFilters.Linguist.name = Qt Linguist Manual qhp.Linguist.customFilters.Linguist.filterAttributes = qt tools linguist qhp.Linguist.subprojects = manual examples diff --git a/tools/qdoc3/test/qmake.qdocconf b/tools/qdoc3/test/qmake.qdocconf index c357cfb..57299ae 100644 --- a/tools/qdoc3/test/qmake.qdocconf +++ b/tools/qdoc3/test/qmake.qdocconf @@ -17,7 +17,7 @@ qhp.qmake.namespace = com.trolltech.qmake.450 qhp.qmake.virtualFolder = qdoc qhp.qmake.indexTitle = QMake Manual qhp.qmake.extraFiles = classic.css images/qt-logo.png images/trolltech-logo.png -qhp.qmake.filterAttributes = qt 4.5.0 tools qmake +qhp.qmake.filterAttributes = qt 4.6.0 tools qmake qhp.qmake.customFilters.qmake.name = qmake Manual qhp.qmake.customFilters.qmake.filterAttributes = qt tools qmake qhp.qmake.subprojects = manual diff --git a/tools/qdoc3/test/qt-build-docs.qdocconf b/tools/qdoc3/test/qt-build-docs.qdocconf index 25cdc5a..a13ea94 100644 --- a/tools/qdoc3/test/qt-build-docs.qdocconf +++ b/tools/qdoc3/test/qt-build-docs.qdocconf @@ -35,9 +35,9 @@ qhp.Qt.extraFiles = classic.css \ images/dynamiclayouts-example.png \ images/stylesheet-coffee-plastique.png -qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.5.0 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0 +qhp.Qt.filterAttributes = qt 4.6.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.6.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.6.0 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes diff --git a/tools/qdoc3/test/qt.qdocconf b/tools/qdoc3/test/qt.qdocconf index 4d401a4..e07882c 100644 --- a/tools/qdoc3/test/qt.qdocconf +++ b/tools/qdoc3/test/qt.qdocconf @@ -37,9 +37,9 @@ qhp.Qt.extraFiles = classic.css \ images/dynamiclayouts-example.png \ images/stylesheet-coffee-plastique.png -qhp.Qt.filterAttributes = qt 4.5.0 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.5.0 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.5.0 +qhp.Qt.filterAttributes = qt 4.6.0 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.6.0 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.6.0 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes diff --git a/tools/qvfb/S60-QVGA-Candybar.qrc b/tools/qvfb/S60-QVGA-Candybar.qrc new file mode 100644 index 0000000..8138484 --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/skins"> + <file>S60-QVGA-Candybar.skin</file> +</qresource> +</RCC> diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png Binary files differnew file mode 100644 index 0000000..89d40cb --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar-down.png diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png Binary files differnew file mode 100644 index 0000000..0d0e598 --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.png diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin new file mode 100644 index 0000000..4f8fe5d --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/S60-QVGA-Candybar.skin @@ -0,0 +1,15 @@ +[SkinFile] +Up=S60-QVGA-Candybar.png +Down=S60-QVGA-Candybar-down.png +Screen=61 93 240 320 +Areas=7 +HasMouseHover=false + + +"Context1" 0x01100000 54 469 151 469 140 483 88 485 81 496 54 498 +"Back" 0x01000061 211 468 307 467 307 498 278 497 219 486 +"Select" 0x01010000 165 491 196 522 +"Left" 0x1000012 149 474 166 492 163 519 143 538 142 481 +"Down" 0x1000015 164 521 195 522 212 539 204 545 154 544 145 536 +"Right" 0x1000014 214 475 219 487 219 528 212 539 196 522 197 492 +"Up" 0x1000013 150 474 156 467 209 467 213 476 197 489 165 489 diff --git a/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf b/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf new file mode 100644 index 0000000..e349dbc --- /dev/null +++ b/tools/qvfb/S60-QVGA-Candybar.skin/defaultbuttons.conf @@ -0,0 +1,78 @@ +[Translation] +File=QtopiaDefaults +Context=Buttons +[Menu] +Rows=4 +Columns=3 +Map=123456789*0# +Default=5 +1=Applications/camera.desktop +2=Applications/datebook.desktop +3=Applications +4=Applications/qtmail.desktop +5=Applications/addressbook.desktop +6=Games +7=Settings/Beaming.desktop +8=Applications/simapp.desktop,Applications/calculator.desktop +9=Settings +*=Applications/mediarecorder.desktop +0=Applications/todolist.desktop +#=Documents +Animator=Bounce +AnimatorBackground=Radial +[SoftKeys] +Count=3 +Key0=Context1 +Key1=Select +Key2=Back +[SystemButtons] +Count=5 +Key0=Context1 +Key1=Select +Key2=Back +Key3=Flip +Key4=Backspace +[TextButtons] +Buttons=0123456789*# +Hold0='0 +Hold1='1 +Hold2='2 +Hold3='3 +Hold4='4 +Hold5='5 +Hold6='6 +Hold7='7 +Hold8='8 +Hold9='9 +Hold*=symbol +Hold#=mode +Tap0=space +Tap1="\".,'?!-@:1" +Tap2="\"a\xe4\xe5\xe6\xe0\xe1\xe2\x62\x63\xe7\x32" +Tap3="\"de\xe8\xe9\xea\x66\x33" +Tap4="\"ghi\xec\xed\xee\x34" +Tap5="\"jkl5" +Tap6="\"mn\xf1o\xf6\xf8\xf2\xf3\x36" +Tap7="\"pqrs\xdf\x37" +Tap8="\"tu\xfc\xf9\xfav8" +Tap9="\"wxyz9" +Tap*=modify +Tap#=shift +[LocaleTextButtons] +Buttons=23456789 +Tap2[]='abc +Tap3[]='def +Tap4[]='ghi +Tap5[]='jkl +Tap6[]='mno +Tap7[]='pqrs +Tap8[]='tuv +Tap9[]='wxyz +[PhoneTextButtons] +Buttons=*# +Tap*='*+pw +Hold*=+ +Tap#=# +Hold#=mode +[Device] +PrimaryInput=Keypad diff --git a/tools/qvfb/S60-nHD-Touchscreen.qrc b/tools/qvfb/S60-nHD-Touchscreen.qrc new file mode 100644 index 0000000..daf0cc3 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/skins"> + <file>S60-nHD-Touchscreen.skin</file> +</qresource> +</RCC> diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png Binary files differnew file mode 100644 index 0000000..7253e38 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen-down.png diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png Binary files differnew file mode 100644 index 0000000..675563e --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.png diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin new file mode 100644 index 0000000..ed25d0e --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/S60-nHD-Touchscreen.skin @@ -0,0 +1,10 @@ +[SkinFile] +Up=S60-nHD-Touchscreen.png +Down=S60-nHD-Touchscreen-down.png +Screen=53 183 360 640 +Areas=3 +HasMouseHover=false + +"Call" 0x01100004 76 874 171 899 +"Hangup" 0x01100005 300 876 393 899 +"Home" 0x1000010 174 878 298 899 diff --git a/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf b/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf new file mode 100644 index 0000000..6665125 --- /dev/null +++ b/tools/qvfb/S60-nHD-Touchscreen.skin/defaultbuttons.conf @@ -0,0 +1,53 @@ +[Translation] +File=QtopiaDefaults +Context=Buttons +[Menu] +Rows=4 +Columns=3 +Map=123456789*0# +Default=5 +1=Applications/camera.desktop +2=Applications/mediaplayer.desktop +3=Applications/simapp.desktop,Applications/calculator.desktop +4=Applications/qtmail.desktop +5=Applications/addressbook.desktop +6=Applications/datebook.desktop +7=Games +8=Settings/beaming.desktop +9=Applications/todolist.desktop +*=Settings +0=Applications +#=Documents +Animator=Bounce +AnimatorBackground=Radial +[SoftKeys] +Count=3 +Key0=Context1 +Key1=Select +Key2=Back +[SystemButtons] +Count=5 +Key0=Context1 +Key1=Back +Key2=Select +Key3=Call +Key4=Hangup +[Device] +PrimaryInput=Touchscreen +[Button] +Count=2 +[Button0] +Name[]=Home Button +Key=Home +PressedActionMappable=0 +PressedActionService=TaskManager +PressedActionMessage=multitask() +HeldActionMappable=0 +HeldActionService=TaskManager +HeldActionMessage=showRunningTasks() +[Button1] +Name=Power Button +Key=Hangup +HeldActionService=Launcher +HeldActionMessage=execute(QString) +HeldActionArgs=@ByteArray(\0\0\0\x1\0\0\0\n\0\0\0\0\x10\0s\0h\0u\0t\0\x64\0o\0w\0n) diff --git a/tools/qvfb/qvfb.pro b/tools/qvfb/qvfb.pro index a3b55ab..85c4d96 100644 --- a/tools/qvfb/qvfb.pro +++ b/tools/qvfb/qvfb.pro @@ -69,5 +69,6 @@ RESOURCES += qvfb.qrc \ TouchscreenPhone.qrc \ Trolltech-Keypad.qrc \ Trolltech-Touchscreen.qrc \ - PortableMedia.qrc - + PortableMedia.qrc \ + S60-QVGA-Candybar.qrc \ + S60-nHD-Touchscreen.qrc diff --git a/tools/shared/qtgradienteditor/qtgradientdialog.cpp b/tools/shared/qtgradienteditor/qtgradientdialog.cpp index 032cb16..950a1d2 100644 --- a/tools/shared/qtgradienteditor/qtgradientdialog.cpp +++ b/tools/shared/qtgradienteditor/qtgradientdialog.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientDialog -*/ - #include "qtgradientdialog.h" #include "ui_qtgradientdialog.h" #include <QtGui/QPushButton> diff --git a/tools/shared/qtgradienteditor/qtgradienteditor.cpp b/tools/shared/qtgradienteditor/qtgradienteditor.cpp index 9eca9d8..42d7767 100644 --- a/tools/shared/qtgradienteditor/qtgradienteditor.cpp +++ b/tools/shared/qtgradienteditor/qtgradienteditor.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientEditor -*/ - #include "qtgradienteditor.h" #include "qtgradientstopscontroller.h" #include "ui_qtgradienteditor.h" diff --git a/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp b/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp index cbc53db..c9ef03b 100644 --- a/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp +++ b/tools/shared/qtgradienteditor/qtgradientstopscontroller.cpp @@ -39,10 +39,6 @@ ** ****************************************************************************/ -/* -TRANSLATOR qdesigner_internal::QtGradientStopsController -*/ - #include "qtgradientstopscontroller.h" #include "ui_qtgradienteditor.h" #include "qtgradientstopsmodel.h" diff --git a/tools/tools.pro b/tools/tools.pro index ffc5d63..c76174c 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -22,6 +22,8 @@ mac { SUBDIRS += macdeployqt } +SUBDIRS += kmap2qmap + contains(QT_CONFIG, dbus):SUBDIRS += qdbus !wince*:contains(QT_CONFIG, xmlpatterns): SUBDIRS += xmlpatterns embedded: SUBDIRS += makeqpf |