summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
authorOlivier Goffart <olivier.goffart@nokia.com>2010-07-14 15:38:16 +0200
committerOlivier Goffart <olivier.goffart@nokia.com>2010-07-14 15:41:27 +0200
commit9a1d97707a374e740890a93c74100663e9ebc1a1 (patch)
treee111be11c96438455186aa56f360dccdbc59207d /src/plugins
parenta66b93d2f31e22ed76f7016046dfe67dcb8da615 (diff)
parent9eba87bd92aa2de00e2c191119bc9a9e015e1de5 (diff)
downloadqt-creator-9a1d97707a374e740890a93c74100663e9ebc1a1.tar.gz
Merge branch 'qmljsinspector'
Conflicts: src/plugins/qmlinspector/qmlinspectorplugin.cpp src/plugins/qmljseditor/qmljseditor.h src/plugins/qmljsinspector/QmlJSInspector.pluginspec src/tools/qml/qmlobserver/editor/resizehandleitem.h
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/bineditor/bineditor.cpp27
-rw-r--r--src/plugins/bineditor/bineditor.h4
-rw-r--r--src/plugins/bineditor/bineditorplugin.cpp16
-rw-r--r--src/plugins/cmakeprojectmanager/cmakehighlighter.cpp2
-rw-r--r--src/plugins/cmakeprojectmanager/cmakehighlighter.h3
-rw-r--r--src/plugins/coreplugin/coreplugin.cpp6
-rw-r--r--src/plugins/coreplugin/coreplugin.h2
-rw-r--r--src/plugins/coreplugin/coreplugin.pro52
-rw-r--r--src/plugins/coreplugin/coreplugin_dependencies.pri2
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.cpp28
-rw-r--r--src/plugins/coreplugin/editormanager/editormanager.h2
-rw-r--r--src/plugins/coreplugin/editortoolbar.cpp2
-rw-r--r--src/plugins/coreplugin/filemanager.cpp8
-rw-r--r--src/plugins/coreplugin/helpmanager.cpp7
-rw-r--r--src/plugins/coreplugin/helpmanager.h2
-rw-r--r--src/plugins/coreplugin/iversioncontrol.h15
-rw-r--r--src/plugins/coreplugin/mainwindow.cpp6
-rw-r--r--src/plugins/coreplugin/progressmanager/progressmanager_win.cpp2
-rw-r--r--src/plugins/coreplugin/ssh/ne7sshobject.cpp81
-rw-r--r--src/plugins/coreplugin/ssh/ne7sshobject.h80
-rw-r--r--src/plugins/coreplugin/ssh/sftpchannel.cpp756
-rw-r--r--src/plugins/coreplugin/ssh/sftpchannel.h120
-rw-r--r--src/plugins/coreplugin/ssh/sftpchannel_p.h130
-rw-r--r--src/plugins/coreplugin/ssh/sftpdefs.cpp32
-rw-r--r--src/plugins/coreplugin/ssh/sftpdefs.h48
-rw-r--r--src/plugins/coreplugin/ssh/sftpincomingpacket.cpp230
-rw-r--r--src/plugins/coreplugin/ssh/sftpincomingpacket_p.h111
-rw-r--r--src/plugins/coreplugin/ssh/sftpoperation.cpp176
-rw-r--r--src/plugins/coreplugin/ssh/sftpoperation_p.h193
-rw-r--r--src/plugins/coreplugin/ssh/sftpoutgoingpacket.cpp202
-rw-r--r--src/plugins/coreplugin/ssh/sftpoutgoingpacket_p.h83
-rw-r--r--src/plugins/coreplugin/ssh/sftppacket.cpp54
-rw-r--r--src/plugins/coreplugin/ssh/sftppacket_p.h108
-rw-r--r--src/plugins/coreplugin/ssh/sshbotanconversions_p.h97
-rw-r--r--src/plugins/coreplugin/ssh/sshcapabilities.cpp103
-rw-r--r--src/plugins/coreplugin/ssh/sshcapabilities_p.h72
-rw-r--r--src/plugins/coreplugin/ssh/sshchannel.cpp244
-rw-r--r--src/plugins/coreplugin/ssh/sshchannel_p.h111
-rw-r--r--src/plugins/coreplugin/ssh/sshchannelmanager.cpp188
-rw-r--r--src/plugins/coreplugin/ssh/sshchannelmanager_p.h89
-rw-r--r--src/plugins/coreplugin/ssh/sshconnection.cpp768
-rw-r--r--src/plugins/coreplugin/ssh/sshconnection.h145
-rw-r--r--src/plugins/coreplugin/ssh/sshconnection_p.h157
-rw-r--r--src/plugins/coreplugin/ssh/sshcryptofacility.cpp369
-rw-r--r--src/plugins/coreplugin/ssh/sshcryptofacility_p.h154
-rw-r--r--src/plugins/coreplugin/ssh/sshdelayedsignal.cpp165
-rw-r--r--src/plugins/coreplugin/ssh/sshdelayedsignal_p.h190
-rw-r--r--src/plugins/coreplugin/ssh/ssherrors.h43
-rw-r--r--src/plugins/coreplugin/ssh/sshexception_p.h89
-rw-r--r--src/plugins/coreplugin/ssh/sshincomingpacket.cpp442
-rw-r--r--src/plugins/coreplugin/ssh/sshincomingpacket_p.h186
-rw-r--r--src/plugins/coreplugin/ssh/sshkeyexchange.cpp197
-rw-r--r--src/plugins/coreplugin/ssh/sshkeyexchange_p.h87
-rw-r--r--src/plugins/coreplugin/ssh/sshkeygenerator.cpp155
-rw-r--r--src/plugins/coreplugin/ssh/sshkeygenerator.h54
-rw-r--r--src/plugins/coreplugin/ssh/sshoutgoingpacket.cpp284
-rw-r--r--src/plugins/coreplugin/ssh/sshoutgoingpacket_p.h98
-rw-r--r--src/plugins/coreplugin/ssh/sshpacket.cpp167
-rw-r--r--src/plugins/coreplugin/ssh/sshpacket_p.h137
-rw-r--r--src/plugins/coreplugin/ssh/sshpacketparser.cpp153
-rw-r--r--src/plugins/coreplugin/ssh/sshpacketparser_p.h81
-rw-r--r--src/plugins/coreplugin/ssh/sshremoteprocess.cpp270
-rw-r--r--src/plugins/coreplugin/ssh/sshremoteprocess.h130
-rw-r--r--src/plugins/coreplugin/ssh/sshremoteprocess_p.h96
-rw-r--r--src/plugins/coreplugin/ssh/sshsendfacility.cpp191
-rw-r--r--src/plugins/coreplugin/ssh/sshsendfacility_p.h90
-rw-r--r--src/plugins/cpaster/cpasterplugin.cpp3
-rw-r--r--src/plugins/cpaster/cpasterplugin.h2
-rw-r--r--src/plugins/cppeditor/cppchecksymbols.cpp (renamed from src/plugins/cppeditor/cppcheckundefinedsymbols.cpp)66
-rw-r--r--src/plugins/cppeditor/cppchecksymbols.h (renamed from src/plugins/cppeditor/cppcheckundefinedsymbols.h)28
-rw-r--r--src/plugins/cppeditor/cppdeclfromdef.cpp250
-rw-r--r--src/plugins/cppeditor/cppdeclfromdef.h67
-rw-r--r--src/plugins/cppeditor/cppeditor.cpp346
-rw-r--r--src/plugins/cppeditor/cppeditor.h36
-rw-r--r--src/plugins/cppeditor/cppeditor.pro10
-rw-r--r--src/plugins/cppeditor/cpphighlighter.cpp2
-rw-r--r--src/plugins/cppeditor/cpphighlighter.h5
-rw-r--r--src/plugins/cppeditor/cpphoverhandler.cpp466
-rw-r--r--src/plugins/cppeditor/cpphoverhandler.h49
-rw-r--r--src/plugins/cppeditor/cppoutline.cpp117
-rw-r--r--src/plugins/cppeditor/cppoutline.h11
-rw-r--r--src/plugins/cppeditor/cppplugin.cpp23
-rw-r--r--src/plugins/cppeditor/cppplugin.h10
-rw-r--r--src/plugins/cppeditor/cppquickfix.cpp140
-rw-r--r--src/plugins/cpptools/cppcodecompletion.cpp72
-rw-r--r--src/plugins/cpptools/cppcodecompletion.h3
-rw-r--r--src/plugins/cpptools/cppcodeformatter.cpp35
-rw-r--r--src/plugins/cpptools/cppcodeformatter.h29
-rw-r--r--src/plugins/cpptools/cppfindreferences.cpp2
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.cpp3
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.h2
-rw-r--r--src/plugins/cpptools/searchsymbols.cpp7
-rw-r--r--src/plugins/debugger/cdb/breakpoint.cpp8
-rw-r--r--src/plugins/debugger/cdb/coreengine.cpp17
-rw-r--r--src/plugins/debugger/cdb/coreengine.h3
-rw-r--r--src/plugins/debugger/debuggeragents.cpp27
-rw-r--r--src/plugins/debugger/debuggeragents.h2
-rw-r--r--src/plugins/debugger/debuggerdialogs.cpp3
-rw-r--r--src/plugins/debugger/debuggerengine.h2
-rw-r--r--src/plugins/debugger/debuggerplugin.cpp3
-rw-r--r--src/plugins/debugger/debuggerplugin.h2
-rw-r--r--src/plugins/debugger/gdb/remotegdbprocess.cpp250
-rw-r--r--src/plugins/debugger/gdb/remotegdbprocess.h38
-rw-r--r--src/plugins/debugger/gdb/remoteplaingdbadapter.cpp4
-rw-r--r--src/plugins/debugger/gdb/tcftrkgdbadapter.cpp4
-rw-r--r--src/plugins/debugger/shared/sharedlibraryinjector.cpp2
-rw-r--r--src/plugins/debugger/watchutils.cpp4
-rw-r--r--src/plugins/fakevim/fakevimhandler.cpp198
-rw-r--r--src/plugins/fakevim/fakevimhandler.h3
-rw-r--r--src/plugins/fakevim/fakevimplugin.cpp123
-rw-r--r--src/plugins/fakevim/fakevimplugin.h2
-rw-r--r--src/plugins/find/basetextfind.cpp18
-rw-r--r--src/plugins/find/basetextfind.h4
-rw-r--r--src/plugins/find/currentdocumentfind.cpp7
-rw-r--r--src/plugins/find/currentdocumentfind.h2
-rw-r--r--src/plugins/find/find.pro3
-rw-r--r--src/plugins/find/findplugin.cpp8
-rw-r--r--src/plugins/find/findplugin.h2
-rw-r--r--src/plugins/find/findtoolbar.cpp29
-rw-r--r--src/plugins/find/findtoolbar.h2
-rw-r--r--src/plugins/find/findtoolwindow.cpp2
-rw-r--r--src/plugins/find/findwidget.ui28
-rw-r--r--src/plugins/find/ifindfilter.cpp174
-rw-r--r--src/plugins/find/ifindfilter.h2
-rw-r--r--src/plugins/find/ifindsupport.h2
-rw-r--r--src/plugins/find/searchresultwindow.cpp242
-rw-r--r--src/plugins/find/searchresultwindow.h2
-rw-r--r--src/plugins/find/textfindconstants.h2
-rw-r--r--src/plugins/help/helpfindsupport.h4
-rw-r--r--src/plugins/help/helpplugin.cpp74
-rw-r--r--src/plugins/help/helpplugin.h7
-rw-r--r--src/plugins/perforce/perforcesettings.cpp19
-rw-r--r--src/plugins/perforce/perforcesettings.h3
-rw-r--r--src/plugins/perforce/perforceversioncontrol.cpp8
-rw-r--r--src/plugins/perforce/perforceversioncontrol.h1
-rw-r--r--src/plugins/perforce/settingspage.cpp2
-rw-r--r--src/plugins/perforce/settingspage.ui17
-rw-r--r--src/plugins/projectexplorer/allprojectsfind.cpp2
-rw-r--r--src/plugins/projectexplorer/allprojectsfind.h2
-rw-r--r--src/plugins/projectexplorer/buildconfigdialog.cpp1
-rw-r--r--src/plugins/projectexplorer/buildmanager.cpp44
-rw-r--r--src/plugins/projectexplorer/buildmanager.h10
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.cpp57
-rw-r--r--src/plugins/projectexplorer/compileoutputwindow.h7
-rw-r--r--src/plugins/projectexplorer/currentprojectfind.cpp2
-rw-r--r--src/plugins/projectexplorer/currentprojectfind.h2
-rw-r--r--src/plugins/projectexplorer/customexecutablerunconfiguration.cpp1
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizard.cpp7
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizard.pri6
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardparameters.cpp20
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardparameters.h1
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardpreprocessor.cpp268
-rw-r--r--src/plugins/projectexplorer/customwizard/customwizardpreprocessor.h58
-rw-r--r--src/plugins/projectexplorer/miniprojecttargetselector.cpp1
-rw-r--r--src/plugins/projectexplorer/outputformatter.cpp19
-rw-r--r--src/plugins/projectexplorer/outputformatter.h6
-rw-r--r--src/plugins/projectexplorer/outputwindow.cpp91
-rw-r--r--src/plugins/projectexplorer/outputwindow.h25
-rw-r--r--src/plugins/projectexplorer/project.h1
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp206
-rw-r--r--src/plugins/projectexplorer/projectexplorer.h9
-rw-r--r--src/plugins/projectexplorer/projectexplorerconstants.h4
-rw-r--r--src/plugins/projectexplorer/projectexplorersettings.h11
-rw-r--r--src/plugins/projectexplorer/projectexplorersettingspage.cpp6
-rw-r--r--src/plugins/projectexplorer/projectexplorersettingspage.ui11
-rw-r--r--src/plugins/projectexplorer/projectnodes.cpp6
-rw-r--r--src/plugins/projectexplorer/projectnodes.h2
-rw-r--r--src/plugins/projectexplorer/projectwelcomepagewidget.cpp7
-rw-r--r--src/plugins/projectexplorer/projectwindow.cpp1
-rw-r--r--src/plugins/projectexplorer/projectwindow.h12
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp22
-rw-r--r--src/plugins/projectexplorer/runconfiguration.h5
-rw-r--r--src/plugins/projectexplorer/runsettingspropertiespage.h1
-rw-r--r--src/plugins/projectexplorer/target.h2
-rw-r--r--src/plugins/projectexplorer/targetsettingspanel.cpp1
-rw-r--r--src/plugins/projectexplorer/targetsettingspanel.h5
-rw-r--r--src/plugins/projectexplorer/taskhub.h2
-rw-r--r--src/plugins/projectexplorer/taskwindow.h4
-rw-r--r--src/plugins/projectexplorer/vcsannotatetaskhandler.h3
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.cpp55
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.h51
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp19
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/qml/ItemView.qml2
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/contextpanetextwidget.cpp16
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.cpp27
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.h4
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditor.pri6
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp65
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h59
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp27
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h3
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp5
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateslist.qml2
-rw-r--r--src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h3
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp12
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/metainfoparser.cpp4
-rw-r--r--src/plugins/qmldesigner/designersettings.cpp2
-rw-r--r--src/plugins/qmldesigner/qmlcontextpane.cpp62
-rw-r--r--src/plugins/qmldesigner/qmlcontextpane.h2
-rw-r--r--src/plugins/qmldesigner/settingspage.ui4
-rw-r--r--src/plugins/qmljseditor/qmljscodecompletion.cpp17
-rw-r--r--src/plugins/qmljseditor/qmljscodecompletion.h3
-rw-r--r--src/plugins/qmljseditor/qmljseditor.cpp196
-rw-r--r--src/plugins/qmljseditor/qmljseditor.h26
-rw-r--r--src/plugins/qmljseditor/qmljseditor.pro8
-rw-r--r--src/plugins/qmljseditor/qmljseditorconstants.h2
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.cpp23
-rw-r--r--src/plugins/qmljseditor/qmljseditorplugin.h3
-rw-r--r--src/plugins/qmljseditor/qmljshighlighter.cpp2
-rw-r--r--src/plugins/qmljseditor/qmljshighlighter.h3
-rw-r--r--src/plugins/qmljseditor/qmljshoverhandler.cpp2
-rw-r--r--src/plugins/qmljseditor/qmljsoutline.cpp303
-rw-r--r--src/plugins/qmljseditor/qmljsoutline.h46
-rw-r--r--src/plugins/qmljseditor/qmloutlinemodel.cpp337
-rw-r--r--src/plugins/qmljseditor/qmloutlinemodel.h55
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.cpp (renamed from src/plugins/qmlprojectmanager/qmltaskmanager.cpp)11
-rw-r--r--src/plugins/qmljseditor/qmltaskmanager.h (renamed from src/plugins/qmlprojectmanager/qmltaskmanager.h)6
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorplugin.cpp3
-rw-r--r--src/plugins/qmljsinspector/qmljsinspectorplugin.h2
-rw-r--r--src/plugins/qmlprojectmanager/images/qml_wizard.pngbin556 -> 586 bytes
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp9
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectconstants.h2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.cpp1
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectmanager.pro8
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.cpp18
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectplugin.h4
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp2
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp6
-rw-r--r--src/plugins/qmlprojectmanager/qmlprojectruncontrol.h2
-rw-r--r--src/plugins/qt4projectmanager/gettingstartedwelcomepagewidget.h2
-rw-r--r--src/plugins/qt4projectmanager/profilehighlighter.cpp2
-rw-r--r--src/plugins/qt4projectmanager/profilehighlighter.h3
-rw-r--r--src/plugins/qt4projectmanager/projectloadwizard.cpp1
-rw-r--r--src/plugins/qt4projectmanager/projectloadwizard.h6
-rw-r--r--src/plugins/qt4projectmanager/qmakeparser.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.cpp112
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.h18
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeployables.cpp23
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeployables.h10
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.cpp473
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.h146
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.cpp82
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.h49
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.cpp72
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.h39
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.ui34
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.cpp15
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.h4
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoglobal.cpp50
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoglobal.h65
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemomanager.cpp4
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemomanager.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp23
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h10
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp22
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.h1
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.ui29
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.cpp89
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.h13
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp475
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h75
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemorunfactories.cpp5
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.cpp91
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.h13
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemosshconfigdialog.cpp18
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemosshthread.cpp215
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/maemosshthread.h132
-rw-r--r--src/plugins/qt4projectmanager/qt-maemo/qt-maemo.pri17
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp6
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h2
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp6
-rw-r--r--src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h2
-rw-r--r--src/plugins/qt4projectmanager/qt4nodes.cpp2
-rw-r--r--src/plugins/qt4projectmanager/qt4projectmanager.cpp2
-rw-r--r--src/plugins/qt4projectmanager/qt4projectmanager.pro6
-rw-r--r--src/plugins/qt4projectmanager/qt4runconfiguration.cpp6
-rw-r--r--src/plugins/qt4projectmanager/qt4runconfiguration.h2
-rw-r--r--src/plugins/qt4projectmanager/qt4target.cpp6
-rw-r--r--src/plugins/qt4projectmanager/qtoptionspage.h2
-rw-r--r--src/plugins/qt4projectmanager/qtoutputformatter.cpp (renamed from src/plugins/qmlprojectmanager/qmloutputformatter.cpp)90
-rw-r--r--src/plugins/qt4projectmanager/qtoutputformatter.h (renamed from src/plugins/qmlprojectmanager/qmloutputformatter.h)21
-rw-r--r--src/plugins/qt4projectmanager/qtversionmanager.h2
-rw-r--r--src/plugins/texteditor/basetextdocument.cpp12
-rw-r--r--src/plugins/texteditor/basetextdocument.h9
-rw-r--r--src/plugins/texteditor/basetextdocumentlayout.cpp40
-rw-r--r--src/plugins/texteditor/basetextdocumentlayout.h21
-rw-r--r--src/plugins/texteditor/basetexteditor.cpp53
-rw-r--r--src/plugins/texteditor/basetexteditor.h5
-rw-r--r--src/plugins/texteditor/completionsupport.cpp2
-rw-r--r--src/plugins/texteditor/completionwidget.cpp15
-rw-r--r--src/plugins/texteditor/completionwidget.h3
-rw-r--r--src/plugins/texteditor/displaysettings.cpp7
-rw-r--r--src/plugins/texteditor/displaysettings.h1
-rw-r--r--src/plugins/texteditor/displaysettingspage.cpp5
-rw-r--r--src/plugins/texteditor/displaysettingspage.ui7
-rw-r--r--src/plugins/texteditor/findincurrentfile.cpp2
-rw-r--r--src/plugins/texteditor/findincurrentfile.h2
-rw-r--r--src/plugins/texteditor/findinfiles.cpp2
-rw-r--r--src/plugins/texteditor/findinfiles.h2
-rw-r--r--src/plugins/texteditor/generichighlighter/highlighter.cpp2
-rw-r--r--src/plugins/texteditor/generichighlighter/highlighter.h5
-rw-r--r--src/plugins/texteditor/icompletioncollector.h25
-rw-r--r--src/plugins/texteditor/outlinefactory.cpp5
-rw-r--r--src/plugins/texteditor/quickfix.cpp2
-rw-r--r--src/plugins/texteditor/quickfix.h2
-rw-r--r--src/plugins/texteditor/refactoringchanges.cpp22
-rw-r--r--src/plugins/texteditor/refactoringchanges.h7
-rw-r--r--src/plugins/texteditor/syntaxhighlighter.cpp743
-rw-r--r--src/plugins/texteditor/syntaxhighlighter.h111
-rw-r--r--src/plugins/texteditor/tabsettings.h1
-rw-r--r--src/plugins/texteditor/texteditor.pro2
-rw-r--r--src/plugins/texteditor/texteditorplugin.cpp2
-rw-r--r--src/plugins/vcsbase/baseannotationhighlighter.cpp2
-rw-r--r--src/plugins/vcsbase/baseannotationhighlighter.h4
-rw-r--r--src/plugins/vcsbase/diffhighlighter.cpp2
-rw-r--r--src/plugins/vcsbase/diffhighlighter.h5
-rw-r--r--src/plugins/vcsbase/vcsbaseeditor.cpp6
321 files changed, 14477 insertions, 3286 deletions
diff --git a/src/plugins/bineditor/bineditor.cpp b/src/plugins/bineditor/bineditor.cpp
index 0a6dbcfbb2..23b5d04ffb 100644
--- a/src/plugins/bineditor/bineditor.cpp
+++ b/src/plugins/bineditor/bineditor.cpp
@@ -1042,7 +1042,7 @@ bool BinEditor::event(QEvent *e) {
selEnd = selStart + 1;
byteCount = 1;
}
- if (byteCount <= 8) {
+ if (m_hexCursor && byteCount <= 8) {
const QPoint &startPoint = offsetToPos(selStart);
const QPoint &endPoint = offsetToPos(selEnd);
const QPoint expandedEndPoint
@@ -1138,14 +1138,25 @@ void BinEditor::keyPressEvent(QKeyEvent *e)
} break;
case Qt::Key_Home:
- setCursorPosition((e->modifiers() & Qt::ControlModifier) ?
- 0 : (m_cursorPosition/16 * 16), moveMode);
+ if (e->modifiers() & Qt::ControlModifier) {
+ if (m_inLazyMode)
+ emit startOfFileRequested(editorInterface());
+ else
+ setCursorPosition(0);
+ } else {
+ setCursorPosition(m_cursorPosition/16 * 16, moveMode);
+ }
break;
case Qt::Key_End:
- setCursorPosition((e->modifiers() & Qt::ControlModifier) ?
- (m_size-1) : (m_cursorPosition/16 * 16 + 15), moveMode);
+ if (e->modifiers() & Qt::ControlModifier) {
+ if (m_inLazyMode)
+ emit endOfFileRequested(editorInterface());
+ else
+ setCursorPosition(m_size - 1);
+ } else {
+ setCursorPosition(m_cursorPosition/16 * 16 + 15, moveMode);
+ }
break;
-
default:
if (m_readOnly)
break;
@@ -1380,7 +1391,7 @@ void BinEditor::jumpToAddress(quint64 address)
{
if (address >= m_baseAddr && address < m_baseAddr + m_data.size())
setCursorPosition(address - m_baseAddr);
- else
+ else if (m_inLazyMode)
emit newRangeRequested(editorInterface(), address);
}
@@ -1392,7 +1403,7 @@ void BinEditor::setNewWindowRequestAllowed()
QPoint BinEditor::offsetToPos(int offset)
{
const int x = m_labelWidth + (offset % 16) * m_columnWidth;
- const int y = (offset / 16) * m_lineHeight;
+ const int y = (offset / 16 - verticalScrollBar()->value()) * m_lineHeight;
return QPoint(x, y);
}
diff --git a/src/plugins/bineditor/bineditor.h b/src/plugins/bineditor/bineditor.h
index cea7b8d168..70241a7e63 100644
--- a/src/plugins/bineditor/bineditor.h
+++ b/src/plugins/bineditor/bineditor.h
@@ -84,7 +84,7 @@ public:
};
int cursorPosition() const;
- void setCursorPosition(int pos, MoveMode moveMode = MoveAnchor);
+ Q_INVOKABLE void setCursorPosition(int pos, MoveMode moveMode = MoveAnchor);
void jumpToAddress(quint64 address);
void setModified(bool);
@@ -133,6 +133,8 @@ Q_SIGNALS:
void lazyDataRequested(Core::IEditor *editor, quint64 block, bool synchronous);
void newWindowRequested(quint64 address);
void newRangeRequested(Core::IEditor *, quint64 address);
+ void startOfFileRequested(Core::IEditor *);
+ void endOfFileRequested(Core::IEditor *);
protected:
void scrollContentsBy(int dx, int dy);
diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp
index b6fa501118..b7858db4f3 100644
--- a/src/plugins/bineditor/bineditorplugin.cpp
+++ b/src/plugins/bineditor/bineditorplugin.cpp
@@ -154,6 +154,8 @@ public:
return result;
}
+ void replace(const QString &, const QString &,
+ Find::IFindSupport::FindFlags) { }
bool replaceStep(const QString &, const QString &,
Find::IFindSupport::FindFlags) { return false;}
int replaceAll(const QString &, const QString &,
@@ -180,6 +182,10 @@ public:
this, SLOT(provideData(Core::IEditor *, quint64)));
connect(m_editor, SIGNAL(newRangeRequested(Core::IEditor*,quint64)),
this, SLOT(provideNewRange(Core::IEditor*,quint64)));
+ connect(m_editor, SIGNAL(startOfFileRequested(Core::IEditor*)), this,
+ SLOT(handleStartOfFileRequested(Core::IEditor*)));
+ connect(m_editor, SIGNAL(endOfFileRequested(Core::IEditor*)), this,
+ SLOT(handleEndOfFileRequested(Core::IEditor*)));
}
~BinEditorFile() {}
@@ -209,7 +215,7 @@ public:
&& file.open(QIODevice::ReadOnly)) {
m_fileName = fileName;
qint64 maxRange = 64 * 1024 * 1024;
- if (file.isSequential() && file.size() <= maxRange) {
+ if (file.size() <= maxRange) {
m_editor->setData(file.readAll());
} else {
m_editor->setLazyData(offset, maxRange);
@@ -241,6 +247,14 @@ private slots:
open(m_fileName, offset);
}
+ void handleStartOfFileRequested(Core::IEditor *) {
+ open(m_fileName, 0);
+ }
+
+ void handleEndOfFileRequested(Core::IEditor *) {
+ open(m_fileName, QFileInfo(m_fileName).size() - 1);
+ }
+
public:
void setFilename(const QString &filename) {
diff --git a/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp b/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp
index b42a570586..39172a36a4 100644
--- a/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp
+++ b/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp
@@ -46,7 +46,7 @@ static bool isVariable(const QString &word)
CMakeHighlighter::CMakeHighlighter(QTextDocument *document) :
- QSyntaxHighlighter(document)
+ TextEditor::SyntaxHighlighter(document)
{
}
diff --git a/src/plugins/cmakeprojectmanager/cmakehighlighter.h b/src/plugins/cmakeprojectmanager/cmakehighlighter.h
index dce5acf057..5c76a21807 100644
--- a/src/plugins/cmakeprojectmanager/cmakehighlighter.h
+++ b/src/plugins/cmakeprojectmanager/cmakehighlighter.h
@@ -30,6 +30,7 @@
#ifndef CMAKEHIGHLIGHTER_H
#define CMAKEHIGHLIGHTER_H
+#include <texteditor/syntaxhighlighter.h>
#include <QtCore/QtAlgorithms>
#include <QtGui/QSyntaxHighlighter>
#include <QtGui/QTextCharFormat>
@@ -40,7 +41,7 @@ namespace Internal {
/* This is a simple syntax highlighter for CMake files.
* It highlights variables, commands, strings and comments.
* Multi-line strings and variables inside strings are also recognized. */
-class CMakeHighlighter : public QSyntaxHighlighter
+class CMakeHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
public:
diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp
index 8e0107ed40..f589e8703c 100644
--- a/src/plugins/coreplugin/coreplugin.cpp
+++ b/src/plugins/coreplugin/coreplugin.cpp
@@ -34,7 +34,6 @@
#include "modemanager.h"
#include "fileiconprovider.h"
#include "designmode.h"
-#include "ssh/ne7sshobject.h"
#include <extensionsystem/pluginmanager.h>
@@ -89,7 +88,6 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
m_designMode = new DesignMode(editorManager);
addObject(m_designMode);
- Ne7SshObject::instance();
}
return success;
}
@@ -110,10 +108,10 @@ void CorePlugin::fileOpenRequest(const QString &f)
remoteCommand(QStringList(), QStringList(f));
}
-void CorePlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown()
{
m_mainWindow->aboutToShutdown();
- Ne7SshObject::removeInstance();
+ return SynchronousShutdown;
}
Q_EXPORT_PLUGIN(CorePlugin)
diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h
index bd60255047..af0f85f94b 100644
--- a/src/plugins/coreplugin/coreplugin.h
+++ b/src/plugins/coreplugin/coreplugin.h
@@ -49,7 +49,7 @@ public:
virtual bool initialize(const QStringList &arguments, QString *errorMessage = 0);
virtual void extensionsInitialized();
- virtual void aboutToShutdown();
+ virtual ShutdownFlag aboutToShutdown();
virtual void remoteCommand(const QStringList & /* options */, const QStringList &args);
public slots:
diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro
index 3b607d037b..ad81f39dc6 100644
--- a/src/plugins/coreplugin/coreplugin.pro
+++ b/src/plugins/coreplugin/coreplugin.pro
@@ -85,10 +85,27 @@ SOURCES += mainwindow.cpp \
editormanager/systemeditor.cpp \
designmode.cpp \
editortoolbar.cpp \
- ssh/ne7sshobject.cpp \
- ssh/sshconnection.cpp \
+ helpmanager.cpp \
+ ssh/sshsendfacility.cpp \
+ ssh/sshremoteprocess.cpp \
+ ssh/sshpacketparser.cpp \
+ ssh/sshpacket.cpp \
+ ssh/sshoutgoingpacket.cpp \
ssh/sshkeygenerator.cpp \
- helpmanager.cpp
+ ssh/sshkeyexchange.cpp \
+ ssh/sshincomingpacket.cpp \
+ ssh/sshcryptofacility.cpp \
+ ssh/sshconnection.cpp \
+ ssh/sshchannelmanager.cpp \
+ ssh/sshchannel.cpp \
+ ssh/sshcapabilities.cpp \
+ ssh/sftppacket.cpp \
+ ssh/sftpoutgoingpacket.cpp \
+ ssh/sftpoperation.cpp \
+ ssh/sftpincomingpacket.cpp \
+ ssh/sftpdefs.cpp \
+ ssh/sftpchannel.cpp \
+ ssh/sshdelayedsignal.cpp
HEADERS += mainwindow.h \
editmode.h \
@@ -171,10 +188,33 @@ HEADERS += mainwindow.h \
editormanager/systemeditor.h \
designmode.h \
editortoolbar.h \
- ssh/ne7sshobject.h \
- ssh/sshconnection.h \
+ helpmanager.h \
+ ssh/sshsendfacility_p.h \
+ ssh/sshremoteprocess.h \
+ ssh/sshremoteprocess_p.h \
+ ssh/sshpacketparser_p.h \
+ ssh/sshpacket_p.h \
+ ssh/sshoutgoingpacket_p.h \
ssh/sshkeygenerator.h \
- helpmanager.h
+ ssh/sshkeyexchange_p.h \
+ ssh/sshincomingpacket_p.h \
+ ssh/sshexception_p.h \
+ ssh/ssherrors.h \
+ ssh/sshcryptofacility_p.h \
+ ssh/sshconnection.h \
+ ssh/sshconnection_p.h \
+ ssh/sshchannelmanager_p.h \
+ ssh/sshchannel_p.h \
+ ssh/sshcapabilities_p.h \
+ ssh/sshbotanconversions_p.h \
+ ssh/sftppacket_p.h \
+ ssh/sftpoutgoingpacket_p.h \
+ ssh/sftpoperation_p.h \
+ ssh/sftpincomingpacket_p.h \
+ ssh/sftpdefs.h \
+ ssh/sftpchannel.h \
+ ssh/sftpchannel_p.h \
+ ssh/sshdelayedsignal_p.h
FORMS += dialogs/newdialog.ui \
actionmanager/commandmappings.ui \
diff --git a/src/plugins/coreplugin/coreplugin_dependencies.pri b/src/plugins/coreplugin/coreplugin_dependencies.pri
index 8726e1be08..e908601b22 100644
--- a/src/plugins/coreplugin/coreplugin_dependencies.pri
+++ b/src/plugins/coreplugin/coreplugin_dependencies.pri
@@ -1,3 +1,3 @@
include(../../libs/extensionsystem/extensionsystem.pri)
include(../../libs/utils/utils.pri)
-include(../../libs/3rdparty/net7ssh/net7ssh.pri)
+include(../../libs/3rdparty/botan/botan.pri)
diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp
index a9eb40b686..fc3ee7d4e0 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.cpp
+++ b/src/plugins/coreplugin/editormanager/editormanager.cpp
@@ -1080,7 +1080,7 @@ IEditor *EditorManager::createEditor(const QString &editorId,
mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("text/plain"));
}
// open text files > 48 MB in binary editor
- if (fileInfo.size() > qint64(3) << 24 && mimeType.type().startsWith(QLatin1String("text")))
+ if (fileInfo.size() > maxTextFileSize() && mimeType.type().startsWith(QLatin1String("text")))
mimeType = m_d->m_core->mimeDatabase()->findByType(QLatin1String("application/octet-stream"));
factories = editorFactories(mimeType, true);
} else {
@@ -1389,13 +1389,22 @@ EditorManager::ReadOnlyAction
QWidget *parent,
bool displaySaveAsButton)
{
+ // Version Control: If automatic open is desired, open right away.
+ bool promptVCS = false;
+ if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation)) {
+ if (versionControl->settingsFlags() & IVersionControl::AutoOpen)
+ return RO_OpenVCS;
+ promptVCS = true;
+ }
+
+ // Create message box.
QMessageBox msgBox(QMessageBox::Question, tr("File is Read Only"),
- tr("The file %1 is read only.").arg(QDir::toNativeSeparators(fileName)),
+ tr("The file <i>%1</i> is read only.").arg(QDir::toNativeSeparators(fileName)),
QMessageBox::Cancel, parent);
- QPushButton *sccButton = 0;
- if (versionControl && versionControl->supportsOperation(IVersionControl::OpenOperation))
- sccButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
+ QPushButton *vcsButton = 0;
+ if (promptVCS)
+ vcsButton = msgBox.addButton(tr("Open with VCS (%1)").arg(versionControl->displayName()), QMessageBox::AcceptRole);
QPushButton *makeWritableButton = msgBox.addButton(tr("Make writable"), QMessageBox::AcceptRole);
@@ -1403,11 +1412,11 @@ EditorManager::ReadOnlyAction
if (displaySaveAsButton)
saveAsButton = msgBox.addButton(tr("Save as ..."), QMessageBox::ActionRole);
- msgBox.setDefaultButton(sccButton ? sccButton : makeWritableButton);
+ msgBox.setDefaultButton(vcsButton ? vcsButton : makeWritableButton);
msgBox.exec();
QAbstractButton *clickedButton = msgBox.clickedButton();
- if (clickedButton == sccButton)
+ if (clickedButton == vcsButton)
return RO_OpenVCS;
if (clickedButton == makeWritableButton)
return RO_MakeWriteable;
@@ -2004,5 +2013,10 @@ void EditorManager::gotoOtherSplit()
}
}
}
+
+qint64 EditorManager::maxTextFileSize()
+{
+ return (qint64(3) << 24);
+}
//===================EditorClosingCoreListener======================
diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h
index e488f040ea..541c5d3355 100644
--- a/src/plugins/coreplugin/editormanager/editormanager.h
+++ b/src/plugins/coreplugin/editormanager/editormanager.h
@@ -199,6 +199,8 @@ public:
QWidget *parent,
bool displaySaveAsButton = false);
+ static qint64 maxTextFileSize();
+
signals:
void currentEditorChanged(Core::IEditor *editor);
void editorCreated(Core::IEditor *editor, const QString &fileName);
diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp
index 2e6fa7f0ae..9242dc3db1 100644
--- a/src/plugins/coreplugin/editortoolbar.cpp
+++ b/src/plugins/coreplugin/editortoolbar.cpp
@@ -274,7 +274,7 @@ void EditorToolBar::listContextMenu(QPoint pos)
if (fileName.isEmpty())
return;
QMenu menu;
- menu.addAction(tr("Copy full path to clipboard"));
+ menu.addAction(tr("Copy Full Path to Clipboard"));
if (menu.exec(m_editorList->mapToGlobal(pos))) {
QApplication::clipboard()->setText(QDir::toNativeSeparators(fileName));
}
diff --git a/src/plugins/coreplugin/filemanager.cpp b/src/plugins/coreplugin/filemanager.cpp
index de29e822b7..de89e4dc8e 100644
--- a/src/plugins/coreplugin/filemanager.cpp
+++ b/src/plugins/coreplugin/filemanager.cpp
@@ -745,7 +745,11 @@ QStringList FileManager::getOpenFileNames(const QString &filters,
const QString pathIn,
QString *selectedFilter)
{
- const QString path = pathIn.isEmpty() ? fileDialogInitialDirectory() : pathIn;
+ QString path = pathIn;
+ if (path.isEmpty()) {
+ if (!d->m_currentFile.isEmpty())
+ path = QFileInfo(d->m_currentFile).absoluteFilePath();
+ }
const QStringList files = QFileDialog::getOpenFileNames(d->m_mainWindow,
tr("Open File"),
path, filters,
@@ -1009,7 +1013,7 @@ QString FileManager::currentFile() const
QString FileManager::fileDialogInitialDirectory() const
{
if (!d->m_currentFile.isEmpty())
- return QFileInfo(d->m_currentFile).absoluteFilePath();
+ return QFileInfo(d->m_currentFile).absolutePath();
return d->m_lastVisitedDirectory;
}
diff --git a/src/plugins/coreplugin/helpmanager.cpp b/src/plugins/coreplugin/helpmanager.cpp
index 7d2ebc92c4..02bd88acff 100644
--- a/src/plugins/coreplugin/helpmanager.cpp
+++ b/src/plugins/coreplugin/helpmanager.cpp
@@ -240,6 +240,13 @@ QUrl HelpManager::findFile(const QUrl &url) const
return m_helpEngine->findFile(url);
}
+QByteArray HelpManager::fileData(const QUrl &url) const
+{
+ if (m_needsSetup)
+ return QByteArray();
+ return m_helpEngine->fileData(url);
+}
+
void HelpManager::handleHelpRequest(const QString &url)
{
emit helpRequested(QUrl(url));
diff --git a/src/plugins/coreplugin/helpmanager.h b/src/plugins/coreplugin/helpmanager.h
index 7df6c55659..a309643092 100644
--- a/src/plugins/coreplugin/helpmanager.h
+++ b/src/plugins/coreplugin/helpmanager.h
@@ -38,6 +38,7 @@
#include <QtCore/QStringList>
#include <QtCore/QUrl>
#include <QtCore/QVariant>
+#include <QtCore/QByteArray>
QT_FORWARD_DECLARE_CLASS(QHelpEngineCore)
QT_FORWARD_DECLARE_CLASS(QSqlQuery)
@@ -64,6 +65,7 @@ public:
QStringList findKeywords(const QString &key, int maxHits = INT_MAX) const;
QUrl findFile(const QUrl &url) const;
+ QByteArray fileData(const QUrl &url) const;
void handleHelpRequest(const QString &url);
QStringList registeredNamespaces() const;
diff --git a/src/plugins/coreplugin/iversioncontrol.h b/src/plugins/coreplugin/iversioncontrol.h
index 0cd29f9438..c402c1fb77 100644
--- a/src/plugins/coreplugin/iversioncontrol.h
+++ b/src/plugins/coreplugin/iversioncontrol.h
@@ -34,13 +34,20 @@
#include <QtCore/QObject>
#include <QtCore/QString>
+#include <QtCore/QFlags>
namespace Core {
class CORE_EXPORT IVersionControl : public QObject
{
Q_OBJECT
+ Q_ENUMS(SettingsFlag Operation)
public:
+ enum SettingsFlag {
+ AutoOpen = 0x1
+ };
+ Q_DECLARE_FLAGS(SettingsFlags, SettingsFlag)
+
enum Operation {
AddOperation, DeleteOperation, OpenOperation, MoveOperation,
CreateRepositoryOperation,
@@ -77,6 +84,12 @@ public:
virtual bool vcsOpen(const QString &fileName) = 0;
/*!
+ * Returns settings.
+ */
+
+ virtual SettingsFlags settingsFlags() const { return 0; }
+
+ /*!
* Called after a file has been added to a project If the version control
* needs to know which files it needs to track you should reimplement this
* function, e.g. 'p4 add', 'cvs add', 'svn add'.
@@ -137,6 +150,8 @@ signals:
// virtual bool sccManaged(const QString &filename) = 0;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(Core::IVersionControl::SettingsFlags)
+
} // namespace Core
#endif // IVERSIONCONTROL_H
diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp
index e2edbfce8b..e8dfa3af65 100644
--- a/src/plugins/coreplugin/mainwindow.cpp
+++ b/src/plugins/coreplugin/mainwindow.cpp
@@ -70,6 +70,7 @@
#include <coreplugin/settingsdatabase.h>
#include <utils/pathchooser.h>
#include <utils/stylehelper.h>
+#include <utils/stringutils.h>
#include <extensionsystem/pluginmanager.h>
#include <QtCore/QDebug>
@@ -238,7 +239,6 @@ void MainWindow::setOverrideColor(const QColor &color)
MainWindow::~MainWindow()
{
- hide();
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
pm->removeObject(m_shortcutSettings);
pm->removeObject(m_generalSettings);
@@ -1134,6 +1134,7 @@ void MainWindow::aboutToShutdown()
disconnect(QApplication::instance(), SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(updateFocusWidget(QWidget*,QWidget*)));
m_activeContext = 0;
+ hide();
}
static const char *settingsGroup = "MainWindow";
@@ -1253,7 +1254,8 @@ void MainWindow::aboutToShowRecentFiles()
bool hasRecentFiles = false;
foreach (const QString &fileName, m_fileManager->recentFiles()) {
hasRecentFiles = true;
- QAction *action = aci->menu()->addAction(fileName);
+ QAction *action = aci->menu()->addAction(
+ Utils::withTildeHomePath(fileName));
action->setData(fileName);
connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
}
diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp
index 05fc9dacd4..882a642ba5 100644
--- a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp
+++ b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp
@@ -95,7 +95,7 @@ void Core::Internal::ProgressManagerPrivate::setApplicationLabel(const QString &
font.setPointSize(font.pointSize()-2);
p.setFont(font);
p.drawText(QRect(QPoint(0,0), pix.size()), Qt::AlignHCenter|Qt::AlignCenter, text);
- pITask->SetOverlayIcon(winId, pix.toWinHICON(), text.utf16());
+ pITask->SetOverlayIcon(winId, pix.toWinHICON(), (wchar_t*)text.utf16());
}
}
diff --git a/src/plugins/coreplugin/ssh/ne7sshobject.cpp b/src/plugins/coreplugin/ssh/ne7sshobject.cpp
deleted file mode 100644
index 9f94a55b97..0000000000
--- a/src/plugins/coreplugin/ssh/ne7sshobject.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of Qt Creator.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "ne7sshobject.h"
-
-#include <QtCore/QMutexLocker>
-
-#include <ne7ssh.h>
-
-namespace Core {
-namespace Internal {
-
-Ne7SshObject *Ne7SshObject::instance()
-{
- if (!m_instance)
- m_instance = new Ne7SshObject;
- return m_instance;
-}
-
-void Ne7SshObject::removeInstance()
-{
- delete m_instance;
-}
-
-Ne7SshObject::Ptr Ne7SshObject::get()
-{
- QMutexLocker locker(&m_mutex);
- QSharedPointer<ne7ssh> shared = m_weakRef.toStrongRef();
- if (!shared) {
- shared = QSharedPointer<ne7ssh>(new ne7ssh);
- m_weakRef = shared;
- }
- return shared;
-}
-
-Ne7SshObject::Ne7SshObject()
-{
-}
-
-Ne7SshObject *Ne7SshObject::m_instance = 0;
-
-} // namespace Internal
-} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/ne7sshobject.h b/src/plugins/coreplugin/ssh/ne7sshobject.h
deleted file mode 100644
index 77d601ec86..0000000000
--- a/src/plugins/coreplugin/ssh/ne7sshobject.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of Qt Creator.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef NE7SSHOBJECT_H
-#define NE7SSHOBJECT_H
-
-#include <coreplugin/core_global.h>
-
-#include <QtCore/QMutex>
-#include <QtCore/QSharedPointer>
-#include <QtCore/QWeakPointer>
-
-class ne7ssh;
-
-namespace Core {
-namespace Internal {
-
-class Ne7SshObject
-{
-public:
- typedef QSharedPointer<ne7ssh> Ptr;
-
- static Ne7SshObject *instance();
- static void removeInstance();
-
- Ptr get();
-
-private:
- Ne7SshObject();
- Ne7SshObject(const Ne7SshObject &);
- Ne7SshObject &operator=(const Ne7SshObject &);
-
- static Ne7SshObject *m_instance;
-
- QWeakPointer<ne7ssh> m_weakRef;
- QMutex m_mutex;
-};
-
-} // namespace Internal
-} // namespace Core
-
-#endif // NE7SSHOBJECT_H
diff --git a/src/plugins/coreplugin/ssh/sftpchannel.cpp b/src/plugins/coreplugin/ssh/sftpchannel.cpp
new file mode 100644
index 0000000000..2cb31a3bce
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpchannel.cpp
@@ -0,0 +1,756 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftpchannel.h"
+#include "sftpchannel_p.h"
+
+#include "sshdelayedsignal_p.h"
+#include "sshexception_p.h"
+#include "sshsendfacility_p.h"
+
+#include <QtCore/QFile>
+#include <QtCore/QWeakPointer>
+
+namespace Core {
+
+namespace Internal {
+namespace {
+ const quint32 ProtocolVersion = 3;
+
+ QString errorMessage(const QString &serverMessage,
+ const QString &alternativeMessage)
+ {
+ return serverMessage.isEmpty() ? alternativeMessage : serverMessage;
+ }
+
+ QString errorMessage(const SftpStatusResponse &response,
+ const QString &alternativeMessage)
+ {
+ return response.status == SSH_FX_OK ? QString()
+ : errorMessage(response.errorString, alternativeMessage);
+ }
+} // anonymous namespace
+} // namespace Internal
+
+SftpChannel::SftpChannel(quint32 channelId,
+ Internal::SshSendFacility &sendFacility)
+ : d(new Internal::SftpChannelPrivate(channelId, sendFacility, this))
+{
+}
+
+SftpChannel::State SftpChannel::state() const
+{
+ switch (d->channelState()) {
+ case Internal::AbstractSshChannel::Inactive:
+ return Uninitialized;
+ case Internal::AbstractSshChannel::SessionRequested:
+ return Initializing;
+ case Internal::AbstractSshChannel::CloseRequested:
+ return Closing;
+ case Internal::AbstractSshChannel::Closed:
+ return Closed;
+ case Internal::AbstractSshChannel::SessionEstablished:
+ return d->m_sftpState == Internal::SftpChannelPrivate::Initialized
+ ? Initialized : Initializing;
+ default:
+ Q_ASSERT(!"Oh no, we forgot to handle a channel state!");
+ return Closed; // For the compiler.
+ }
+}
+
+void SftpChannel::initialize()
+{
+ d->requestSessionStart();
+ d->m_sftpState = Internal::SftpChannelPrivate::SubsystemRequested;
+}
+
+void SftpChannel::closeChannel()
+{
+ d->closeChannel();
+}
+
+SftpJobId SftpChannel::listDirectory(const QString &path)
+{
+ return d->createJob(Internal::SftpListDir::Ptr(
+ new Internal::SftpListDir(++d->m_nextJobId, path)));
+}
+
+SftpJobId SftpChannel::createDirectory(const QString &path)
+{
+ return d->createJob(Internal::SftpMakeDir::Ptr(
+ new Internal::SftpMakeDir(++d->m_nextJobId, path)));
+}
+
+SftpJobId SftpChannel::removeDirectory(const QString &path)
+{
+ return d->createJob(Internal::SftpRmDir::Ptr(
+ new Internal::SftpRmDir(++d->m_nextJobId, path)));
+}
+
+SftpJobId SftpChannel::removeFile(const QString &path)
+{
+ return d->createJob(Internal::SftpRm::Ptr(
+ new Internal::SftpRm(++d->m_nextJobId, path)));
+}
+
+SftpJobId SftpChannel::renameFileOrDirectory(const QString &oldPath,
+ const QString &newPath)
+{
+ return d->createJob(Internal::SftpRename::Ptr(
+ new Internal::SftpRename(++d->m_nextJobId, oldPath, newPath)));
+}
+
+SftpJobId SftpChannel::createFile(const QString &path, SftpOverwriteMode mode)
+{
+ return d->createJob(Internal::SftpCreateFile::Ptr(
+ new Internal::SftpCreateFile(++d->m_nextJobId, path, mode)));
+}
+
+SftpJobId SftpChannel::uploadFile(const QString &localFilePath,
+ const QString &remoteFilePath, SftpOverwriteMode mode)
+{
+ QSharedPointer<QFile> localFile(new QFile(localFilePath));
+ if (!localFile->open(QIODevice::ReadOnly))
+ return SftpInvalidJob;
+ return d->createJob(Internal::SftpUpload::Ptr(
+ new Internal::SftpUpload(++d->m_nextJobId, remoteFilePath, localFile, mode)));
+}
+
+SftpJobId SftpChannel::downloadFile(const QString &remoteFilePath,
+ const QString &localFilePath, SftpOverwriteMode mode)
+{
+ QSharedPointer<QFile> localFile(new QFile(localFilePath));
+ if (mode == SftpSkipExisting && localFile->exists())
+ return SftpInvalidJob;
+ QIODevice::OpenMode openMode = QIODevice::WriteOnly;
+ if (mode == SftpOverwriteExisting)
+ openMode |= QIODevice::Truncate;
+ else if (mode == SftpAppendToExisting)
+ openMode |= QIODevice::Append;
+ if (!localFile->open(openMode))
+ return SftpInvalidJob;
+ return d->createJob(Internal::SftpDownload::Ptr(
+ new Internal::SftpDownload(++d->m_nextJobId, remoteFilePath, localFile)));
+}
+
+SftpChannel::~SftpChannel()
+{
+ delete d;
+}
+
+
+namespace Internal {
+
+SftpChannelPrivate::SftpChannelPrivate(quint32 channelId,
+ SshSendFacility &sendFacility, SftpChannel *sftp)
+ : AbstractSshChannel(channelId, sendFacility),
+ m_nextJobId(0), m_sftpState(Inactive), m_sftp(sftp)
+{
+}
+
+SftpJobId SftpChannelPrivate::createJob(const AbstractSftpOperation::Ptr &job)
+{
+ if (m_sftp->state() != SftpChannel::Initialized)
+ return SftpInvalidJob;
+ m_jobs.insert(job->jobId, job);
+ sendData(job->initialPacket(m_outgoingPacket).rawData());
+ return job->jobId;
+}
+
+void SftpChannelPrivate::handleChannelSuccess()
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("sftp subsystem initialized");
+#endif
+ sendData(m_outgoingPacket.generateInit(ProtocolVersion).rawData());
+ m_sftpState = InitSent;
+}
+
+void SftpChannelPrivate::handleChannelFailure()
+{
+ if (m_sftpState != SubsystemRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_FAILURE packet.");
+ }
+ createDelayedInitFailedSignal(SSH_TR("Server could not start sftp subsystem."));
+ closeChannel();
+}
+
+void SftpChannelPrivate::handleChannelDataInternal(const QByteArray &data)
+{
+ m_incomingData += data;
+ m_incomingPacket.consumeData(m_incomingData);
+ while (m_incomingPacket.isComplete()) {
+ handleCurrentPacket();
+ m_incomingPacket.clear();
+ m_incomingPacket.consumeData(m_incomingData);
+ }
+}
+
+void SftpChannelPrivate::handleChannelExtendedDataInternal(quint32 type,
+ const QByteArray &data)
+{
+ qWarning("Unexpected extended data '%s' of type %d on SFTP channel.",
+ data.data(), type);
+}
+
+void SftpChannelPrivate::handleCurrentPacket()
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Handling SFTP packet of type %d", m_incomingPacket.type());
+#endif
+ switch (m_incomingPacket.type()) {
+ case SSH_FXP_VERSION:
+ handleServerVersion();
+ break;
+ case SSH_FXP_HANDLE:
+ handleHandle();
+ break;
+ case SSH_FXP_NAME:
+ handleName();
+ break;
+ case SSH_FXP_STATUS:
+ handleStatus();
+ break;
+ case SSH_FXP_DATA:
+ handleReadData();
+ break;
+ case SSH_FXP_ATTRS:
+ handleAttrs();
+ break;
+ default:
+ throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected packet.",
+ SSH_TR("Unexpected packet of type %d.").arg(m_incomingPacket.type()));
+ }
+}
+
+void SftpChannelPrivate::handleServerVersion()
+{
+ checkChannelActive();
+ if (m_sftpState != InitSent) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_VERSION packet.");
+ }
+
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("sftp init received");
+#endif
+ const quint32 serverVersion = m_incomingPacket.extractServerVersion();
+ if (serverVersion != ProtocolVersion) {
+ createDelayedInitFailedSignal(SSH_TR("Protocol version mismatch: Expected %1, got %2")
+ .arg(serverVersion).arg(ProtocolVersion));
+ closeChannel();
+ } else {
+ m_sftpState = Initialized;
+ createDelayedInitializedSignal();
+ }
+}
+
+void SftpChannelPrivate::handleHandle()
+{
+ const SftpHandleResponse &response = m_incomingPacket.asHandleResponse();
+ JobMap::Iterator it = lookupJob(response.requestId);
+ const QSharedPointer<AbstractSftpOperationWithHandle> job
+ = it.value().dynamicCast<AbstractSftpOperationWithHandle>();
+ if (job.isNull()) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_HANDLE packet.");
+ }
+ if (job->state != AbstractSftpOperationWithHandle::OpenRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_HANDLE packet.");
+ }
+ job->remoteHandle = response.handle;
+ job->state = AbstractSftpOperationWithHandle::Open;
+
+ switch (it.value()->type()) {
+ case AbstractSftpOperation::ListDir:
+ handleLsHandle(it);
+ break;
+ case AbstractSftpOperation::CreateFile:
+ handleCreateFileHandle(it);
+ break;
+ case AbstractSftpOperation::Download:
+ handleGetHandle(it);
+ break;
+ case AbstractSftpOperation::Upload:
+ handlePutHandle(it);
+ break;
+ default:
+ Q_ASSERT(!"Oh no, I forgot to handle an SFTP operation type!");
+ }
+}
+
+void SftpChannelPrivate::handleLsHandle(const JobMap::Iterator &it)
+{
+ SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
+ sendData(m_outgoingPacket.generateReadDir(op->remoteHandle,
+ op->jobId).rawData());
+}
+
+void SftpChannelPrivate::handleCreateFileHandle(const JobMap::Iterator &it)
+{
+ SftpCreateFile::Ptr op = it.value().staticCast<SftpCreateFile>();
+ sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle,
+ op->jobId).rawData());
+}
+
+void SftpChannelPrivate::handleGetHandle(const JobMap::Iterator &it)
+{
+ SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
+ sendData(m_outgoingPacket.generateFstat(op->remoteHandle,
+ op->jobId).rawData());
+ op->statRequested = true;
+}
+
+void SftpChannelPrivate::handlePutHandle(const JobMap::Iterator &it)
+{
+ SftpUpload::Ptr op = it.value().staticCast<SftpUpload>();
+
+ // OpenSSH does not implement the RFC's append functionality, so we
+ // have to emulate it.
+ if (op->mode == SftpAppendToExisting) {
+ sendData(m_outgoingPacket.generateFstat(op->remoteHandle,
+ op->jobId).rawData());
+ op->statRequested = true;
+ } else {
+ spawnWriteRequests(it);
+ }
+}
+
+void SftpChannelPrivate::handleStatus()
+{
+ const SftpStatusResponse &response = m_incomingPacket.asStatusResponse();
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("%s: status = %d", Q_FUNC_INFO, response.status);
+#endif
+ JobMap::Iterator it = lookupJob(response.requestId);
+ switch (it.value()->type()) {
+ case AbstractSftpOperation::ListDir:
+ handleLsStatus(it, response);
+ break;
+ case AbstractSftpOperation::Download:
+ handleGetStatus(it, response);
+ break;
+ case AbstractSftpOperation::Upload:
+ handlePutStatus(it, response);
+ break;
+ case AbstractSftpOperation::MakeDir:
+ case AbstractSftpOperation::RmDir:
+ case AbstractSftpOperation::Rm:
+ case AbstractSftpOperation::Rename:
+ case AbstractSftpOperation::CreateFile:
+ handleStatusGeneric(it, response);
+ break;
+ }
+}
+
+void SftpChannelPrivate::handleStatusGeneric(const JobMap::Iterator &it,
+ const SftpStatusResponse &response)
+{
+ AbstractSftpOperation::Ptr op = it.value();
+ const QString error = errorMessage(response, SSH_TR("Unknown error."));
+ createDelayedJobFinishedSignal(op->jobId, error);
+ m_jobs.erase(it);
+}
+
+void SftpChannelPrivate::handleLsStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response)
+{
+ SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
+ switch (op->state) {
+ case SftpListDir::OpenRequested:
+ createDelayedJobFinishedSignal(op->jobId, errorMessage(response.errorString,
+ SSH_TR("Remote directory could not be opened for reading.")));
+ m_jobs.erase(it);
+ break;
+ case SftpListDir::Open:
+ if (response.status != SSH_FX_EOF)
+ reportRequestError(op, errorMessage(response.errorString,
+ SSH_TR("Failed to list remote directory contents.")));
+ op->state = SftpListDir::CloseRequested;
+ sendData(m_outgoingPacket.generateCloseHandle(op->remoteHandle,
+ op->jobId).rawData());
+ break;
+ case SftpListDir::CloseRequested:
+ if (!op->hasError) {
+ const QString error = errorMessage(response,
+ SSH_TR("Failed to close remote directory."));
+ createDelayedJobFinishedSignal(op->jobId, error);
+ }
+ m_jobs.erase(it);
+ break;
+ default:
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_STATUS packet.");
+ }
+}
+
+void SftpChannelPrivate::handleGetStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response)
+{
+ SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
+ switch (op->state) {
+ case SftpDownload::OpenRequested:
+ createDelayedJobFinishedSignal(op->jobId,
+ errorMessage(response.errorString,
+ SSH_TR("Failed to open remote file for reading.")));
+ m_jobs.erase(it);
+ break;
+ case SftpDownload::Open:
+ if (op->statRequested) {
+ reportRequestError(op, errorMessage(response.errorString,
+ SSH_TR("Failed to stat remote file.")));
+ sendTransferCloseHandle(op, response.requestId);
+ } else {
+ if ((response.status != SSH_FX_EOF || response.requestId != op->eofId)
+ && !op->hasError)
+ reportRequestError(op, errorMessage(response.errorString,
+ SSH_TR("Failed to read remote file.")));
+ finishTransferRequest(it);
+ }
+ break;
+ case SftpDownload::CloseRequested:
+ Q_ASSERT(op->inFlightCount == 1);
+ if (!op->hasError) {
+ if (response.status == SSH_FX_OK)
+ createDelayedJobFinishedSignal(op->jobId);
+ else
+ reportRequestError(op, errorMessage(response.errorString,
+ SSH_TR("Failed to close remote file.")));
+ }
+ removeTransferRequest(it);
+ break;
+ default:
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_STATUS packet.");
+ }
+}
+
+void SftpChannelPrivate::handlePutStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response)
+{
+ SftpUpload::Ptr job = it.value().staticCast<SftpUpload>();
+ switch (job->state) {
+ case SftpUpload::OpenRequested:
+ createDelayedJobFinishedSignal(job->jobId,
+ errorMessage(response.errorString,
+ SSH_TR("Failed to open remote file for writing.")));
+ m_jobs.erase(it);
+ break;
+ case SftpUpload::Open:
+ if (response.status == SSH_FX_OK) {
+ sendWriteRequest(it);
+ } else if(!job->hasError) {
+ reportRequestError(job, errorMessage(response.errorString,
+ SSH_TR("Failed to write remote file.")));
+ finishTransferRequest(it);
+ }
+ break;
+ case SftpUpload::CloseRequested:
+ Q_ASSERT(job->inFlightCount == 1);
+ if (!job->hasError) {
+ const QString error = errorMessage(response,
+ SSH_TR("Failed to close remote file."));
+ createDelayedJobFinishedSignal(job->jobId, error);
+ }
+ m_jobs.erase(it);
+ break;
+ default:
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_STATUS packet.");
+ }
+}
+
+void SftpChannelPrivate::handleName()
+{
+ const SftpNameResponse &response = m_incomingPacket.asNameResponse();
+ JobMap::Iterator it = lookupJob(response.requestId);
+ switch (it.value()->type()) {
+ case AbstractSftpOperation::ListDir: {
+ SftpListDir::Ptr op = it.value().staticCast<SftpListDir>();
+ if (op->state != SftpListDir::Open) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_NAME packet.");
+ }
+
+ for (int i = 0; i < response.files.count(); ++i) {
+ const SftpFile &file = response.files.at(i);
+ createDelayedDataAvailableSignal(op->jobId, file.fileName);
+ }
+ sendData(m_outgoingPacket.generateReadDir(op->remoteHandle,
+ op->jobId).rawData());
+ break;
+ }
+ default:
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_NAME packet.");
+ }
+}
+
+void SftpChannelPrivate::handleReadData()
+{
+ const SftpDataResponse &response = m_incomingPacket.asDataResponse();
+ JobMap::Iterator it = lookupJob(response.requestId);
+ if (it.value()->type() != AbstractSftpOperation::Download) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_DATA packet.");
+ }
+
+ SftpDownload::Ptr op = it.value().staticCast<SftpDownload>();
+ if (op->hasError) {
+ finishTransferRequest(it);
+ return;
+ }
+
+ if (!op->localFile->seek(op->offsets[response.requestId])) {
+ reportRequestError(op, op->localFile->errorString());
+ finishTransferRequest(it);
+ return;
+ }
+
+ if (op->localFile->write(response.data) != response.data.size()) {
+ reportRequestError(op, op->localFile->errorString());
+ finishTransferRequest(it);
+ return;
+ }
+
+ if (op->offset >= op->fileSize && op->fileSize != 0)
+ finishTransferRequest(it);
+ else
+ sendReadRequest(op, response.requestId);
+}
+
+void SftpChannelPrivate::handleAttrs()
+{
+ const SftpAttrsResponse &response = m_incomingPacket.asAttrsResponse();
+ JobMap::Iterator it = lookupJob(response.requestId);
+ AbstractSftpTransfer::Ptr transfer
+ = it.value().dynamicCast<AbstractSftpTransfer>();
+ if (!transfer || transfer->state != AbstractSftpTransfer::Open
+ || !transfer->statRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_FXP_ATTRS packet.");
+ }
+ Q_ASSERT(transfer->type() == AbstractSftpOperation::Upload
+ || transfer->type() == AbstractSftpOperation::Download);
+
+ if (transfer->type() == AbstractSftpOperation::Download) {
+ SftpDownload::Ptr op = transfer.staticCast<SftpDownload>();
+ if (response.attrs.sizePresent) {
+ op->fileSize = response.attrs.size;
+ } else {
+ op->fileSize = 0;
+ op->eofId = op->jobId;
+ }
+ op->statRequested = false;
+ spawnReadRequests(op);
+ } else {
+ SftpUpload::Ptr op = transfer.staticCast<SftpUpload>();
+ if (response.attrs.sizePresent) {
+ op->offset = response.attrs.size;
+ spawnWriteRequests(it);
+ } else {
+ reportRequestError(op, SSH_TR("Cannot append to remote file: "
+ "Server does not support file size attribute."));
+ sendTransferCloseHandle(op, op->jobId);
+ }
+ }
+}
+
+SftpChannelPrivate::JobMap::Iterator SftpChannelPrivate::lookupJob(SftpJobId id)
+{
+ JobMap::Iterator it = m_jobs.find(id);
+ if (it == m_jobs.end()) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid request id in SFTP packet.");
+ }
+ return it;
+}
+
+void SftpChannelPrivate::closeHook()
+{
+ createClosedSignal();
+}
+
+void SftpChannelPrivate::handleOpenSuccessInternal()
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("SFTP session started");
+#endif
+ m_sendFacility.sendSftpPacket(remoteChannel());
+ m_sftpState = SubsystemRequested;
+}
+
+void SftpChannelPrivate::handleOpenFailureInternal()
+{
+ if (channelState() != SessionRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
+ }
+ createDelayedInitFailedSignal(SSH_TR("Server could not start session."));
+}
+
+void SftpChannelPrivate::sendReadRequest(const SftpDownload::Ptr &job,
+ quint32 requestId)
+{
+ Q_ASSERT(job->eofId == SftpInvalidJob);
+ sendData(m_outgoingPacket.generateReadFile(job->remoteHandle, job->offset,
+ AbstractSftpPacket::MaxDataSize, requestId).rawData());
+ job->offsets[requestId] = job->offset;
+ job->offset += AbstractSftpPacket::MaxDataSize;
+ if (job->offset >= job->fileSize)
+ job->eofId = requestId;
+}
+
+void SftpChannelPrivate::reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job,
+ const QString &error)
+{
+ createDelayedJobFinishedSignal(job->jobId, error);
+ job->hasError = true;
+}
+
+void SftpChannelPrivate::finishTransferRequest(const JobMap::Iterator &it)
+{
+ AbstractSftpTransfer::Ptr job = it.value().staticCast<AbstractSftpTransfer>();
+ if (job->inFlightCount == 1)
+ sendTransferCloseHandle(job, it.key());
+ else
+ removeTransferRequest(it);
+}
+
+void SftpChannelPrivate::sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job,
+ quint32 requestId)
+{
+ sendData(m_outgoingPacket.generateCloseHandle(job->remoteHandle,
+ requestId).rawData());
+ job->state = SftpDownload::CloseRequested;
+}
+
+void SftpChannelPrivate::removeTransferRequest(const JobMap::Iterator &it)
+{
+ --it.value().staticCast<AbstractSftpTransfer>()->inFlightCount;
+ m_jobs.erase(it);
+}
+
+void SftpChannelPrivate::sendWriteRequest(const JobMap::Iterator &it)
+{
+ SftpUpload::Ptr job = it.value().staticCast<SftpUpload>();
+ QByteArray data = job->localFile->read(AbstractSftpPacket::MaxDataSize);
+ if (job->localFile->error() != QFile::NoError) {
+ if (!job->hasError) {
+ reportRequestError(job, SSH_TR("Error reading local file: %1")
+ .arg(job->localFile->errorString()));
+ }
+ finishTransferRequest(it);
+ } else if (data.isEmpty()) {
+ finishTransferRequest(it);
+ } else {
+ sendData(m_outgoingPacket.generateWriteFile(job->remoteHandle,
+ job->offset, data, it.key()).rawData());
+ job->offset += AbstractSftpPacket::MaxDataSize;
+ }
+}
+
+void SftpChannelPrivate::spawnWriteRequests(const JobMap::Iterator &it)
+{
+ SftpUpload::Ptr op = it.value().staticCast<SftpUpload>();
+ op->calculateInFlightCount(AbstractSftpPacket::MaxDataSize);
+ sendWriteRequest(it);
+ for (int i = 1; i < op->inFlightCount; ++i)
+ sendWriteRequest(m_jobs.insert(++m_nextJobId, op));
+}
+
+void SftpChannelPrivate::spawnReadRequests(const SftpDownload::Ptr &job)
+{
+ job->calculateInFlightCount(AbstractSftpPacket::MaxDataSize);
+ sendReadRequest(job, job->jobId);
+ for (int i = 1; i < job->inFlightCount; ++i) {
+ const quint32 requestId = ++m_nextJobId;
+ m_jobs.insert(requestId, job);
+ sendReadRequest(job, requestId);
+ }
+}
+
+void SftpChannelPrivate::createDelayedInitFailedSignal(const QString &reason)
+{
+ new SftpInitializationFailedSignal(this, QWeakPointer<SftpChannel>(m_sftp),
+ reason);
+}
+
+void SftpChannelPrivate::emitInitializationFailedSignal(const QString &reason)
+{
+ emit m_sftp->initializationFailed(reason);
+}
+
+void SftpChannelPrivate::createDelayedInitializedSignal()
+{
+ new SftpInitializedSignal(this, QWeakPointer<SftpChannel>(m_sftp));
+}
+
+void SftpChannelPrivate::emitInitialized()
+{
+ emit m_sftp->initialized();
+}
+
+void SftpChannelPrivate::createDelayedJobFinishedSignal(SftpJobId jobId,
+ const QString &error)
+{
+ new SftpJobFinishedSignal(this, QWeakPointer<SftpChannel>(m_sftp), jobId, error);
+}
+
+void SftpChannelPrivate::emitJobFinished(SftpJobId jobId, const QString &error)
+{
+ emit m_sftp->finished(jobId, error);
+}
+
+void SftpChannelPrivate::createDelayedDataAvailableSignal(SftpJobId jobId,
+ const QString &data)
+{
+ new SftpDataAvailableSignal(this, QWeakPointer<SftpChannel>(m_sftp), jobId, data);
+}
+
+void SftpChannelPrivate::emitDataAvailable(SftpJobId jobId, const QString &data)
+{
+ emit m_sftp->dataAvailable(jobId, data);
+}
+
+void SftpChannelPrivate::createClosedSignal()
+{
+ new SftpClosedSignal(this, QWeakPointer<SftpChannel>(m_sftp));
+}
+
+void SftpChannelPrivate::emitClosed()
+{
+ emit m_sftp->closed();
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sftpchannel.h b/src/plugins/coreplugin/ssh/sftpchannel.h
new file mode 100644
index 0000000000..6b4ffea7e5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpchannel.h
@@ -0,0 +1,120 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTCHANNEL_H
+#define SFTCHANNEL_H
+
+#include "sftpdefs.h"
+#include "sftpincomingpacket_p.h"
+
+#include <coreplugin/core_global.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QString>
+
+namespace Core {
+
+namespace Internal {
+class SftpChannelPrivate;
+class SshChannelManager;
+class SshSendFacility;
+} // namespace Internal
+
+/*
+ * This class provides SFTP operations.
+ * Objects are created via SshConnection::createSftpChannel().
+ * The channel needs to be initialized with
+ * a call to initialize() and is closed via closeChannel(). After closing
+ * a channel, no more operations are possible. It cannot be re-opened
+ * using initialize(); use SshConnection::createSftpChannel() if you need
+ * a new one.
+ * After the initialized() signal has been emitted, operations can be started.
+ * All SFTP operations are asynchronous (non-blocking) and can be in-flight
+ * simultaneously (though callers must ensure that concurrently running jobs
+ * are independent of each other, e.g. they must not write to the same file).
+ * Operations are identified by their job id, which is returned by
+ * the respective member function. If the function can right away detect that
+ * the operation cannot succeed, it returns SftpInvalidJob. If an error occurs
+ * later, the finishedWithError() signal is emitted for the respective job.
+ * Note that directory names must not have a trailing slash.
+ */
+class CORE_EXPORT SftpChannel : public QObject
+{
+ Q_OBJECT
+
+ friend class Internal::SftpChannelPrivate;
+ friend class Internal::SshChannelManager;
+public:
+ typedef QSharedPointer<SftpChannel> Ptr;
+
+ enum State { Uninitialized, Initializing, Initialized, Closing, Closed };
+ State state() const;
+
+ void initialize();
+ void closeChannel();
+
+ SftpJobId listDirectory(const QString &dirPath);
+ SftpJobId createDirectory(const QString &dirPath);
+ SftpJobId removeDirectory(const QString &dirPath);
+ SftpJobId removeFile(const QString &filePath);
+ SftpJobId renameFileOrDirectory(const QString &oldPath,
+ const QString &newPath);
+ SftpJobId createFile(const QString &filePath, SftpOverwriteMode mode);
+ SftpJobId uploadFile(const QString &localFilePath,
+ const QString &remoteFilePath, SftpOverwriteMode mode);
+ SftpJobId downloadFile(const QString &remoteFilePath,
+ const QString &localFilePath, SftpOverwriteMode mode);
+
+ ~SftpChannel();
+
+signals:
+ void initialized();
+ void initializationFailed(const QString &reason);
+ void closed();
+
+ // error.isEmpty <=> finished successfully
+ void finished(Core::SftpJobId job, const QString &error = QString());
+
+ /*
+ * This signal is only emitted by the "List Directory" operation,
+ * one file at a time.
+ */
+ void dataAvailable(SftpJobId job, const QString &data);
+
+private:
+ SftpChannel(quint32 channelId, Internal::SshSendFacility &sendFacility);
+
+ Internal::SftpChannelPrivate *d;
+};
+
+} // namespace Core
+
+#endif // SFTPCHANNEL_H
diff --git a/src/plugins/coreplugin/ssh/sftpchannel_p.h b/src/plugins/coreplugin/ssh/sftpchannel_p.h
new file mode 100644
index 0000000000..8d27a2c01d
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpchannel_p.h
@@ -0,0 +1,130 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTCHANNEL_P_H
+#define SFTCHANNEL_P_H
+
+#include "sftpdefs.h"
+#include "sftpincomingpacket_p.h"
+#include "sftpoperation_p.h"
+#include "sftpoutgoingpacket_p.h"
+#include "sshchannel_p.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QMap>
+
+namespace Core {
+class SftpChannel;
+namespace Internal {
+
+class SftpChannelPrivate : public AbstractSshChannel
+{
+ friend class Core::SftpChannel;
+public:
+
+ enum SftpState { Inactive, SubsystemRequested, InitSent, Initialized };
+
+ virtual void handleChannelSuccess();
+ virtual void handleChannelFailure();
+
+ virtual void closeHook();
+
+ void emitInitializationFailedSignal(const QString &reason);
+ void emitInitialized();
+ void emitJobFinished(SftpJobId jobId, const QString &error);
+ void emitDataAvailable(SftpJobId jobId, const QString &data);
+ void emitClosed();
+
+private:
+ typedef QMap<SftpJobId, AbstractSftpOperation::Ptr> JobMap;
+
+ SftpChannelPrivate(quint32 channelId, SshSendFacility &sendFacility,
+ SftpChannel *sftp);
+ SftpJobId createJob(const AbstractSftpOperation::Ptr &job);
+
+ virtual void handleOpenSuccessInternal();
+ virtual void handleOpenFailureInternal();
+ virtual void handleChannelDataInternal(const QByteArray &data);
+ virtual void handleChannelExtendedDataInternal(quint32 type,
+ const QByteArray &data);
+
+ void handleCurrentPacket();
+ void handleServerVersion();
+ void handleHandle();
+ void handleStatus();
+ void handleName();
+ void handleReadData();
+ void handleAttrs();
+
+ void handleStatusGeneric(const JobMap::Iterator &it,
+ const SftpStatusResponse &response);
+ void handleLsStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response);
+ void handleGetStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response);
+ void handlePutStatus(const JobMap::Iterator &it,
+ const SftpStatusResponse &response);
+
+ void handleLsHandle(const JobMap::Iterator &it);
+ void handleCreateFileHandle(const JobMap::Iterator &it);
+ void handleGetHandle(const JobMap::Iterator &it);
+ void handlePutHandle(const JobMap::Iterator &it);
+
+ void spawnReadRequests(const SftpDownload::Ptr &job);
+ void spawnWriteRequests(const JobMap::Iterator &it);
+ void sendReadRequest(const SftpDownload::Ptr &job, quint32 requestId);
+ void sendWriteRequest(const JobMap::Iterator &it);
+ void finishTransferRequest(const JobMap::Iterator &it);
+ void removeTransferRequest(const JobMap::Iterator &it);
+ void reportRequestError(const AbstractSftpOperationWithHandle::Ptr &job,
+ const QString &error);
+ void sendTransferCloseHandle(const AbstractSftpTransfer::Ptr &job,
+ quint32 requestId);
+
+ void createDelayedInitFailedSignal(const QString &reason);
+ void createDelayedInitializedSignal();
+ void createDelayedJobFinishedSignal(SftpJobId jobId,
+ const QString &error = QString());
+ void createDelayedDataAvailableSignal(SftpJobId jobId, const QString &data);
+ void createClosedSignal();
+
+ JobMap::Iterator lookupJob(SftpJobId id);
+ JobMap m_jobs;
+ SftpOutgoingPacket m_outgoingPacket;
+ SftpIncomingPacket m_incomingPacket;
+ QByteArray m_incomingData;
+ SftpJobId m_nextJobId;
+ SftpState m_sftpState;
+ SftpChannel *m_sftp;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SFTPCHANNEL_P_H
diff --git a/src/plugins/coreplugin/ssh/sftpdefs.cpp b/src/plugins/coreplugin/ssh/sftpdefs.cpp
new file mode 100644
index 0000000000..6a2f6de45e
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpdefs.cpp
@@ -0,0 +1,32 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftpdefs.h"
+
+namespace Core { const SftpJobId SftpInvalidJob = 0; }
diff --git a/src/plugins/coreplugin/ssh/sftpdefs.h b/src/plugins/coreplugin/ssh/sftpdefs.h
new file mode 100644
index 0000000000..5f59582a05
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpdefs.h
@@ -0,0 +1,48 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTPDEFS_H
+#define SFTPDEFS_H
+
+#include <coreplugin/core_global.h>
+
+#include <QtCore/QtGlobal>
+
+namespace Core {
+
+typedef quint32 SftpJobId;
+CORE_EXPORT extern const SftpJobId SftpInvalidJob;
+
+enum SftpOverwriteMode {
+ SftpOverwriteExisting, SftpAppendToExisting, SftpSkipExisting
+};
+
+} // namespace Core
+
+#endif // SFTPDEFS_H
diff --git a/src/plugins/coreplugin/ssh/sftpincomingpacket.cpp b/src/plugins/coreplugin/ssh/sftpincomingpacket.cpp
new file mode 100644
index 0000000000..804bdb21d5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpincomingpacket.cpp
@@ -0,0 +1,230 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftpincomingpacket_p.h"
+
+#include "sshexception_p.h"
+#include "sshpacketparser_p.h"
+
+namespace Core {
+namespace Internal {
+
+namespace {
+ const int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+ const int SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
+ const int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+ const int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
+ const int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+} // anonymous namespace
+
+SftpIncomingPacket::SftpIncomingPacket() : m_length(0)
+{
+}
+
+void SftpIncomingPacket::consumeData(QByteArray &newData)
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("%s: current data size = %d, new data size = %d", Q_FUNC_INFO,
+ m_data.size(), newData.size());
+#endif
+
+ if (isComplete() || dataSize() + newData.size() < sizeof m_length)
+ return;
+
+ if (dataSize() < sizeof m_length) {
+ moveFirstBytes(m_data, newData, sizeof m_length - m_data.size());
+ m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
+ if (m_length < static_cast<quint32>(TypeOffset + 1)
+ || m_length > MaxPacketSize) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid length field in SFTP packet.");
+ }
+ }
+
+ moveFirstBytes(m_data, newData,
+ qMin<quint32>(m_length - dataSize() + 4, newData.size()));
+}
+
+void SftpIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
+ int n)
+{
+ target.append(source.left(n));
+ source.remove(0, n);
+}
+
+bool SftpIncomingPacket::isComplete() const
+{
+ return m_length == dataSize() - 4;
+}
+
+void SftpIncomingPacket::clear()
+{
+ m_data.clear();
+ m_length = 0;
+}
+
+quint32 SftpIncomingPacket::extractServerVersion() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_VERSION);
+ try {
+ return SshPacketParser::asUint32(m_data, TypeOffset + 1);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_VERSION packet.");
+ }
+}
+
+SftpHandleResponse SftpIncomingPacket::asHandleResponse() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_HANDLE);
+ try {
+ SftpHandleResponse response;
+ quint32 offset = RequestIdOffset;
+ response.requestId = SshPacketParser::asUint32(m_data, &offset);
+ response.handle = SshPacketParser::asString(m_data, &offset);
+ return response;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_HANDLE packet");
+ }
+}
+
+SftpStatusResponse SftpIncomingPacket::asStatusResponse() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_STATUS);
+ try {
+ SftpStatusResponse response;
+ quint32 offset = RequestIdOffset;
+ response.requestId = SshPacketParser::asUint32(m_data, &offset);
+ response.status = static_cast<SftpStatusCode>(SshPacketParser::asUint32(m_data, &offset));
+ response.errorString = SshPacketParser::asUserString(m_data, &offset);
+ response.language = SshPacketParser::asString(m_data, &offset);
+ return response;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_STATUS packet.");
+ }
+}
+
+SftpNameResponse SftpIncomingPacket::asNameResponse() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_NAME);
+ try {
+ SftpNameResponse response;
+ quint32 offset = RequestIdOffset;
+ response.requestId = SshPacketParser::asUint32(m_data, &offset);
+ const quint32 count = SshPacketParser::asUint32(m_data, &offset);
+ for (quint32 i = 0; i < count; ++i)
+ response.files << asFile(offset);
+ return response;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_NAME packet.");
+ }
+}
+
+SftpDataResponse SftpIncomingPacket::asDataResponse() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_DATA);
+ try {
+ SftpDataResponse response;
+ quint32 offset = RequestIdOffset;
+ response.requestId = SshPacketParser::asUint32(m_data, &offset);
+ response.data = SshPacketParser::asString(m_data, &offset);
+ return response;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_DATA packet.");
+ }
+}
+
+SftpAttrsResponse SftpIncomingPacket::asAttrsResponse() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_FXP_ATTRS);
+ try {
+ SftpAttrsResponse response;
+ quint32 offset = RequestIdOffset;
+ response.requestId = SshPacketParser::asUint32(m_data, &offset);
+ response.attrs = asFileAttributes(offset);
+ return response;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_FXP_ATTRS packet.");
+ }
+}
+
+SftpFile SftpIncomingPacket::asFile(quint32 &offset) const
+{
+ SftpFile file;
+ file.fileName
+ = QString::fromUtf8(SshPacketParser::asString(m_data, &offset));
+ file.longName
+ = QString::fromUtf8(SshPacketParser::asString(m_data, &offset));
+ file.attributes = asFileAttributes(offset);
+ return file;
+}
+
+SftpFileAttributes SftpIncomingPacket::asFileAttributes(quint32 &offset) const
+{
+ SftpFileAttributes attributes;
+ const quint32 flags = SshPacketParser::asUint32(m_data, &offset);
+ attributes.sizePresent = flags & SSH_FILEXFER_ATTR_SIZE;
+ attributes.timesPresent = flags & SSH_FILEXFER_ATTR_ACMODTIME;
+ attributes.uidAndGidPresent = flags & SSH_FILEXFER_ATTR_UIDGID;
+ attributes.permissionsPresent = flags & SSH_FILEXFER_ATTR_PERMISSIONS;
+ if (attributes.sizePresent)
+ attributes.size = SshPacketParser::asUint64(m_data, &offset);
+ if (attributes.uidAndGidPresent) {
+ attributes.uid = SshPacketParser::asUint32(m_data, &offset);
+ attributes.gid = SshPacketParser::asUint32(m_data, &offset);
+ }
+ if (attributes.permissionsPresent)
+ attributes.permissions = SshPacketParser::asUint32(m_data, &offset);
+ if (attributes.timesPresent) {
+ attributes.atime = SshPacketParser::asUint32(m_data, &offset);
+ attributes.mtime = SshPacketParser::asUint32(m_data, &offset);
+ }
+ if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
+ const quint32 count = SshPacketParser::asUint32(m_data, &offset);
+ for (quint32 i = 0; i < count; ++i) {
+ SshPacketParser::asString(m_data, &offset);
+ SshPacketParser::asString(m_data, &offset);
+ }
+ }
+ return attributes;
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sftpincomingpacket_p.h b/src/plugins/coreplugin/ssh/sftpincomingpacket_p.h
new file mode 100644
index 0000000000..5a5b8d42fe
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpincomingpacket_p.h
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTPINCOMINGPACKET_P_H
+#define SFTPINCOMINGPACKET_P_H
+
+#include "sftppacket_p.h"
+
+namespace Core {
+namespace Internal {
+
+struct SftpHandleResponse {
+ quint32 requestId;
+ QByteArray handle;
+};
+
+struct SftpStatusResponse {
+ quint32 requestId;
+ SftpStatusCode status;
+ QString errorString;
+ QByteArray language;
+};
+
+struct SftpFileAttributes {
+ bool sizePresent;
+ bool timesPresent;
+ bool uidAndGidPresent;
+ bool permissionsPresent;
+ quint64 size;
+ quint32 uid;
+ quint32 gid;
+ quint32 permissions;
+ quint32 atime;
+ quint32 mtime;
+};
+
+struct SftpFile {
+ QString fileName;
+ QString longName; // Not present in later RFCs, so we don't expose this to the user.
+ SftpFileAttributes attributes;
+};
+
+struct SftpNameResponse {
+ quint32 requestId;
+ QList<SftpFile> files;
+};
+
+struct SftpDataResponse {
+ quint32 requestId;
+ QByteArray data;
+};
+
+struct SftpAttrsResponse {
+ quint32 requestId;
+ SftpFileAttributes attrs;
+};
+
+class SftpIncomingPacket : public AbstractSftpPacket
+{
+public:
+ SftpIncomingPacket();
+
+ void consumeData(QByteArray &data);
+ void clear();
+ bool isComplete() const;
+ quint32 extractServerVersion() const;
+ SftpHandleResponse asHandleResponse() const;
+ SftpStatusResponse asStatusResponse() const;
+ SftpNameResponse asNameResponse() const;
+ SftpDataResponse asDataResponse() const;
+ SftpAttrsResponse asAttrsResponse() const;
+
+private:
+ void moveFirstBytes(QByteArray &target, QByteArray &source, int n);
+
+ SftpFileAttributes asFileAttributes(quint32 &offset) const;
+ SftpFile asFile(quint32 &offset) const;
+
+ quint32 m_length;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SFTPINCOMINGPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sftpoperation.cpp b/src/plugins/coreplugin/ssh/sftpoperation.cpp
new file mode 100644
index 0000000000..8acb126db1
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpoperation.cpp
@@ -0,0 +1,176 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftpoperation_p.h"
+
+#include "sftpoutgoingpacket_p.h"
+
+#include <QtCore/QTime>
+#include <QtCore/QFile>
+
+namespace Core {
+namespace Internal {
+
+AbstractSftpOperation::AbstractSftpOperation(SftpJobId jobId) : jobId(jobId)
+{
+}
+
+AbstractSftpOperation::~AbstractSftpOperation() { }
+
+
+SftpMakeDir::SftpMakeDir(SftpJobId jobId, const QString &path)
+ : AbstractSftpOperation(jobId), remoteDir(path)
+{
+}
+
+SftpOutgoingPacket &SftpMakeDir::initialPacket(SftpOutgoingPacket &packet)
+{
+ return packet.generateMkDir(remoteDir, jobId);
+}
+
+
+SftpRmDir::SftpRmDir(SftpJobId, const QString &path)
+ : AbstractSftpOperation(jobId), remoteDir(path)
+{
+}
+
+SftpOutgoingPacket &SftpRmDir::initialPacket(SftpOutgoingPacket &packet)
+{
+ return packet.generateRmDir(remoteDir, jobId);
+}
+
+
+SftpRm::SftpRm(SftpJobId jobId, const QString &path)
+ : AbstractSftpOperation(jobId), remoteFile(path) {}
+
+SftpOutgoingPacket &SftpRm::initialPacket(SftpOutgoingPacket &packet)
+{
+ return packet.generateRm(remoteFile, jobId);
+}
+
+
+SftpRename::SftpRename(SftpJobId jobId, const QString &oldPath,
+ const QString &newPath)
+ : AbstractSftpOperation(jobId), oldPath(oldPath), newPath(newPath)
+{
+}
+
+SftpOutgoingPacket &SftpRename::initialPacket(SftpOutgoingPacket &packet)
+{
+ return packet.generateRename(oldPath, newPath, jobId);
+}
+
+
+AbstractSftpOperationWithHandle::AbstractSftpOperationWithHandle(SftpJobId jobId,
+ const QString &remotePath)
+ : AbstractSftpOperation(jobId),
+ remotePath(remotePath), state(Inactive), hasError(false)
+{
+}
+
+AbstractSftpOperationWithHandle::~AbstractSftpOperationWithHandle() { }
+
+
+SftpListDir::SftpListDir(SftpJobId jobId, const QString &path)
+ : AbstractSftpOperationWithHandle(jobId, path)
+{
+}
+
+SftpOutgoingPacket &SftpListDir::initialPacket(SftpOutgoingPacket &packet)
+{
+ state = OpenRequested;
+ return packet.generateOpenDir(remotePath, jobId);
+}
+
+
+SftpCreateFile::SftpCreateFile(SftpJobId jobId, const QString &path,
+ SftpOverwriteMode mode)
+ : AbstractSftpOperationWithHandle(jobId, path), mode(mode)
+{
+}
+
+SftpOutgoingPacket & SftpCreateFile::initialPacket(SftpOutgoingPacket &packet)
+{
+ state = OpenRequested;
+ return packet.generateOpenFileForWriting(remotePath, mode, jobId);
+}
+
+
+const int AbstractSftpTransfer::MaxInFlightCount = 10; // Experimentally found to be enough.
+
+AbstractSftpTransfer::AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile)
+ : AbstractSftpOperationWithHandle(jobId, remotePath),
+ localFile(localFile), fileSize(0), offset(0), inFlightCount(0),
+ statRequested(false)
+{
+}
+
+void AbstractSftpTransfer::calculateInFlightCount(quint32 chunkSize)
+{
+ if (fileSize == 0) {
+ inFlightCount = 1;
+ } else {
+ inFlightCount = fileSize / chunkSize;
+ if (fileSize % chunkSize)
+ ++inFlightCount;
+ if (inFlightCount > MaxInFlightCount)
+ inFlightCount = MaxInFlightCount;
+ }
+}
+
+
+SftpDownload::SftpDownload(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile)
+ : AbstractSftpTransfer(jobId, remotePath, localFile), eofId(SftpInvalidJob)
+{
+}
+
+SftpOutgoingPacket &SftpDownload::initialPacket(SftpOutgoingPacket &packet)
+{
+ state = OpenRequested;
+ return packet.generateOpenFileForReading(remotePath, jobId);
+}
+
+
+SftpUpload::SftpUpload(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode)
+ : AbstractSftpTransfer(jobId, remotePath, localFile), mode(mode)
+{
+ fileSize = localFile->size();
+}
+
+SftpOutgoingPacket &SftpUpload::initialPacket(SftpOutgoingPacket &packet)
+{
+ state = OpenRequested;
+ return packet.generateOpenFileForWriting(remotePath, mode, jobId);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sftpoperation_p.h b/src/plugins/coreplugin/ssh/sftpoperation_p.h
new file mode 100644
index 0000000000..6598781332
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpoperation_p.h
@@ -0,0 +1,193 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTPOPERATION_P_H
+#define SFTPOPERATION_P_H
+
+#include "sftpdefs.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QMap>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QFile;
+QT_END_NAMESPACE
+
+namespace Core {
+namespace Internal {
+
+class SftpOutgoingPacket;
+
+struct AbstractSftpOperation
+{
+ typedef QSharedPointer<AbstractSftpOperation> Ptr;
+ enum Type {
+ ListDir, MakeDir, RmDir, Rm, Rename, CreateFile, Download, Upload
+ };
+
+ AbstractSftpOperation(SftpJobId jobId);
+ virtual ~AbstractSftpOperation();
+ virtual Type type() const=0;
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet)=0;
+
+ const SftpJobId jobId;
+
+private:
+ AbstractSftpOperation(const AbstractSftpOperation &);
+ AbstractSftpOperation &operator=(const AbstractSftpOperation &);
+};
+
+struct SftpMakeDir : public AbstractSftpOperation
+{
+ typedef QSharedPointer<SftpMakeDir> Ptr;
+
+ SftpMakeDir(SftpJobId jobId, const QString &path);
+ virtual Type type() const { return MakeDir; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ const QString remoteDir;
+};
+
+struct SftpRmDir : public AbstractSftpOperation
+{
+ typedef QSharedPointer<SftpRmDir> Ptr;
+
+ SftpRmDir(SftpJobId jobId, const QString &path);
+ virtual Type type() const { return RmDir; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ const QString remoteDir;
+};
+
+struct SftpRm : public AbstractSftpOperation
+{
+ typedef QSharedPointer<SftpRm> Ptr;
+
+ SftpRm(SftpJobId jobId, const QString &path);
+ virtual Type type() const { return Rm; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ const QString remoteFile;
+};
+
+struct SftpRename : public AbstractSftpOperation
+{
+ typedef QSharedPointer<SftpRename> Ptr;
+
+ SftpRename(SftpJobId jobId, const QString &oldPath, const QString &newPath);
+ virtual Type type() const { return Rename; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ const QString oldPath;
+ const QString newPath;
+};
+
+
+struct AbstractSftpOperationWithHandle : public AbstractSftpOperation
+{
+ typedef QSharedPointer<AbstractSftpOperationWithHandle> Ptr;
+ enum State { Inactive, OpenRequested, Open, CloseRequested };
+
+ AbstractSftpOperationWithHandle(SftpJobId jobId, const QString &remotePath);
+ ~AbstractSftpOperationWithHandle();
+
+ const QString remotePath;
+ QByteArray remoteHandle;
+ State state;
+ bool hasError;
+};
+
+
+struct SftpListDir : public AbstractSftpOperationWithHandle
+{
+ typedef QSharedPointer<SftpListDir> Ptr;
+
+ SftpListDir(SftpJobId jobId, const QString &path);
+ virtual Type type() const { return ListDir; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+};
+
+
+struct SftpCreateFile : public AbstractSftpOperationWithHandle
+{
+ typedef QSharedPointer<SftpCreateFile> Ptr;
+
+ SftpCreateFile(SftpJobId jobId, const QString &path, SftpOverwriteMode mode);
+ virtual Type type() const { return CreateFile; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ const SftpOverwriteMode mode;
+};
+
+struct AbstractSftpTransfer : public AbstractSftpOperationWithHandle
+{
+ typedef QSharedPointer<AbstractSftpTransfer> Ptr;
+
+ AbstractSftpTransfer(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile);
+ void calculateInFlightCount(quint32 chunkSize);
+
+ static const int MaxInFlightCount;
+
+ const QSharedPointer<QFile> localFile;
+ quint64 fileSize;
+ quint64 offset;
+ int inFlightCount;
+ bool statRequested;
+};
+
+struct SftpDownload : public AbstractSftpTransfer
+{
+ typedef QSharedPointer<SftpDownload> Ptr;
+ SftpDownload(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile);
+ virtual Type type() const { return Download; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ QMap<quint32, quint32> offsets;
+ SftpJobId eofId;
+};
+
+struct SftpUpload : public AbstractSftpTransfer
+{
+ typedef QSharedPointer<SftpUpload> Ptr;
+
+ SftpUpload(SftpJobId jobId, const QString &remotePath,
+ const QSharedPointer<QFile> &localFile, SftpOverwriteMode mode);
+ virtual Type type() const { return Upload; }
+ virtual SftpOutgoingPacket &initialPacket(SftpOutgoingPacket &packet);
+
+ SftpOverwriteMode mode;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SFTPOPERATION_P_H
diff --git a/src/plugins/coreplugin/ssh/sftpoutgoingpacket.cpp b/src/plugins/coreplugin/ssh/sftpoutgoingpacket.cpp
new file mode 100644
index 0000000000..57fd85467e
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpoutgoingpacket.cpp
@@ -0,0 +1,202 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftpoutgoingpacket_p.h"
+
+#include "sshpacket_p.h"
+
+#include <QtCore/QtEndian>
+
+namespace Core {
+namespace Internal {
+
+namespace {
+ const quint32 DefaultAttributes = 0;
+ const quint32 SSH_FXF_READ = 0x00000001;
+ const quint32 SSH_FXF_WRITE = 0x00000002;
+ const quint32 SSH_FXF_APPEND = 0x00000004;
+ const quint32 SSH_FXF_CREAT = 0x00000008;
+ const quint32 SSH_FXF_TRUNC = 0x00000010;
+ const quint32 SSH_FXF_EXCL = 0x00000020;
+}
+
+SftpOutgoingPacket::SftpOutgoingPacket()
+{
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateInit(quint32 version)
+{
+ return init(SSH_FXP_INIT, 0).appendInt(version).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateOpenDir(const QString &path,
+ quint32 requestId)
+{
+ return init(SSH_FXP_OPENDIR, requestId).appendString(path).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateReadDir(const QByteArray &handle,
+ quint32 requestId)
+{
+ return init(SSH_FXP_READDIR, requestId).appendString(handle).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateCloseHandle(const QByteArray &handle,
+ quint32 requestId)
+{
+ return init(SSH_FXP_CLOSE, requestId).appendString(handle).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateMkDir(const QString &path,
+ quint32 requestId)
+{
+ return init(SSH_FXP_MKDIR, requestId).appendString(path)
+ .appendInt(DefaultAttributes).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateRmDir(const QString &path,
+ quint32 requestId)
+{
+ return init(SSH_FXP_RMDIR, requestId).appendString(path).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateRm(const QString &path,
+ quint32 requestId)
+{
+ return init(SSH_FXP_REMOVE, requestId).appendString(path).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateRename(const QString &oldPath,
+ const QString &newPath, quint32 requestId)
+{
+ return init(SSH_FXP_RENAME, requestId).appendString(oldPath)
+ .appendString(newPath).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForWriting(const QString &path,
+ SftpOverwriteMode mode, quint32 requestId)
+{
+ return generateOpenFile(path, Write, mode, requestId);
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFileForReading(const QString &path,
+ quint32 requestId)
+{
+ // Note: Overwrite mode is irrelevant and will be ignored.
+ return generateOpenFile(path, Read, SftpSkipExisting, requestId);
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateReadFile(const QByteArray &handle,
+ quint64 offset, quint32 length, quint32 requestId)
+{
+ return init(SSH_FXP_READ, requestId).appendString(handle).appendInt64(offset)
+ .appendInt(length).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateFstat(const QByteArray &handle,
+ quint32 requestId)
+{
+ return init(SSH_FXP_FSTAT, requestId).appendString(handle).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateWriteFile(const QByteArray &handle,
+ quint64 offset, const QByteArray &data, quint32 requestId)
+{
+ return init(SSH_FXP_WRITE, requestId).appendString(handle)
+ .appendInt64(offset).appendString(data).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::generateOpenFile(const QString &path,
+ OpenType openType, SftpOverwriteMode mode, quint32 requestId)
+{
+ quint32 pFlags;
+ switch (openType) {
+ case Read:
+ pFlags = SSH_FXF_READ;
+ break;
+ case Write:
+ pFlags = SSH_FXF_WRITE | SSH_FXF_CREAT;
+ switch (mode) {
+ case SftpOverwriteExisting: pFlags |= SSH_FXF_TRUNC; break;
+ case SftpAppendToExisting: pFlags |= SSH_FXF_APPEND; break;
+ case SftpSkipExisting: pFlags |= SSH_FXF_EXCL; break;
+ }
+ break;
+ }
+ return init(SSH_FXP_OPEN, requestId).appendString(path).appendInt(pFlags)
+ .appendInt(DefaultAttributes).finalize();
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::init(SftpPacketType type,
+ quint32 requestId)
+{
+ m_data.resize(TypeOffset + 1);
+ m_data[TypeOffset] = type;
+ if (type != SSH_FXP_INIT) {
+ appendInt(requestId);
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Generating SFTP packet of type %d with request id %u", type,
+ requestId);
+#endif
+ }
+ return *this;
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::appendInt(quint32 val)
+{
+ m_data.append(AbstractSshPacket::encodeInt(val));
+ return *this;
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::appendInt64(quint64 value)
+{
+ m_data.append(AbstractSshPacket::encodeInt(value));
+ return *this;
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QString &string)
+{
+ m_data.append(AbstractSshPacket::encodeString(string.toUtf8()));
+ return *this;
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::appendString(const QByteArray &string)
+{
+ m_data += AbstractSshPacket::encodeString(string);
+ return *this;
+}
+
+SftpOutgoingPacket &SftpOutgoingPacket::finalize()
+{
+ AbstractSshPacket::setLengthField(m_data);
+ return *this;
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sftpoutgoingpacket_p.h b/src/plugins/coreplugin/ssh/sftpoutgoingpacket_p.h
new file mode 100644
index 0000000000..4f456e8754
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftpoutgoingpacket_p.h
@@ -0,0 +1,83 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTPOUTGOINGPACKET_P_H
+#define SFTPOUTGOINGPACKET_P_H
+
+#include "sftppacket_p.h"
+#include "sftpdefs.h"
+
+namespace Core {
+namespace Internal {
+
+class SftpOutgoingPacket : public AbstractSftpPacket
+{
+public:
+ SftpOutgoingPacket();
+ SftpOutgoingPacket &generateInit(quint32 version);
+ SftpOutgoingPacket &generateOpenDir(const QString &path, quint32 requestId);
+ SftpOutgoingPacket &generateReadDir(const QByteArray &handle,
+ quint32 requestId);
+ SftpOutgoingPacket &generateCloseHandle(const QByteArray &handle,
+ quint32 requestId);
+ SftpOutgoingPacket &generateMkDir(const QString &path, quint32 requestId);
+ SftpOutgoingPacket &generateRmDir(const QString &path, quint32 requestId);
+ SftpOutgoingPacket &generateRm(const QString &path, quint32 requestId);
+ SftpOutgoingPacket &generateRename(const QString &oldPath,
+ const QString &newPath, quint32 requestId);
+ SftpOutgoingPacket &generateOpenFileForWriting(const QString &path,
+ SftpOverwriteMode mode, quint32 requestId);
+ SftpOutgoingPacket &generateOpenFileForReading(const QString &path,
+ quint32 requestId);
+ SftpOutgoingPacket &generateReadFile(const QByteArray &handle,
+ quint64 offset, quint32 length, quint32 requestId);
+ SftpOutgoingPacket &generateFstat(const QByteArray &handle,
+ quint32 requestId);
+ SftpOutgoingPacket &generateWriteFile(const QByteArray &handle,
+ quint64 offset, const QByteArray &data, quint32 requestId);
+
+private:
+ static QByteArray encodeString(const QString &string);
+
+ enum OpenType { Read, Write };
+ SftpOutgoingPacket &generateOpenFile(const QString &path, OpenType openType,
+ SftpOverwriteMode mode, quint32 requestId);
+
+ SftpOutgoingPacket &init(SftpPacketType type, quint32 requestId);
+ SftpOutgoingPacket &appendInt(quint32 value);
+ SftpOutgoingPacket &appendInt64(quint64 value);
+ SftpOutgoingPacket &appendString(const QString &string);
+ SftpOutgoingPacket &appendString(const QByteArray &string);
+ SftpOutgoingPacket &finalize();
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SFTPOUTGOINGPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sftppacket.cpp b/src/plugins/coreplugin/ssh/sftppacket.cpp
new file mode 100644
index 0000000000..0064bf39fa
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftppacket.cpp
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sftppacket_p.h"
+
+#include "sshpacketparser_p.h"
+
+namespace Core {
+namespace Internal {
+
+const quint32 AbstractSftpPacket::MaxDataSize = 32768;
+const quint32 AbstractSftpPacket::MaxPacketSize = 34000;
+const int AbstractSftpPacket::TypeOffset = 4;
+const int AbstractSftpPacket::RequestIdOffset = TypeOffset + 1;
+const int AbstractSftpPacket::PayloadOffset = RequestIdOffset + 4;
+
+
+AbstractSftpPacket::AbstractSftpPacket()
+{
+}
+
+quint32 AbstractSftpPacket::requestId() const
+{
+ return SshPacketParser::asUint32(m_data, RequestIdOffset);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sftppacket_p.h b/src/plugins/coreplugin/ssh/sftppacket_p.h
new file mode 100644
index 0000000000..aae1a15a97
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sftppacket_p.h
@@ -0,0 +1,108 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SFTPPACKET_P_H
+#define SFTPPACKET_P_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+enum SftpPacketType {
+ SSH_FXP_INIT = 1,
+ SSH_FXP_VERSION = 2,
+ SSH_FXP_OPEN = 3,
+ SSH_FXP_CLOSE = 4,
+ SSH_FXP_READ = 5,
+ SSH_FXP_WRITE = 6,
+ SSH_FXP_LSTAT = 7,
+ SSH_FXP_FSTAT = 8,
+ SSH_FXP_SETSTAT = 9,
+ SSH_FXP_FSETSTAT = 10,
+ SSH_FXP_OPENDIR = 11,
+ SSH_FXP_READDIR = 12,
+ SSH_FXP_REMOVE = 13,
+ SSH_FXP_MKDIR = 14,
+ SSH_FXP_RMDIR = 15,
+ SSH_FXP_REALPATH = 16,
+ SSH_FXP_STAT = 17,
+ SSH_FXP_RENAME = 18,
+ SSH_FXP_READLINK = 19,
+ SSH_FXP_SYMLINK = 20, // Removed from later protocol versions. Try not to use.
+
+ SSH_FXP_STATUS = 101,
+ SSH_FXP_HANDLE = 102,
+ SSH_FXP_DATA = 103,
+ SSH_FXP_NAME = 104,
+ SSH_FXP_ATTRS = 105,
+
+ SSH_FXP_EXTENDED = 200,
+ SSH_FXP_EXTENDED_REPLY = 201
+};
+
+enum SftpStatusCode {
+ SSH_FX_OK = 0,
+ SSH_FX_EOF = 1,
+ SSH_FX_NO_SUCH_FILE = 2,
+ SSH_FX_PERMISSION_DENIED = 3,
+ SSH_FX_FAILURE = 4,
+ SSH_FX_BAD_MESSAGE = 5,
+ SSH_FX_NO_CONNECTION = 6,
+ SSH_FX_CONNECTION_LOST = 7,
+ SSH_FX_OP_UNSUPPORTED = 8
+};
+
+class AbstractSftpPacket
+{
+public:
+ AbstractSftpPacket();
+ quint32 requestId() const;
+ const QByteArray &rawData() const { return m_data; }
+ SftpPacketType type() const { return static_cast<SftpPacketType>(m_data.at(TypeOffset)); }
+
+ static const quint32 MaxDataSize; // "Pure" data size per read/writepacket.
+ static const quint32 MaxPacketSize;
+
+protected:
+ quint32 dataSize() const { return static_cast<quint32>(m_data.size()); }
+
+ static const int TypeOffset;
+ static const int RequestIdOffset;
+ static const int PayloadOffset;
+
+ QByteArray m_data;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SFTPPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sshbotanconversions_p.h b/src/plugins/coreplugin/ssh/sshbotanconversions_p.h
new file mode 100644
index 0000000000..0582977942
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshbotanconversions_p.h
@@ -0,0 +1,97 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef BYTEARRAYCONVERSIONS_P_H
+#define BYTEARRAYCONVERSIONS_P_H
+
+#include "sshcapabilities_p.h"
+
+#include <botan/rng.h>
+#include <botan/secmem.h>
+
+namespace Core {
+namespace Internal {
+
+inline const Botan::byte *convertByteArray(const QByteArray &a)
+{
+ return reinterpret_cast<const Botan::byte *>(a.constData());
+}
+
+inline Botan::byte *convertByteArray(QByteArray &a)
+{
+ return reinterpret_cast<Botan::byte *>(a.data());
+}
+
+inline QByteArray convertByteArray(const Botan::SecureVector<Botan::byte> &v)
+{
+ return QByteArray(reinterpret_cast<const char *>(v.begin()), v.size());
+}
+
+inline const char *botanKeyExchangeAlgoName(const QByteArray &rfcAlgoName)
+{
+ Q_ASSERT(rfcAlgoName == SshCapabilities::DiffieHellmanGroup1Sha1
+ || rfcAlgoName == SshCapabilities::DiffieHellmanGroup14Sha1);
+ return rfcAlgoName == SshCapabilities::DiffieHellmanGroup1Sha1
+ ? "modp/ietf/1024" : "modp/ietf/2048";
+}
+
+inline const char *botanCryptAlgoName(const QByteArray &rfcAlgoName)
+{
+ Q_ASSERT(rfcAlgoName == SshCapabilities::CryptAlgo3Des
+ || rfcAlgoName == SshCapabilities::CryptAlgoAes128);
+ return rfcAlgoName == SshCapabilities::CryptAlgo3Des
+ ? "TripleDES" : "AES-128";
+}
+
+inline const char *botanEmsaAlgoName(const QByteArray &rfcAlgoName)
+{
+ Q_ASSERT(rfcAlgoName == SshCapabilities::PubKeyDss
+ || rfcAlgoName == SshCapabilities::PubKeyRsa);
+ return rfcAlgoName == SshCapabilities::PubKeyDss
+ ? "EMSA1(SHA-1)" : "EMSA3(SHA-1)";
+}
+
+inline const char *botanSha1Name() { return "SHA-1"; }
+
+inline const char *botanHMacAlgoName(const QByteArray &rfcAlgoName)
+{
+ Q_ASSERT(rfcAlgoName == SshCapabilities::HMacSha1);
+ return botanSha1Name();
+}
+
+inline quint32 botanHMacKeyLen(const QByteArray &rfcAlgoName)
+{
+ Q_ASSERT(rfcAlgoName == SshCapabilities::HMacSha1);
+ return 20;
+}
+
+} // namespace Internal
+} // namespace Core
+
+#endif // BYTEARRAYCONVERSIONS_P_H
diff --git a/src/plugins/coreplugin/ssh/sshcapabilities.cpp b/src/plugins/coreplugin/ssh/sshcapabilities.cpp
new file mode 100644
index 0000000000..56db394206
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshcapabilities.cpp
@@ -0,0 +1,103 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshcapabilities_p.h"
+
+#include "sshexception_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+namespace {
+ QByteArray listAsByteArray(const QList<QByteArray> &list)
+ {
+ QByteArray array;
+ foreach(const QByteArray &elem, list)
+ array += elem + ',';
+ if (!array.isEmpty())
+ array.remove(array.count() - 1, 1);
+ return array;
+ }
+} // anonymous namspace
+
+const QByteArray SshCapabilities::DiffieHellmanGroup1Sha1("diffie-hellman-group1-sha1");
+const QByteArray SshCapabilities::DiffieHellmanGroup14Sha1("diffie-hellman-group14-sha1");
+const QList<QByteArray> SshCapabilities::KeyExchangeMethods
+ = QList<QByteArray>() << SshCapabilities::DiffieHellmanGroup1Sha1
+ << SshCapabilities::DiffieHellmanGroup14Sha1;
+
+const QByteArray SshCapabilities::PubKeyDss("ssh-dss");
+const QByteArray SshCapabilities::PubKeyRsa("ssh-rsa");
+const QList<QByteArray> SshCapabilities::PublicKeyAlgorithms
+ = QList<QByteArray>() << SshCapabilities::PubKeyRsa
+ << SshCapabilities::PubKeyDss;
+
+const QByteArray SshCapabilities::CryptAlgo3Des("3des-cbc");
+const QByteArray SshCapabilities::CryptAlgoAes128("aes128-cbc");
+const QList<QByteArray> SshCapabilities::EncryptionAlgorithms
+ = QList<QByteArray>() << SshCapabilities::CryptAlgoAes128
+ << SshCapabilities::CryptAlgo3Des;
+
+const QByteArray SshCapabilities::HMacSha1("hmac-sha1");
+const QByteArray SshCapabilities::HMacSha196("hmac-sha1-96");
+const QList<QByteArray> SshCapabilities::MacAlgorithms
+ = QList<QByteArray>() /* << SshCapabilities::HMacSha196 */
+ << SshCapabilities::HMacSha1;
+
+const QList<QByteArray> SshCapabilities::CompressionAlgorithms
+ = QList<QByteArray>() << "none";
+
+const QByteArray SshCapabilities::SshConnectionService("ssh-connection");
+
+const QByteArray SshCapabilities::PublicKeyAuthMethod("publickey");
+const QByteArray SshCapabilities::PasswordAuthMethod("password");
+
+
+QByteArray SshCapabilities::findBestMatch(const QList<QByteArray> &myCapabilities,
+ const QList<QByteArray> &serverCapabilities)
+{
+ foreach (const QByteArray &myCapability, myCapabilities) {
+ if (serverCapabilities.contains(myCapability))
+ return myCapability;
+ }
+
+ throw SshServerException(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ "Server and client capabilities don't match.",
+ QCoreApplication::translate("SshConnection",
+ "Server and client capabilities don't match. "
+ "Client list was: %1.\nServer list was %2.")
+ .arg(listAsByteArray(myCapabilities).data())
+ .arg(listAsByteArray(serverCapabilities).data()));
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshcapabilities_p.h b/src/plugins/coreplugin/ssh/sshcapabilities_p.h
new file mode 100644
index 0000000000..7c58c830f5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshcapabilities_p.h
@@ -0,0 +1,72 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef CAPABILITIES_P_H
+#define CAPABILITIES_P_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+namespace Core {
+namespace Internal {
+
+class SshCapabilities
+{
+public:
+ static const QByteArray DiffieHellmanGroup1Sha1;
+ static const QByteArray DiffieHellmanGroup14Sha1;
+ static const QList<QByteArray> KeyExchangeMethods;
+
+ static const QByteArray PubKeyDss;
+ static const QByteArray PubKeyRsa;
+ static const QList<QByteArray> PublicKeyAlgorithms;
+
+ static const QByteArray CryptAlgo3Des;
+ static const QByteArray CryptAlgoAes128;
+ static const QList<QByteArray> EncryptionAlgorithms;
+
+ static const QByteArray HMacSha1;
+ static const QByteArray HMacSha196;
+ static const QList<QByteArray> MacAlgorithms;
+
+ static const QList<QByteArray> CompressionAlgorithms;
+
+ static const QByteArray SshConnectionService;
+
+ static const QByteArray PublicKeyAuthMethod;
+ static const QByteArray PasswordAuthMethod;
+
+ static QByteArray findBestMatch(const QList<QByteArray> &myCapabilities,
+ const QList<QByteArray> &serverCapabilities);
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // CAPABILITIES_P_H
diff --git a/src/plugins/coreplugin/ssh/sshchannel.cpp b/src/plugins/coreplugin/ssh/sshchannel.cpp
new file mode 100644
index 0000000000..6e1b9c43f5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshchannel.cpp
@@ -0,0 +1,244 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshchannel_p.h"
+
+#include "sshincomingpacket_p.h"
+#include "sshsendfacility_p.h"
+
+#include <botan/exceptn.h>
+
+namespace Core {
+namespace Internal {
+
+namespace {
+ const quint32 MinMaxPacketSize = 32768;
+ const quint32 MaxPacketSize = 16 * 1024 * 1024;
+ const quint32 InitialWindowSize = MaxPacketSize;
+ const quint32 NoChannel = 0xffffffffu;
+} // anonymous namespace
+
+AbstractSshChannel::AbstractSshChannel(quint32 channelId,
+ SshSendFacility &sendFacility)
+ : m_sendFacility(sendFacility), m_localChannel(channelId),
+ m_remoteChannel(NoChannel), m_localWindowSize(InitialWindowSize),
+ m_remoteWindowSize(0), m_state(Inactive)
+{
+}
+
+AbstractSshChannel::~AbstractSshChannel()
+{
+
+}
+
+void AbstractSshChannel::setChannelState(ChannelState state)
+{
+ m_state = state;
+ if (state == Closed)
+ closeHook();
+}
+
+void AbstractSshChannel::requestSessionStart()
+{
+ // Note: We are just being paranoid here about the Botan exceptions,
+ // which are extremely unlikely to happen, because if there was a problem
+ // with our cryptography stuff, it would have hit us before, on
+ // establishing the connection.
+ try {
+ m_sendFacility.sendSessionPacket(m_localChannel, InitialWindowSize,
+ MaxPacketSize);
+ setChannelState(SessionRequested);
+ } catch (Botan::Exception &e) {
+ m_errorString = QString::fromAscii(e.what());
+ closeChannel();
+ }
+}
+
+void AbstractSshChannel::sendData(const QByteArray &data)
+{
+ try {
+ m_sendBuffer += data;
+ flushSendBuffer();
+ } catch (Botan::Exception &e) {
+ m_errorString = QString::fromAscii(e.what());
+ closeChannel();
+ }
+}
+
+void AbstractSshChannel::handleWindowAdjust(quint32 bytesToAdd)
+{
+ checkChannelActive();
+
+ const quint64 newValue = m_remoteWindowSize + bytesToAdd;
+ if (newValue > 0xffffffffu) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Illegal window size requested.");
+ }
+
+ m_remoteWindowSize = newValue;
+ flushSendBuffer();
+}
+
+void AbstractSshChannel::flushSendBuffer()
+{
+ const quint32 bytesToSend
+ = qMin<quint32>(m_remoteWindowSize, m_sendBuffer.size());
+ if (bytesToSend > 0) {
+ const QByteArray &data = m_sendBuffer.left(bytesToSend);
+ m_sendFacility.sendChannelDataPacket(m_remoteChannel, data);
+ m_sendBuffer.remove(0, bytesToSend);
+ m_remoteWindowSize -= bytesToSend;
+ }
+}
+
+void AbstractSshChannel::handleOpenSuccess(quint32 remoteChannelId,
+ quint32 remoteWindowSize, quint32 remoteMaxPacketSize)
+{
+ if (m_state != SessionRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet.");
+ }
+
+ if (remoteMaxPacketSize < MinMaxPacketSize) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Maximum packet size too low.");
+ }
+
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Channel opened. remote channel id: %u, remote window size: %u, "
+ "remote max packet size: %u",
+ remoteChannelId, remoteWindowSize, remoteMaxPacketSize);
+#endif
+ m_remoteChannel = remoteChannelId;
+ m_remoteWindowSize = remoteWindowSize;
+ m_remoteMaxPacketSize = remoteMaxPacketSize;
+ setChannelState(SessionEstablished);
+ handleOpenSuccessInternal();
+}
+
+void AbstractSshChannel::handleOpenFailure(const QString &reason)
+{
+ if (m_state != SessionRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
+ }
+
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Channel open request failed for channel %u", m_localChannel);
+#endif
+ m_errorString = reason;
+ handleOpenFailureInternal();
+}
+
+void AbstractSshChannel::handleChannelEof()
+{
+ if (m_state == Inactive || m_state == Closed) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_EOF message.");
+ }
+ m_localWindowSize = 0;
+}
+
+void AbstractSshChannel::handleChannelClose()
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Receiving CLOSE for channel %u", m_localChannel);
+#endif
+ if (channelState() == Inactive || channelState() == Closed) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_CLOSE message.");
+ }
+ closeChannel();
+ setChannelState(Closed);
+}
+
+void AbstractSshChannel::handleChannelData(const QByteArray &data)
+{
+ const int bytesToDeliver = handleChannelOrExtendedChannelData(data);
+ handleChannelDataInternal(bytesToDeliver == data.size()
+ ? data : data.left(bytesToDeliver));
+}
+
+void AbstractSshChannel::handleChannelExtendedData(quint32 type, const QByteArray &data)
+{
+ const int bytesToDeliver = handleChannelOrExtendedChannelData(data);
+ handleChannelExtendedDataInternal(type, bytesToDeliver == data.size()
+ ? data : data.left(bytesToDeliver));
+}
+
+void AbstractSshChannel::handleChannelRequest(const SshIncomingPacket &packet)
+{
+ qWarning("Ignoring unknown request type '%s'",
+ packet.extractChannelRequestType().data());
+}
+
+int AbstractSshChannel::handleChannelOrExtendedChannelData(const QByteArray &data)
+{
+ checkChannelActive();
+
+ const int bytesToDeliver = qMin<quint32>(data.size(), maxDataSize());
+ if (bytesToDeliver != data.size())
+ qWarning("Misbehaving server does not respect local window, clipping.");
+
+ m_localWindowSize -= bytesToDeliver;
+ if (m_localWindowSize < MaxPacketSize) {
+ m_localWindowSize += MaxPacketSize;
+ m_sendFacility.sendWindowAdjustPacket(m_remoteChannel,
+ MaxPacketSize);
+ }
+ return bytesToDeliver;
+}
+
+void AbstractSshChannel::closeChannel()
+{
+ if (m_state != CloseRequested && m_state != Closed) {
+ if (m_state == Inactive) {
+ setChannelState(Closed);
+ } else {
+ setChannelState(CloseRequested);
+ m_sendFacility.sendChannelEofPacket(m_remoteChannel);
+ m_sendFacility.sendChannelClosePacket(m_remoteChannel);
+ }
+ }
+}
+
+void AbstractSshChannel::checkChannelActive()
+{
+ if (channelState() == Inactive || channelState() == Closed)
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Channel not open.");
+}
+
+quint32 AbstractSshChannel::maxDataSize() const
+{
+ return qMin(m_localWindowSize, MaxPacketSize);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshchannel_p.h b/src/plugins/coreplugin/ssh/sshchannel_p.h
new file mode 100644
index 0000000000..993357d871
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshchannel_p.h
@@ -0,0 +1,111 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHCHANNEL_P_H
+#define SSHCHANNEL_P_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+class SshIncomingPacket;
+class SshSendFacility;
+
+class AbstractSshChannel
+{
+public:
+ enum ChannelState {
+ Inactive, SessionRequested, SessionEstablished, CloseRequested, Closed
+ };
+
+ ChannelState channelState() const { return m_state; }
+ void setChannelState(ChannelState state);
+
+ void setError(const QString &error) { m_errorString = error; }
+ QString errorString() const { return m_errorString; }
+
+ quint32 localChannelId() const { return m_localChannel; }
+ quint32 remoteChannel() const { return m_remoteChannel; }
+
+ virtual void handleChannelSuccess()=0;
+ virtual void handleChannelFailure()=0;
+ virtual void handleChannelRequest(const SshIncomingPacket &packet);
+
+ virtual void closeHook()=0;
+
+ void handleOpenSuccess(quint32 remoteChannelId, quint32 remoteWindowSize,
+ quint32 remoteMaxPacketSize);
+ void handleOpenFailure(const QString &reason);
+ void handleWindowAdjust(quint32 bytesToAdd);
+ void handleChannelEof();
+ void handleChannelClose();
+ void handleChannelData(const QByteArray &data);
+ void handleChannelExtendedData(quint32 type, const QByteArray &data);
+
+ void requestSessionStart();
+ void sendData(const QByteArray &data);
+ void closeChannel();
+
+ virtual ~AbstractSshChannel();
+
+protected:
+ AbstractSshChannel(quint32 channelId, SshSendFacility &sendFacility);
+
+ quint32 maxDataSize() const;
+ void checkChannelActive();
+
+ SshSendFacility &m_sendFacility;
+
+private:
+ virtual void handleOpenSuccessInternal()=0;
+ virtual void handleOpenFailureInternal()=0;
+ virtual void handleChannelDataInternal(const QByteArray &data)=0;
+ virtual void handleChannelExtendedDataInternal(quint32 type,
+ const QByteArray &data)=0;
+
+ void setState(ChannelState newState);
+ void flushSendBuffer();
+ int handleChannelOrExtendedChannelData(const QByteArray &data);
+
+ const quint32 m_localChannel;
+ quint32 m_remoteChannel;
+ quint32 m_localWindowSize;
+ quint32 m_remoteWindowSize;
+ quint32 m_remoteMaxPacketSize;
+ ChannelState m_state;
+ QByteArray m_sendBuffer;
+ QString m_errorString;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHCHANNEL_P_H
diff --git a/src/plugins/coreplugin/ssh/sshchannelmanager.cpp b/src/plugins/coreplugin/ssh/sshchannelmanager.cpp
new file mode 100644
index 0000000000..c7d3113697
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshchannelmanager.cpp
@@ -0,0 +1,188 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshchannelmanager_p.h"
+
+#include "sftpchannel.h"
+#include "sftpchannel_p.h"
+#include "sshincomingpacket_p.h"
+#include "sshremoteprocess.h"
+#include "sshremoteprocess_p.h"
+#include "sshsendfacility_p.h"
+
+#include <QtCore/QList>
+
+namespace Core {
+namespace Internal {
+
+SshChannelManager::SshChannelManager(SshSendFacility &sendFacility)
+ : m_sendFacility(sendFacility), m_nextLocalChannelId(0)
+{
+}
+
+SshChannelManager::~SshChannelManager() {}
+
+void SshChannelManager::handleChannelRequest(const SshIncomingPacket &packet)
+{
+ lookupChannel(packet.extractRecipientChannel())
+ ->handleChannelRequest(packet);
+}
+
+void SshChannelManager::handleChannelOpen(const SshIncomingPacket &)
+{
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Server tried to open channel on client.");
+}
+
+void SshChannelManager::handleChannelOpenFailure(const SshIncomingPacket &packet)
+{
+ const SshChannelOpenFailure &failure = packet.extractChannelOpenFailure();
+ ChannelIterator it = lookupChannelAsIterator(failure.localChannel);
+ try {
+ it.value()->handleOpenFailure(failure.reasonString);
+ } catch (SshServerException &e) {
+ removeChannel(it);
+ throw e;
+ }
+ removeChannel(it);
+}
+
+void SshChannelManager::handleChannelOpenConfirmation(const SshIncomingPacket &packet)
+{
+ const SshChannelOpenConfirmation &confirmation
+ = packet.extractChannelOpenConfirmation();
+ lookupChannel(confirmation.localChannel)->handleOpenSuccess(confirmation.remoteChannel,
+ confirmation.remoteWindowSize, confirmation.remoteMaxPacketSize);
+}
+
+void SshChannelManager::handleChannelSuccess(const SshIncomingPacket &packet)
+{
+ lookupChannel(packet.extractRecipientChannel())->handleChannelSuccess();
+}
+
+void SshChannelManager::handleChannelFailure(const SshIncomingPacket &packet)
+{
+ lookupChannel(packet.extractRecipientChannel())->handleChannelFailure();
+}
+
+void SshChannelManager::handleChannelWindowAdjust(const SshIncomingPacket &packet)
+{
+ const SshChannelWindowAdjust adjust = packet.extractWindowAdjust();
+ lookupChannel(adjust.localChannel)->handleWindowAdjust(adjust.bytesToAdd);
+}
+
+void SshChannelManager::handleChannelData(const SshIncomingPacket &packet)
+{
+ const SshChannelData &data = packet.extractChannelData();
+ lookupChannel(data.localChannel)->handleChannelData(data.data);
+}
+
+void SshChannelManager::handleChannelExtendedData(const SshIncomingPacket &packet)
+{
+ const SshChannelExtendedData &data = packet.extractChannelExtendedData();
+ lookupChannel(data.localChannel)->handleChannelExtendedData(data.type, data.data);
+}
+
+void SshChannelManager::handleChannelEof(const SshIncomingPacket &packet)
+{
+ AbstractSshChannel * const channel
+ = lookupChannel(packet.extractRecipientChannel(), true);
+ if (channel)
+ channel->handleChannelEof();
+}
+
+void SshChannelManager::handleChannelClose(const SshIncomingPacket &packet)
+{
+ const quint32 channelId = packet.extractRecipientChannel();
+
+ ChannelIterator it = lookupChannelAsIterator(channelId, true);
+ if (it != m_channels.end()) {
+ it.value()->handleChannelClose();
+ removeChannel(it);
+ }
+}
+
+SshChannelManager::ChannelIterator SshChannelManager::lookupChannelAsIterator(quint32 channelId,
+ bool allowNotFound)
+{
+ ChannelIterator it = m_channels.find(channelId);
+ if (it == m_channels.end() && !allowNotFound) {
+ throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid channel id.",
+ SSH_TR("Invalid channel id %1").arg(channelId));
+ }
+ return it;
+}
+
+AbstractSshChannel *SshChannelManager::lookupChannel(quint32 channelId,
+ bool allowNotFound)
+{
+ ChannelIterator it = lookupChannelAsIterator(channelId, allowNotFound);
+ return it == m_channels.end() ? 0 : it.value();
+}
+
+Core::SshRemoteProcess::Ptr SshChannelManager::createRemoteProcess(const QByteArray &command)
+{
+ SshRemoteProcess::Ptr proc(new SshRemoteProcess(command, m_nextLocalChannelId++, m_sendFacility));
+ insertChannel(proc->d, proc);
+ return proc;
+}
+
+Core::SftpChannel::Ptr SshChannelManager::createSftpChannel()
+{
+ SftpChannel::Ptr sftp(new SftpChannel(m_nextLocalChannelId++, m_sendFacility));
+ insertChannel(sftp->d, sftp);
+ return sftp;
+}
+
+void SshChannelManager::insertChannel(AbstractSshChannel *priv,
+ const QSharedPointer<QObject> &pub)
+{
+ m_channels.insert(priv->localChannelId(), priv);
+ m_sessions.insert(priv, pub);
+}
+
+void SshChannelManager::closeAllChannels()
+{
+ for (ChannelIterator it = m_channels.begin(); it != m_channels.end(); ++it)
+ it.value()->closeChannel();
+ m_channels.clear();
+ m_sessions.clear();
+}
+
+void SshChannelManager::removeChannel(ChannelIterator it)
+{
+ Q_ASSERT(it != m_channels.end() && "Unexpected channel lookup failure.");
+ const int removeCount = m_sessions.remove(it.value());
+ Q_ASSERT(removeCount == 1 && "Session for channel not found.");
+ m_channels.erase(it);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshchannelmanager_p.h b/src/plugins/coreplugin/ssh/sshchannelmanager_p.h
new file mode 100644
index 0000000000..fe62c00924
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshchannelmanager_p.h
@@ -0,0 +1,89 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHCHANNELLAYER_P_H
+#define SSHCHANNELLAYER_P_H
+
+#include <QtCore/QHash>
+#include <QtCore/QSharedPointer>
+
+namespace Core {
+
+class SftpChannel;
+class SshRemoteProcess;
+
+namespace Internal {
+
+class AbstractSshChannel;
+class SshIncomingPacket;
+class SshSendFacility;
+
+class SshChannelManager
+{
+public:
+ SshChannelManager(SshSendFacility &sendFacility);
+ ~SshChannelManager();
+
+ QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
+ QSharedPointer<SftpChannel> createSftpChannel();
+ void closeAllChannels();
+
+ void handleChannelRequest(const SshIncomingPacket &packet);
+ void handleChannelOpen(const SshIncomingPacket &packet);
+ void handleChannelOpenFailure(const SshIncomingPacket &packet);
+ void handleChannelOpenConfirmation(const SshIncomingPacket &packet);
+ void handleChannelSuccess(const SshIncomingPacket &packet);
+ void handleChannelFailure(const SshIncomingPacket &packet);
+ void handleChannelWindowAdjust(const SshIncomingPacket &packet);
+ void handleChannelData(const SshIncomingPacket &packet);
+ void handleChannelExtendedData(const SshIncomingPacket &packet);
+ void handleChannelEof(const SshIncomingPacket &packet);
+ void handleChannelClose(const SshIncomingPacket &packet);
+
+private:
+ typedef QHash<quint32, AbstractSshChannel *>::Iterator ChannelIterator;
+
+ ChannelIterator lookupChannelAsIterator(quint32 channelId,
+ bool allowNotFound = false);
+ AbstractSshChannel *lookupChannel(quint32 channelId,
+ bool allowNotFound = false);
+ void removeChannel(ChannelIterator it);
+ void insertChannel(AbstractSshChannel *priv,
+ const QSharedPointer<QObject> &pub);
+
+ SshSendFacility &m_sendFacility;
+ QHash<quint32, AbstractSshChannel *> m_channels;
+ QHash<AbstractSshChannel *, QSharedPointer<QObject> > m_sessions;
+ quint32 m_nextLocalChannelId;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHCHANNELLAYER_P_H
diff --git a/src/plugins/coreplugin/ssh/sshconnection.cpp b/src/plugins/coreplugin/ssh/sshconnection.cpp
index 7433bbebd8..fbf63d7670 100644
--- a/src/plugins/coreplugin/ssh/sshconnection.cpp
+++ b/src/plugins/coreplugin/ssh/sshconnection.cpp
@@ -1,19 +1,20 @@
-/****************************************************************************
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
-** This file is part of Qt Creator.
+** Commercial Usage
**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
+**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
@@ -21,471 +22,540 @@
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
#include "sshconnection.h"
+#include "sshconnection_p.h"
-#include "ne7sshobject.h"
+#include "sftpchannel.h"
+#include "sshcapabilities_p.h"
+#include "sshchannelmanager_p.h"
+#include "sshcryptofacility_p.h"
+#include "sshexception_p.h"
+#include "sshkeyexchange_p.h"
-#include <QtCore/QCoreApplication>
-#include <QtCore/QDir>
-#include <QtCore/QFileInfo>
-#include <QtCore/QMutex>
-#include <QtCore/QThread>
-#include <QtCore/QWaitCondition>
-
-#include <ne7ssh.h>
+#include <botan/exceptn.h>
+#include <botan/init.h>
-#include <exception>
+#include <QtCore/QFile>
+#include <QtCore/QMutex>
+#include <QtNetwork/QTcpSocket>
namespace Core {
namespace {
+ const QByteArray ClientId("SSH-2.0-QtCreator\r\n");
-class GenericSshConnection
-{
- Q_DECLARE_TR_FUNCTIONS(GenericSshConnection)
-public:
- GenericSshConnection(const SshServerInfo &server)
- : ssh(Internal::Ne7SshObject::instance()->get()),
- m_server(server),
- m_channel(-1)
- { }
-
- ~GenericSshConnection()
- {
- quit();
- }
+ bool staticInitializationsDone = false;
+ QMutex staticInitMutex;
- bool start(bool shell, void (*callbackFunc)(void *), void *callbackArg)
+ void doStaticInitializationsIfNecessary()
{
- Q_ASSERT(m_channel == -1);
-
- try {
- const QString *authString;
- int (ne7ssh::*connFunc)(const char *, int, const char *,
- const char *, bool, int, void (*)(void *), void *);
- if (m_server.authType == SshServerInfo::AuthByPwd) {
- authString = &m_server.pwd;
- connFunc = &ne7ssh::connectWithPassword;
- } else {
- authString = &m_server.privateKeyFile;
- connFunc = &ne7ssh::connectWithKey;
+ if (!staticInitializationsDone) {
+ staticInitMutex.lock();
+ if (!staticInitializationsDone) {
+ Botan::LibraryInitializer::initialize("thread_safe=true");
+ qRegisterMetaType<SshError>("SshError");
+ staticInitializationsDone = true;
}
- m_channel = (ssh.data()->*connFunc)(m_server.host.toLatin1(),
- m_server.port, m_server.uname.toAscii(), authString->toLatin1(),
- shell, m_server.timeout, callbackFunc, callbackArg);
- if (m_channel == -1) {
- setError(tr("Could not connect to host."), false);
- return false;
- }
- } catch (const std::exception &e) {
- // Should in theory not be necessary, but Net7 leaks Botan exceptions.
- setError(tr("Error in cryptography backend: %1")
- .arg(QLatin1String(e.what())), false);
- return false;
- }
-
- return true;
- }
-
- void quit()
- {
- const int channel = m_channel;
- if (channel != -1) {
- m_channel = -1;
- if (!ssh->close(channel))
- qWarning("%s: close() failed.", Q_FUNC_INFO);
+ staticInitMutex.unlock();
}
}
+}
- bool isConnected() const { return channel() != -1; }
- bool hasError() const { return !m_error.isEmpty(); }
- QString error() const { return m_error; }
- int channel() const { return m_channel; }
- QString lastNe7Error() { return ssh->errors()->pop(channel()); }
- const SshServerInfo &server() { return m_server; }
+// TODO: Mechanism for checking the host key. First connection to host: save, later: compare
- void setError(const QString error, bool appendNe7ErrMsg)
- {
- m_error = error;
- if (appendNe7ErrMsg)
- m_error += QLatin1String(": ") + lastNe7Error();
- }
-
- QSharedPointer<ne7ssh> ssh;
-private:
- const SshServerInfo m_server;
- QString m_error;
- int m_channel;
-};
+SshConnection::Ptr SshConnection::create()
+{
+ doStaticInitializationsIfNecessary();
+ return Ptr(new SshConnection);
+}
-char *alloc(size_t n)
+SshConnection::SshConnection() : d(new Internal::SshConnectionPrivate(this))
{
- return new char[n];
+ connect(d, SIGNAL(connected()), this, SIGNAL(connected()),
+ Qt::QueuedConnection);
+ connect(d, SIGNAL(dataAvailable(QString)), this,
+ SIGNAL(dataAvailable(QString)), Qt::QueuedConnection);
+ connect(d, SIGNAL(disconnected()), this, SIGNAL(disconnected()),
+ Qt::QueuedConnection);
+ connect(d, SIGNAL(error(SshError)), this, SIGNAL(error(SshError)),
+ Qt::QueuedConnection);
}
-} // anonymous namespace
+void SshConnection::connectToHost(const SshConnectionParameters &serverInfo)
+{
+ d->connectToHost(serverInfo);
+}
-namespace Internal {
+void SshConnection::disconnectFromHost()
+{
+ d->closeConnection(Internal::SSH_DISCONNECT_BY_APPLICATION, SshNoError, "",
+ QString());
+}
-struct InteractiveSshConnectionPrivate
+SshConnection::State SshConnection::state() const
{
- InteractiveSshConnectionPrivate(const SshServerInfo &server)
- : conn(server), outputReader(0) {}
+ switch (d->state()) {
+ case Internal::SocketUnconnected:
+ return Unconnected;
+ case Internal::ConnectionEstablished:
+ return Connected;
+ default:
+ return Connecting;
+ }
+}
- GenericSshConnection conn;
- ConnectionOutputReader *outputReader;
- QByteArray remoteOutput;
- QMutex mutex;
- QWaitCondition waitCond;
-};
+SshError SshConnection::errorState() const
+{
+ return d->error();
+}
-struct NonInteractiveSshConnectionPrivate
+QString SshConnection::errorString() const
{
- NonInteractiveSshConnectionPrivate(const SshServerInfo &server)
- : conn(server) {}
+ return d->errorString();
+}
- GenericSshConnection conn;
- Ne7SftpSubsystem sftp;
-};
+SshConnectionParameters SshConnection::connectionParameters() const
+{
+ return d->m_connParams;
+}
-class ConnectionOutputReader : public QThread
+SshConnection::~SshConnection()
{
-public:
- ConnectionOutputReader(InteractiveSshConnection *parent)
- : QThread(parent), m_conn(parent), m_stopRequested(false),
- m_dataAvailable(false)
- {}
+ disconnect();
+ disconnectFromHost();
+ delete d;
+}
- ~ConnectionOutputReader()
- {
- stop();
- wait();
- }
+QSharedPointer<SshRemoteProcess> SshConnection::createRemoteProcess(const QByteArray &command)
+{
+ return state() == Connected
+ ? d->createRemoteProcess(command) : QSharedPointer<SshRemoteProcess>();
+}
- void stop()
- {
- m_mutex.lock();
- m_stopRequested = true;
- m_waitCond.wakeOne();
- m_mutex.unlock();
- }
+QSharedPointer<SftpChannel> SshConnection::createSftpChannel()
+{
+ return state() == Connected
+ ? d->createSftpChannel() : QSharedPointer<SftpChannel>();
+}
- void dataAvailable()
- {
- m_mutex.lock();
- m_dataAvailable = true;
- m_waitCond.wakeOne();
- m_mutex.unlock();
- }
-private:
- virtual void run()
- {
- while (true) {
- m_mutex.lock();
- if (m_stopRequested) {
- m_mutex.unlock();
- return;
- }
- const int channel = m_conn->d->conn.channel();
- if (!m_dataAvailable || channel == -1)
- m_waitCond.wait(&m_mutex);
- m_dataAvailable = false;
- m_mutex.unlock();
- QScopedPointer<char, QScopedPointerArrayDeleter<char> >
- output(m_conn->d->conn.ssh->readAndReset(channel, alloc));
- if (output) {
- m_conn->d->mutex.lock();
- m_conn->d->remoteOutput += output.data();
- emit m_conn->remoteOutputAvailable();
- m_conn->d->mutex.unlock();
- }
- }
- }
+namespace Internal {
- InteractiveSshConnection *m_conn;
- bool m_stopRequested;
- bool m_dataAvailable;
- QMutex m_mutex;
- QWaitCondition m_waitCond;
-};
+SshConnectionPrivate::SshConnectionPrivate(SshConnection *conn)
+ : m_socket(new QTcpSocket(this)), m_state(SocketUnconnected),
+ m_sendFacility(m_socket),
+ m_channelManager(new SshChannelManager(m_sendFacility)),
+ m_error(SshNoError), m_ignoreNextPacket(false), m_conn(conn)
+{
+ setupPacketHandlers();
+ connect(&m_timeoutTimer, SIGNAL(timeout()), this, SLOT(handleTimeout()));
+}
-} // namespace Internal
+SshConnectionPrivate::~SshConnectionPrivate()
+{
+ disconnect();
+}
+void SshConnectionPrivate::setupPacketHandlers()
+{
+ typedef SshConnectionPrivate This;
+
+ setupPacketHandler(SSH_MSG_KEXINIT, StateList() << SocketConnected,
+ &This::handleKeyExchangeInitPacket);
+ setupPacketHandler(SSH_MSG_KEXDH_REPLY, StateList() << KeyExchangeStarted,
+ &This::handleKeyExchangeReplyPacket);
+
+ setupPacketHandler(SSH_MSG_NEWKEYS, StateList() << KeyExchangeSuccess,
+ &This::handleNewKeysPacket);
+ setupPacketHandler(SSH_MSG_SERVICE_ACCEPT,
+ StateList() << UserAuthServiceRequested,
+ &This::handleServiceAcceptPacket);
+ setupPacketHandler(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
+ StateList() << UserAuthRequested, &This::handlePasswordExpiredPacket);
+ setupPacketHandler(SSH_MSG_GLOBAL_REQUEST,
+ StateList() << ConnectionEstablished, &This::handleGlobalRequest);
+
+ const StateList authReqList = StateList() << UserAuthRequested;
+ setupPacketHandler(SSH_MSG_USERAUTH_BANNER, authReqList,
+ &This::handleUserAuthBannerPacket);
+ setupPacketHandler(SSH_MSG_USERAUTH_SUCCESS, authReqList,
+ &This::handleUserAuthSuccessPacket);
+ setupPacketHandler(SSH_MSG_USERAUTH_FAILURE, authReqList,
+ &This::handleUserAuthFailurePacket);
+
+ const StateList connectedList
+ = StateList() << ConnectionEstablished;
+ setupPacketHandler(SSH_MSG_CHANNEL_REQUEST, connectedList,
+ &This::handleChannelRequest);
+ setupPacketHandler(SSH_MSG_CHANNEL_OPEN, connectedList,
+ &This::handleChannelOpen);
+ setupPacketHandler(SSH_MSG_CHANNEL_OPEN_FAILURE, connectedList,
+ &This::handleChannelOpenFailure);
+ setupPacketHandler(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, connectedList,
+ &This::handleChannelOpenConfirmation);
+ setupPacketHandler(SSH_MSG_CHANNEL_SUCCESS, connectedList,
+ &This::handleChannelSuccess);
+ setupPacketHandler(SSH_MSG_CHANNEL_FAILURE, connectedList,
+ &This::handleChannelFailure);
+ setupPacketHandler(SSH_MSG_CHANNEL_WINDOW_ADJUST, connectedList,
+ &This::handleChannelWindowAdjust);
+ setupPacketHandler(SSH_MSG_CHANNEL_DATA, connectedList,
+ &This::handleChannelData);
+ setupPacketHandler(SSH_MSG_CHANNEL_EXTENDED_DATA, connectedList,
+ &This::handleChannelExtendedData);
+
+ const StateList connectedOrClosedList
+ = StateList() << SocketUnconnected << ConnectionEstablished;
+ setupPacketHandler(SSH_MSG_CHANNEL_EOF, connectedOrClosedList,
+ &This::handleChannelEof);
+ setupPacketHandler(SSH_MSG_CHANNEL_CLOSE, connectedOrClosedList,
+ &This::handleChannelClose);
+
+ setupPacketHandler(SSH_MSG_DISCONNECT, StateList() << SocketConnected
+ << KeyExchangeStarted << KeyExchangeSuccess
+ << UserAuthServiceRequested << UserAuthRequested
+ << ConnectionEstablished, &This::handleDisconnect);
+}
-namespace {
+void SshConnectionPrivate::setupPacketHandler(SshPacketType type,
+ const SshConnectionPrivate::StateList &states,
+ SshConnectionPrivate::PacketHandler handler)
+{
+ m_packetHandlers.insert(type, HandlerInStates(states, handler));
+}
-void wakeupReader(void *opaqueReader)
+void SshConnectionPrivate::handleSocketConnected()
{
- static_cast<Internal::ConnectionOutputReader*>(opaqueReader)->dataAvailable();
+ m_state = SocketConnected;
+ sendData(ClientId);
}
-} // Anonymous namespace
+void SshConnectionPrivate::handleIncomingData()
+{
+ if (m_state == SocketUnconnected)
+ return; // For stuff queued in the event loop after we've called closeConnection();
+
+ try {
+ m_incomingData += m_socket->readAll();
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("state = %d, remote data size = %d", m_state,
+ m_incomingData.count());
+#endif
+ if (m_state == SocketConnected)
+ handleServerId();
+ handlePackets();
+ } catch (SshServerException &e) {
+ closeConnection(e.error, SshProtocolError, e.errorStringServer,
+ tr("SSH Protocol error: %1").arg(e.errorStringUser));
+ } catch (SshClientException &e) {
+ closeConnection(SSH_DISCONNECT_BY_APPLICATION, e.error, "",
+ e.errorString);
+ } catch (Botan::Exception &e) {
+ closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshInternalError, "",
+ tr("Botan exception: %1").arg(e.what()));
+ }
+}
+void SshConnectionPrivate::handleServerId()
+{
+ const int idOffset = m_incomingData.indexOf("SSH-");
+ if (idOffset == -1)
+ return;
+ m_incomingData.remove(0, idOffset);
+ if (m_incomingData.size() < 7)
+ return;
+ const QByteArray &version = m_incomingData.mid(4, 3);
+ if (version != "2.0") {
+ throw SshServerException(SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ "Invalid protocol version.",
+ tr("Invalid protocol version: Expected '2.0', got '%1'.")
+ .arg(SshPacketParser::asUserString(version)));
+ }
+ const int endOffset = m_incomingData.indexOf("\r\n");
+ if (endOffset == -1)
+ return;
+ if (m_incomingData.at(7) != '-') {
+ throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid server id.", tr("Invalid server id '%1'.")
+ .arg(SshPacketParser::asUserString(m_incomingData)));
+ }
-InteractiveSshConnection::InteractiveSshConnection(const SshServerInfo &server)
- : d(new Internal::InteractiveSshConnectionPrivate(server))
-{
- d->outputReader = new Internal::ConnectionOutputReader(this);
+ m_keyExchange.reset(new SshKeyExchange(m_sendFacility));
+ m_keyExchange->sendKexInitPacket(m_incomingData.left(endOffset));
+ m_incomingData.remove(0, endOffset + 2);
}
-InteractiveSshConnection::~InteractiveSshConnection()
+void SshConnectionPrivate::handlePackets()
{
- d->conn.ssh->send("exit\n", d->conn.channel());
- quit();
- delete d;
+ m_incomingPacket.consumeData(m_incomingData);
+ while (m_incomingPacket.isComplete()) {
+ handleCurrentPacket();
+ m_incomingPacket.clear();
+ m_incomingPacket.consumeData(m_incomingData);
+ }
}
-bool InteractiveSshConnection::start()
+void SshConnectionPrivate::handleCurrentPacket()
{
- if (isConnected())
- return true;
+ Q_ASSERT(m_incomingPacket.isComplete());
+ Q_ASSERT(m_state == KeyExchangeStarted || !m_ignoreNextPacket);
- if (!d->conn.start(true, wakeupReader, d->outputReader))
- return false;
+ if (m_ignoreNextPacket) {
+ m_ignoreNextPacket = false;
+ return;
+ }
- d->outputReader->start();
- return true;
+ QHash<SshPacketType, HandlerInStates>::ConstIterator it
+ = m_packetHandlers.find(m_incomingPacket.type());
+ if (it == m_packetHandlers.end()) {
+ m_sendFacility.sendMsgUnimplementedPacket(m_incomingPacket.serverSeqNr());
+ return;
+ }
+ if (!it.value().first.contains(m_state)) {
+ throw SshServerException(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected packet.", tr("Unexpected packet of type %1.")
+ .arg(m_incomingPacket.type()));
+ }
+ (this->*it.value().second)();
}
-bool InteractiveSshConnection::sendInput(const QByteArray &input)
+void SshConnectionPrivate::handleKeyExchangeInitPacket()
{
- if (!d->conn.ssh->send(input.data(), d->conn.channel())) {
- d->conn.setError(tr("Error sending input"), true);
- return false;
- }
- return true;
+ // If the server sends a guessed packet, the guess must be wrong,
+ // because the algorithms we support requires us to initiate the
+ // key exchange.
+ if (m_keyExchange->sendDhInitPacket(m_incomingPacket))
+ m_ignoreNextPacket = true;
+ m_state = KeyExchangeStarted;
}
-void InteractiveSshConnection::quit()
+void SshConnectionPrivate::handleKeyExchangeReplyPacket()
{
- d->mutex.lock();
- d->waitCond.wakeOne();
- d->mutex.unlock();
- d->outputReader->stop();
- d->conn.quit();
+ m_keyExchange->sendNewKeysPacket(m_incomingPacket,
+ ClientId.left(ClientId.size() - 2));
+ m_sendFacility.recreateKeys(*m_keyExchange);
+ m_state = KeyExchangeSuccess;
}
-QByteArray InteractiveSshConnection::waitForRemoteOutput(int msecs)
+void SshConnectionPrivate::handleNewKeysPacket()
{
- d->mutex.lock();
- if (d->remoteOutput.isEmpty())
- d->waitCond.wait(&d->mutex, msecs == -1 ? ULONG_MAX : msecs);
- const QByteArray remoteOutput = d->remoteOutput;
- d->remoteOutput.clear();
- d->mutex.unlock();
- return remoteOutput;
+ m_incomingPacket.recreateKeys(*m_keyExchange);
+ m_keyExchange.reset();
+ m_sendFacility.sendUserAuthServiceRequestPacket();
+ m_state = UserAuthServiceRequested;
}
+void SshConnectionPrivate::handleServiceAcceptPacket()
+{
+ if (m_connParams.authType == SshConnectionParameters::AuthByPwd) {
+ m_sendFacility.sendUserAuthByPwdRequestPacket(m_connParams.uname.toUtf8(),
+ SshCapabilities::SshConnectionService, m_connParams.pwd.toUtf8());
+ } else {
+ QFile privKeyFile(m_connParams.privateKeyFile);
+ bool couldOpen = privKeyFile.open(QIODevice::ReadOnly);
+ QByteArray contents;
+ if (couldOpen)
+ contents = privKeyFile.readAll();
+ if (!couldOpen || privKeyFile.error() != QFile::NoError) {
+ throw SshClientException(SshKeyFileError,
+ tr("Could not read private key file: %1")
+ .arg(privKeyFile.errorString()));
+ }
-InteractiveSshConnection::Ptr InteractiveSshConnection::create(const SshServerInfo &server)
-{
- return Ptr(new InteractiveSshConnection(server));
+ m_sendFacility.createAuthenticationKey(contents);
+ m_sendFacility.sendUserAuthByKeyRequestPacket(m_connParams.uname.toUtf8(),
+ SshCapabilities::SshConnectionService);
+ }
+ m_state = UserAuthRequested;
}
-bool InteractiveSshConnection::isConnected() const
+void SshConnectionPrivate::handlePasswordExpiredPacket()
{
- return d->conn.isConnected();
+ if (m_connParams.authType == SshConnectionParameters::AuthByKey) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Got SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, but did not use password.");
+ }
+
+ throw SshClientException(SshAuthenticationError, tr("Password expired."));
}
-bool InteractiveSshConnection::hasError() const
+void SshConnectionPrivate::handleUserAuthBannerPacket()
{
- return d->conn.hasError();
+ emit dataAvailable(m_incomingPacket.extractUserAuthBanner().message);
}
-QString InteractiveSshConnection::error() const
+void SshConnectionPrivate::handleGlobalRequest()
{
- return d->conn.error();
+ m_sendFacility.sendRequestFailurePacket();
}
-
-namespace {
-
-class FileMgr
+void SshConnectionPrivate::handleUserAuthSuccessPacket()
{
-public:
- FileMgr(const QString &filePath, const char *mode)
- : m_file(fopen(filePath.toLatin1().data(), mode)) {}
- ~FileMgr() { if (m_file) fclose(m_file); }
- FILE *file() const { return m_file; }
-private:
- FILE * const m_file;
-};
-
-} // Anonymous namespace
+ m_state = ConnectionEstablished;
+ m_timeoutTimer.stop();
+ emit connected();
+}
-SftpConnection::SftpConnection(const SshServerInfo &server)
- : d(new Internal::NonInteractiveSshConnectionPrivate(server))
-{ }
+void SshConnectionPrivate::handleUserAuthFailurePacket()
+{
+ const QString errorMsg = m_connParams.authType == SshConnectionParameters::AuthByPwd
+ ? tr("Server rejected password.") : tr("Server rejected key.");
+ throw SshClientException(SshAuthenticationError, errorMsg);
+}
+void SshConnectionPrivate::handleDebugPacket()
+{
+ const SshDebug &msg = m_incomingPacket.extractDebug();
+ if (msg.display)
+ emit dataAvailable(msg.message);
+}
-SftpConnection::~SftpConnection()
+void SshConnectionPrivate::handleChannelRequest()
{
- quit();
- delete d;
+ m_channelManager->handleChannelRequest(m_incomingPacket);
}
-bool SftpConnection::start()
+void SshConnectionPrivate::handleChannelOpen()
{
- if (isConnected())
- return true;
- if (!d->conn.start(false, 0, 0))
- return false;
- if (!d->conn.ssh->initSftp(d->sftp, d->conn.channel())
- || !d->sftp.setTimeout(d->conn.server().timeout)) {
- d->conn.setError(tr("Error setting up SFTP subsystem"), true);
- quit();
- return false;
- }
- return true;
+ m_channelManager->handleChannelOpen(m_incomingPacket);
}
-bool SftpConnection::transferFiles(const QList<SftpTransferInfo> &transferList)
+void SshConnectionPrivate::handleChannelOpenFailure()
{
- for (int i = 0; i < transferList.count(); ++i) {
- const SftpTransferInfo &transfer = transferList.at(i);
- bool success;
- if (transfer.type == SftpTransferInfo::Upload) {
- success = upload(transfer.localFilePath, transfer.remoteFilePath);
- } else {
- success = download(transfer.remoteFilePath, transfer.localFilePath);
- }
- if (!success)
- return false;
- }
+ m_channelManager->handleChannelOpenFailure(m_incomingPacket);
+}
- return true;
+void SshConnectionPrivate::handleChannelOpenConfirmation()
+{
+ m_channelManager->handleChannelOpenConfirmation(m_incomingPacket);
}
-bool SftpConnection::upload(const QString &localFilePath,
- const QByteArray &remoteFilePath)
+void SshConnectionPrivate::handleChannelSuccess()
{
- FileMgr fileMgr(localFilePath, "rb");
- if (!fileMgr.file()) {
- d->conn.setError(tr("Could not open file '%1'").arg(localFilePath),
- false);
- return false;
- }
+ m_channelManager->handleChannelSuccess(m_incomingPacket);
+}
- if (!d->sftp.put(fileMgr.file(), remoteFilePath.data())) {
- d->conn.setError(tr("Could not uplodad file '%1'")
- .arg(localFilePath), true);
- return false;
- }
+void SshConnectionPrivate::handleChannelFailure()
+{
+ m_channelManager->handleChannelFailure(m_incomingPacket);
+}
- emit fileCopied(localFilePath);
- return true;
+void SshConnectionPrivate::handleChannelWindowAdjust()
+{
+ m_channelManager->handleChannelWindowAdjust(m_incomingPacket);
}
-bool SftpConnection::download(const QByteArray &remoteFilePath,
- const QString &localFilePath)
+void SshConnectionPrivate::handleChannelData()
{
- FileMgr fileMgr(localFilePath, "wb");
- if (!fileMgr.file()) {
- d->conn.setError(tr("Could not open file '%1'").arg(localFilePath),
- false);
- return false;
- }
+ m_channelManager->handleChannelData(m_incomingPacket);
+}
- if (!d->sftp.get(remoteFilePath.data(), fileMgr.file())) {
- d->conn.setError(tr("Could not copy remote file '%1' to local file '%2'")
- .arg(remoteFilePath, localFilePath), false);
- return false;
- }
+void SshConnectionPrivate::handleChannelExtendedData()
+{
+ m_channelManager->handleChannelExtendedData(m_incomingPacket);
+}
- emit fileCopied(remoteFilePath);
- return true;
+void SshConnectionPrivate::handleChannelEof()
+{
+ m_channelManager->handleChannelEof(m_incomingPacket);
}
-bool SftpConnection::createRemoteDir(const QByteArray &remoteDir)
+void SshConnectionPrivate::handleChannelClose()
{
- if (!d->sftp.mkdir(remoteDir.data())) {
- d->conn.setError(tr("Could not create remote directory"), true);
- return false;
- }
- return true;
+ m_channelManager->handleChannelClose(m_incomingPacket);
}
-bool SftpConnection::removeRemoteDir(const QByteArray &remoteDir)
+void SshConnectionPrivate::handleDisconnect()
{
- if (!d->sftp.rmdir(remoteDir.data())) {
- d->conn.setError(tr("Could not remove remote directory"), true);
- return false;
- }
- return true;
+ const SshDisconnect msg = m_incomingPacket.extractDisconnect();
+ throw SshServerException(SSH_DISCONNECT_CONNECTION_LOST,
+ "", tr("Server closed connection: %1").arg(msg.description));
}
-QByteArray SftpConnection::listRemoteDirContents(const QByteArray &remoteDir,
- bool withAttributes, bool &ok)
+void SshConnectionPrivate::sendData(const QByteArray &data)
{
- const char * const buffer = d->sftp.ls(remoteDir.data(), withAttributes);
- if (!buffer) {
- d->conn.setError(tr("Could not get remote directory contents"), true);
- ok = false;
- return QByteArray();
- }
- ok = true;
- return QByteArray(buffer);
+ m_socket->write(data);
}
-bool SftpConnection::removeRemoteFile(const QByteArray &remoteFile)
+void SshConnectionPrivate::handleSocketDisconnected()
{
- if (!d->sftp.rm(remoteFile.data())) {
- d->conn.setError(tr("Could not remove remote file"), true);
- return false;
- }
- return true;
+ closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshClosedByServerError,
+ "Connection closed unexpectedly.",
+ tr("Connection closed unexpectedly."));
}
-bool SftpConnection::changeRemoteWorkingDir(const QByteArray &newRemoteDir)
+void SshConnectionPrivate::handleSocketError()
{
- if (!d->sftp.cd(newRemoteDir.data())) {
- d->conn.setError(tr("Could not change remote working directory"), true);
- return false;
+ if (m_error == SshNoError) {
+ closeConnection(SSH_DISCONNECT_CONNECTION_LOST, SshSocketError,
+ "Network error", m_socket->errorString());
}
- return true;
}
-void SftpConnection::quit()
+void SshConnectionPrivate::handleTimeout()
{
- d->conn.quit();
+ if (m_state != ConnectionEstablished)
+ closeConnection(SSH_DISCONNECT_BY_APPLICATION, SshTimeoutError, "",
+ tr("Connection timed out."));
}
-bool SftpConnection::isConnected() const
-{
- return d->conn.isConnected();
+void SshConnectionPrivate::connectToHost(const SshConnectionParameters &serverInfo)
+{
+ m_incomingData.clear();
+ m_incomingPacket.reset();
+ m_sendFacility.reset();
+ m_error = SshNoError;
+ m_ignoreNextPacket = false;
+ m_errorString.clear();
+ connect(m_socket, SIGNAL(connected()), this, SLOT(handleSocketConnected()));
+ connect(m_socket, SIGNAL(readyRead()), this, SLOT(handleIncomingData()));
+ connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
+ SLOT(handleSocketError()));
+ connect(m_socket, SIGNAL(disconnected()), this,
+ SLOT(handleSocketDisconnected()));
+ this->m_connParams = serverInfo;
+ m_state = SocketConnecting;
+ m_timeoutTimer.start(m_connParams.timeout * 1000);
+ m_socket->connectToHost(serverInfo.host, serverInfo.port);
}
-bool SftpConnection::hasError() const
-{
- return d->conn.hasError();
+void SshConnectionPrivate::closeConnection(SshErrorCode sshError,
+ SshError userError, const QByteArray &serverErrorString,
+ const QString &userErrorString)
+{
+ // Prevent endless loops by recursive exceptions.
+ if (m_state == SocketUnconnected || m_error != SshNoError)
+ return;
+
+ m_error = userError;
+ m_errorString = userErrorString;
+ m_timeoutTimer.stop();
+ disconnect(m_socket, 0, this, 0);
+ try {
+ m_channelManager->closeAllChannels();
+ m_sendFacility.sendDisconnectPacket(sshError, serverErrorString);
+ } catch (Botan::Exception &) {} // Nothing sensible to be done here.
+ if (m_error != SshNoError)
+ emit error(userError);
+ if (m_state == ConnectionEstablished)
+ emit disconnected();
+ m_socket->disconnectFromHost();
+ m_state = SocketUnconnected;
}
-QString SftpConnection::error() const
+QSharedPointer<SshRemoteProcess> SshConnectionPrivate::createRemoteProcess(const QByteArray &command)
{
- return d->conn.error();
+ return m_channelManager->createRemoteProcess(command);
}
-SftpConnection::Ptr SftpConnection::create(const SshServerInfo &server)
+QSharedPointer<SftpChannel> SshConnectionPrivate::createSftpChannel()
{
- return Ptr(new SftpConnection(server));
+ return m_channelManager->createSftpChannel();
}
+} // namespace Internal
} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshconnection.h b/src/plugins/coreplugin/ssh/sshconnection.h
index 8c7b59f594..e7f73995a4 100644
--- a/src/plugins/coreplugin/ssh/sshconnection.h
+++ b/src/plugins/coreplugin/ssh/sshconnection.h
@@ -1,19 +1,20 @@
-/****************************************************************************
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
-** This file is part of Qt Creator.
+** Commercial Usage
**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
+**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
@@ -21,27 +22,16 @@
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+**************************************************************************/
#ifndef SSHCONNECTION_H
#define SSHCONNECTION_H
+#include "ssherrors.h"
+
#include <coreplugin/core_global.h>
#include <QtCore/QByteArray>
@@ -50,14 +40,14 @@
#include <QtCore/QString>
namespace Core {
+class SftpChannel;
+class SshRemoteProcess;
namespace Internal {
- struct InteractiveSshConnectionPrivate;
- struct NonInteractiveSshConnectionPrivate;
- class ConnectionOutputReader;
-}
+class SshConnectionPrivate;
+} // namespace Internal
-struct CORE_EXPORT SshServerInfo
+struct CORE_EXPORT SshConnectionParameters
{
QString host;
QString uname;
@@ -69,85 +59,44 @@ struct CORE_EXPORT SshServerInfo
};
-class CORE_EXPORT InteractiveSshConnection : public QObject
+/*
+ * This class provides an SSH connection, implementing protocol version 2.0
+ * It can spawn channels for remote execution and SFTP operations (version 3).
+ * It operates asynchronously (non-blocking) and is not thread-safe.
+ */
+class CORE_EXPORT SshConnection : public QObject
{
Q_OBJECT
- Q_DISABLE_COPY(InteractiveSshConnection)
- friend class Internal::ConnectionOutputReader;
+ Q_DISABLE_COPY(SshConnection)
public:
- typedef QSharedPointer<InteractiveSshConnection> Ptr;
+ enum State { Unconnected, Connecting, Connected };
+ typedef QSharedPointer<SshConnection> Ptr;
- static Ptr create(const SshServerInfo &server);
+ static Ptr create();
- bool start();
- void quit();
- bool isConnected() const;
- bool sendInput(const QByteArray &input); // Should normally end in newline.
- QByteArray waitForRemoteOutput(int msecs = -1);
- bool hasError() const;
- QString error() const;
- ~InteractiveSshConnection();
+ void connectToHost(const SshConnectionParameters &serverInfo);
+ void disconnectFromHost();
+ State state() const;
+ SshError errorState() const;
+ QString errorString() const;
+ SshConnectionParameters connectionParameters() const;
+ ~SshConnection();
-signals:
- void remoteOutputAvailable();
-
-private:
- InteractiveSshConnection(const SshServerInfo &server);
-
- struct Internal::InteractiveSshConnectionPrivate *d;
-};
-
-
-struct CORE_EXPORT SftpTransferInfo
-{
- enum Type { Upload, Download };
-
- SftpTransferInfo(const QString &localFilePath,
- const QByteArray &remoteFilePath, Type type)
- : localFilePath(localFilePath),
- remoteFilePath(remoteFilePath),
- type(type)
- {
- }
-
- QString localFilePath;
- QByteArray remoteFilePath;
- Type type;
-};
-
-class CORE_EXPORT SftpConnection : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(SftpConnection)
-public:
- typedef QSharedPointer<SftpConnection> Ptr;
-
- static Ptr create(const SshServerInfo &server);
- bool start();
- void quit();
- bool isConnected() const;
- bool hasError() const;
- QString error() const;
- bool upload(const QString &localFilePath, const QByteArray &remoteFilePath);
- bool download(const QByteArray &remoteFilePath, const QString &localFilePath);
- bool transferFiles(const QList<SftpTransferInfo> &transferList);
- bool createRemoteDir(const QByteArray &remoteDir);
- bool removeRemoteDir(const QByteArray &remoteDir);
- bool removeRemoteFile(const QByteArray &remoteFile);
- bool changeRemoteWorkingDir(const QByteArray &newRemoteDir);
- QByteArray listRemoteDirContents(const QByteArray &remoteDir,
- bool withAttributes, bool &ok);
- ~SftpConnection();
+ QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
+ QSharedPointer<SftpChannel> createSftpChannel();
signals:
- void fileCopied(const QString &filePath);
+ void connected();
+ void disconnected();
+ void dataAvailable(const QString &message);
+ void error(SshError);
private:
- SftpConnection(const SshServerInfo &server);
+ SshConnection();
- Internal::NonInteractiveSshConnectionPrivate *d;
+ Internal::SshConnectionPrivate *d;
};
-} // namespace Core
+} // namespace Internal
#endif // SSHCONNECTION_H
diff --git a/src/plugins/coreplugin/ssh/sshconnection_p.h b/src/plugins/coreplugin/ssh/sshconnection_p.h
new file mode 100644
index 0000000000..c20ccf78b5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshconnection_p.h
@@ -0,0 +1,157 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHCONNECTION_P_H
+#define SSHCONNECTION_P_H
+
+#include "sshconnection.h"
+#include "sshexception_p.h"
+#include "sshincomingpacket_p.h"
+#include "sshremoteprocess.h"
+#include "sshsendfacility_p.h"
+
+#include <QtCore/QHash>
+#include <QtCore/QList>
+#include <QtCore/QObject>
+#include <QtCore/QPair>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QTimer>
+
+QT_BEGIN_NAMESPACE
+class QTcpSocket;
+QT_END_NAMESPACE
+
+namespace Botan { class Exception; }
+
+namespace Core {
+class SftpChannel;
+
+namespace Internal {
+class SshChannelManager;
+
+// NOTE: When you add stuff here, don't forget to update m_packetHandlers.
+enum SshStateInternal {
+ SocketUnconnected, // initial and after disconnect
+ SocketConnecting, // After connectToHost()
+ SocketConnected, // After socket's connected() signal
+ KeyExchangeStarted, // After server's KEXINIT message
+ KeyExchangeSuccess, // After server's DH_REPLY message
+ UserAuthServiceRequested,
+ UserAuthRequested,
+
+ ConnectionEstablished // After service has been started
+ // ...
+};
+
+class SshConnectionPrivate : public QObject
+{
+ Q_OBJECT
+ friend class Core::SshConnection;
+public:
+ SshConnectionPrivate(SshConnection *conn);
+ ~SshConnectionPrivate();
+
+ void connectToHost(const SshConnectionParameters &serverInfo);
+ void closeConnection(SshErrorCode sshError, SshError userError,
+ const QByteArray &serverErrorString, const QString &userErrorString);
+ QSharedPointer<SshRemoteProcess> createRemoteProcess(const QByteArray &command);
+ QSharedPointer<SftpChannel> createSftpChannel();
+ SshStateInternal state() const { return m_state; }
+ SshError error() const { return m_error; }
+ QString errorString() const { return m_errorString; }
+
+signals:
+ void connected();
+ void disconnected();
+ void dataAvailable(const QString &message);
+ void error(SshError);
+
+private:
+ Q_SLOT void handleSocketConnected();
+ Q_SLOT void handleIncomingData();
+ Q_SLOT void handleSocketError();
+ Q_SLOT void handleSocketDisconnected();
+ Q_SLOT void handleTimeout();
+
+ void handleServerId();
+ void handlePackets();
+ void handleCurrentPacket();
+ void handleKeyExchangeInitPacket();
+ void handleKeyExchangeReplyPacket();
+ void handleNewKeysPacket();
+ void handleServiceAcceptPacket();
+ void handlePasswordExpiredPacket();
+ void handleUserAuthSuccessPacket();
+ void handleUserAuthFailurePacket();
+ void handleUserAuthBannerPacket();
+ void handleGlobalRequest();
+ void handleDebugPacket();
+ void handleChannelRequest();
+ void handleChannelOpen();
+ void handleChannelOpenFailure();
+ void handleChannelOpenConfirmation();
+ void handleChannelSuccess();
+ void handleChannelFailure();
+ void handleChannelWindowAdjust();
+ void handleChannelData();
+ void handleChannelExtendedData();
+ void handleChannelEof();
+ void handleChannelClose();
+ void handleDisconnect();
+
+ void sendData(const QByteArray &data);
+
+ typedef void (SshConnectionPrivate::*PacketHandler)();
+ typedef QList<SshStateInternal> StateList;
+ void setupPacketHandlers();
+ void setupPacketHandler(SshPacketType type, const StateList &states,
+ PacketHandler handler);
+
+ typedef QPair<StateList, PacketHandler> HandlerInStates;
+ QHash<SshPacketType, HandlerInStates> m_packetHandlers;
+
+ QTcpSocket *m_socket;
+ SshStateInternal m_state;
+ SshIncomingPacket m_incomingPacket;
+ SshSendFacility m_sendFacility;
+ QScopedPointer<SshChannelManager> m_channelManager;
+ SshConnectionParameters m_connParams;
+ QByteArray m_incomingData;
+ SshError m_error;
+ QString m_errorString;
+ QScopedPointer<SshKeyExchange> m_keyExchange;
+ QTimer m_timeoutTimer;
+ bool m_ignoreNextPacket;
+ SshConnection *m_conn;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHCONNECTION_P_H
diff --git a/src/plugins/coreplugin/ssh/sshcryptofacility.cpp b/src/plugins/coreplugin/ssh/sshcryptofacility.cpp
new file mode 100644
index 0000000000..fd2fe32044
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshcryptofacility.cpp
@@ -0,0 +1,369 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshcryptofacility_p.h"
+
+#include "sshbotanconversions_p.h"
+#include "sshcapabilities_p.h"
+#include "sshexception_p.h"
+#include "sshkeyexchange_p.h"
+#include "sshpacket_p.h"
+
+#include <botan/ber_dec.h>
+#include <botan/botan.h>
+#include <botan/cbc.h>
+#include <botan/dsa.h>
+#include <botan/hash.h>
+#include <botan/hmac.h>
+#include <botan/look_pk.h>
+#include <botan/pipe.h>
+#include <botan/pkcs8.h>
+#include <botan/pubkey.h>
+#include <botan/rsa.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QList>
+
+#include <string>
+
+using namespace Botan;
+
+namespace Core {
+namespace Internal {
+
+SshAbstractCryptoFacility::SshAbstractCryptoFacility()
+ : m_cipherBlockSize(0), m_macLength(0)
+{
+}
+
+SshAbstractCryptoFacility::~SshAbstractCryptoFacility() {}
+
+void SshAbstractCryptoFacility::clearKeys()
+{
+ m_cipherBlockSize = 0;
+ m_macLength = 0;
+ m_sessionId.clear();
+ m_pipe.reset(0);
+ m_hMac.reset(0);
+}
+
+void SshAbstractCryptoFacility::recreateKeys(const SshKeyExchange &kex)
+{
+ checkInvariant();
+
+ if (m_sessionId.isEmpty())
+ m_sessionId = kex.h();
+ Algorithm_Factory &af = global_state().algorithm_factory();
+ const std::string &cryptAlgo = botanCryptAlgoName(cryptAlgoName(kex));
+ BlockCipher * const cipher = af.prototype_block_cipher(cryptAlgo)->clone();
+
+ m_cipherBlockSize = cipher->BLOCK_SIZE;
+ const QByteArray ivData = generateHash(kex, ivChar(), m_cipherBlockSize);
+ const InitializationVector iv(convertByteArray(ivData), m_cipherBlockSize);
+
+ const quint32 keySize = max_keylength_of(cryptAlgo);
+ const QByteArray cryptKeyData = generateHash(kex, keyChar(), keySize);
+ SymmetricKey cryptKey(convertByteArray(cryptKeyData), keySize);
+
+ BlockCipherMode * const cipherMode
+ = makeCipherMode(cipher, new Null_Padding, iv, cryptKey);
+ m_pipe.reset(new Pipe(cipherMode));
+
+ m_macLength = botanHMacKeyLen(hMacAlgoName(kex));
+ const QByteArray hMacKeyData = generateHash(kex, macChar(), macLength());
+ SymmetricKey hMacKey(convertByteArray(hMacKeyData), macLength());
+ const HashFunction * const hMacProto
+ = af.prototype_hash_function(botanHMacAlgoName(hMacAlgoName(kex)));
+ m_hMac.reset(new HMAC(hMacProto->clone()));
+ m_hMac->set_key(hMacKey);
+}
+
+void SshAbstractCryptoFacility::convert(QByteArray &data, quint32 offset,
+ quint32 dataSize) const
+{
+ Q_ASSERT(offset + dataSize <= static_cast<quint32>(data.size()));
+ checkInvariant();
+
+ // Session id empty => No key exchange has happened yet.
+ if (dataSize == 0 || m_sessionId.isEmpty())
+ return;
+
+ if (dataSize % cipherBlockSize() != 0) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid packet size");
+ }
+ m_pipe->process_msg(reinterpret_cast<const byte *>(data.constData()) + offset,
+ dataSize);
+ quint32 bytesRead = m_pipe->read(reinterpret_cast<byte *>(data.data()) + offset,
+ dataSize, m_pipe->message_count() - 1); // Can't use Pipe::LAST_MESSAGE because of a VC bug.
+ Q_ASSERT(bytesRead == dataSize);
+}
+
+QByteArray SshAbstractCryptoFacility::generateMac(const QByteArray &data,
+ quint32 dataSize) const
+{
+ return m_sessionId.isEmpty()
+ ? QByteArray()
+ : convertByteArray(m_hMac->process(reinterpret_cast<const byte *>(data.constData()),
+ dataSize));
+}
+
+QByteArray SshAbstractCryptoFacility::generateHash(const SshKeyExchange &kex,
+ char c, quint32 length)
+{
+ const QByteArray &k = kex.k();
+ const QByteArray &h = kex.h();
+ QByteArray data(k);
+ data.append(h).append(c).append(m_sessionId);
+ SecureVector<byte> key
+ = kex.hash()->process(convertByteArray(data), data.size());
+ while (key.size() < length) {
+ SecureVector<byte> tmpKey;
+ tmpKey.append(convertByteArray(k), k.size());
+ tmpKey.append(convertByteArray(h), h.size());
+ tmpKey.append(key);
+ key.append(kex.hash()->process(tmpKey));
+ }
+ return QByteArray(reinterpret_cast<const char *>(key.begin()), length);
+}
+
+void SshAbstractCryptoFacility::checkInvariant() const
+{
+ Q_ASSERT(m_sessionId.isEmpty() == !m_pipe);
+}
+
+
+const QByteArray SshEncryptionFacility::PrivKeyFileStartLineRsa("-----BEGIN RSA PRIVATE KEY-----");
+const QByteArray SshEncryptionFacility::PrivKeyFileStartLineDsa("-----BEGIN DSA PRIVATE KEY-----");
+const QByteArray SshEncryptionFacility::PrivKeyFileEndLineRsa("-----END RSA PRIVATE KEY-----");
+const QByteArray SshEncryptionFacility::PrivKeyFileEndLineDsa("-----END DSA PRIVATE KEY-----");
+
+QByteArray SshEncryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const
+{
+ return kex.encryptionAlgo();
+}
+
+QByteArray SshEncryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
+{
+ return kex.hMacAlgoClientToServer();
+}
+
+BlockCipherMode *SshEncryptionFacility::makeCipherMode(BlockCipher *cipher,
+ BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv,
+ const SymmetricKey &key)
+{
+ return new CBC_Encryption(cipher, paddingMethod, key, iv);
+}
+
+void SshEncryptionFacility::encrypt(QByteArray &data) const
+{
+ convert(data, 0, data.size());
+}
+
+void SshEncryptionFacility::createAuthenticationKey(const QByteArray &privKeyFileContents)
+{
+ if (privKeyFileContents == m_cachedPrivKeyContents)
+ return;
+
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("%s: Key not cached, reading", Q_FUNC_INFO);
+#endif
+ QList<BigInt> pubKeyParams;
+ QList<BigInt> allKeyParams;
+ try {
+ createAuthenticationKeyFromPKCS8(privKeyFileContents, pubKeyParams,
+ allKeyParams);
+ } catch (Botan::Exception &) {
+ createAuthenticationKeyFromOpenSSL(privKeyFileContents, pubKeyParams,
+ allKeyParams);
+ }
+
+ foreach (const BigInt &b, allKeyParams) {
+ if (b.is_zero()) {
+ throw SshClientException(SshKeyFileError,
+ SSH_TR("Decoding of private key file failed."));
+ }
+ }
+
+ m_authPubKeyBlob = AbstractSshPacket::encodeString(m_authKeyAlgoName);
+ foreach (const BigInt &b, pubKeyParams)
+ m_authPubKeyBlob += AbstractSshPacket::encodeMpInt(b);
+ m_cachedPrivKeyContents = privKeyFileContents;
+}
+
+void SshEncryptionFacility::createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents,
+ QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams)
+{
+ Pipe pipe;
+ pipe.process_msg(convertByteArray(privKeyFileContents),
+ privKeyFileContents.size());
+ Private_Key * const key = PKCS8::load_key(pipe, m_rng);
+ if (DSA_PrivateKey * const dsaKey = dynamic_cast<DSA_PrivateKey *>(key)) {
+ m_authKey.reset(dsaKey);
+ pubKeyParams << dsaKey->group_p() << dsaKey->group_q()
+ << dsaKey->group_g() << dsaKey->get_y();
+ allKeyParams << pubKeyParams << dsaKey->get_x();
+ } else if (RSA_PrivateKey * const rsaKey = dynamic_cast<RSA_PrivateKey *>(key)) {
+ m_authKey.reset(rsaKey);
+ pubKeyParams << rsaKey->get_e() << rsaKey->get_n();
+ allKeyParams << pubKeyParams << rsaKey->get_p() << rsaKey->get_q()
+ << rsaKey->get_d();
+ } else {
+ throw Botan::Exception();
+ }
+}
+
+void SshEncryptionFacility::createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents,
+ QList<BigInt> &pubKeyParams, QList<BigInt> &allKeyParams)
+{
+ bool syntaxOk = true;
+ QList<QByteArray> lines = privKeyFileContents.split('\n');
+ while (lines.last().isEmpty())
+ lines.removeLast();
+ if (lines.count() < 3) {
+ syntaxOk = false;
+ } else if (lines.first() == PrivKeyFileStartLineRsa) {
+ if (lines.last() != PrivKeyFileEndLineRsa)
+ syntaxOk =false;
+ else
+ m_authKeyAlgoName = SshCapabilities::PubKeyRsa;
+ } else if (lines.first() == PrivKeyFileStartLineDsa) {
+ if (lines.last() != PrivKeyFileEndLineDsa)
+ syntaxOk = false;
+ else
+ m_authKeyAlgoName = SshCapabilities::PubKeyDss;
+ } else {
+ syntaxOk = false;
+ }
+ if (!syntaxOk) {
+ throw SshClientException(SshKeyFileError,
+ SSH_TR("Private key file has unexpected format."));
+ }
+
+ QByteArray privateKeyBlob;
+ for (int i = 1; i < lines.size() - 1; ++i)
+ privateKeyBlob += lines.at(i);
+ privateKeyBlob = QByteArray::fromBase64(privateKeyBlob);
+
+ BER_Decoder decoder(convertByteArray(privateKeyBlob),
+ privateKeyBlob.size());
+ BER_Decoder sequence = decoder.start_cons(SEQUENCE);
+ quint32 version;
+ sequence.decode (version);
+ if (version != 0) {
+ throw SshClientException(SshKeyFileError,
+ SSH_TR("Private key encoding has version %1, expected 0.")
+ .arg(version));
+ }
+
+ if (m_authKeyAlgoName == SshCapabilities::PubKeyDss) {
+ BigInt p, q, g, y, x;
+ sequence.decode (p).decode (q).decode (g).decode (y).decode (x);
+ DSA_PrivateKey * const dsaKey
+ = new DSA_PrivateKey(m_rng, DL_Group(p, q, g), x);
+ m_authKey.reset(dsaKey);
+ pubKeyParams << p << q << g << y;
+ allKeyParams << pubKeyParams << x;
+ } else {
+ BigInt p, q, e, d, n;
+ sequence.decode (n).decode (e).decode (d).decode (p).decode (q);
+ RSA_PrivateKey * const rsaKey
+ = new RSA_PrivateKey (m_rng, p, q, e, d, n);
+ m_authKey.reset(rsaKey);
+ pubKeyParams << e << n;
+ allKeyParams << pubKeyParams << p << q << d;
+ }
+
+ sequence.discard_remaining();
+ sequence.verify_end();
+}
+
+QByteArray SshEncryptionFacility::authenticationAlgorithmName() const
+{
+ Q_ASSERT(m_authKey);
+ return m_authKeyAlgoName;
+}
+
+QByteArray SshEncryptionFacility::authenticationKeySignature(const QByteArray &data) const
+{
+ Q_ASSERT(m_authKey);
+
+ QScopedPointer<PK_Signer> signer(get_pk_signer (*m_authKey,
+ botanEmsaAlgoName(m_authKeyAlgoName)));
+ QByteArray dataToSign = AbstractSshPacket::encodeString(sessionId()) + data;
+ QByteArray signature
+ = convertByteArray(signer->sign_message(convertByteArray(dataToSign),
+ dataToSign.size(), m_rng));
+ return AbstractSshPacket::encodeString(m_authKeyAlgoName)
+ + AbstractSshPacket::encodeString(signature);
+}
+
+QByteArray SshEncryptionFacility::getRandomNumbers(int count) const
+{
+ QByteArray data;
+ data.resize(count);
+ m_rng.randomize(convertByteArray(data), count);
+ return data;
+}
+
+SshEncryptionFacility::~SshEncryptionFacility() {}
+
+
+QByteArray SshDecryptionFacility::cryptAlgoName(const SshKeyExchange &kex) const
+{
+ return kex.decryptionAlgo();
+}
+
+QByteArray SshDecryptionFacility::hMacAlgoName(const SshKeyExchange &kex) const
+{
+ return kex.hMacAlgoServerToClient();
+}
+
+BlockCipherMode *SshDecryptionFacility::makeCipherMode(BlockCipher *cipher,
+ BlockCipherModePaddingMethod *paddingMethod, const InitializationVector &iv,
+ const SymmetricKey &key)
+{
+ return new CBC_Decryption(cipher, paddingMethod, key, iv);
+}
+
+void SshDecryptionFacility::decrypt(QByteArray &data, quint32 offset,
+ quint32 dataSize) const
+{
+ convert(data, offset, dataSize);
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Decrypted data:");
+ const char * const start = data.constData() + offset;
+ const char * const end = start + dataSize;
+ for (const char *c = start; c < end; ++c)
+ qDebug() << "'" << *c << "' (0x" << (static_cast<int>(*c) & 0xff) << ")";
+#endif
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshcryptofacility_p.h b/src/plugins/coreplugin/ssh/sshcryptofacility_p.h
new file mode 100644
index 0000000000..f60e6d4b8d
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshcryptofacility_p.h
@@ -0,0 +1,154 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHABSTRACTCRYPTOFACILITY_P_H
+#define SSHABSTRACTCRYPTOFACILITY_P_H
+
+#include <botan/auto_rng.h>
+#include <botan/symkey.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QScopedPointer>
+
+namespace Botan {
+ class BigInt;
+ class BlockCipher;
+ class BlockCipherMode;
+ class BlockCipherModePaddingMethod;
+ class HashFunction;
+ class HMAC;
+ class Pipe;
+ class PK_Signing_Key;
+}
+
+namespace Core {
+namespace Internal {
+
+class SshKeyExchange;
+
+class SshAbstractCryptoFacility
+{
+public:
+ virtual ~SshAbstractCryptoFacility();
+
+ void clearKeys();
+ void recreateKeys(const SshKeyExchange &kex);
+ QByteArray generateMac(const QByteArray &data, quint32 dataSize) const;
+ quint32 cipherBlockSize() const { return m_cipherBlockSize; }
+ quint32 macLength() const { return m_macLength; }
+
+protected:
+ SshAbstractCryptoFacility();
+ void convert(QByteArray &data, quint32 offset, quint32 dataSize) const;
+ QByteArray sessionId() const { return m_sessionId; }
+
+private:
+ SshAbstractCryptoFacility(const SshAbstractCryptoFacility &);
+ SshAbstractCryptoFacility &operator=(const SshAbstractCryptoFacility &);
+
+ virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const=0;
+ virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const=0;
+ virtual Botan::BlockCipherMode *makeCipherMode(Botan::BlockCipher *cipher,
+ Botan::BlockCipherModePaddingMethod *paddingMethod,
+ const Botan::InitializationVector &iv,
+ const Botan::SymmetricKey &key)=0;
+ virtual char ivChar() const=0;
+ virtual char keyChar() const=0;
+ virtual char macChar() const=0;
+
+ QByteArray generateHash(const SshKeyExchange &kex, char c, quint32 length);
+ void checkInvariant() const;
+
+ QByteArray m_sessionId;
+ QScopedPointer<Botan::Pipe> m_pipe;
+ QScopedPointer<Botan::HMAC> m_hMac;
+ quint32 m_cipherBlockSize;
+ quint32 m_macLength;
+};
+
+class SshEncryptionFacility : public SshAbstractCryptoFacility
+{
+public:
+ void encrypt(QByteArray &data) const;
+
+ void createAuthenticationKey(const QByteArray &privKeyFileContents);
+ QByteArray authenticationAlgorithmName() const;
+ QByteArray authenticationPublicKey() const { return m_authPubKeyBlob; }
+ QByteArray authenticationKeySignature(const QByteArray &data) const;
+ QByteArray getRandomNumbers(int count) const;
+
+ ~SshEncryptionFacility();
+
+private:
+ virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
+ virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
+ virtual Botan::BlockCipherMode *makeCipherMode(Botan::BlockCipher *cipher,
+ Botan::BlockCipherModePaddingMethod *paddingMethod,
+ const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
+ virtual char ivChar() const { return 'A'; }
+ virtual char keyChar() const { return 'C'; }
+ virtual char macChar() const { return 'E'; }
+
+ void createAuthenticationKeyFromPKCS8(const QByteArray &privKeyFileContents,
+ QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams);
+ void createAuthenticationKeyFromOpenSSL(const QByteArray &privKeyFileContents,
+ QList<Botan::BigInt> &pubKeyParams, QList<Botan::BigInt> &allKeyParams);
+
+ static const QByteArray PrivKeyFileStartLineRsa;
+ static const QByteArray PrivKeyFileStartLineDsa;
+ static const QByteArray PrivKeyFileEndLineRsa;
+ static const QByteArray PrivKeyFileEndLineDsa;
+
+ QByteArray m_authKeyAlgoName;
+ QByteArray m_authPubKeyBlob;
+ QByteArray m_cachedPrivKeyContents;
+ QScopedPointer<Botan::PK_Signing_Key> m_authKey;
+ mutable Botan::AutoSeeded_RNG m_rng;
+};
+
+class SshDecryptionFacility : public SshAbstractCryptoFacility
+{
+public:
+ void decrypt(QByteArray &data, quint32 offset, quint32 dataSize) const;
+
+private:
+ virtual QByteArray cryptAlgoName(const SshKeyExchange &kex) const;
+ virtual QByteArray hMacAlgoName(const SshKeyExchange &kex) const;
+ virtual Botan::BlockCipherMode *makeCipherMode(Botan::BlockCipher *cipher,
+ Botan::BlockCipherModePaddingMethod *paddingMethod,
+ const Botan::InitializationVector &iv, const Botan::SymmetricKey &key);
+ virtual char ivChar() const { return 'B'; }
+ virtual char keyChar() const { return 'D'; }
+ virtual char macChar() const { return 'F'; }
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHABSTRACTCRYPTOFACILITY_P_H
diff --git a/src/plugins/coreplugin/ssh/sshdelayedsignal.cpp b/src/plugins/coreplugin/ssh/sshdelayedsignal.cpp
new file mode 100644
index 0000000000..d35075bf77
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshdelayedsignal.cpp
@@ -0,0 +1,165 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshdelayedsignal_p.h"
+
+#include "sftpchannel_p.h"
+#include "sshremoteprocess_p.h"
+
+#include <QtCore/QTimer>
+
+namespace Core {
+namespace Internal {
+
+SshDelayedSignal::SshDelayedSignal(const QWeakPointer<QObject> &checkObject)
+ : m_checkObject(checkObject)
+{
+ QTimer::singleShot(0, this, SLOT(handleTimeout()));
+}
+
+void SshDelayedSignal::handleTimeout()
+{
+ if (!m_checkObject.isNull())
+ emitSignal();
+ deleteLater();
+}
+
+
+SftpDelayedSignal::SftpDelayedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject)
+ : SshDelayedSignal(checkObject), m_privChannel(privChannel) {}
+
+
+SftpInitializationFailedSignal::SftpInitializationFailedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QString &reason)
+ : SftpDelayedSignal(privChannel, checkObject), m_reason(reason) {}
+
+void SftpInitializationFailedSignal::emitSignal()
+{
+ m_privChannel->emitInitializationFailedSignal(m_reason);
+}
+
+
+SftpInitializedSignal::SftpInitializedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject)
+ : SftpDelayedSignal(privChannel, checkObject) {}
+
+void SftpInitializedSignal::emitSignal()
+{
+ m_privChannel->emitInitialized();
+}
+
+
+SftpJobFinishedSignal::SftpJobFinishedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, SftpJobId jobId,
+ const QString &error)
+ : SftpDelayedSignal(privChannel, checkObject), m_jobId(jobId), m_error(error)
+{
+}
+
+void SftpJobFinishedSignal::emitSignal()
+{
+ m_privChannel->emitJobFinished(m_jobId, m_error);
+}
+
+
+SftpDataAvailableSignal::SftpDataAvailableSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, SftpJobId jobId,
+ const QString &data)
+ : SftpDelayedSignal(privChannel, checkObject), m_jobId(jobId), m_data(data) {}
+
+void SftpDataAvailableSignal::emitSignal()
+{
+ m_privChannel->emitDataAvailable(m_jobId, m_data);
+}
+
+
+SftpClosedSignal::SftpClosedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject)
+ : SftpDelayedSignal(privChannel, checkObject) {}
+
+void SftpClosedSignal::emitSignal()
+{
+ m_privChannel->emitClosed();
+}
+
+
+SshRemoteProcessDelayedSignal::SshRemoteProcessDelayedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject)
+ : SshDelayedSignal(checkObject), m_privChannel(privChannel) {}
+
+
+SshRemoteProcessStartedSignal::SshRemoteProcessStartedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject)
+ : SshRemoteProcessDelayedSignal(privChannel, checkObject) {}
+
+void SshRemoteProcessStartedSignal::emitSignal()
+{
+ m_privChannel->emitStartedSignal();
+}
+
+
+SshRemoteProcessOutputAvailableSignal::SshRemoteProcessOutputAvailableSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QByteArray &output)
+ : SshRemoteProcessDelayedSignal(privChannel, checkObject), m_output(output)
+{
+}
+
+void SshRemoteProcessOutputAvailableSignal::emitSignal()
+{
+ m_privChannel->emitOutputAvailableSignal(m_output);
+}
+
+
+SshRemoteProcessErrorOutputAvailableSignal::SshRemoteProcessErrorOutputAvailableSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QByteArray &output)
+ : SshRemoteProcessDelayedSignal(privChannel, checkObject), m_output(output)
+{
+}
+
+void SshRemoteProcessErrorOutputAvailableSignal::emitSignal()
+{
+ m_privChannel->emitErrorOutputAvailableSignal(m_output);
+}
+
+
+SshRemoteProcessClosedSignal::SshRemoteProcessClosedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, int exitStatus)
+ : SshRemoteProcessDelayedSignal(privChannel, checkObject),
+ m_exitStatus(exitStatus)
+{
+}
+
+void SshRemoteProcessClosedSignal::emitSignal()
+{
+ m_privChannel->emitClosedSignal(m_exitStatus);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshdelayedsignal_p.h b/src/plugins/coreplugin/ssh/sshdelayedsignal_p.h
new file mode 100644
index 0000000000..09163fb686
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshdelayedsignal_p.h
@@ -0,0 +1,190 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHDELAYEDSIGNAL_P_H
+#define SSHDELAYEDSIGNAL_P_H
+
+#include "sftpdefs.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QString>
+#include <QtCore/QWeakPointer>
+
+namespace Core {
+namespace Internal {
+class SftpChannelPrivate;
+class SshRemoteProcessPrivate;
+
+class SshDelayedSignal : public QObject
+{
+ Q_OBJECT
+public:
+ SshDelayedSignal(const QWeakPointer<QObject> &checkObject);
+
+private:
+ Q_SLOT void handleTimeout();
+ virtual void emitSignal()=0;
+
+ const QWeakPointer<QObject> m_checkObject;
+};
+
+
+class SftpDelayedSignal : public SshDelayedSignal
+{
+public:
+ SftpDelayedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject);
+
+protected:
+ SftpChannelPrivate * const m_privChannel;
+};
+
+class SftpInitializationFailedSignal : public SftpDelayedSignal
+{
+public:
+ SftpInitializationFailedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QString &reason);
+
+private:
+ virtual void emitSignal();
+
+ const QString m_reason;
+};
+
+class SftpInitializedSignal : public SftpDelayedSignal
+{
+public:
+ SftpInitializedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject);
+
+private:
+ virtual void emitSignal();
+};
+
+class SftpJobFinishedSignal : public SftpDelayedSignal
+{
+public:
+ SftpJobFinishedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, SftpJobId jobId,
+ const QString &error);
+
+private:
+ virtual void emitSignal();
+
+ const SftpJobId m_jobId;
+ const QString m_error;
+};
+
+class SftpDataAvailableSignal : public SftpDelayedSignal
+{
+public:
+ SftpDataAvailableSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, SftpJobId jobId,
+ const QString &data);
+
+private:
+ virtual void emitSignal();
+
+ const SftpJobId m_jobId;
+ const QString m_data;
+};
+
+class SftpClosedSignal : public SftpDelayedSignal
+{
+public:
+ SftpClosedSignal(SftpChannelPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject);
+
+private:
+ virtual void emitSignal();
+};
+
+
+class SshRemoteProcessDelayedSignal : public SshDelayedSignal
+{
+public:
+ SshRemoteProcessDelayedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject);
+
+protected:
+ SshRemoteProcessPrivate * const m_privChannel;
+};
+
+class SshRemoteProcessStartedSignal : public SshRemoteProcessDelayedSignal
+{
+public:
+ SshRemoteProcessStartedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject);
+
+private:
+ virtual void emitSignal();
+};
+
+class SshRemoteProcessOutputAvailableSignal
+ : public SshRemoteProcessDelayedSignal
+{
+public:
+ SshRemoteProcessOutputAvailableSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QByteArray &output);
+
+private:
+ virtual void emitSignal();
+
+ const QByteArray m_output;
+};
+
+class SshRemoteProcessErrorOutputAvailableSignal
+ : public SshRemoteProcessDelayedSignal
+{
+public:
+ SshRemoteProcessErrorOutputAvailableSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, const QByteArray &output);
+
+private:
+ virtual void emitSignal();
+
+ const QByteArray m_output;
+};
+
+class SshRemoteProcessClosedSignal : public SshRemoteProcessDelayedSignal
+{
+public:
+ SshRemoteProcessClosedSignal(SshRemoteProcessPrivate *privChannel,
+ const QWeakPointer<QObject> &checkObject, int exitStatus);
+
+private:
+ virtual void emitSignal();
+
+ const int m_exitStatus;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHDELAYEDSIGNAL_P_H
diff --git a/src/plugins/coreplugin/ssh/ssherrors.h b/src/plugins/coreplugin/ssh/ssherrors.h
new file mode 100644
index 0000000000..01587edfc5
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/ssherrors.h
@@ -0,0 +1,43 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHERRORS_P_H
+#define SSHERRORS_P_H
+
+namespace Core {
+
+enum SshError {
+ SshNoError, SshSocketError, SshTimeoutError, SshProtocolError,
+ SshHostKeyError, SshKeyFileError, SshAuthenticationError,
+ SshClosedByServerError, SshInternalError
+};
+
+} // namespace Core
+
+#endif // SSHERRORS_P_H
diff --git a/src/plugins/coreplugin/ssh/sshexception_p.h b/src/plugins/coreplugin/ssh/sshexception_p.h
new file mode 100644
index 0000000000..6812fabc49
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshexception_p.h
@@ -0,0 +1,89 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHEXCEPTION_P_H
+#define SSHEXCEPTION_P_H
+
+#include "ssherrors.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+enum SshErrorCode {
+ SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT = 1,
+ SSH_DISCONNECT_PROTOCOL_ERROR = 2,
+ SSH_DISCONNECT_KEY_EXCHANGE_FAILED = 3,
+ SSH_DISCONNECT_RESERVED = 4,
+ SSH_DISCONNECT_MAC_ERROR = 5,
+ SSH_DISCONNECT_COMPRESSION_ERROR = 6,
+ SSH_DISCONNECT_SERVICE_NOT_AVAILABLE = 7,
+ SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8,
+ SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE = 9,
+ SSH_DISCONNECT_CONNECTION_LOST = 10,
+ SSH_DISCONNECT_BY_APPLICATION = 11,
+ SSH_DISCONNECT_TOO_MANY_CONNECTIONS = 12,
+ SSH_DISCONNECT_AUTH_CANCELLED_BY_USER = 13,
+ SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14,
+ SSH_DISCONNECT_ILLEGAL_USER_NAME = 15
+};
+
+#define SSH_TR(string) QCoreApplication::translate("SshConnection", string)
+
+#define SSH_SERVER_EXCEPTION(error, errorString) \
+ SshServerException((error), (errorString), SSH_TR(errorString))
+
+struct SshServerException
+{
+ SshServerException(SshErrorCode error, const QByteArray &errorStringServer,
+ const QString &errorStringUser)
+ : error(error), errorStringServer(errorStringServer),
+ errorStringUser(errorStringUser) {}
+
+ const SshErrorCode error;
+ const QByteArray errorStringServer;
+ const QString errorStringUser;
+};
+
+struct SshClientException
+{
+ SshClientException(SshError error, const QString &errorString)
+ : error(error), errorString(errorString) {}
+
+ const SshError error;
+ const QString errorString;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHEXCEPTION_P_H
diff --git a/src/plugins/coreplugin/ssh/sshincomingpacket.cpp b/src/plugins/coreplugin/ssh/sshincomingpacket.cpp
new file mode 100644
index 0000000000..fdc274bbbd
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshincomingpacket.cpp
@@ -0,0 +1,442 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshincomingpacket_p.h"
+
+#include "sshcapabilities_p.h"
+
+namespace Core {
+namespace Internal {
+
+const QByteArray SshIncomingPacket::ExitStatusType("exit-status");
+const QByteArray SshIncomingPacket::ExitSignalType("exit-signal");
+
+SshIncomingPacket::SshIncomingPacket() : m_serverSeqNr(0) { }
+
+quint32 SshIncomingPacket::cipherBlockSize() const
+{
+ return qMax(m_decrypter.cipherBlockSize(), 8U);
+}
+
+quint32 SshIncomingPacket::macLength() const
+{
+ return m_decrypter.macLength();
+}
+
+void SshIncomingPacket::recreateKeys(const SshKeyExchange &keyExchange)
+{
+ m_decrypter.recreateKeys(keyExchange);
+}
+
+void SshIncomingPacket::reset()
+{
+ clear();
+ m_serverSeqNr = 0;
+ m_decrypter.clearKeys();
+}
+
+void SshIncomingPacket::consumeData(QByteArray &newData)
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("%s: current data size = %d, new data size = %d",
+ Q_FUNC_INFO, m_data.size(), newData.size());
+#endif
+
+ if (isComplete() || newData.isEmpty())
+ return;
+
+ /*
+ * Until we have reached the minimum packet size, we cannot decrypt the
+ * length field.
+ */
+ const quint32 minSize = minPacketSize();
+ if (currentDataSize() < minSize) {
+ const int bytesToTake
+ = qMin<quint32>(minSize - currentDataSize(), newData.size());
+ moveFirstBytes(m_data, newData, bytesToTake);
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Took %d bytes from new data", bytesToTake);
+#endif
+ if (currentDataSize() < minSize)
+ return;
+ }
+
+ const int bytesToTake
+ = qMin<quint32>(length() + 4 + macLength() - currentDataSize(),
+ newData.size());
+ moveFirstBytes(m_data, newData, bytesToTake);
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Took %d bytes from new data", bytesToTake);
+#endif
+ if (isComplete()) {
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Message complete. Overall size: %u, payload size: %u",
+ m_data.size(), m_length - paddingLength() - 1);
+#endif
+ decrypt();
+ ++m_serverSeqNr;
+ }
+}
+
+void SshIncomingPacket::decrypt()
+{
+ Q_ASSERT(isComplete());
+ const quint32 netDataLength = length() + 4;
+ m_decrypter.decrypt(m_data, cipherBlockSize(),
+ netDataLength - cipherBlockSize());
+ const QByteArray &mac = m_data.mid(netDataLength, macLength());
+ if (mac != generateMac(m_decrypter, m_serverSeqNr)) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_MAC_ERROR,
+ "Message authentication failed.");
+ }
+}
+
+void SshIncomingPacket::moveFirstBytes(QByteArray &target, QByteArray &source,
+ int n)
+{
+ target.append(source.left(n));
+ source.remove(0, n);
+}
+
+SshKeyExchangeInit SshIncomingPacket::extractKeyExchangeInitData() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_KEXINIT);
+
+ SshKeyExchangeInit exchangeData;
+ try {
+ quint32 offset = TypeOffset + 1;
+ std::memcpy(exchangeData.cookie, &m_data.constData()[offset],
+ sizeof exchangeData.cookie);
+ offset += sizeof exchangeData.cookie;
+ exchangeData.keyAlgorithms
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.serverHostKeyAlgorithms
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.encryptionAlgorithmsClientToServer
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.encryptionAlgorithmsServerToClient
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.macAlgorithmsClientToServer
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.macAlgorithmsServerToClient
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.compressionAlgorithmsClientToServer
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.compressionAlgorithmsServerToClient
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.languagesClientToServer
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.languagesServerToClient
+ = SshPacketParser::asNameList(m_data, &offset);
+ exchangeData.firstKexPacketFollows
+ = SshPacketParser::asBool(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ "Key exchange failed: Server sent invalid SSH_MSG_KEXINIT packet.");
+ }
+ return exchangeData;
+}
+
+SshKeyExchangeReply SshIncomingPacket::extractKeyExchangeReply(const QByteArray &pubKeyAlgo) const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_KEXDH_REPLY);
+
+ try {
+ SshKeyExchangeReply replyData;
+ quint32 offset = TypeOffset + 1;
+ const quint32 k_sLength
+ = SshPacketParser::asUint32(m_data, &offset);
+ if (offset + k_sLength > currentDataSize())
+ throw SshPacketParseException();
+ replyData.k_s = m_data.mid(offset - 4, k_sLength + 4);
+ if (SshPacketParser::asString(m_data, &offset) != pubKeyAlgo)
+ throw SshPacketParseException();
+
+ // DSS: p and q, RSA: e and n
+ replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
+ replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
+
+ // g and y
+ if (pubKeyAlgo == SshCapabilities::PubKeyDss) {
+ replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
+ replyData.parameters << SshPacketParser::asBigInt(m_data, &offset);
+ }
+
+ replyData.f = SshPacketParser::asBigInt(m_data, &offset);
+ offset += 4;
+ if (SshPacketParser::asString(m_data, &offset) != pubKeyAlgo)
+ throw SshPacketParseException();
+ replyData.signatureBlob = SshPacketParser::asString(m_data, &offset);
+ return replyData;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ "Key exchange failed: "
+ "Server sent invalid SSH_MSG_KEXDH_REPLY packet.");
+ }
+}
+
+SshDisconnect SshIncomingPacket::extractDisconnect() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_DISCONNECT);
+
+ SshDisconnect msg;
+ try {
+ quint32 offset = TypeOffset + 1;
+ msg.reasonCode = SshPacketParser::asUint32(m_data, &offset);
+ msg.description = SshPacketParser::asUserString(m_data, &offset);
+ msg.language = SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_DISCONNECT.");
+ }
+
+ return msg;
+}
+
+SshUserAuthBanner SshIncomingPacket::extractUserAuthBanner() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_USERAUTH_BANNER);
+
+ try {
+ SshUserAuthBanner msg;
+ quint32 offset = TypeOffset + 1;
+ msg.message = SshPacketParser::asUserString(m_data, &offset);
+ msg.language = SshPacketParser::asString(m_data, &offset);
+ return msg;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_USERAUTH_BANNER.");
+ }
+}
+
+SshDebug SshIncomingPacket::extractDebug() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_DEBUG);
+
+ try {
+ SshDebug msg;
+ quint32 offset = TypeOffset + 1;
+ msg.display = SshPacketParser::asBool(m_data, &offset);
+ msg.message = SshPacketParser::asUserString(m_data, &offset);
+ msg.language = SshPacketParser::asString(m_data, &offset);
+ return msg;
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_USERAUTH_BANNER.");
+ }
+}
+
+SshChannelOpenFailure SshIncomingPacket::extractChannelOpenFailure() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_FAILURE);
+
+ SshChannelOpenFailure openFailure;
+ try {
+ quint32 offset = TypeOffset + 1;
+ openFailure.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ openFailure.reasonCode = SshPacketParser::asUint32(m_data, &offset);
+ openFailure.reasonString = SshPacketParser::asString(m_data, &offset);
+ openFailure.language = SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Server sent invalid SSH_MSG_CHANNEL_OPEN_FAILURE packet.");
+ }
+ return openFailure;
+}
+
+SshChannelOpenConfirmation SshIncomingPacket::extractChannelOpenConfirmation() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+
+ SshChannelOpenConfirmation confirmation;
+ try {
+ quint32 offset = TypeOffset + 1;
+ confirmation.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ confirmation.remoteChannel = SshPacketParser::asUint32(m_data, &offset);
+ confirmation.remoteWindowSize = SshPacketParser::asUint32(m_data, &offset);
+ confirmation.remoteMaxPacketSize = SshPacketParser::asUint32(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Server sent invalid SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet.");
+ }
+ return confirmation;
+}
+
+SshChannelWindowAdjust SshIncomingPacket::extractWindowAdjust() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_WINDOW_ADJUST);
+
+ SshChannelWindowAdjust adjust;
+ try {
+ quint32 offset = TypeOffset + 1;
+ adjust.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ adjust.bytesToAdd = SshPacketParser::asUint32(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_WINDOW_ADJUST packet.");
+ }
+ return adjust;
+}
+
+SshChannelData SshIncomingPacket::extractChannelData() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_DATA);
+
+ SshChannelData data;
+ try {
+ quint32 offset = TypeOffset + 1;
+ data.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ data.data = SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_DATA packet.");
+ }
+ return data;
+}
+
+SshChannelExtendedData SshIncomingPacket::extractChannelExtendedData() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_EXTENDED_DATA);
+
+ SshChannelExtendedData data;
+ try {
+ quint32 offset = TypeOffset + 1;
+ data.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ data.type = SshPacketParser::asUint32(m_data, &offset);
+ data.data = SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_EXTENDED_DATA packet.");
+ }
+ return data;
+}
+
+SshChannelExitStatus SshIncomingPacket::extractChannelExitStatus() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
+
+ SshChannelExitStatus exitStatus;
+ try {
+ quint32 offset = TypeOffset + 1;
+ exitStatus.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ const QByteArray &type = SshPacketParser::asString(m_data, &offset);
+ Q_ASSERT(type == ExitStatusType);
+ if (SshPacketParser::asBool(m_data, &offset))
+ throw SshPacketParseException();
+ exitStatus.exitStatus = SshPacketParser::asUint32(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid exit-status packet.");
+ }
+ return exitStatus;
+}
+
+SshChannelExitSignal SshIncomingPacket::extractChannelExitSignal() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
+
+ SshChannelExitSignal exitSignal;
+ try {
+ quint32 offset = TypeOffset + 1;
+ exitSignal.localChannel = SshPacketParser::asUint32(m_data, &offset);
+ const QByteArray &type = SshPacketParser::asString(m_data, &offset);
+ Q_ASSERT(type == ExitSignalType);
+ if (SshPacketParser::asBool(m_data, &offset))
+ throw SshPacketParseException();
+ exitSignal.signal = SshPacketParser::asString(m_data, &offset);
+ exitSignal.coreDumped = SshPacketParser::asBool(m_data, &offset);
+ exitSignal.error = SshPacketParser::asUserString(m_data, &offset);
+ exitSignal.language = SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid exit-signal packet.");
+ }
+ return exitSignal;
+}
+
+quint32 SshIncomingPacket::extractRecipientChannel() const
+{
+ Q_ASSERT(isComplete());
+
+ try {
+ quint32 offset = TypeOffset + 1;
+ return SshPacketParser::asUint32(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Server sent invalid packet.");
+ }
+}
+
+QByteArray SshIncomingPacket::extractChannelRequestType() const
+{
+ Q_ASSERT(isComplete());
+ Q_ASSERT(type() == SSH_MSG_CHANNEL_REQUEST);
+
+ try {
+ quint32 offset = TypeOffset + 1;
+ SshPacketParser::asUint32(m_data, &offset);
+ return SshPacketParser::asString(m_data, &offset);
+ } catch (SshPacketParseException &) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Invalid SSH_MSG_CHANNEL_REQUEST packet.");
+ }
+}
+
+void SshIncomingPacket::calculateLength() const
+{
+ Q_ASSERT(currentDataSize() >= minPacketSize());
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Length field before decryption: %d-%d-%d-%d", m_data.at(0) & 0xff,
+ m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff);
+#endif
+ m_decrypter.decrypt(m_data, 0, cipherBlockSize());
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Length field after decryption: %d-%d-%d-%d", m_data.at(0) & 0xff, m_data.at(1) & 0xff, m_data.at(2) & 0xff, m_data.at(3) & 0xff);
+ qDebug("message type = %d", m_data.at(TypeOffset));
+#endif
+ m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("decrypted length is %u", m_length);
+#endif
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshincomingpacket_p.h b/src/plugins/coreplugin/ssh/sshincomingpacket_p.h
new file mode 100644
index 0000000000..9b10c8f799
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshincomingpacket_p.h
@@ -0,0 +1,186 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHINCOMINGPACKET_P_H
+#define SSHINCOMINGPACKET_P_H
+
+#include "sshpacket_p.h"
+
+#include "sshcryptofacility_p.h"
+#include "sshpacketparser_p.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+class SshKeyExchange;
+
+struct SshKeyExchangeInit
+{
+ char cookie[16];
+ SshNameList keyAlgorithms;
+ SshNameList serverHostKeyAlgorithms;
+ SshNameList encryptionAlgorithmsClientToServer;
+ SshNameList encryptionAlgorithmsServerToClient;
+ SshNameList macAlgorithmsClientToServer;
+ SshNameList macAlgorithmsServerToClient;
+ SshNameList compressionAlgorithmsClientToServer;
+ SshNameList compressionAlgorithmsServerToClient;
+ SshNameList languagesClientToServer;
+ SshNameList languagesServerToClient;
+ bool firstKexPacketFollows;
+};
+
+struct SshKeyExchangeReply
+{
+ QByteArray k_s;
+ QList<Botan::BigInt> parameters; // DSS: p, q, g, y. RSA: e, n.
+ Botan::BigInt f;
+ QByteArray signatureBlob;
+};
+
+struct SshDisconnect
+{
+ quint32 reasonCode;
+ QString description;
+ QByteArray language;
+};
+
+struct SshUserAuthBanner
+{
+ QString message;
+ QByteArray language;
+};
+
+struct SshDebug
+{
+ bool display;
+ QString message;
+ QByteArray language;
+};
+
+struct SshChannelOpenFailure
+{
+ quint32 localChannel;
+ quint32 reasonCode;
+ QString reasonString;
+ QByteArray language;
+};
+
+struct SshChannelOpenConfirmation
+{
+ quint32 localChannel;
+ quint32 remoteChannel;
+ quint32 remoteWindowSize;
+ quint32 remoteMaxPacketSize;
+};
+
+struct SshChannelWindowAdjust
+{
+ quint32 localChannel;
+ quint32 bytesToAdd;
+};
+
+struct SshChannelData
+{
+ quint32 localChannel;
+ QByteArray data;
+};
+
+struct SshChannelExtendedData
+{
+ quint32 localChannel;
+ quint32 type;
+ QByteArray data;
+};
+
+struct SshChannelExitStatus
+{
+ quint32 localChannel;
+ quint32 exitStatus;
+};
+
+struct SshChannelExitSignal
+{
+ quint32 localChannel;
+ QByteArray signal;
+ bool coreDumped;
+ QString error;
+ QByteArray language;
+};
+
+
+class SshIncomingPacket : public AbstractSshPacket
+{
+public:
+ SshIncomingPacket();
+
+ void consumeData(QByteArray &data);
+ void recreateKeys(const SshKeyExchange &keyExchange);
+ void reset();
+
+ SshKeyExchangeInit extractKeyExchangeInitData() const;
+ SshKeyExchangeReply extractKeyExchangeReply(const QByteArray &pubKeyAlgo) const;
+ SshDisconnect extractDisconnect() const;
+ SshUserAuthBanner extractUserAuthBanner() const;
+ SshDebug extractDebug() const;
+
+ SshChannelOpenFailure extractChannelOpenFailure() const;
+ SshChannelOpenConfirmation extractChannelOpenConfirmation() const;
+ SshChannelWindowAdjust extractWindowAdjust() const;
+ SshChannelData extractChannelData() const;
+ SshChannelExtendedData extractChannelExtendedData() const;
+ SshChannelExitStatus extractChannelExitStatus() const;
+ SshChannelExitSignal extractChannelExitSignal() const;
+ quint32 extractRecipientChannel() const;
+ QByteArray extractChannelRequestType() const;
+
+ quint32 serverSeqNr() const { return m_serverSeqNr; }
+
+ static const QByteArray ExitStatusType;
+ static const QByteArray ExitSignalType;
+
+private:
+ virtual quint32 cipherBlockSize() const;
+ virtual quint32 macLength() const;
+ virtual void calculateLength() const;
+
+ void decrypt();
+ void moveFirstBytes(QByteArray &target, QByteArray &source, int n);
+
+ quint32 m_serverSeqNr;
+ SshDecryptionFacility m_decrypter;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHINCOMINGPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sshkeyexchange.cpp b/src/plugins/coreplugin/ssh/sshkeyexchange.cpp
new file mode 100644
index 0000000000..7875d2ecd0
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshkeyexchange.cpp
@@ -0,0 +1,197 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshkeyexchange_p.h"
+
+#include "sshbotanconversions_p.h"
+#include "sshcapabilities_p.h"
+#include "sshsendfacility_p.h"
+#include "sshexception_p.h"
+#include "sshincomingpacket_p.h"
+
+#include <botan/botan.h>
+#include <botan/dsa.h>
+#include <botan/look_pk.h>
+#include <botan/pubkey.h>
+#include <botan/rsa.h>
+
+#include <string>
+
+using namespace Botan;
+
+namespace Core {
+namespace Internal {
+
+namespace {
+
+ // For debugging
+ void printNameList(const char *listName, const SshNameList &list)
+ {
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("%s:", listName);
+ foreach (const QByteArray &name, list.names)
+ qDebug("%s", name.constData());
+#else
+ Q_UNUSED(listName);
+ Q_UNUSED(list);
+#endif
+ }
+} // anonymous namespace
+
+SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility)
+ : m_sendFacility(sendFacility)
+{
+}
+
+SshKeyExchange::~SshKeyExchange() {}
+
+void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId)
+{
+ m_serverId = serverId;
+ const AbstractSshPacket::Payload &payload
+ = m_sendFacility.sendKeyExchangeInitPacket();
+ m_clientKexInitPayload = QByteArray(payload.data, payload.size);
+}
+
+bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit)
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("server requests key exchange");
+#endif
+ serverKexInit.printRawBytes();
+ SshKeyExchangeInit kexInitParams
+ = serverKexInit.extractKeyExchangeInitData();
+
+ printNameList("Key Algorithms", kexInitParams.keyAlgorithms);
+ printNameList("Server Host Key Algorithms", kexInitParams.serverHostKeyAlgorithms);
+ printNameList("Encryption algorithms client to server", kexInitParams.encryptionAlgorithmsClientToServer);
+ printNameList("Encryption algorithms server to client", kexInitParams.encryptionAlgorithmsServerToClient);
+ printNameList("MAC algorithms client to server", kexInitParams.macAlgorithmsClientToServer);
+ printNameList("MAC algorithms server to client", kexInitParams.macAlgorithmsServerToClient);
+ printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
+ printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
+ printNameList("Languages client to server", kexInitParams.languagesClientToServer);
+ printNameList("Languages server to client", kexInitParams.languagesServerToClient);
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("First packet follows: %d", kexInitParams.firstKexPacketFollows);
+#endif
+
+ const QByteArray &keyAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods,
+ kexInitParams.keyAlgorithms.names);
+ m_serverHostKeyAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms,
+ kexInitParams.serverHostKeyAlgorithms.names);
+ m_encryptionAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
+ kexInitParams.encryptionAlgorithmsClientToServer.names);
+ m_decryptionAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
+ kexInitParams.encryptionAlgorithmsServerToClient.names);
+ m_c2sHMacAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms,
+ kexInitParams.macAlgorithmsClientToServer.names);
+ m_s2cHMacAlgo
+ = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms,
+ kexInitParams.macAlgorithmsServerToClient.names);
+ SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
+ kexInitParams.compressionAlgorithmsClientToServer.names);
+ SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
+ kexInitParams.compressionAlgorithmsServerToClient.names);
+
+ AutoSeeded_RNG rng;
+ m_dhKey.reset(new DH_PrivateKey(rng,
+ DL_Group(botanKeyExchangeAlgoName(keyAlgo))));
+
+ const AbstractSshPacket::Payload &payload = serverKexInit.payLoad();
+ m_serverKexInitPayload = QByteArray(payload.data, payload.size);
+ m_sendFacility.sendKeyDhInitPacket(m_dhKey->get_y());
+ return kexInitParams.firstKexPacketFollows;
+}
+
+void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
+ const QByteArray &clientId)
+{
+ const SshKeyExchangeReply &reply
+ = dhReply.extractKeyExchangeReply(m_serverHostKeyAlgo);
+ if (reply.f <= 0 || reply.f >= m_dhKey->group_p()) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ "Server sent invalid f.");
+ }
+
+ QByteArray concatenatedData = AbstractSshPacket::encodeString(clientId);
+ concatenatedData += AbstractSshPacket::encodeString(m_serverId);
+ concatenatedData += AbstractSshPacket::encodeString(m_clientKexInitPayload);
+ concatenatedData += AbstractSshPacket::encodeString(m_serverKexInitPayload);
+ concatenatedData += reply.k_s;
+ concatenatedData += AbstractSshPacket::encodeMpInt(m_dhKey->get_y());
+ concatenatedData += AbstractSshPacket::encodeMpInt(reply.f);
+ SymmetricKey k = m_dhKey->derive_key(reply.f);
+ m_k = AbstractSshPacket::encodeMpInt(BigInt(k.begin(), k.length()));
+ concatenatedData += m_k;
+
+ m_hash.reset(get_hash(botanSha1Name()));
+ const SecureVector<byte> &hashResult
+ = m_hash->process(convertByteArray(concatenatedData),
+ concatenatedData.size());
+ m_h = convertByteArray(hashResult);
+
+ QScopedPointer<Public_Key> sigKey;
+ QScopedPointer<PK_Verifier> verifier;
+ if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) {
+ const DL_Group group(reply.parameters.at(0), reply.parameters.at(1),
+ reply.parameters.at(2));
+ DSA_PublicKey * const dsaKey
+ = new DSA_PublicKey(group, reply.parameters.at(3));
+ sigKey.reset(dsaKey);
+ verifier.reset(get_pk_verifier(*dsaKey,
+ botanEmsaAlgoName(SshCapabilities::PubKeyDss)));
+ } else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) {
+ RSA_PublicKey * const rsaKey
+ = new RSA_PublicKey(reply.parameters.at(1), reply.parameters.at(0));
+ sigKey.reset(rsaKey);
+ verifier.reset(get_pk_verifier(*rsaKey,
+ botanEmsaAlgoName(SshCapabilities::PubKeyRsa)));
+ } else {
+ Q_ASSERT(!"Impossible: Neither DSS nor RSA!");
+ }
+ const byte * const botanH = convertByteArray(m_h);
+ const Botan::byte * const botanSig
+ = convertByteArray(reply.signatureBlob);
+ if (!verifier->verify_message(botanH, m_h.size(), botanSig,
+ reply.signatureBlob.size())) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ "Invalid signature in SSH_MSG_KEXDH_REPLY packet.");
+ }
+
+ m_sendFacility.sendNewKeysPacket();
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshkeyexchange_p.h b/src/plugins/coreplugin/ssh/sshkeyexchange_p.h
new file mode 100644
index 0000000000..076f5bedd6
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshkeyexchange_p.h
@@ -0,0 +1,87 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHKEYEXCHANGE_P_H
+#define SSHKEYEXCHANGE_P_H
+
+#include <botan/dh.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QScopedPointer>
+
+namespace Botan { class HashFunction; }
+
+namespace Core {
+namespace Internal {
+
+class SshSendFacility;
+class SshIncomingPacket;
+
+class SshKeyExchange
+{
+public:
+ SshKeyExchange(SshSendFacility &sendFacility);
+ ~SshKeyExchange();
+
+ void sendKexInitPacket(const QByteArray &serverId);
+
+ // Returns true <=> the server sends a guessed package.
+ bool sendDhInitPacket(const SshIncomingPacket &serverKexInit);
+
+ void sendNewKeysPacket(const SshIncomingPacket &dhReply,
+ const QByteArray &clientId);
+
+ QByteArray k() const { return m_k; }
+ QByteArray h() const { return m_h; }
+ Botan::HashFunction *hash() const { return m_hash.data(); }
+ QByteArray encryptionAlgo() const { return m_encryptionAlgo; }
+ QByteArray decryptionAlgo() const { return m_decryptionAlgo; }
+ QByteArray hMacAlgoClientToServer() const { return m_c2sHMacAlgo; }
+ QByteArray hMacAlgoServerToClient() const { return m_s2cHMacAlgo; }
+
+private:
+ QByteArray m_serverId;
+ QByteArray m_clientKexInitPayload;
+ QByteArray m_serverKexInitPayload;
+ QScopedPointer<Botan::DH_PrivateKey> m_dhKey;
+ QByteArray m_k;
+ QByteArray m_h;
+ QByteArray m_serverHostKeyAlgo;
+ QByteArray m_encryptionAlgo;
+ QByteArray m_decryptionAlgo;
+ QByteArray m_c2sHMacAlgo;
+ QByteArray m_s2cHMacAlgo;
+ QScopedPointer<Botan::HashFunction> m_hash;
+ SshSendFacility &m_sendFacility;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHKEYEXCHANGE_P_H
diff --git a/src/plugins/coreplugin/ssh/sshkeygenerator.cpp b/src/plugins/coreplugin/ssh/sshkeygenerator.cpp
index 17a63886e3..976d0094c7 100644
--- a/src/plugins/coreplugin/ssh/sshkeygenerator.cpp
+++ b/src/plugins/coreplugin/ssh/sshkeygenerator.cpp
@@ -1,55 +1,138 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
#include "sshkeygenerator.h"
-#include "ne7sshobject.h"
+#include "sshbotanconversions_p.h"
+#include "sshcapabilities_p.h"
+#include "sshpacket_p.h"
-#include <QtCore/QFile>
-#include <QtCore/QTemporaryFile>
+#include <botan/auto_rng.h>
+#include <botan/bigint.h>
+#include <botan/der_enc.h>
+#include <botan/dsa.h>
+#include <botan/pem.h>
+#include <botan/pkcs8.h>
+#include <botan/rsa.h>
+#include <botan/x509_key.h>
-#include <ne7ssh.h>
+#include <QtCore/QDateTime>
namespace Core {
-SshKeyGenerator::SshKeyGenerator()
-{
-}
+using namespace Botan;
+using namespace Internal;
-bool SshKeyGenerator::generateKeys(KeyType type, const QString &id, int keySize)
+SshKeyGenerator::SshKeyGenerator() { }
+
+bool SshKeyGenerator::generateKeys(KeyType type, PrivateKeyFormat format,
+ int keySize)
{
- QTemporaryFile tmpPubKeyFile;
- QTemporaryFile tmpPrivKeyFile;
- if (!tmpPubKeyFile.open() || !tmpPrivKeyFile.open()) {
- m_error = tr("Error creating temporary files.");
- return false;
- }
- tmpPubKeyFile.setAutoRemove(false);
- tmpPubKeyFile.close();
- tmpPrivKeyFile.close();
- const char * const typeStr = type == Rsa ? "rsa" : "dsa";
- Internal::Ne7SshObject::Ptr ne7Object
- = Internal::Ne7SshObject::instance()->get();
- if (!ne7Object->generateKeyPair(typeStr, id.toUtf8(),
- tmpPrivKeyFile.fileName().toUtf8(),
- tmpPubKeyFile.fileName().toUtf8(), keySize)) {
- // TODO: Race condition on pop() call. Perhaps not use Net7 errors? Or hack API
- m_error = tr("Error generating keys: %1")
- .arg(ne7Object->errors()->pop());
+ try {
+ AutoSeeded_RNG rng;
+ KeyPtr key;
+ if (type == Rsa)
+ key = KeyPtr(new RSA_PrivateKey(rng, keySize));
+ else
+ key = KeyPtr(new DSA_PrivateKey(rng, DL_Group(rng, DL_Group::Strong,
+ keySize)));
+ return format == Pkcs8
+ ? generatePkcs8Keys(key) : generateOpenSslKeys(key, type);
+ } catch (Botan::Exception &e) {
+ m_error = tr("Error generating key: %1").arg(e.what());
return false;
}
+}
- if (!tmpPubKeyFile.open() || !tmpPrivKeyFile.open()) {
- m_error = tr("Error reading temporary files.");
- return false;
+bool SshKeyGenerator::generatePkcs8Keys(const KeyPtr &key)
+{
+ generatePkcs8Key(key, false);
+ generatePkcs8Key(key, true);
+ return true;
+}
+
+void SshKeyGenerator::generatePkcs8Key(const KeyPtr &key, bool privateKey)
+{
+ Pipe pipe;
+ pipe.start_msg();
+ QByteArray *keyData;
+ if (privateKey) {
+ PKCS8::encode(*key, pipe);
+ keyData = &m_privateKey;
+ } else {
+ X509::encode(*key, pipe);
+ keyData = &m_publicKey;
}
+ pipe.end_msg();
+ keyData->resize(pipe.remaining(pipe.message_count() - 1));
+ pipe.read(convertByteArray(*keyData), keyData->size(),
+ pipe.message_count() - 1);
+}
- m_publicKey = tmpPubKeyFile.readAll();
- m_privateKey = tmpPrivKeyFile.readAll();
- if (tmpPubKeyFile.error() != QFile::NoError
- || tmpPrivKeyFile.error() != QFile::NoError) {
- m_error = tr("Error reading temporary files.");
- return false;
+bool SshKeyGenerator::generateOpenSslKeys(const KeyPtr &key, KeyType type)
+{
+ QList<BigInt> publicParams;
+ QList<BigInt> allParams;
+ QByteArray keyId;
+ if (type == Rsa) {
+ const QSharedPointer<RSA_PrivateKey> rsaKey
+ = key.dynamicCast<RSA_PrivateKey>();
+ publicParams << rsaKey->get_e() << rsaKey->get_n();
+ allParams << rsaKey->get_n() << rsaKey->get_e() << rsaKey->get_d()
+ << rsaKey->get_p() << rsaKey->get_q();
+ keyId = SshCapabilities::PubKeyRsa;
+ } else {
+ const QSharedPointer<DSA_PrivateKey> dsaKey
+ = key.dynamicCast<DSA_PrivateKey>();
+ publicParams << dsaKey->group_p() << dsaKey->group_q()
+ << dsaKey->group_g() << dsaKey->get_y();
+ allParams << publicParams << dsaKey->get_x();
+ keyId = SshCapabilities::PubKeyDss;
}
- m_type = type;
+ QByteArray publicKeyBlob = AbstractSshPacket::encodeString(keyId);
+ foreach (const BigInt &b, publicParams)
+ publicKeyBlob += AbstractSshPacket::encodeMpInt(b);
+ publicKeyBlob = publicKeyBlob.toBase64();
+ const QByteArray id = "QtCreator/"
+ + QDateTime::currentDateTime().toString(Qt::ISODate).toUtf8();
+ m_publicKey = keyId + ' ' + publicKeyBlob + ' ' + id;
+
+ DER_Encoder encoder;
+ encoder.start_cons(SEQUENCE).encode (0U);
+ foreach (const BigInt &b, allParams)
+ encoder.encode(b);
+ encoder.end_cons();
+ const char * const label
+ = type == Rsa ? "RSA PRIVATE KEY" : "DSA PRIVATE KEY";
+ m_privateKey
+ = QByteArray(PEM_Code::encode (encoder.get_contents(), label).c_str());
return true;
}
diff --git a/src/plugins/coreplugin/ssh/sshkeygenerator.h b/src/plugins/coreplugin/ssh/sshkeygenerator.h
index a68237e576..ada06150a0 100644
--- a/src/plugins/coreplugin/ssh/sshkeygenerator.h
+++ b/src/plugins/coreplugin/ssh/sshkeygenerator.h
@@ -1,10 +1,43 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
#ifndef SSHKEYGENERATOR_H
#define SSHKEYGENERATOR_H
#include <coreplugin/core_global.h>
#include <QtCore/QCoreApplication>
-#include <QtCore/QPair>
+#include <QtCore/QSharedPointer>
+
+namespace Botan {
+ class Private_Key;
+}
namespace Core {
@@ -13,19 +46,28 @@ class CORE_EXPORT SshKeyGenerator
Q_DECLARE_TR_FUNCTIONS(SshKeyGenerator)
public:
enum KeyType { Rsa, Dsa };
+ enum PrivateKeyFormat { Pkcs8, OpenSsl };
SshKeyGenerator();
- bool generateKeys(KeyType type, const QString &id, int keySize);
+ bool generateKeys(KeyType type, PrivateKeyFormat format, int keySize);
QString error() const { return m_error; }
- QString privateKey() const { return m_privateKey; }
- QString publicKey() const { return m_publicKey; }
+ QByteArray privateKey() const { return m_privateKey; }
+ QByteArray publicKey() const { return m_publicKey; }
KeyType type() const { return m_type; }
+ PrivateKeyFormat format() const { return m_format; }
private:
+ typedef QSharedPointer<Botan::Private_Key> KeyPtr;
+
+ bool generatePkcs8Keys(const KeyPtr &key);
+ void generatePkcs8Key(const KeyPtr &key, bool privateKey);
+ bool generateOpenSslKeys(const KeyPtr &key, KeyType type);
+
QString m_error;
- QString m_publicKey;
- QString m_privateKey;
+ QByteArray m_publicKey;
+ QByteArray m_privateKey;
KeyType m_type;
+ PrivateKeyFormat m_format;
};
} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshoutgoingpacket.cpp b/src/plugins/coreplugin/ssh/sshoutgoingpacket.cpp
new file mode 100644
index 0000000000..c6cf99443d
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshoutgoingpacket.cpp
@@ -0,0 +1,284 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshoutgoingpacket_p.h"
+
+#include "sshcapabilities_p.h"
+#include "sshcryptofacility_p.h"
+
+#include <QtCore/QtEndian>
+
+namespace Core {
+namespace Internal {
+
+SshOutgoingPacket::SshOutgoingPacket(const SshEncryptionFacility &encrypter,
+ const quint32 &seqNr) : m_encrypter(encrypter), m_seqNr(seqNr)
+{
+}
+
+quint32 SshOutgoingPacket::cipherBlockSize() const
+{
+ return qMax(m_encrypter.cipherBlockSize(), 4U);
+}
+
+quint32 SshOutgoingPacket::macLength() const
+{
+ return m_encrypter.macLength();
+}
+
+void SshOutgoingPacket::generateKeyExchangeInitPacket()
+{
+ const QByteArray &supportedkeyExchangeMethods
+ = encodeNameList(SshCapabilities::KeyExchangeMethods);
+ const QByteArray &supportedPublicKeyAlgorithms
+ = encodeNameList(SshCapabilities::PublicKeyAlgorithms);
+ const QByteArray &supportedEncryptionAlgorithms
+ = encodeNameList(SshCapabilities::EncryptionAlgorithms);
+ const QByteArray &supportedMacAlgorithms
+ = encodeNameList(SshCapabilities::MacAlgorithms);
+ const QByteArray &supportedCompressionAlgorithms
+ = encodeNameList(SshCapabilities::CompressionAlgorithms);
+ const QByteArray &supportedLanguages = encodeNameList(QList<QByteArray>());
+
+ init(SSH_MSG_KEXINIT);
+ m_data += m_encrypter.getRandomNumbers(16);
+ m_data.append(supportedkeyExchangeMethods);
+ m_data.append(supportedPublicKeyAlgorithms);
+ m_data.append(supportedEncryptionAlgorithms)
+ .append(supportedEncryptionAlgorithms);
+ m_data.append(supportedMacAlgorithms).append(supportedMacAlgorithms);
+ m_data.append(supportedCompressionAlgorithms)
+ .append(supportedCompressionAlgorithms);
+ m_data.append(supportedLanguages).append(supportedLanguages);
+ appendBool(false); // No guessed packet.
+ m_data.append(QByteArray(4, 0)); // Reserved.
+ finalize();
+}
+
+void SshOutgoingPacket::generateKeyDhInitPacket(const Botan::BigInt &e)
+{
+ init(SSH_MSG_KEXDH_INIT).appendMpInt(e).finalize();
+}
+
+void SshOutgoingPacket::generateNewKeysPacket()
+{
+ init(SSH_MSG_NEWKEYS).finalize();
+}
+
+void SshOutgoingPacket::generateUserAuthServiceRequestPacket()
+{
+ generateServiceRequest("ssh-userauth");
+}
+
+void SshOutgoingPacket::generateServiceRequest(const QByteArray &service)
+{
+ init(SSH_MSG_SERVICE_REQUEST).appendString(service).finalize();
+}
+
+void SshOutgoingPacket::generateUserAuthByPwdRequestPacket(const QByteArray &user,
+ const QByteArray &service, const QByteArray &pwd)
+{
+ init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
+ .appendString("password").appendBool(false).appendString(pwd)
+ .finalize();
+}
+
+void SshOutgoingPacket::generateUserAuthByKeyRequestPacket(const QByteArray &user,
+ const QByteArray &service)
+{
+ init(SSH_MSG_USERAUTH_REQUEST).appendString(user).appendString(service)
+ .appendString("publickey").appendBool(true)
+ .appendString(m_encrypter.authenticationAlgorithmName())
+ .appendString(m_encrypter.authenticationPublicKey());
+ const QByteArray &dataToSign = m_data.mid(PayloadOffset);
+ appendString(m_encrypter.authenticationKeySignature(dataToSign));
+ finalize();
+}
+
+void SshOutgoingPacket::generateRequestFailurePacket()
+{
+ init(SSH_MSG_REQUEST_FAILURE).finalize();
+}
+
+void SshOutgoingPacket::generateSessionPacket(quint32 channelId,
+ quint32 windowSize, quint32 maxPacketSize)
+{
+ init(SSH_MSG_CHANNEL_OPEN).appendString("session").appendInt(channelId)
+ .appendInt(windowSize).appendInt(maxPacketSize).finalize();
+}
+
+void SshOutgoingPacket::generateEnvPacket(quint32 remoteChannel,
+ const QByteArray &var, const QByteArray &value)
+{
+ init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("env")
+ .appendBool(false).appendString(var).appendString(value);
+}
+
+void SshOutgoingPacket::generateExecPacket(quint32 remoteChannel,
+ const QByteArray &command)
+{
+ init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel).appendString("exec")
+ .appendBool(true).appendString(command).finalize();
+}
+
+void SshOutgoingPacket::generateSftpPacket(quint32 remoteChannel)
+{
+ init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
+ .appendString("subsystem").appendBool(true).appendString("sftp")
+ .finalize();
+}
+
+void SshOutgoingPacket::generateWindowAdjustPacket(quint32 remoteChannel,
+ quint32 bytesToAdd)
+{
+ init(SSH_MSG_CHANNEL_WINDOW_ADJUST).appendInt(remoteChannel)
+ .appendInt(bytesToAdd).finalize();
+}
+
+void SshOutgoingPacket::generateChannelDataPacket(quint32 remoteChannel,
+ const QByteArray &data)
+{
+ init(SSH_MSG_CHANNEL_DATA).appendInt(remoteChannel).appendString(data)
+ .finalize();
+}
+
+void SshOutgoingPacket::generateChannelSignalPacket(quint32 remoteChannel,
+ const QByteArray &signalName)
+{
+ init(SSH_MSG_CHANNEL_REQUEST).appendInt(remoteChannel)
+ .appendString("signal").appendBool(false).appendString(signalName)
+ .finalize();
+}
+
+void SshOutgoingPacket::generateChannelEofPacket(quint32 remoteChannel)
+{
+ init(SSH_MSG_CHANNEL_EOF).appendInt(remoteChannel).finalize();
+}
+
+void SshOutgoingPacket::generateChannelClosePacket(quint32 remoteChannel)
+{
+ init(SSH_MSG_CHANNEL_CLOSE).appendInt(remoteChannel).finalize();
+}
+
+void SshOutgoingPacket::generateDisconnectPacket(SshErrorCode reason,
+ const QByteArray &reasonString)
+{
+ init(SSH_MSG_DISCONNECT).appendInt(reason).appendString(reasonString)
+ .appendString(QByteArray()).finalize();
+}
+
+void SshOutgoingPacket::generateMsgUnimplementedPacket(quint32 serverSeqNr)
+{
+ init(SSH_MSG_UNIMPLEMENTED).appendInt(serverSeqNr).finalize();
+}
+
+SshOutgoingPacket &SshOutgoingPacket::appendInt(quint32 val)
+{
+ m_data.append(encodeInt(val));
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::appendMpInt(const Botan::BigInt &number)
+{
+ m_data.append(encodeMpInt(number));
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::appendBool(bool b)
+{
+ m_data += static_cast<char>(b);
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::appendString(const QByteArray &string)
+{
+ m_data.append(encodeString(string));
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::init(SshPacketType type)
+{
+ m_data.resize(TypeOffset + 1);
+ m_data[TypeOffset] = type;
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::setPadding()
+{
+ m_data += m_encrypter.getRandomNumbers(MinPaddingLength);
+ int padLength = MinPaddingLength;
+ const int divisor = sizeDivisor();
+ const int mod = m_data.size() % divisor;
+ padLength += divisor - mod;
+ m_data += m_encrypter.getRandomNumbers(padLength - MinPaddingLength);
+ m_data[PaddingLengthOffset] = padLength;
+ return *this;
+}
+
+SshOutgoingPacket &SshOutgoingPacket::encrypt()
+{
+ const QByteArray &mac
+ = generateMac(m_encrypter, m_seqNr);
+ m_encrypter.encrypt(m_data);
+ m_data += mac;
+ return *this;
+}
+
+void SshOutgoingPacket::finalize()
+{
+ setPadding();
+ setLengthField(m_data);
+ m_length = m_data.size() - 4;
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Encrypting packet of type %u", m_data.at(TypeOffset));
+#endif
+ encrypt();
+ Q_ASSERT(isComplete());
+}
+
+int SshOutgoingPacket::sizeDivisor() const
+{
+ return qMax(cipherBlockSize(), 8U);
+}
+
+QByteArray SshOutgoingPacket::encodeNameList(const QList<QByteArray> &list)
+{
+ QByteArray data;
+ data.resize(4);
+ for (int i = 0; i < list.count(); ++i) {
+ if (i > 0)
+ data.append(',');
+ data.append(list.at(i));
+ }
+ AbstractSshPacket::setLengthField(data);
+ return data;
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshoutgoingpacket_p.h b/src/plugins/coreplugin/ssh/sshoutgoingpacket_p.h
new file mode 100644
index 0000000000..eb9c2f520d
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshoutgoingpacket_p.h
@@ -0,0 +1,98 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHOUTGOINGPACKET_P_H
+#define SSHOUTGOINGPACKET_P_H
+
+#include "sshpacket_p.h"
+
+namespace Core {
+namespace Internal {
+
+class SshEncryptionFacility;
+
+class SshOutgoingPacket : public AbstractSshPacket
+{
+public:
+ SshOutgoingPacket(const SshEncryptionFacility &encrypter,
+ const quint32 &seqNr);
+
+ void generateKeyExchangeInitPacket();
+ void generateKeyDhInitPacket(const Botan::BigInt &e);
+ void generateNewKeysPacket();
+ void generateDisconnectPacket(SshErrorCode reason,
+ const QByteArray &reasonString);
+ void generateMsgUnimplementedPacket(quint32 serverSeqNr);
+ void generateUserAuthServiceRequestPacket();
+ void generateUserAuthByPwdRequestPacket(const QByteArray &user,
+ const QByteArray &service, const QByteArray &pwd);
+ void generateUserAuthByKeyRequestPacket(const QByteArray &user,
+ const QByteArray &service);
+ void generateRequestFailurePacket();
+ void generateSessionPacket(quint32 channelId, quint32 windowSize,
+ quint32 maxPacketSize);
+ void generateEnvPacket(quint32 remoteChannel, const QByteArray &var,
+ const QByteArray &value);
+ void generateExecPacket(quint32 remoteChannel, const QByteArray &command);
+ void generateSftpPacket(quint32 remoteChannel);
+ void generateWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd);
+ void generateChannelDataPacket(quint32 remoteChannel,
+ const QByteArray &data);
+ void generateChannelSignalPacket(quint32 remoteChannel,
+ const QByteArray &signalName);
+ void generateChannelEofPacket(quint32 remoteChannel);
+ void generateChannelClosePacket(quint32 remoteChannel);
+
+private:
+ virtual quint32 cipherBlockSize() const;
+ virtual quint32 macLength() const;
+
+ static QByteArray encodeNameList(const QList<QByteArray> &list);
+
+ void generateServiceRequest(const QByteArray &service);
+
+ SshOutgoingPacket &init(SshPacketType type);
+ SshOutgoingPacket &setPadding();
+ SshOutgoingPacket &encrypt();
+ void finalize();
+
+ SshOutgoingPacket &appendInt(quint32 val);
+ SshOutgoingPacket &appendString(const QByteArray &string);
+ SshOutgoingPacket &appendMpInt(const Botan::BigInt &number);
+ SshOutgoingPacket &appendBool(bool b);
+ int sizeDivisor() const;
+
+ const SshEncryptionFacility &m_encrypter;
+ const quint32 &m_seqNr;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHOUTGOINGPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sshpacket.cpp b/src/plugins/coreplugin/ssh/sshpacket.cpp
new file mode 100644
index 0000000000..ff70509355
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshpacket.cpp
@@ -0,0 +1,167 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshpacket_p.h"
+
+#include "sshcapabilities_p.h"
+#include "sshcryptofacility_p.h"
+#include "sshexception_p.h"
+#include "sshpacketparser_p.h"
+
+#include <QtCore/QDebug>
+
+#include <cctype>
+
+namespace Core {
+namespace Internal {
+
+const quint32 AbstractSshPacket::PaddingLengthOffset = 4;
+const quint32 AbstractSshPacket::PayloadOffset = PaddingLengthOffset + 1;
+const quint32 AbstractSshPacket::TypeOffset = PayloadOffset;
+const quint32 AbstractSshPacket::MinPaddingLength = 4;
+
+namespace {
+
+ void printByteArray(const QByteArray &data)
+ {
+#ifdef CREATOR_SSH_DEBUG
+ for (int i = 0; i < data.count(); ++i)
+ qDebug() << std::hex << (static_cast<unsigned int>(data[i]) & 0xff) << " ";
+#else
+ Q_UNUSED(data);
+#endif
+ }
+} // anonymous namespace
+
+
+AbstractSshPacket::AbstractSshPacket() : m_length(0) { }
+AbstractSshPacket::~AbstractSshPacket() {}
+
+bool AbstractSshPacket::isComplete() const
+{
+ if (currentDataSize() < minPacketSize())
+ return false;
+ Q_ASSERT(4 + length() + macLength() >= currentDataSize());
+ return 4 + length() + macLength() == currentDataSize();
+}
+
+void AbstractSshPacket::clear()
+{
+ m_data.clear();
+ m_length = 0;
+}
+
+SshPacketType AbstractSshPacket::type() const
+{
+ Q_ASSERT(isComplete());
+ return static_cast<SshPacketType>(m_data.at(TypeOffset));
+}
+
+AbstractSshPacket::Payload AbstractSshPacket::payLoad() const
+{
+ Payload p;
+ p.data = m_data.constData() + PayloadOffset;
+ p.size = length() - paddingLength() - 1;
+ return p;
+}
+
+void AbstractSshPacket::printRawBytes() const
+{
+ printByteArray(m_data);
+}
+
+QByteArray AbstractSshPacket::encodeString(const QByteArray &string)
+{
+ QByteArray data;
+ data.resize(4);
+ data += string;
+ setLengthField(data);
+ return data;
+}
+
+QByteArray AbstractSshPacket::encodeMpInt(const Botan::BigInt &number)
+{
+ if (number.is_zero())
+ return QByteArray(4, 0);
+
+ int stringLength = number.bytes();
+ const bool positiveAndMsbSet = number.sign() == Botan::BigInt::Positive
+ && (number.byte_at(stringLength - 1) & 0x80);
+ if (positiveAndMsbSet)
+ ++stringLength;
+ QByteArray data;
+ data.resize(4 + stringLength);
+ int pos = 4;
+ if (positiveAndMsbSet)
+ data[pos++] = '\0';
+ number.binary_encode(reinterpret_cast<Botan::byte *>(data.data()) + pos);
+ setLengthField(data);
+ return data;
+}
+
+int AbstractSshPacket::paddingLength() const
+{
+ return m_data[PaddingLengthOffset];
+}
+
+quint32 AbstractSshPacket::length() const
+{
+ //Q_ASSERT(currentDataSize() >= minPacketSize());
+ if (m_length == 0)
+ calculateLength();
+ return m_length;
+}
+
+void AbstractSshPacket::calculateLength() const
+{
+ m_length = SshPacketParser::asUint32(m_data, static_cast<quint32>(0));
+}
+
+QByteArray AbstractSshPacket::generateMac(const SshAbstractCryptoFacility &crypt,
+ quint32 seqNr) const
+{
+ const quint32 seqNrBe = qToBigEndian(seqNr);
+ QByteArray data(reinterpret_cast<const char *>(&seqNrBe), sizeof seqNrBe);
+ data += QByteArray(m_data.constData(), length() + 4);
+ return crypt.generateMac(data, data.size());
+}
+
+quint32 AbstractSshPacket::minPacketSize() const
+{
+ return qMax<quint32>(cipherBlockSize(), 16) + macLength();
+}
+
+void AbstractSshPacket::setLengthField(QByteArray &data)
+{
+ const quint32 length = qToBigEndian(data.size() - 4);
+ data.replace(0, 4, reinterpret_cast<const char *>(&length), 4);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshpacket_p.h b/src/plugins/coreplugin/ssh/sshpacket_p.h
new file mode 100644
index 0000000000..7120f001fd
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshpacket_p.h
@@ -0,0 +1,137 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHPACKET_P_H
+#define SSHPACKET_P_H
+
+#include "sshexception_p.h"
+
+#include <QtCore/QtEndian>
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+#include <botan/bigint.h>
+
+namespace Core {
+namespace Internal {
+
+enum SshPacketType {
+ SSH_MSG_DISCONNECT = 1,
+ SSH_MSG_IGNORE = 2,
+ SSH_MSG_UNIMPLEMENTED = 3,
+ SSH_MSG_DEBUG = 4,
+ SSH_MSG_SERVICE_REQUEST = 5,
+ SSH_MSG_SERVICE_ACCEPT = 6,
+
+ SSH_MSG_KEXINIT = 20,
+ SSH_MSG_NEWKEYS = 21,
+ SSH_MSG_KEXDH_INIT = 30,
+ SSH_MSG_KEXDH_REPLY = 31,
+
+ SSH_MSG_USERAUTH_REQUEST = 50,
+ SSH_MSG_USERAUTH_FAILURE = 51,
+ SSH_MSG_USERAUTH_SUCCESS = 52,
+ SSH_MSG_USERAUTH_BANNER = 53,
+ SSH_MSG_USERAUTH_PK_OK = 60,
+ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60,
+
+ SSH_MSG_GLOBAL_REQUEST = 80,
+ SSH_MSG_REQUEST_SUCCESS = 81,
+ SSH_MSG_REQUEST_FAILURE = 82,
+
+ SSH_MSG_CHANNEL_OPEN = 90,
+ SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91,
+ SSH_MSG_CHANNEL_OPEN_FAILURE = 92,
+ SSH_MSG_CHANNEL_WINDOW_ADJUST = 93,
+ SSH_MSG_CHANNEL_DATA = 94,
+ SSH_MSG_CHANNEL_EXTENDED_DATA = 95,
+ SSH_MSG_CHANNEL_EOF = 96,
+ SSH_MSG_CHANNEL_CLOSE = 97,
+ SSH_MSG_CHANNEL_REQUEST = 98,
+ SSH_MSG_CHANNEL_SUCCESS = 99,
+ SSH_MSG_CHANNEL_FAILURE = 100,
+};
+
+enum SshExtendedDataType { SSH_EXTENDED_DATA_STDERR = 1 };
+
+class SshAbstractCryptoFacility;
+
+class AbstractSshPacket
+{
+public:
+ virtual ~AbstractSshPacket();
+
+ void clear();
+ bool isComplete() const;
+ SshPacketType type() const;
+
+ static QByteArray encodeString(const QByteArray &string);
+ static QByteArray encodeMpInt(const Botan::BigInt &number);
+ template<typename T> static QByteArray encodeInt(T value)
+ {
+ const T valMsb = qToBigEndian(value);
+ return QByteArray(reinterpret_cast<const char *>(&valMsb), sizeof valMsb);
+ }
+
+ static void setLengthField(QByteArray &data);
+
+ void printRawBytes() const; // For Debugging.
+
+ const QByteArray &rawData() const { return m_data; }
+
+ struct Payload { const char *data; quint32 size; };
+ Payload payLoad() const;
+
+protected:
+ AbstractSshPacket();
+
+ virtual quint32 cipherBlockSize() const=0;
+ virtual quint32 macLength() const=0;
+ virtual void calculateLength() const;
+
+ quint32 length() const;
+ int paddingLength() const;
+ quint32 minPacketSize() const;
+ quint32 currentDataSize() const { return m_data.size(); }
+ QByteArray generateMac(const SshAbstractCryptoFacility &crypt,
+ quint32 seqNr) const;
+
+ static const quint32 PaddingLengthOffset;
+ static const quint32 PayloadOffset;
+ static const quint32 TypeOffset;
+ static const quint32 MinPaddingLength;
+
+ mutable QByteArray m_data;
+ mutable quint32 m_length;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHPACKET_P_H
diff --git a/src/plugins/coreplugin/ssh/sshpacketparser.cpp b/src/plugins/coreplugin/ssh/sshpacketparser.cpp
new file mode 100644
index 0000000000..2f339c03d9
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshpacketparser.cpp
@@ -0,0 +1,153 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshpacketparser_p.h"
+
+#include <cctype>
+
+namespace Core {
+namespace Internal {
+
+namespace { quint32 size(const QByteArray &data) { return data.size(); } }
+
+QString SshPacketParser::asUserString(const QByteArray &rawString)
+{
+ QByteArray filteredString;
+ filteredString.resize(rawString.size());
+ for (int i = 0; i < rawString.size(); ++i) {
+ const char c = rawString.at(i);
+ filteredString[i]
+ = std::isprint(c) || c == '\n' || c == '\r' || c == '\t' ? c : '?';
+ }
+ return QString::fromUtf8(filteredString);
+}
+
+bool SshPacketParser::asBool(const QByteArray &data, quint32 offset)
+{
+ if (size(data) <= offset)
+ throw SshPacketParseException();
+ return data.at(offset);
+}
+
+bool SshPacketParser::asBool(const QByteArray &data, quint32 *offset)
+{
+ bool b = asBool(data, *offset);
+ ++(*offset);
+ return b;
+}
+
+
+quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 offset)
+{
+ if (size(data) < offset + 4)
+ throw SshPacketParseException();
+ const quint32 value = ((data.at(offset) & 0xff) << 24)
+ + ((data.at(offset + 1) & 0xff) << 16)
+ + ((data.at(offset + 2) & 0xff) << 8) + (data.at(offset + 3) & 0xff);
+ return value;
+}
+
+quint32 SshPacketParser::asUint32(const QByteArray &data, quint32 *offset)
+{
+ const quint32 v = asUint32(data, *offset);
+ *offset += 4;
+ return v;
+}
+
+quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 offset)
+{
+ if (size(data) < offset + 8)
+ throw SshPacketParseException();
+ const quint64 value = (static_cast<quint64>(data.at(offset) & 0xff) << 56)
+ + (static_cast<quint64>(data.at(offset + 1) & 0xff) << 48)
+ + (static_cast<quint64>(data.at(offset + 2) & 0xff) << 40)
+ + (static_cast<quint64>(data.at(offset + 3) & 0xff) << 32)
+ + ((data.at(offset + 4) & 0xff) << 24)
+ + ((data.at(offset + 5) & 0xff) << 16)
+ + ((data.at(offset + 6) & 0xff) << 8)
+ + (data.at(offset + 7) & 0xff);
+ return value;
+}
+
+quint64 SshPacketParser::asUint64(const QByteArray &data, quint32 *offset)
+{
+ const quint64 val = asUint64(data, *offset);
+ *offset += 8;
+ return val;
+}
+
+QByteArray SshPacketParser::asString(const QByteArray &data, quint32 *offset)
+{
+ const quint32 length = asUint32(data, offset);
+ if (size(data) < *offset + length)
+ throw SshPacketParseException();
+ const QByteArray &string = data.mid(*offset, length);
+ *offset += length;
+ return string;
+}
+
+QString SshPacketParser::asUserString(const QByteArray &data, quint32 *offset)
+{
+ return asUserString(asString(data, offset));
+}
+
+SshNameList SshPacketParser::asNameList(const QByteArray &data, quint32 *offset)
+{
+ const quint32 length = asUint32(data, offset);
+ const int listEndPos = *offset + length;
+ if (data.size() < listEndPos)
+ throw SshPacketParseException();
+ SshNameList names(length + 4);
+ int nextNameOffset = *offset;
+ int nextCommaOffset = data.indexOf(',', nextNameOffset);
+ while (nextNameOffset > 0 && nextNameOffset < listEndPos) {
+ const int stringEndPos = nextCommaOffset == -1
+ || nextCommaOffset > listEndPos ? listEndPos : nextCommaOffset;
+ names.names << QByteArray(data.constData() + nextNameOffset,
+ stringEndPos - nextNameOffset);
+ nextNameOffset = nextCommaOffset + 1;
+ nextCommaOffset = data.indexOf(',', nextNameOffset);
+ }
+ *offset += length;
+ return names;
+}
+
+Botan::BigInt SshPacketParser::asBigInt(const QByteArray &data, quint32 *offset)
+{
+ const quint32 length = asUint32(data, offset);
+ if (length == 0)
+ return Botan::BigInt();
+ const Botan::byte *numberStart
+ = reinterpret_cast<const Botan::byte *>(data.constData() + *offset);
+ *offset += length;
+ return Botan::BigInt::decode(numberStart, length);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshpacketparser_p.h b/src/plugins/coreplugin/ssh/sshpacketparser_p.h
new file mode 100644
index 0000000000..253f256b7b
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshpacketparser_p.h
@@ -0,0 +1,81 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHPACKETPARSER_P_H
+#define SSHPACKETPARSER_P_H
+
+#include <botan/bigint.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+namespace Core {
+namespace Internal {
+
+struct SshNameList
+{
+ SshNameList() : originalLength(0) {}
+ SshNameList(quint32 originalLength) : originalLength(originalLength) {}
+ quint32 originalLength;
+ QList<QByteArray> names;
+};
+
+class SshPacketParseException { };
+
+// This class's functions try to read a byte array at a certain offset
+// as the respective chunk of data as specified in the SSH RFCs.
+// If they succeed, they update the offset, so they can easily
+// be called in succession by client code.
+// For convenience, some have also versions that don't update the offset,
+// so they can be called with rvalues if the new value is not needed.
+// If they fail, they throw an SshPacketParseException.
+class SshPacketParser
+{
+public:
+ static bool asBool(const QByteArray &data, quint32 offset);
+ static bool asBool(const QByteArray &data, quint32 *offset);
+ static quint16 asUint16(const QByteArray &data, quint32 offset);
+ static quint16 asUint16(const QByteArray &data, quint32 *offset);
+ static quint64 asUint64(const QByteArray &data, quint32 offset);
+ static quint64 asUint64(const QByteArray &data, quint32 *offset);
+ static quint32 asUint32(const QByteArray &data, quint32 offset);
+ static quint32 asUint32(const QByteArray &data, quint32 *offset);
+ static QByteArray asString(const QByteArray &data, quint32 *offset);
+ static QString asUserString(const QByteArray &data, quint32 *offset);
+ static SshNameList asNameList(const QByteArray &data, quint32 *offset);
+ static Botan::BigInt asBigInt(const QByteArray &data, quint32 *offset);
+
+ static QString asUserString(const QByteArray &rawString);
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHPACKETPARSER_P_H
diff --git a/src/plugins/coreplugin/ssh/sshremoteprocess.cpp b/src/plugins/coreplugin/ssh/sshremoteprocess.cpp
new file mode 100644
index 0000000000..c9566e9aca
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshremoteprocess.cpp
@@ -0,0 +1,270 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshremoteprocess.h"
+#include "sshremoteprocess_p.h"
+
+#include "sshdelayedsignal_p.h"
+#include "sshincomingpacket_p.h"
+#include "sshsendfacility_p.h"
+
+#include <botan/exceptn.h>
+
+namespace Core {
+
+const QByteArray SshRemoteProcess::AbrtSignal("ABRT");
+const QByteArray SshRemoteProcess::AlrmSignal("ALRM");
+const QByteArray SshRemoteProcess::FpeSignal("FPE");
+const QByteArray SshRemoteProcess::HupSignal("HUP");
+const QByteArray SshRemoteProcess::IllSignal("ILL");
+const QByteArray SshRemoteProcess::IntSignal("INT");
+const QByteArray SshRemoteProcess::KillSignal("KILL");
+const QByteArray SshRemoteProcess::PipeSignal("PIPE");
+const QByteArray SshRemoteProcess::QuitSignal("QUIT");
+const QByteArray SshRemoteProcess::SegvSignal("SEGV");
+const QByteArray SshRemoteProcess::TermSignal("TERM");
+const QByteArray SshRemoteProcess::Usr1Signal("USR1");
+const QByteArray SshRemoteProcess::Usr2Signal("USR2");
+
+SshRemoteProcess::SshRemoteProcess(const QByteArray &command, quint32 channelId,
+ Internal::SshSendFacility &sendFacility)
+ : d(new Internal::SshRemoteProcessPrivate(command, channelId, sendFacility, this))
+{
+}
+
+SshRemoteProcess::~SshRemoteProcess()
+{
+ Q_ASSERT(d->channelState() == Internal::SshRemoteProcessPrivate::Inactive
+ || d->channelState() == Internal::SshRemoteProcessPrivate::CloseRequested
+ || d->channelState() == Internal::SshRemoteProcessPrivate::Closed);
+ delete d;
+}
+
+void SshRemoteProcess::addToEnvironment(const QByteArray &var, const QByteArray &value)
+{
+ if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive)
+ d->m_env << qMakePair(var, value); // Cached locally and sent on start()
+}
+
+void SshRemoteProcess::start()
+{
+ if (d->channelState() == Internal::SshRemoteProcessPrivate::Inactive) {
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("process start requested, channel id = %u", d->localChannelId());
+#endif
+ d->requestSessionStart();
+ }
+}
+
+void SshRemoteProcess::sendSignal(const QByteArray &signal)
+{
+ try {
+ if (isRunning())
+ d->m_sendFacility.sendChannelSignalPacket(d->remoteChannel(),
+ signal);
+ } catch (Botan::Exception &e) {
+ d->setError(QString::fromAscii(e.what()));
+ d->closeChannel();
+ }
+}
+
+void SshRemoteProcess::closeChannel()
+{
+ d->closeChannel();
+}
+
+void SshRemoteProcess::sendInput(const QByteArray &data)
+{
+ if (isRunning())
+ d->sendData(data);
+}
+
+bool SshRemoteProcess::isRunning() const
+{
+ return d->m_procState == Internal::SshRemoteProcessPrivate::Running;
+}
+
+QString SshRemoteProcess::errorString() const { return d->errorString(); }
+
+int SshRemoteProcess::exitCode() const { return d->m_exitCode; }
+
+QByteArray SshRemoteProcess::exitSignal() const { return d->m_signal; }
+
+namespace Internal {
+
+SshRemoteProcessPrivate::SshRemoteProcessPrivate(const QByteArray &command,
+ quint32 channelId, SshSendFacility &sendFacility, SshRemoteProcess *proc)
+ : AbstractSshChannel(channelId, sendFacility), m_procState(NotYetStarted),
+ m_wasRunning(false), m_exitCode(0), m_command(command), m_proc(proc)
+{
+}
+
+void SshRemoteProcessPrivate::setProcState(ProcessState newState)
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("channel: old state = %d,new state = %d", m_procState, newState);
+#endif
+ m_procState = newState;
+ if (newState == StartFailed) {
+ createClosedSignal(SshRemoteProcess::FailedToStart);
+ } else if (newState == Running) {
+ m_wasRunning = true;
+ createStartedSignal();
+ }
+}
+
+void SshRemoteProcessPrivate::closeHook()
+{
+ if (m_wasRunning) {
+ if (!m_signal.isEmpty())
+ createClosedSignal(SshRemoteProcess::KilledBySignal);
+ else
+ createClosedSignal(SshRemoteProcess::ExitedNormally);
+ }
+}
+
+void SshRemoteProcessPrivate::handleOpenSuccessInternal()
+{
+ foreach (const EnvVar &envVar, m_env) {
+ m_sendFacility.sendEnvPacket(remoteChannel(), envVar.first,
+ envVar.second);
+ }
+
+ m_sendFacility.sendExecPacket(remoteChannel(), m_command);
+ setProcState(ExecRequested);
+}
+
+void SshRemoteProcessPrivate::handleOpenFailureInternal()
+{
+ setProcState(StartFailed);
+}
+
+void SshRemoteProcessPrivate::handleChannelSuccess()
+{
+ if (m_procState != ExecRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_SUCCESS message.");
+ }
+ setProcState(Running);
+}
+
+void SshRemoteProcessPrivate::handleChannelFailure()
+{
+ if (m_procState != ExecRequested) {
+ throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_PROTOCOL_ERROR,
+ "Unexpected SSH_MSG_CHANNEL_FAILURE message.");
+ }
+
+ setProcState(StartFailed);
+ closeChannel();
+}
+
+void SshRemoteProcessPrivate::handleChannelDataInternal(const QByteArray &data)
+{
+ createOutputAvailableSignal(data);
+}
+
+void SshRemoteProcessPrivate::handleChannelExtendedDataInternal(quint32 type,
+ const QByteArray &data)
+{
+ if (type != SSH_EXTENDED_DATA_STDERR)
+ qWarning("Unknown extended data type %u", type);
+ else
+ createErrorOutputAvailableSignal(data);
+}
+
+void SshRemoteProcessPrivate::handleChannelRequest(const SshIncomingPacket &packet)
+{
+ checkChannelActive();
+ const QByteArray &requestType = packet.extractChannelRequestType();
+ if (requestType == SshIncomingPacket::ExitStatusType) {
+ const SshChannelExitStatus status = packet.extractChannelExitStatus();
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Process exiting with exit code %d", status.exitStatus);
+#endif
+ m_exitCode = status.exitStatus;
+ m_procState = Exited;
+ } else if (requestType == SshIncomingPacket::ExitSignalType) {
+ const SshChannelExitSignal &signal = packet.extractChannelExitSignal();
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Exit due to signal %s", signal.signal.data());
+#endif
+ setError(signal.error);
+ m_signal = signal.signal;
+ m_procState = Exited;
+ } else {
+ qWarning("Ignoring unknown request type '%s'", requestType.data());
+ }
+}
+
+void SshRemoteProcessPrivate::createStartedSignal()
+{
+ new SshRemoteProcessStartedSignal(this, QWeakPointer<SshRemoteProcess>(m_proc));
+}
+
+void SshRemoteProcessPrivate::emitStartedSignal()
+{
+ emit m_proc->started();
+}
+
+void SshRemoteProcessPrivate::createOutputAvailableSignal(const QByteArray &output)
+{
+ new SshRemoteProcessOutputAvailableSignal(this,
+ QWeakPointer<SshRemoteProcess>(m_proc), output);
+}
+
+void SshRemoteProcessPrivate::emitOutputAvailableSignal(const QByteArray &output)
+{
+ emit m_proc->outputAvailable(output);
+}
+
+void SshRemoteProcessPrivate::createErrorOutputAvailableSignal(const QByteArray &output)
+{
+ new SshRemoteProcessErrorOutputAvailableSignal(this,
+ QWeakPointer<SshRemoteProcess>(m_proc), output);
+}
+
+void SshRemoteProcessPrivate::emitErrorOutputAvailableSignal(const QByteArray &output)
+{
+ emit m_proc->errorOutputAvailable(output);
+}
+
+void SshRemoteProcessPrivate::createClosedSignal(int exitStatus)
+{
+ new SshRemoteProcessClosedSignal(this,
+ QWeakPointer<SshRemoteProcess>(m_proc), exitStatus);
+}
+
+void SshRemoteProcessPrivate::emitClosedSignal(int exitStatus)
+{
+ emit m_proc->closed(exitStatus);
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshremoteprocess.h b/src/plugins/coreplugin/ssh/sshremoteprocess.h
new file mode 100644
index 0000000000..941894990f
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshremoteprocess.h
@@ -0,0 +1,130 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHREMOTECOMMAND_H
+#define SSHREMOTECOMMAND_H
+
+#include <coreplugin/core_global.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QByteArray;
+QT_END_NAMESPACE
+
+namespace Core {
+namespace Internal {
+class SshChannelManager;
+class SshRemoteProcessPrivate;
+class SshSendFacility;
+} // namespace Internal
+
+
+/*
+ * This class implements an SSH channel for running a remote process.
+ * Objects are created via SshConnection::createRemoteProcess.
+ * The process is started via the start() member function.
+ * A closeChannel() function is provided, but rarely useful, because
+ * a) when the process ends, the channel is closed automatically, and
+ * b) closing a channel will not necessarily kill the remote process.
+ * Therefore, the only sensible use case for calling closeChannel() is to
+ * get rid of an SshRemoteProces object before the process is actually started.
+ * Note that the process does not have a terminal, so you can't use it
+ * for applications that require one.
+ */
+class CORE_EXPORT SshRemoteProcess : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(SshRemoteProcess)
+
+ friend class Internal::SshChannelManager;
+ friend class Internal::SshRemoteProcessPrivate;
+
+public:
+ typedef QSharedPointer<SshRemoteProcess> Ptr;
+ enum ExitStatus { FailedToStart, KilledBySignal, ExitedNormally };
+
+ static const QByteArray AbrtSignal;
+ static const QByteArray AlrmSignal;
+ static const QByteArray FpeSignal;
+ static const QByteArray HupSignal;
+ static const QByteArray IllSignal;
+ static const QByteArray IntSignal;
+ static const QByteArray KillSignal;
+ static const QByteArray PipeSignal;
+ static const QByteArray QuitSignal;
+ static const QByteArray SegvSignal;
+ static const QByteArray TermSignal;
+ static const QByteArray Usr1Signal;
+ static const QByteArray Usr2Signal;
+
+ ~SshRemoteProcess();
+
+ /*
+ * Note that this is of limited value in practice, because servers are
+ * usually configured to ignore such requests for security reasons.
+ */
+ void addToEnvironment(const QByteArray &var, const QByteArray &value);
+
+ void start();
+ void closeChannel();
+
+ bool isRunning() const;
+ QString errorString() const;
+ int exitCode() const;
+ QByteArray exitSignal() const;
+
+ // Note: This is ignored by the OpenSSH server.
+ void sendSignal(const QByteArray &signal);
+ void kill() { sendSignal(KillSignal); }
+
+ void sendInput(const QByteArray &data); // Should usually have a trailing newline.
+
+signals:
+ void started();
+ void outputAvailable(const QByteArray &output);
+ void errorOutputAvailable(const QByteArray &output);
+
+ /*
+ * Parameter is of type ExitStatus, but we use int because of
+ * signal/slot awkwardness (full namespace required).
+ */
+ void closed(int exitStatus);
+
+private:
+ SshRemoteProcess(const QByteArray &command, quint32 channelId,
+ Internal::SshSendFacility &sendFacility);
+
+ Internal::SshRemoteProcessPrivate *d;
+};
+
+} // namespace Core
+
+#endif // SSHREMOTECOMMAND_H
diff --git a/src/plugins/coreplugin/ssh/sshremoteprocess_p.h b/src/plugins/coreplugin/ssh/sshremoteprocess_p.h
new file mode 100644
index 0000000000..951ca24731
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshremoteprocess_p.h
@@ -0,0 +1,96 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHREMOTEPROCESS_P_H
+#define SSHREMOTEPROCESS_P_H
+
+#include "sshchannel_p.h"
+
+#include <QtCore/QList>
+#include <QtCore/QPair>
+
+namespace Core {
+class SshRemoteProcess;
+
+namespace Internal {
+class SshSendFacility;
+
+class SshRemoteProcessPrivate : public AbstractSshChannel
+{
+ friend class Core::SshRemoteProcess;
+public:
+ enum ProcessState {
+ NotYetStarted, ExecRequested, StartFailed,Running, Exited
+ };
+
+ virtual void handleChannelSuccess();
+ virtual void handleChannelFailure();
+
+ virtual void closeHook();
+
+ void emitStartedSignal();
+ void emitOutputAvailableSignal(const QByteArray &output);
+ void emitErrorOutputAvailableSignal(const QByteArray &output);
+ void emitClosedSignal(int exitStatus);
+
+private:
+ SshRemoteProcessPrivate(const QByteArray &command, quint32 channelId,
+ SshSendFacility &sendFacility, SshRemoteProcess *proc);
+
+ virtual void handleOpenSuccessInternal();
+ virtual void handleOpenFailureInternal();
+ virtual void handleChannelDataInternal(const QByteArray &data);
+ virtual void handleChannelExtendedDataInternal(quint32 type,
+ const QByteArray &data);
+ virtual void handleChannelRequest(const SshIncomingPacket &packet);
+
+ void setProcState(ProcessState newState);
+
+ void createStartedSignal();
+ void createOutputAvailableSignal(const QByteArray &output);
+ void createErrorOutputAvailableSignal(const QByteArray &output);
+ void createClosedSignal(int exitStatus);
+
+ ProcessState m_procState;
+ bool m_wasRunning;
+ QByteArray m_signal;
+ int m_exitCode;
+
+ const QByteArray m_command;
+
+ typedef QPair<QByteArray, QByteArray> EnvVar;
+ QList<EnvVar> m_env;
+
+ SshRemoteProcess *m_proc;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHREMOTEPROCESS_P_H
diff --git a/src/plugins/coreplugin/ssh/sshsendfacility.cpp b/src/plugins/coreplugin/ssh/sshsendfacility.cpp
new file mode 100644
index 0000000000..3c793af24d
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshsendfacility.cpp
@@ -0,0 +1,191 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "sshsendfacility_p.h"
+
+#include "sshkeyexchange_p.h"
+#include "sshoutgoingpacket_p.h"
+
+#include <QtNetwork/QTcpSocket>
+
+namespace Core {
+namespace Internal {
+
+SshSendFacility::SshSendFacility(QTcpSocket *socket)
+ : m_clientSeqNr(0), m_socket(socket),
+ m_outgoingPacket(m_encrypter, m_clientSeqNr)
+{
+}
+
+void SshSendFacility::sendPacket()
+{
+#ifdef CREATOR_SSH_DEBUG
+ qDebug("Sending packet, client seq nr is %u", m_clientSeqNr);
+#endif
+ m_socket->write(m_outgoingPacket.rawData());
+ ++m_clientSeqNr;
+}
+
+void SshSendFacility::reset()
+{
+ m_clientSeqNr = 0;
+ m_encrypter.clearKeys();
+}
+
+void SshSendFacility::recreateKeys(const SshKeyExchange &keyExchange)
+{
+ m_encrypter.recreateKeys(keyExchange);
+}
+
+void SshSendFacility::createAuthenticationKey(const QByteArray &privKeyFileContents)
+{
+ m_encrypter.createAuthenticationKey(privKeyFileContents);
+}
+
+SshOutgoingPacket::Payload SshSendFacility::sendKeyExchangeInitPacket()
+{
+ m_outgoingPacket.generateKeyExchangeInitPacket();
+ sendPacket();
+ return m_outgoingPacket.payLoad();
+}
+
+void SshSendFacility::sendKeyDhInitPacket(const Botan::BigInt &e)
+{
+ m_outgoingPacket.generateKeyDhInitPacket(e);
+ sendPacket();
+}
+
+void SshSendFacility::sendNewKeysPacket()
+{
+ m_outgoingPacket.generateNewKeysPacket();
+ sendPacket();
+}
+
+void SshSendFacility::sendDisconnectPacket(SshErrorCode reason,
+ const QByteArray &reasonString)
+{
+ m_outgoingPacket.generateDisconnectPacket(reason, reasonString);
+ sendPacket();
+ }
+
+void SshSendFacility::sendMsgUnimplementedPacket(quint32 serverSeqNr)
+{
+ m_outgoingPacket.generateMsgUnimplementedPacket(serverSeqNr);
+ sendPacket();
+}
+
+void SshSendFacility::sendUserAuthServiceRequestPacket()
+{
+ m_outgoingPacket.generateUserAuthServiceRequestPacket();
+ sendPacket();
+}
+
+void SshSendFacility::sendUserAuthByPwdRequestPacket(const QByteArray &user,
+ const QByteArray &service, const QByteArray &pwd)
+{
+ m_outgoingPacket.generateUserAuthByPwdRequestPacket(user, service, pwd);
+ sendPacket();
+ }
+
+void SshSendFacility::sendUserAuthByKeyRequestPacket(const QByteArray &user,
+ const QByteArray &service)
+{
+ m_outgoingPacket.generateUserAuthByKeyRequestPacket(user, service);
+ sendPacket();
+}
+
+void SshSendFacility::sendRequestFailurePacket()
+{
+ m_outgoingPacket.generateRequestFailurePacket();
+ sendPacket();
+}
+
+void SshSendFacility::sendSessionPacket(quint32 channelId, quint32 windowSize,
+ quint32 maxPacketSize)
+{
+ m_outgoingPacket.generateSessionPacket(channelId, windowSize,
+ maxPacketSize);
+ sendPacket();
+}
+
+void SshSendFacility::sendEnvPacket(quint32 remoteChannel,
+ const QByteArray &var, const QByteArray &value)
+{
+ m_outgoingPacket.generateEnvPacket(remoteChannel, var, value);
+ sendPacket();
+}
+
+void SshSendFacility::sendExecPacket(quint32 remoteChannel,
+ const QByteArray &command)
+{
+ m_outgoingPacket.generateExecPacket(remoteChannel, command);
+ sendPacket();
+}
+
+void SshSendFacility::sendSftpPacket(quint32 remoteChannel)
+{
+ m_outgoingPacket.generateSftpPacket(remoteChannel);
+ sendPacket();
+}
+
+void SshSendFacility::sendWindowAdjustPacket(quint32 remoteChannel,
+ quint32 bytesToAdd)
+{
+ m_outgoingPacket.generateWindowAdjustPacket(remoteChannel, bytesToAdd);
+ sendPacket();
+}
+
+void SshSendFacility::sendChannelDataPacket(quint32 remoteChannel,
+ const QByteArray &data)
+{
+ m_outgoingPacket.generateChannelDataPacket(remoteChannel, data);
+ sendPacket();
+}
+
+void SshSendFacility::sendChannelSignalPacket(quint32 remoteChannel,
+ const QByteArray &signalName)
+{
+ m_outgoingPacket.generateChannelSignalPacket(remoteChannel, signalName);
+ sendPacket();
+}
+
+void SshSendFacility::sendChannelEofPacket(quint32 remoteChannel)
+{
+ m_outgoingPacket.generateChannelEofPacket(remoteChannel);
+ sendPacket();
+}
+
+void SshSendFacility::sendChannelClosePacket(quint32 remoteChannel)
+{
+ m_outgoingPacket.generateChannelClosePacket(remoteChannel);
+ sendPacket();
+}
+
+} // namespace Internal
+} // namespace Core
diff --git a/src/plugins/coreplugin/ssh/sshsendfacility_p.h b/src/plugins/coreplugin/ssh/sshsendfacility_p.h
new file mode 100644
index 0000000000..6f1cdf76f3
--- /dev/null
+++ b/src/plugins/coreplugin/ssh/sshsendfacility_p.h
@@ -0,0 +1,90 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef SSHCONNECTIONOUTSTATE_P_H
+#define SSHCONNECTIONOUTSTATE_P_H
+
+#include "sshcryptofacility_p.h"
+#include "sshoutgoingpacket_p.h"
+
+QT_BEGIN_NAMESPACE
+class QTcpSocket;
+QT_END_NAMESPACE
+
+
+namespace Core {
+namespace Internal {
+class SshKeyExchange;
+
+class SshSendFacility
+{
+public:
+ SshSendFacility(QTcpSocket *socket);
+ void reset();
+ void recreateKeys(const SshKeyExchange &keyExchange);
+ void createAuthenticationKey(const QByteArray &privKeyFileContents);
+
+ SshOutgoingPacket::Payload sendKeyExchangeInitPacket();
+ void sendKeyDhInitPacket(const Botan::BigInt &e);
+ void sendNewKeysPacket();
+ void sendDisconnectPacket(SshErrorCode reason,
+ const QByteArray &reasonString);
+ void sendMsgUnimplementedPacket(quint32 serverSeqNr);
+ void sendUserAuthServiceRequestPacket();
+ void sendUserAuthByPwdRequestPacket(const QByteArray &user,
+ const QByteArray &service, const QByteArray &pwd);
+ void sendUserAuthByKeyRequestPacket(const QByteArray &user,
+ const QByteArray &service);
+ void sendRequestFailurePacket();
+ void sendSessionPacket(quint32 channelId, quint32 windowSize,
+ quint32 maxPacketSize);
+ void sendEnvPacket(quint32 remoteChannel, const QByteArray &var,
+ const QByteArray &value);
+ void sendExecPacket(quint32 remoteChannel, const QByteArray &command);
+ void sendSftpPacket(quint32 remoteChannel);
+ void sendWindowAdjustPacket(quint32 remoteChannel, quint32 bytesToAdd);
+ void sendChannelDataPacket(quint32 remoteChannel, const QByteArray &data);
+ void sendChannelSignalPacket(quint32 remoteChannel,
+ const QByteArray &signalName);
+ void sendChannelEofPacket(quint32 remoteChannel);
+ void sendChannelClosePacket(quint32 remoteChannel);
+
+private:
+ void sendPacket();
+
+ quint32 m_clientSeqNr;
+ SshEncryptionFacility m_encrypter;
+ QTcpSocket *m_socket;
+ SshOutgoingPacket m_outgoingPacket;
+};
+
+} // namespace Internal
+} // namespace Core
+
+#endif // SSHCONNECTIONOUTSTATE_P_H
diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp
index 626f1f1b3a..0756b1b77d 100644
--- a/src/plugins/cpaster/cpasterplugin.cpp
+++ b/src/plugins/cpaster/cpasterplugin.cpp
@@ -144,7 +144,7 @@ void CodepasterPlugin::extensionsInitialized()
{
}
-void CodepasterPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag CodepasterPlugin::aboutToShutdown()
{
// Delete temporary, fetched files
foreach(const QString &fetchedSnippet, m_fetchedSnippets) {
@@ -152,6 +152,7 @@ void CodepasterPlugin::aboutToShutdown()
if (file.exists())
file.remove();
}
+ return SynchronousShutdown;
}
void CodepasterPlugin::postEditor()
diff --git a/src/plugins/cpaster/cpasterplugin.h b/src/plugins/cpaster/cpasterplugin.h
index b75158b0c9..6dcc8a902b 100644
--- a/src/plugins/cpaster/cpasterplugin.h
+++ b/src/plugins/cpaster/cpasterplugin.h
@@ -55,7 +55,7 @@ public:
virtual bool initialize(const QStringList &arguments, QString *error_message);
virtual void extensionsInitialized();
- virtual void aboutToShutdown();
+ virtual ShutdownFlag aboutToShutdown();
public slots:
void postEditor();
diff --git a/src/plugins/cppeditor/cppcheckundefinedsymbols.cpp b/src/plugins/cppeditor/cppchecksymbols.cpp
index 0e16c51bb8..0e9af30b39 100644
--- a/src/plugins/cppeditor/cppcheckundefinedsymbols.cpp
+++ b/src/plugins/cppeditor/cppchecksymbols.cpp
@@ -27,7 +27,7 @@
**
**************************************************************************/
-#include "cppcheckundefinedsymbols.h"
+#include "cppchecksymbols.h"
#include <cplusplus/Overview.h>
#include <Names.h>
@@ -117,8 +117,8 @@ protected:
return;
} else if (const QualifiedNameId *q = name->asQualifiedNameId()) {
- for (unsigned i = 0; i < q->nameCount(); ++i)
- addType(q->nameAt(i));
+ addType(q->base());
+ addType(q->name());
} else if (name->isNameId() || name->isTemplateNameId()) {
addType(name->identifier());
@@ -255,13 +255,13 @@ protected:
} // end of anonymous namespace
-CheckUndefinedSymbols::Future CheckUndefinedSymbols::go(Document::Ptr doc, const LookupContext &context)
+CheckSymbols::Future CheckSymbols::go(Document::Ptr doc, const LookupContext &context)
{
Q_ASSERT(doc);
- return (new CheckUndefinedSymbols(doc, context))->start();
+ return (new CheckSymbols(doc, context))->start();
}
-CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc, const LookupContext &context)
+CheckSymbols::CheckSymbols(Document::Ptr doc, const LookupContext &context)
: ASTVisitor(doc->translationUnit()), _doc(doc), _context(context)
{
_fileName = doc->fileName();
@@ -270,10 +270,10 @@ CheckUndefinedSymbols::CheckUndefinedSymbols(Document::Ptr doc, const LookupCont
_scopes = collectTypes.scopes();
}
-CheckUndefinedSymbols::~CheckUndefinedSymbols()
+CheckSymbols::~CheckSymbols()
{ }
-void CheckUndefinedSymbols::run()
+void CheckSymbols::run()
{
if (! isCanceled())
runFunctor();
@@ -281,7 +281,7 @@ void CheckUndefinedSymbols::run()
reportFinished();
}
-void CheckUndefinedSymbols::runFunctor()
+void CheckSymbols::runFunctor()
{
_diagnosticMessages.clear();
@@ -291,14 +291,14 @@ void CheckUndefinedSymbols::runFunctor()
}
}
-bool CheckUndefinedSymbols::warning(unsigned line, unsigned column, const QString &text, unsigned length)
+bool CheckSymbols::warning(unsigned line, unsigned column, const QString &text, unsigned length)
{
Document::DiagnosticMessage m(Document::DiagnosticMessage::Warning, _fileName, line, column, text, length);
_diagnosticMessages.append(m);
return false;
}
-bool CheckUndefinedSymbols::warning(AST *ast, const QString &text)
+bool CheckSymbols::warning(AST *ast, const QString &text)
{
const Token &firstToken = tokenAt(ast->firstToken());
const Token &lastToken = tokenAt(ast->lastToken() - 1);
@@ -311,7 +311,7 @@ bool CheckUndefinedSymbols::warning(AST *ast, const QString &text)
return false;
}
-bool CheckUndefinedSymbols::preVisit(AST *)
+bool CheckSymbols::preVisit(AST *)
{
if (isCanceled())
return false;
@@ -319,7 +319,7 @@ bool CheckUndefinedSymbols::preVisit(AST *)
return true;
}
-bool CheckUndefinedSymbols::visit(NamespaceAST *ast)
+bool CheckSymbols::visit(NamespaceAST *ast)
{
if (ast->identifier_token) {
const Token &tok = tokenAt(ast->identifier_token);
@@ -334,22 +334,22 @@ bool CheckUndefinedSymbols::visit(NamespaceAST *ast)
return true;
}
-bool CheckUndefinedSymbols::visit(UsingDirectiveAST *)
+bool CheckSymbols::visit(UsingDirectiveAST *)
{
return true;
}
-bool CheckUndefinedSymbols::visit(SimpleDeclarationAST *)
+bool CheckSymbols::visit(SimpleDeclarationAST *)
{
return true;
}
-bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *)
+bool CheckSymbols::visit(NamedTypeSpecifierAST *)
{
return true;
}
-void CheckUndefinedSymbols::checkNamespace(NameAST *name)
+void CheckSymbols::checkNamespace(NameAST *name)
{
if (! name)
return;
@@ -369,9 +369,9 @@ void CheckUndefinedSymbols::checkNamespace(NameAST *name)
warning(line, column, QCoreApplication::translate("CheckUndefinedSymbols", "Expected a namespace-name"), length);
}
-void CheckUndefinedSymbols::checkName(NameAST *ast)
+void CheckSymbols::checkName(NameAST *ast)
{
- if (ast->name) {
+ if (ast && ast->name) {
if (const Identifier *ident = ast->name->identifier()) {
const QByteArray id = QByteArray::fromRawData(ident->chars(), ident->size());
if (_potentialTypes.contains(id)) {
@@ -383,25 +383,25 @@ void CheckUndefinedSymbols::checkName(NameAST *ast)
}
}
-bool CheckUndefinedSymbols::visit(SimpleNameAST *ast)
+bool CheckSymbols::visit(SimpleNameAST *ast)
{
checkName(ast);
return true;
}
-bool CheckUndefinedSymbols::visit(TemplateIdAST *ast)
+bool CheckSymbols::visit(TemplateIdAST *ast)
{
checkName(ast);
return true;
}
-bool CheckUndefinedSymbols::visit(DestructorNameAST *ast)
+bool CheckSymbols::visit(DestructorNameAST *ast)
{
checkName(ast);
return true;
}
-bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast)
+bool CheckSymbols::visit(QualifiedNameAST *ast)
{
if (ast->name) {
Scope *scope = findScope(ast);
@@ -439,7 +439,7 @@ bool CheckUndefinedSymbols::visit(QualifiedNameAST *ast)
return false;
}
-bool CheckUndefinedSymbols::visit(TypenameTypeParameterAST *ast)
+bool CheckSymbols::visit(TypenameTypeParameterAST *ast)
{
if (ast->name && ast->name->name) {
if (const Identifier *templId = ast->name->name->identifier()) {
@@ -454,24 +454,24 @@ bool CheckUndefinedSymbols::visit(TypenameTypeParameterAST *ast)
return true;
}
-bool CheckUndefinedSymbols::visit(TemplateTypeParameterAST *ast)
+bool CheckSymbols::visit(TemplateTypeParameterAST *ast)
{
checkName(ast->name);
return true;
}
-bool CheckUndefinedSymbols::visit(TemplateDeclarationAST *ast)
+bool CheckSymbols::visit(TemplateDeclarationAST *ast)
{
_templateDeclarationStack.append(ast);
return true;
}
-void CheckUndefinedSymbols::endVisit(TemplateDeclarationAST *)
+void CheckSymbols::endVisit(TemplateDeclarationAST *)
{
_templateDeclarationStack.takeFirst();
}
-void CheckUndefinedSymbols::addTypeUsage(const Use &use)
+void CheckSymbols::addTypeUsage(const Use &use)
{
_typeUsages.append(use);
@@ -479,7 +479,7 @@ void CheckUndefinedSymbols::addTypeUsage(const Use &use)
flush();
}
-void CheckUndefinedSymbols::addTypeUsage(ClassOrNamespace *b, NameAST *ast)
+void CheckSymbols::addTypeUsage(ClassOrNamespace *b, NameAST *ast)
{
if (! b)
return;
@@ -500,7 +500,7 @@ void CheckUndefinedSymbols::addTypeUsage(ClassOrNamespace *b, NameAST *ast)
//qDebug() << "added use" << oo(ast->name) << line << column << length;
}
-void CheckUndefinedSymbols::addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast)
+void CheckSymbols::addTypeUsage(const QList<Symbol *> &candidates, NameAST *ast)
{
unsigned startToken = ast->firstToken();
if (DestructorNameAST *dtor = ast->asDestructorName())
@@ -530,7 +530,7 @@ void CheckUndefinedSymbols::addTypeUsage(const QList<Symbol *> &candidates, Name
}
}
-unsigned CheckUndefinedSymbols::startOfTemplateDeclaration(TemplateDeclarationAST *ast) const
+unsigned CheckSymbols::startOfTemplateDeclaration(TemplateDeclarationAST *ast) const
{
if (ast->declaration) {
if (TemplateDeclarationAST *templ = ast->declaration->asTemplateDeclaration())
@@ -542,7 +542,7 @@ unsigned CheckUndefinedSymbols::startOfTemplateDeclaration(TemplateDeclarationAS
return ast->firstToken();
}
-Scope *CheckUndefinedSymbols::findScope(AST *ast) const
+Scope *CheckSymbols::findScope(AST *ast) const
{
Scope *scope = 0;
@@ -561,7 +561,7 @@ Scope *CheckUndefinedSymbols::findScope(AST *ast) const
return scope;
}
-void CheckUndefinedSymbols::flush()
+void CheckSymbols::flush()
{
if (_typeUsages.isEmpty())
return;
diff --git a/src/plugins/cppeditor/cppcheckundefinedsymbols.h b/src/plugins/cppeditor/cppchecksymbols.h
index 4dbc0b52f3..1e5d74c1b3 100644
--- a/src/plugins/cppeditor/cppcheckundefinedsymbols.h
+++ b/src/plugins/cppeditor/cppchecksymbols.h
@@ -27,8 +27,8 @@
**
**************************************************************************/
-#ifndef CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H
-#define CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H
+#ifndef CPLUSPLUS_CHECKSYMBOLS_H
+#define CPLUSPLUS_CHECKSYMBOLS_H
#include "cppsemanticinfo.h"
@@ -41,12 +41,12 @@
namespace CPlusPlus {
-class CheckUndefinedSymbols:
+class CheckSymbols:
protected ASTVisitor,
public QtConcurrent::RunFunctionTaskBase<CppEditor::Internal::SemanticInfo::Use>
{
public:
- virtual ~CheckUndefinedSymbols();
+ virtual ~CheckSymbols();
typedef CppEditor::Internal::SemanticInfo::Use Use;
@@ -56,11 +56,27 @@ public:
typedef QFuture<Use> Future;
static Future go(Document::Ptr doc, const LookupContext &context);
+ static QMap<int, QVector<Use> > chunks(const QFuture<Use> &future, int from, int to)
+ {
+ QMap<int, QVector<Use> > chunks;
+
+ for (int i = from; i < to; ++i) {
+ const Use use = future.resultAt(i);
+ if (! use.line)
+ continue; // skip it, it's an invalid use.
+
+ const int blockNumber = use.line - 1;
+ chunks[blockNumber].append(use);
+ }
+
+ return chunks;
+ }
+
protected:
using ASTVisitor::visit;
using ASTVisitor::endVisit;
- CheckUndefinedSymbols(Document::Ptr doc, const LookupContext &context);
+ CheckSymbols(Document::Ptr doc, const LookupContext &context);
bool warning(unsigned line, unsigned column, const QString &text, unsigned length = 0);
bool warning(AST *ast, const QString &text);
@@ -107,4 +123,4 @@ private:
} // end of namespace CPlusPlus
-#endif // CPLUSPLUS_CHECKUNDEFINEDSYMBOLS_H
+#endif // CPLUSPLUS_CHECKSYMBOLS_H
diff --git a/src/plugins/cppeditor/cppdeclfromdef.cpp b/src/plugins/cppeditor/cppdeclfromdef.cpp
new file mode 100644
index 0000000000..23a11d7a50
--- /dev/null
+++ b/src/plugins/cppeditor/cppdeclfromdef.cpp
@@ -0,0 +1,250 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "cppdeclfromdef.h"
+
+#include <Literals.h> //### remove
+#include <QDebug> //###remove
+
+#include <AST.h>
+#include <ASTVisitor.h>
+#include <CoreTypes.h>
+#include <Names.h>
+#include <Symbols.h>
+#include <TranslationUnit.h>
+#include <cplusplus/ASTPath.h>
+#include <cplusplus/LookupContext.h>
+#include <cplusplus/Overview.h>
+
+#include <QtCore/QCoreApplication>
+
+using namespace CPlusPlus;
+using namespace CppEditor::Internal;
+using namespace CppTools;
+
+using CppEditor::CppRefactoringChanges;
+
+namespace {
+
+class InsertionPointFinder: public ASTVisitor
+{
+public:
+ InsertionPointFinder(Document::Ptr doc, const QString &className)
+ : ASTVisitor(doc->translationUnit())
+ , _doc(doc)
+ , _className(className)
+ {}
+
+ void operator()(int *line, int *column)
+ {
+ if (!line && !column)
+ return;
+ _line = 0;
+ _column = 0;
+
+ AST *ast = translationUnit()->ast();
+ accept(ast);
+
+ if (line)
+ *line = _line - 1;
+ if (column)
+ *column = _column - 1;
+ }
+
+protected:
+ using ASTVisitor::visit;
+
+ bool visit(ClassSpecifierAST *ast)
+ {
+ if (!ast->symbol || _className != QLatin1String(ast->symbol->identifier()->chars()))
+ return true;
+
+ unsigned currentVisibility = (tokenKind(ast->classkey_token) == T_CLASS) ? T_PUBLIC : T_PRIVATE;
+ unsigned insertBefore = 0;
+
+ for (DeclarationListAST *iter = ast->member_specifier_list; iter; iter = iter->next) {
+ DeclarationAST *decl = iter->value;
+ if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) {
+ const unsigned token = xsDecl->access_specifier_token;
+ const int kind = tokenKind(token);
+ if (kind == T_PUBLIC) {
+ currentVisibility = T_PUBLIC;
+ } else if (kind == T_PROTECTED) {
+ if (currentVisibility == T_PUBLIC) {
+ insertBefore = token;
+ break;
+ } else {
+ currentVisibility = T_PROTECTED;
+ }
+ } else if (kind == T_PRIVATE) {
+ if (currentVisibility == T_PUBLIC
+ || currentVisibility == T_PROTECTED) {
+ insertBefore = token;
+ break;
+ } else {
+ currentVisibility = T_PRIVATE;
+ }
+ }
+ }
+ }
+
+ if (!insertBefore)
+ insertBefore = ast->rbrace_token;
+
+ getTokenStartPosition(insertBefore, &_line, &_column);
+
+ return false;
+ }
+
+private:
+ Document::Ptr _doc;
+ QString _className;
+
+ unsigned _line;
+ unsigned _column;
+};
+
+QString prettyMinimalType(const FullySpecifiedType &ty,
+ const LookupContext &context,
+ Scope *source,
+ ClassOrNamespace *target)
+{
+ Overview oo;
+
+ if (const NamedType *namedTy = ty->asNamedType())
+ return oo.prettyTypeWithName(ty, context.minimalName(namedTy->name(),
+ source,
+ target),
+ context.control().data());
+ else
+ return oo(ty);
+}
+
+} // anonymous namespace
+
+DeclFromDef::DeclFromDef(TextEditor::BaseTextEditor *editor)
+ : CppQuickFixOperation(editor)
+{}
+
+QString DeclFromDef::description() const
+{
+ return QCoreApplication::tr("Create Declaration from Definition", "DeclFromDef");
+}
+
+int DeclFromDef::match(const QList<CPlusPlus::AST *> &path)
+{
+ m_targetFileName.clear();
+ m_targetSymbolName.clear();
+ m_decl.clear();
+
+ FunctionDefinitionAST *funDef = 0;
+ int idx = 0;
+ for (; idx < path.size(); ++idx) {
+ AST *node = path.at(idx);
+ if (FunctionDefinitionAST *candidate = node->asFunctionDefinition()) {
+ if (!funDef)
+ funDef = candidate;
+ } else if (node->asClassSpecifier()) {
+ return -1;
+ }
+ }
+
+ if (!funDef || !funDef->symbol)
+ return -1;
+
+ Function *method = funDef->symbol;
+ LookupContext context(document(), snapshot());
+
+ if (ClassOrNamespace *targetBinding = context.lookupParent(method)) {
+ foreach (Symbol *s, targetBinding->symbols()) {
+ if (Class *clazz = s->asClass()) {
+ m_targetFileName = QLatin1String(clazz->fileName());
+ m_targetSymbolName = QLatin1String(clazz->identifier()->chars());
+
+ m_decl = generateDeclaration(method, targetBinding);
+
+ return idx;
+ } // ### TODO: support insertion into namespaces
+ }
+ }
+
+ return -1;
+}
+
+void DeclFromDef::createChanges()
+{
+ CppRefactoringChanges *changes = refactoringChanges();
+
+ Document::Ptr targetDoc = changes->document(m_targetFileName);
+ InsertionPointFinder findInsertionPoint(targetDoc, m_targetSymbolName);
+ int line = 0, column = 0;
+ findInsertionPoint(&line, &column);
+
+ int targetPosition1 = changes->positionInFile(m_targetFileName, line, column);
+ int targetPosition2 = changes->positionInFile(m_targetFileName, line + 1, 0) - 1;
+
+ Utils::ChangeSet target;
+ target.insert(targetPosition1, m_decl);
+ changes->changeFile(m_targetFileName, target);
+
+ changes->reindent(m_targetFileName,
+ Utils::ChangeSet::Range(targetPosition1, targetPosition2));
+
+ changes->openEditor(m_targetFileName, line, column);
+}
+
+QString DeclFromDef::generateDeclaration(Function *method, ClassOrNamespace *targetBinding)
+{
+ LookupContext context(document(), snapshot());
+ Overview oo;
+ QString decl;
+
+ decl.append(prettyMinimalType(method->returnType(),
+ context,
+ method->scope(),
+ targetBinding));
+
+ decl.append(QLatin1Char(' '));
+ decl.append(QLatin1String(method->name()->identifier()->chars()));
+ decl.append(QLatin1Char('('));
+ for (unsigned argIdx = 0; argIdx < method->argumentCount(); ++argIdx) {
+ if (argIdx > 0)
+ decl.append(QLatin1String(", "));
+ Argument *arg = method->argumentAt(argIdx)->asArgument();
+ decl.append(prettyMinimalType(arg->type(),
+ context,
+ method->members(),
+ targetBinding));
+ decl.append(QLatin1Char(' '));
+ decl.append(oo(arg->name()));
+ }
+ decl.append(QLatin1String(");\n\n"));
+
+ return decl;
+}
diff --git a/src/plugins/cppeditor/cppdeclfromdef.h b/src/plugins/cppeditor/cppdeclfromdef.h
new file mode 100644
index 0000000000..8cbebc9271
--- /dev/null
+++ b/src/plugins/cppeditor/cppdeclfromdef.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef CPPDECLFROMDEF_H
+#define CPPDECLFROMDEF_H
+
+#include "cppquickfix.h"
+
+#include <CPlusPlusForwardDeclarations.h>
+
+namespace CPlusPlus {
+class ClassOrNamespace;
+} // namespace CPlusPlus
+
+namespace CppEditor {
+namespace Internal {
+
+class DeclFromDef: public CppQuickFixOperation
+{
+public:
+ DeclFromDef(TextEditor::BaseTextEditor *editor);
+
+ virtual int match(const QList<CPlusPlus::AST *> &path);
+ virtual QString description() const;
+ virtual void createChanges();
+
+protected:
+ virtual QString generateDeclaration(CPlusPlus::Function *method,
+ CPlusPlus::ClassOrNamespace *targetBinding);
+
+private:
+ QString m_targetFileName;
+ QString m_targetSymbolName;
+ QString m_decl;
+};
+
+
+} // namespace Internal
+} // namespace CppEditor
+
+#endif // CPPDECLFROMDEF_H
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index cc4238013a..adef080f35 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -31,10 +31,8 @@
#include "cppeditorconstants.h"
#include "cppplugin.h"
#include "cpphighlighter.h"
-#include "cppcheckundefinedsymbols.h"
-
+#include "cppchecksymbols.h"
#include "cppquickfix.h"
-#include <cpptools/cpptoolsplugin.h>
#include <AST.h>
#include <Control.h>
@@ -57,6 +55,7 @@
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/FastPreprocessor.h>
+#include <cpptools/cpptoolsplugin.h>
#include <cpptools/cppmodelmanagerinterface.h>
#include <cpptools/cpptoolsconstants.h>
#include <cpptools/cppcodeformatter.h>
@@ -97,7 +96,7 @@
#include <sstream>
enum {
- UPDATE_METHOD_BOX_INTERVAL = 150,
+ UPDATE_OUTLINE_INTERVAL = 150,
UPDATE_USES_INTERVAL = 300
};
@@ -129,47 +128,6 @@ static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document
return selections;
}
-static QList<QTextEdit::ExtraSelection> createSelections(QTextDocument *document,
- const QList<SemanticInfo::Use> &msgs,
- const QTextCharFormat &format)
-{
- QList<QTextEdit::ExtraSelection> selections;
-
- QTextBlock currentBlock = document->firstBlock();
- unsigned currentLine = 1;
-
- foreach (const SemanticInfo::Use &use, msgs) {
- QTextCursor cursor(document);
-
- if (currentLine != use.line) {
- int delta = use.line - currentLine;
-
- if (delta >= 0) {
- while (delta--)
- currentBlock = currentBlock.next();
- } else {
- currentBlock = document->findBlockByNumber(use.line - 1);
- }
-
- currentLine = use.line;
- }
-
- const int pos = currentBlock.position() + use.column - 1;
- if (pos < 0)
- continue;
-
- cursor.setPosition(pos);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, use.length);
-
- QTextEdit::ExtraSelection sel;
- sel.cursor = cursor;
- sel.format = format;
- selections.append(sel);
- }
-
- return selections;
-}
-
namespace {
class OverviewTreeView : public QTreeView
@@ -189,6 +147,32 @@ public:
}
};
+class OverviewProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ OverviewProxyModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
+ QSortFilterProxyModel(parent),
+ m_sourceModel(sourceModel)
+ {
+ setSourceModel(m_sourceModel);
+ }
+
+ bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
+ {
+ // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
+ const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
+ CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
+ if (symbol && symbol->isGenerated())
+ return false;
+
+ return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+ }
+private:
+ CPlusPlus::OverviewModel *m_sourceModel;
+};
+
+
class FindLocalUses: protected ASTVisitor
{
Scope *_functionScope;
@@ -509,7 +493,7 @@ protected:
{
const Name *name = function->name();
if (const QualifiedNameId *q = name->asQualifiedNameId())
- name = q->unqualifiedNameId();
+ name = q->name();
if (_declarationName->isEqualTo(name))
_functions->append(function);
@@ -617,8 +601,10 @@ CPPEditor::CPPEditor(QWidget *parent)
this, SLOT(onDocumentUpdated(CPlusPlus::Document::Ptr)));
}
- m_highlighteRevision = 0;
- connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(highlightTypeUsages()));
+ m_highlightRevision = 0;
+ m_nextHighlightBlockNumber = 0;
+ connect(&m_highlightWatcher, SIGNAL(resultsReadyAt(int,int)), SLOT(highlightTypeUsages(int,int)));
+ connect(&m_highlightWatcher, SIGNAL(finished()), SLOT(finishTypeUsages()));
}
CPPEditor::~CPPEditor()
@@ -638,51 +624,55 @@ TextEditor::BaseTextEditorEditable *CPPEditor::createEditableInterface()
void CPPEditor::createToolBar(CPPEditorEditable *editable)
{
- m_methodCombo = new QComboBox;
- m_methodCombo->setMinimumContentsLength(22);
+ m_outlineCombo = new QComboBox;
+ m_outlineCombo->setMinimumContentsLength(22);
// Make the combo box prefer to expand
- QSizePolicy policy = m_methodCombo->sizePolicy();
+ QSizePolicy policy = m_outlineCombo->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Expanding);
- m_methodCombo->setSizePolicy(policy);
-
- QTreeView *methodView = new OverviewTreeView;
- methodView->header()->hide();
- methodView->setItemsExpandable(false);
- m_methodCombo->setView(methodView);
- m_methodCombo->setMaxVisibleItems(20);
-
- m_overviewModel = new OverviewModel(this);
- m_proxyModel = new QSortFilterProxyModel(this);
- m_proxyModel->setSourceModel(m_overviewModel);
- if (CppPlugin::instance()->sortedMethodOverview())
+ m_outlineCombo->setSizePolicy(policy);
+
+ QTreeView *outlineView = new OverviewTreeView;
+ outlineView->header()->hide();
+ outlineView->setItemsExpandable(false);
+ m_outlineCombo->setView(outlineView);
+ m_outlineCombo->setMaxVisibleItems(20);
+
+ m_outlineModel = new OverviewModel(this);
+ m_proxyModel = new OverviewProxyModel(m_outlineModel, this);
+ if (CppPlugin::instance()->sortedOutline())
m_proxyModel->sort(0, Qt::AscendingOrder);
else
- m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedMethodOverview()
+ m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline()
m_proxyModel->setDynamicSortFilter(true);
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
- m_methodCombo->setModel(m_proxyModel);
+ m_outlineCombo->setModel(m_proxyModel);
- m_methodCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
- m_sortAction = new QAction(tr("Sort alphabetically"), m_methodCombo);
+ m_outlineCombo->setContextMenuPolicy(Qt::ActionsContextMenu);
+ m_sortAction = new QAction(tr("Sort Alphabetically"), m_outlineCombo);
m_sortAction->setCheckable(true);
- m_sortAction->setChecked(sortedMethodOverview());
- connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedMethodOverview(bool)));
- m_methodCombo->addAction(m_sortAction);
+ m_sortAction->setChecked(sortedOutline());
+ connect(m_sortAction, SIGNAL(toggled(bool)), CppPlugin::instance(), SLOT(setSortedOutline(bool)));
+ m_outlineCombo->addAction(m_sortAction);
+
+ m_updateOutlineTimer = new QTimer(this);
+ m_updateOutlineTimer->setSingleShot(true);
+ m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
+ connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
- m_updateMethodBoxTimer = new QTimer(this);
- m_updateMethodBoxTimer->setSingleShot(true);
- m_updateMethodBoxTimer->setInterval(UPDATE_METHOD_BOX_INTERVAL);
- connect(m_updateMethodBoxTimer, SIGNAL(timeout()), this, SLOT(updateMethodBoxIndexNow()));
+ m_updateOutlineIndexTimer = new QTimer(this);
+ m_updateOutlineIndexTimer->setSingleShot(true);
+ m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
+ connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
m_updateUsesTimer = new QTimer(this);
m_updateUsesTimer->setSingleShot(true);
m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL);
connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow()));
- connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
- connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
- connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
+ connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
+ connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex()));
+ connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip()));
connect(document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(onContentsChanged(int,int,int)));
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
@@ -698,7 +688,7 @@ void CPPEditor::createToolBar(CPPEditorEditable *editable)
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
QList<QAction*> actions = toolBar->actions();
QWidget *w = toolBar->widgetForAction(actions.first());
- static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_methodCombo, 1);
+ static_cast<QHBoxLayout*>(w->layout())->insertWidget(0, m_outlineCombo, 1);
}
void CPPEditor::paste()
@@ -806,10 +796,7 @@ void CPPEditor::onDocumentUpdated(Document::Ptr doc)
m_semanticHighlighter->rehighlight(source);
}
- m_overviewModel->rebuild(doc);
- OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_methodCombo->view());
- treeView->sync();
- updateMethodBoxIndexNow();
+ m_updateOutlineTimer->start();
}
const Macro *CPPEditor::findCanonicalMacro(const QTextCursor &cursor, Document::Ptr doc) const
@@ -985,19 +972,19 @@ void CPPEditor::onContentsChanged(int position, int charsRemoved, int charsAdded
void CPPEditor::updateFileName()
{ }
-void CPPEditor::jumpToMethod(int)
+void CPPEditor::jumpToOutlineElement(int)
{
- QModelIndex index = m_proxyModel->mapToSource(m_methodCombo->view()->currentIndex());
- Symbol *symbol = m_overviewModel->symbolFromIndex(index);
+ QModelIndex index = m_proxyModel->mapToSource(m_outlineCombo->view()->currentIndex());
+ Symbol *symbol = m_outlineModel->symbolFromIndex(index);
if (! symbol)
return;
openCppEditorAt(linkToSymbol(symbol));
}
-void CPPEditor::setSortedMethodOverview(bool sort)
+void CPPEditor::setSortedOutline(bool sort)
{
- if (sort != sortedMethodOverview()) {
+ if (sort != sortedOutline()) {
if (sort)
m_proxyModel->sort(0, Qt::AscendingOrder);
else
@@ -1005,18 +992,38 @@ void CPPEditor::setSortedMethodOverview(bool sort)
bool block = m_sortAction->blockSignals(true);
m_sortAction->setChecked(m_proxyModel->sortColumn() == 0);
m_sortAction->blockSignals(block);
- updateMethodBoxIndexNow();
+ updateOutlineIndexNow();
}
}
-bool CPPEditor::sortedMethodOverview() const
+bool CPPEditor::sortedOutline() const
{
return (m_proxyModel->sortColumn() == 0);
}
-void CPPEditor::updateMethodBoxIndex()
+void CPPEditor::updateOutlineNow()
+{
+ const Snapshot snapshot = m_modelManager->snapshot();
+ Document::Ptr document = snapshot.document(file()->fileName());
+
+ if (!document)
+ return;
+
+ if (document->editorRevision() != editorRevision()) {
+ m_updateOutlineTimer->start();
+ return;
+ }
+
+ m_outlineModel->rebuild(document);
+
+ OverviewTreeView *treeView = static_cast<OverviewTreeView *>(m_outlineCombo->view());
+ treeView->sync();
+ updateOutlineIndexNow();
+}
+
+void CPPEditor::updateOutlineIndex()
{
- m_updateMethodBoxTimer->start();
+ m_updateOutlineIndexTimer->start();
}
void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
@@ -1055,49 +1062,46 @@ void CPPEditor::highlightUses(const QList<SemanticInfo::Use> &uses,
}
}
-void CPPEditor::updateMethodBoxIndexNow()
+void CPPEditor::updateOutlineIndexNow()
{
- if (! m_overviewModel->document())
+ if (!m_outlineModel->document())
return;
- if (m_overviewModel->document()->editorRevision() != editorRevision()) {
- m_updateMethodBoxTimer->start();
+ if (m_outlineModel->document()->editorRevision() != editorRevision()) {
+ m_updateOutlineIndexTimer->start();
return;
}
- m_updateMethodBoxTimer->stop();
+ m_updateOutlineIndexTimer->stop();
- int line = 0, column = 0;
- convertPosition(position(), &line, &column);
+ m_outlineModelIndex = QModelIndex(); //invalidate
+ QModelIndex comboIndex = outlineModelIndex();
- QModelIndex lastIndex;
- const int rc = m_overviewModel->rowCount();
- for (int row = 0; row < rc; ++row) {
- const QModelIndex index = m_overviewModel->index(row, 0, QModelIndex());
- Symbol *symbol = m_overviewModel->symbolFromIndex(index);
- if (symbol && symbol->line() > unsigned(line))
- break;
- lastIndex = index;
- }
+ if (comboIndex.isValid()) {
+ bool blocked = m_outlineCombo->blockSignals(true);
+
+ // There is no direct way to select a non-root item
+ m_outlineCombo->setRootModelIndex(m_proxyModel->mapFromSource(comboIndex.parent()));
+ m_outlineCombo->setCurrentIndex(m_proxyModel->mapFromSource(comboIndex).row());
+ m_outlineCombo->setRootModelIndex(QModelIndex());
- if (lastIndex.isValid()) {
- bool blocked = m_methodCombo->blockSignals(true);
- m_methodCombo->setCurrentIndex(m_proxyModel->mapFromSource(lastIndex).row());
- updateMethodBoxToolTip();
- (void) m_methodCombo->blockSignals(blocked);
+ updateOutlineToolTip();
+
+ m_outlineCombo->blockSignals(blocked);
}
}
-void CPPEditor::updateMethodBoxToolTip()
+void CPPEditor::updateOutlineToolTip()
{
- m_methodCombo->setToolTip(m_methodCombo->currentText());
+ m_outlineCombo->setToolTip(m_outlineCombo->currentText());
}
void CPPEditor::updateUses()
{
+ if (editorRevision() != m_highlightRevision)
+ m_highlighter.cancel();
m_updateUsesTimer->start();
- m_highlighter.cancel();
}
void CPPEditor::updateUsesNow()
@@ -1108,18 +1112,76 @@ void CPPEditor::updateUsesNow()
semanticRehighlight();
}
-void CPPEditor::highlightTypeUsages()
+void CPPEditor::highlightTypeUsages(int from, int to)
{
- if (editorRevision() != m_highlighteRevision)
+ if (editorRevision() != m_highlightRevision)
return; // outdated
else if (m_highlighter.isCanceled())
return; // aborted
- const QList<SemanticInfo::Use> typeUsages = m_highlighter.results();
- setExtraSelections(TypeSelection, createSelections(document(), typeUsages, m_typeFormat));
+ CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
+ Q_ASSERT(highlighter);
+ QTextDocument *doc = document();
+
+ if (m_nextHighlightBlockNumber >= doc->blockCount())
+ return;
+
+ QMap<int, QVector<SemanticInfo::Use> > chunks = CheckSymbols::chunks(m_highlighter, from, to);
+ Q_ASSERT(!chunks.isEmpty());
+ QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
+
+ QMapIterator<int, QVector<SemanticInfo::Use> > it(chunks);
+ while (b.isValid() && it.hasNext()) {
+ it.next();
+ const int blockNumber = it.key();
+ Q_ASSERT(blockNumber < doc->blockCount());
+
+ while (m_nextHighlightBlockNumber < blockNumber) {
+ highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+ b = b.next();
+ ++m_nextHighlightBlockNumber;
+ }
+
+ QList<QTextLayout::FormatRange> formats;
+ foreach (const SemanticInfo::Use &use, it.value()) {
+ QTextLayout::FormatRange formatRange;
+ formatRange.format = m_typeFormat;
+ formatRange.start = use.column - 1;
+ formatRange.length = use.length;
+ formats.append(formatRange);
+ }
+ highlighter->setExtraAdditionalFormats(b, formats);
+ b = b.next();
+ ++m_nextHighlightBlockNumber;
+ }
}
+void CPPEditor::finishTypeUsages()
+{
+ if (editorRevision() != m_highlightRevision)
+ return; // outdated
+
+ else if (m_highlighter.isCanceled())
+ return; // aborted
+
+ CppHighlighter *highlighter = qobject_cast<CppHighlighter*>(baseTextDocument()->syntaxHighlighter());
+ Q_ASSERT(highlighter);
+ QTextDocument *doc = document();
+
+ if (m_nextHighlightBlockNumber >= doc->blockCount())
+ return;
+
+ QTextBlock b = doc->findBlockByNumber(m_nextHighlightBlockNumber);
+
+ while (b.isValid()) {
+ highlighter->setExtraAdditionalFormats(b, QList<QTextLayout::FormatRange>());
+ b = b.next();
+ ++m_nextHighlightBlockNumber;
+ }
+}
+
+
void CPPEditor::switchDeclarationDefinition()
{
if (! m_modelManager)
@@ -1417,6 +1479,23 @@ SemanticInfo CPPEditor::semanticInfo() const
return m_lastSemanticInfo;
}
+CPlusPlus::OverviewModel *CPPEditor::outlineModel() const
+{
+ return m_outlineModel;
+}
+
+QModelIndex CPPEditor::outlineModelIndex()
+{
+ if (!m_outlineModelIndex.isValid()) {
+ int line = 0, column = 0;
+ convertPosition(position(), &line, &column);
+ m_outlineModelIndex = indexForPosition(line, column);
+ emit outlineModelIndexChanged(m_outlineModelIndex);
+ }
+
+ return m_outlineModelIndex;
+}
+
bool CPPEditor::isElectricCharacter(QChar ch) const
{
if (ch == QLatin1Char('{') ||
@@ -1897,9 +1976,10 @@ void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
if (semanticInfo.doc) {
LookupContext context(semanticInfo.doc, semanticInfo.snapshot);
- CheckUndefinedSymbols::Future f = CheckUndefinedSymbols::go(semanticInfo.doc, context);
+ CheckSymbols::Future f = CheckSymbols::go(semanticInfo.doc, context);
m_highlighter = f;
- m_highlighteRevision = semanticInfo.revision;
+ m_highlightRevision = semanticInfo.revision;
+ m_nextHighlightBlockNumber = 0;
m_highlightWatcher.setFuture(m_highlighter);
}
@@ -1910,6 +1990,7 @@ void CPPEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
#endif
}
+
setExtraSelections(UnusedSymbolSelection, unusedSelections);
if (! m_renameSelections.isEmpty())
@@ -2161,3 +2242,26 @@ SemanticInfo SemanticHighlighter::semanticInfo(const Source &source)
return semanticInfo;
}
+
+QModelIndex CPPEditor::indexForPosition(int line, int column, const QModelIndex &rootIndex) const
+{
+ QModelIndex lastIndex = rootIndex;
+
+ const int rowCount = m_outlineModel->rowCount(rootIndex);
+ for (int row = 0; row < rowCount; ++row) {
+ const QModelIndex index = m_outlineModel->index(row, 0, rootIndex);
+ Symbol *symbol = m_outlineModel->symbolFromIndex(index);
+ if (symbol && symbol->line() > unsigned(line))
+ break;
+ lastIndex = index;
+ }
+
+ if (lastIndex != rootIndex) {
+ // recurse
+ lastIndex = indexForPosition(line, column, lastIndex);
+ }
+
+ return lastIndex;
+}
+
+#include "cppeditor.moc"
diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h
index 488bcfd9e2..738a977586 100644
--- a/src/plugins/cppeditor/cppeditor.h
+++ b/src/plugins/cppeditor/cppeditor.h
@@ -42,6 +42,7 @@
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtCore/QFutureWatcher>
+#include <QtCore/QModelIndex>
QT_BEGIN_NAMESPACE
class QComboBox;
@@ -166,6 +167,9 @@ public:
bool isOutdated() const;
SemanticInfo semanticInfo() const;
+ CPlusPlus::OverviewModel *outlineModel() const;
+ QModelIndex outlineModelIndex();
+
virtual void paste(); // reimplemented from BaseTextEditor
virtual void cut(); // reimplemented from BaseTextEditor
@@ -176,10 +180,13 @@ public:
void setObjCEnabled(bool onoff);
bool isObjCEnabled() const;
+Q_SIGNALS:
+ void outlineModelIndexChanged(const QModelIndex &index);
+
public Q_SLOTS:
virtual void setFontSettings(const TextEditor::FontSettings &);
virtual void setTabSettings(const TextEditor::TabSettings &);
- void setSortedMethodOverview(bool sort);
+ void setSortedOutline(bool sort);
void switchDeclarationDefinition();
void jumpToDefinition();
void renameSymbolUnderCursor();
@@ -214,10 +221,11 @@ protected:
private Q_SLOTS:
void updateFileName();
- void jumpToMethod(int index);
- void updateMethodBoxIndex();
- void updateMethodBoxIndexNow();
- void updateMethodBoxToolTip();
+ void jumpToOutlineElement(int index);
+ void updateOutlineNow();
+ void updateOutlineIndex();
+ void updateOutlineIndexNow();
+ void updateOutlineToolTip();
void updateUses();
void updateUsesNow();
void onDocumentUpdated(CPlusPlus::Document::Ptr doc);
@@ -225,7 +233,8 @@ private Q_SLOTS:
void semanticRehighlight();
void updateSemanticInfo(const CppEditor::Internal::SemanticInfo &semanticInfo);
- void highlightTypeUsages();
+ void highlightTypeUsages(int from, int to);
+ void finishTypeUsages();
void performQuickFix(int index);
@@ -234,7 +243,7 @@ private:
void setShowWarningMessage(bool showWarningMessage);
void markSymbols(CPlusPlus::Symbol *canonicalSymbol, const SemanticInfo &info);
- bool sortedMethodOverview() const;
+ bool sortedOutline() const;
CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol, const CPlusPlus::Snapshot &snapshot);
virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
virtual void indent(QTextDocument *doc, const QTextCursor &cursor, QChar typedChar);
@@ -258,15 +267,19 @@ private:
bool openLink(const Link &link) { return openCppEditorAt(link); }
bool openCppEditorAt(const Link &);
+ QModelIndex indexForPosition(int line, int column, const QModelIndex &rootIndex = QModelIndex()) const;
+
static Link linkToSymbol(CPlusPlus::Symbol *symbol);
CppTools::CppModelManagerInterface *m_modelManager;
- QComboBox *m_methodCombo;
- CPlusPlus::OverviewModel *m_overviewModel;
+ QComboBox *m_outlineCombo;
+ CPlusPlus::OverviewModel *m_outlineModel;
+ QModelIndex m_outlineModelIndex;
QSortFilterProxyModel *m_proxyModel;
QAction *m_sortAction;
- QTimer *m_updateMethodBoxTimer;
+ QTimer *m_updateOutlineTimer;
+ QTimer *m_updateOutlineIndexTimer;
QTimer *m_updateUsesTimer;
QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrencesUnusedFormat;
@@ -288,7 +301,8 @@ private:
QFuture<SemanticInfo::Use> m_highlighter;
QFutureWatcher<SemanticInfo::Use> m_highlightWatcher;
- unsigned m_highlighteRevision; // the editor revision that requested the highlight
+ unsigned m_highlightRevision; // the editor revision that requested the highlight
+ int m_nextHighlightBlockNumber;
};
diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro
index 49306644a2..4b017f64fe 100644
--- a/src/plugins/cppeditor/cppeditor.pro
+++ b/src/plugins/cppeditor/cppeditor.pro
@@ -17,9 +17,10 @@ HEADERS += cppplugin.h \
cppclasswizard.h \
cppquickfix.h \
cpprefactoringchanges.h \
- cppcheckundefinedsymbols.h \
+ cppchecksymbols.h \
cppsemanticinfo.h \
- cppoutline.h
+ cppoutline.h \
+ cppdeclfromdef.h
SOURCES += cppplugin.cpp \
cppeditor.cpp \
@@ -29,9 +30,10 @@ SOURCES += cppplugin.cpp \
cppclasswizard.cpp \
cppquickfix.cpp \
cpprefactoringchanges.cpp \
- cppcheckundefinedsymbols.cpp \
+ cppchecksymbols.cpp \
cppsemanticinfo.cpp \
- cppoutline.cpp
+ cppoutline.cpp \
+ cppdeclfromdef.cpp
RESOURCES += cppeditor.qrc
diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp
index 321025058e..c98c35dcdc 100644
--- a/src/plugins/cppeditor/cpphighlighter.cpp
+++ b/src/plugins/cppeditor/cpphighlighter.cpp
@@ -42,7 +42,7 @@ using namespace TextEditor;
using namespace CPlusPlus;
CppHighlighter::CppHighlighter(QTextDocument *document) :
- QSyntaxHighlighter(document)
+ TextEditor::SyntaxHighlighter(document)
{
}
diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h
index 9d7c0496fc..d8fe769371 100644
--- a/src/plugins/cppeditor/cpphighlighter.h
+++ b/src/plugins/cppeditor/cpphighlighter.h
@@ -31,16 +31,17 @@
#define CPPHIGHLIGHTER_H
#include "cppeditorenums.h"
-#include <QtGui/QSyntaxHighlighter>
+#include <texteditor/syntaxhighlighter.h>
#include <QtGui/QTextCharFormat>
#include <QtCore/QtAlgorithms>
namespace CppEditor {
+
namespace Internal {
class CPPEditor;
-class CppHighlighter : public QSyntaxHighlighter
+class CppHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp
index 8115032b1e..5c84e86841 100644
--- a/src/plugins/cppeditor/cpphoverhandler.cpp
+++ b/src/plugins/cppeditor/cpphoverhandler.cpp
@@ -29,7 +29,6 @@
#include "cpphoverhandler.h"
#include "cppeditor.h"
-#include "cppplugin.h"
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
@@ -39,48 +38,69 @@
#include <extensionsystem/pluginmanager.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
+#include <texteditor/displaysettings.h>
#include <debugger/debuggerconstants.h>
+#include <utils/htmldocextractor.h>
-#include <CoreTypes.h>
#include <FullySpecifiedType.h>
-#include <Literals.h>
-#include <Control.h>
-#include <Names.h>
#include <Scope.h>
#include <Symbol.h>
#include <Symbols.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/Overview.h>
#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/SimpleLexer.h>
+#include <cplusplus/LookupContext.h>
+#include <cplusplus/LookupItem.h>
-#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
-#include <QtCore/QSettings>
#include <QtGui/QToolTip>
#include <QtGui/QTextCursor>
-#include <QtGui/QTextBlock>
using namespace CppEditor::Internal;
using namespace CPlusPlus;
using namespace Core;
+namespace {
+ QString removeQualificationIfAny(const QString &name) {
+ int index = name.lastIndexOf(QLatin1Char(':'));
+ if (index == -1)
+ return name;
+ else
+ return name.right(name.length() - index - 1);
+ }
+
+ void moveCursorToEndOfQualifiedName(QTextCursor *tc) {
+ QTextDocument *doc = tc->document();
+ if (!doc)
+ return;
+
+ while (true) {
+ const QChar &ch = doc->characterAt(tc->position());
+ if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
+ tc->movePosition(QTextCursor::NextCharacter);
+ else if (ch == QLatin1Char(':') &&
+ doc->characterAt(tc->position() + 1) == QLatin1Char(':'))
+ tc->movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, 2);
+ else
+ break;
+ }
+ }
+}
+
CppHoverHandler::CppHoverHandler(QObject *parent)
- : QObject(parent)
+ : QObject(parent), m_modelManager(0), m_matchingHelpCandidate(-1)
{
- m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
+ m_modelManager =
+ ExtensionSystem::PluginManager::instance()->getObject<CppTools::CppModelManagerInterface>();
+
+ m_htmlDocExtractor.setLengthReference(1000, true);
// Listen for editor opened events in order to connect to tooltip/helpid requests
connect(ICore::instance()->editorManager(), SIGNAL(editorOpened(Core::IEditor *)),
this, SLOT(editorOpened(Core::IEditor *)));
}
-void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos)
-{
- updateHelpIdAndTooltip(editor, pos);
-}
-
void CppHoverHandler::editorOpened(IEditor *editor)
{
CPPEditorEditable *cppEditor = qobject_cast<CPPEditorEditable *>(editor);
@@ -94,22 +114,59 @@ void CppHoverHandler::editorOpened(IEditor *editor)
this, SLOT(updateContextHelpId(TextEditor::ITextEditor*, int)));
}
+void CppHoverHandler::updateContextHelpId(TextEditor::ITextEditor *editor, int pos)
+{
+ if (!editor)
+ return;
+
+ // If the tooltip is visible and there is a help match, this match is used to update the help
+ // id. Otherwise, the identification process happens.
+ if (!QToolTip::isVisible() || m_matchingHelpCandidate == -1)
+ identifyMatch(editor, pos);
+
+ if (m_matchingHelpCandidate != -1)
+ editor->setContextHelpId(m_helpCandidates.at(m_matchingHelpCandidate).m_helpId);
+ else
+ editor->setContextHelpId(QString());
+}
+
void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos)
{
if (!editor)
return;
- ICore *core = ICore::instance();
- const int dbgcontext = core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
+ editor->setContextHelpId(QString());
- if (core->hasContext(dbgcontext))
+ ICore *core = ICore::instance();
+ const int dbgContext =
+ core->uniqueIDManager()->uniqueIdentifier(Debugger::Constants::C_DEBUGMODE);
+ if (core->hasContext(dbgContext))
return;
- updateHelpIdAndTooltip(editor, pos);
+ identifyMatch(editor, pos);
- if (m_toolTip.isEmpty())
+ if (m_toolTip.isEmpty()) {
QToolTip::hideText();
- else {
+ } else {
+ if (m_matchingHelpCandidate != -1) {
+ QString contents;
+ TextEditor::BaseTextEditor *baseEditor = baseTextEditor(editor);
+ if (baseEditor && baseEditor->displaySettings().m_integrateDocsIntoTooltips)
+ contents = getDocContents();
+ if (!contents.isEmpty()) {
+ m_toolTip = contents;
+ } else {
+ m_toolTip = Qt::escape(m_toolTip);
+ m_toolTip.prepend(QLatin1String("<nobr>"));
+ m_toolTip.append(QLatin1String("</nobr>"));
+ }
+
+ m_toolTip = QString(QLatin1String("<table><tr>"
+ "<td valign=middle>%1</td>"
+ "<td><img src=\":/cppeditor/images/f1.png\"></td>"
+ "</tr></table>")).arg(m_toolTip);
+ }
+
const QPoint pnt = point - QPoint(0,
#ifdef Q_WS_WIN
24
@@ -122,210 +179,241 @@ void CppHoverHandler::showToolTip(TextEditor::ITextEditor *editor, const QPoint
}
}
-static QString buildHelpId(Symbol *symbol, const Name *declarationName)
+void CppHoverHandler::identifyMatch(TextEditor::ITextEditor *editor, int pos)
{
- Scope *scope = 0;
+ resetMatchings();
- if (symbol) {
- scope = symbol->scope();
- declarationName = symbol->name();
- }
+ if (!m_modelManager)
+ return;
- if (! declarationName)
- return QString();
+ const Snapshot &snapshot = m_modelManager->snapshot();
+ Document::Ptr doc = snapshot.document(editor->file()->fileName());
+ if (!doc)
+ return;
- Overview overview;
- overview.setShowArgumentNames(false);
- overview.setShowReturnTypes(false);
+ int line = 0;
+ int column = 0;
+ editor->convertPosition(pos, &line, &column);
- QStringList qualifiedNames;
- qualifiedNames.prepend(overview.prettyName(declarationName));
+ if (!matchDiagnosticMessage(doc, line) &&
+ !matchIncludeFile(doc, line) &&
+ !matchMacroInUse(doc, pos)) {
- for (; scope; scope = scope->enclosingScope()) {
- Symbol *owner = scope->owner();
+ TextEditor::BaseTextEditor *baseEditor = baseTextEditor(editor);
+ if (!baseEditor)
+ return;
- if (owner && owner->name() && ! scope->isEnumScope()) {
- const Name *name = owner->name();
- const Identifier *id = 0;
+ bool extraSelectionTooltip = false;
+ if (!baseEditor->extraSelectionTooltip(pos).isEmpty()) {
+ m_toolTip = baseEditor->extraSelectionTooltip(pos);
+ extraSelectionTooltip = true;
+ }
- if (const NameId *nameId = name->asNameId())
- id = nameId->identifier();
+ QTextCursor tc(baseEditor->document());
+ tc.setPosition(pos);
+ moveCursorToEndOfQualifiedName(&tc);
- else if (const TemplateNameId *nameId = name->asTemplateNameId())
- id = nameId->identifier();
+ // Fetch the expression's code
+ ExpressionUnderCursor expressionUnderCursor;
+ const QString &expression = expressionUnderCursor(tc);
+ Scope *scope = doc->scopeAt(line, column);
- if (id)
- qualifiedNames.prepend(QString::fromLatin1(id->chars(), id->size()));
- }
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(doc, snapshot);
+ const QList<LookupItem> &lookupItems = typeOfExpression(expression, scope);
+ if (lookupItems.isEmpty())
+ return;
+
+ const LookupItem &lookupItem = lookupItems.first(); // ### TODO: select the best candidate.
+ handleLookupItemMatch(lookupItem, !extraSelectionTooltip);
}
- return qualifiedNames.join(QLatin1String("::"));
+ evaluateHelpCandidates();
}
-void CppHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos)
+bool CppHoverHandler::matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document,
+ unsigned line)
{
- m_helpId.clear();
- m_toolTip.clear();
-
- if (!m_modelManager)
- return;
-
- TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
- if (!edit)
- return;
-
- const Snapshot snapshot = m_modelManager->snapshot();
- const QString fileName = editor->file()->fileName();
- Document::Ptr doc = snapshot.document(fileName);
- if (!doc)
- return; // nothing to do
-
- QString formatTooltip = edit->extraSelectionTooltip(pos);
- QTextCursor tc(edit->document());
- tc.setPosition(pos);
-
- const unsigned lineNumber = tc.block().blockNumber() + 1;
-
- // Find the last symbol up to the cursor position
- int line = 0, column = 0;
- editor->convertPosition(tc.position(), &line, &column);
- Scope *scope = doc->scopeAt(line, column);
-
- TypeOfExpression typeOfExpression;
- typeOfExpression.init(doc, snapshot);
-
- // We only want to show F1 if the tooltip matches the help id
- bool showF1 = true;
-
- foreach (const Document::DiagnosticMessage &m, doc->diagnosticMessages()) {
- if (m.line() == lineNumber) {
+ foreach (const Document::DiagnosticMessage &m, document->diagnosticMessages()) {
+ if (m.line() == line) {
m_toolTip = m.text();
- showF1 = false;
- break;
+ return true;
}
}
+ return false;
+}
- QMap<QString, QUrl> helpLinks;
- if (m_toolTip.isEmpty()) {
- foreach (const Document::Include &incl, doc->includes()) {
- if (incl.line() == lineNumber) {
- m_toolTip = QDir::toNativeSeparators(incl.fileName());
- m_helpId = QFileInfo(incl.fileName()).fileName();
- helpLinks = Core::HelpManager::instance()->linksForIdentifier(m_helpId);
- break;
- }
+bool CppHoverHandler::matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line)
+{
+ foreach (const Document::Include &includeFile, document->includes()) {
+ if (includeFile.line() == line) {
+ m_toolTip = QDir::toNativeSeparators(includeFile.fileName());
+ const QString &fileName = QFileInfo(includeFile.fileName()).fileName();
+ m_helpCandidates.append(HelpCandidate(fileName, fileName, HelpCandidate::Include));
+ return true;
}
}
+ return false;
+}
- if (m_helpId.isEmpty()) {
- // Move to the end of a qualified name
- bool stop = false;
- while (!stop) {
- const QChar ch = editor->characterAt(tc.position());
- if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
- tc.setPosition(tc.position() + 1);
- else if (ch == QLatin1Char(':') && editor->characterAt(tc.position() + 1) == QLatin1Char(':')) {
- tc.setPosition(tc.position() + 2);
- } else {
- stop = true;
+bool CppHoverHandler::matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos)
+{
+ foreach (const Document::MacroUse &use, document->macroUses()) {
+ if (use.contains(pos)) {
+ const unsigned begin = use.begin();
+ const QString &name = use.macro().name();
+ if (pos < begin + name.length()) {
+ m_toolTip = use.macro().toString();
+ m_helpCandidates.append(HelpCandidate(name, name, HelpCandidate::Macro));
+ return true;
}
}
+ }
+ return false;
+}
- // Fetch the expression's code
- ExpressionUnderCursor expressionUnderCursor;
- const QString expression = expressionUnderCursor(tc);
-
- const QList<LookupItem> types = typeOfExpression(expression, scope);
-
-
- if (!types.isEmpty()) {
- Overview overview;
- overview.setShowArgumentNames(true);
- overview.setShowReturnTypes(true);
- overview.setShowFullyQualifiedNamed(true);
-
- const LookupItem result = types.first(); // ### TODO: select the best candidate.
- FullySpecifiedType symbolTy = result.type(); // result of `type of expression'.
- Symbol *declaration = result.declaration(); // lookup symbol
- const Name *declarationName = declaration ? declaration->name() : 0;
+void CppHoverHandler::handleLookupItemMatch(const LookupItem &lookupItem, const bool assignTooltip)
+{
+ Symbol *matchingDeclaration = lookupItem.declaration();
+ FullySpecifiedType matchingType = lookupItem.type();
- if (declaration && declaration->scope()
- && declaration->scope()->isClassScope()) {
- Class *enclosingClass = declaration->scope()->owner()->asClass();
- if (const Identifier *id = enclosingClass->identifier()) {
- if (id->isEqualTo(declaration->identifier()))
- declaration = enclosingClass;
- }
+ Overview overview;
+ overview.setShowArgumentNames(true);
+ overview.setShowReturnTypes(true);
+ overview.setShowFullyQualifiedNamed(true);
+
+ if (!matchingDeclaration && assignTooltip) {
+ m_toolTip = overview.prettyType(matchingType, QString());
+ } else {
+ QString qualifiedName;
+ if (matchingDeclaration->enclosingSymbol()->isClass() ||
+ matchingDeclaration->enclosingSymbol()->isNamespace() ||
+ matchingDeclaration->enclosingSymbol()->isEnum()) {
+ const QList<const Name *> &names =
+ LookupContext::fullyQualifiedName(matchingDeclaration);
+ const int size = names.size();
+ for (int i = 0; i < size; ++i) {
+ qualifiedName.append(overview.prettyName(names.at(i)));
+ if (i < size - 1)
+ qualifiedName.append(QLatin1String("::"));
}
+ } else {
+ qualifiedName.append(overview.prettyName(matchingDeclaration->name()));
+ }
- m_helpId = buildHelpId(declaration, declarationName);
-
- if (m_toolTip.isEmpty()) {
- Symbol *symbol = declaration;
-
- if (declaration)
- symbol = declaration;
-
- if (symbol && symbol == declaration && symbol->isClass()) {
- m_toolTip = m_helpId;
-
- } else if (declaration && (declaration->isDeclaration() || declaration->isArgument())) {
- m_toolTip = overview.prettyType(symbolTy, buildHelpId(declaration, declaration->name()));
-
- } else if (symbolTy->isClassType() || symbolTy->isEnumType() ||
- symbolTy->isForwardClassDeclarationType()) {
- m_toolTip = m_helpId;
-
- } else {
- m_toolTip = overview.prettyType(symbolTy, m_helpId);
-
- }
+ if (assignTooltip) {
+ if (matchingDeclaration->isClass() ||
+ matchingDeclaration->isNamespace() ||
+ matchingDeclaration->isForwardClassDeclaration() ||
+ matchingDeclaration->isEnum()) {
+ m_toolTip = qualifiedName;
+ } else {
+ m_toolTip = overview.prettyType(matchingType, qualifiedName);
}
+ }
- // Some docs don't contain the namespace in the documentation pages, for instance
- // there is QtMobility::QContactManager but the help page is for QContactManager.
- // To show their help anyway, try stripping scopes until we find something.
- const QString startHelpId = m_helpId;
- while (!m_helpId.isEmpty()) {
- helpLinks = Core::HelpManager::instance()->linksForIdentifier(m_helpId);
- if (!helpLinks.isEmpty())
- break;
-
- int coloncolonIndex = m_helpId.indexOf(QLatin1String("::"));
- if (coloncolonIndex == -1) {
- m_helpId = startHelpId;
- break;
- }
-
- m_helpId.remove(0, coloncolonIndex + 2);
- }
+ HelpCandidate::Category helpCategory;
+ if (matchingDeclaration->isNamespace() ||
+ matchingDeclaration->isClass() ||
+ matchingDeclaration->isForwardClassDeclaration()) {
+ helpCategory = HelpCandidate::ClassOrNamespace;
+ } else if (matchingDeclaration->isEnum()) {
+ helpCategory = HelpCandidate::Enum;
+ } else if (matchingDeclaration->isTypedef()) {
+ helpCategory = HelpCandidate::Typedef;
+ } else if (matchingDeclaration->isStatic() && !matchingDeclaration->isFunction()) {
+ helpCategory = HelpCandidate::Var;
+ } else {
+ helpCategory = HelpCandidate::Function;
}
+
+ // Help identifiers are simply the name with no signature, arguments or return type.
+ // They might or might not include a qualification. This is why two candidates are
+ // created.
+ overview.setShowArgumentNames(false);
+ overview.setShowReturnTypes(false);
+ overview.setShowFunctionSignatures(false);
+ overview.setShowFullyQualifiedNamed(false);
+ const QString &simpleName = overview.prettyName(matchingDeclaration->name());
+ overview.setShowFunctionSignatures(true);
+ const QString &specifierId = overview.prettyType(matchingType, simpleName);
+
+ m_helpCandidates.append(HelpCandidate(simpleName, specifierId, helpCategory));
+ m_helpCandidates.append(HelpCandidate(qualifiedName, specifierId, helpCategory));
}
+}
- if (m_toolTip.isEmpty()) {
- foreach (const Document::MacroUse &use, doc->macroUses()) {
- if (use.contains(pos)) {
- const Macro m = use.macro();
- m_toolTip = m.toString();
- m_helpId = m.name();
- break;
- }
+void CppHoverHandler::evaluateHelpCandidates()
+{
+ for (int i = 0; i < m_helpCandidates.size(); ++i) {
+ if (helpIdExists(m_helpCandidates.at(i).m_helpId)) {
+ m_matchingHelpCandidate = i;
+ return;
}
}
+}
- if (!formatTooltip.isEmpty())
- m_toolTip = formatTooltip;
+bool CppHoverHandler::helpIdExists(const QString &helpId) const
+{
+ QMap<QString, QUrl> helpLinks = Core::HelpManager::instance()->linksForIdentifier(helpId);
+ if (!helpLinks.isEmpty())
+ return true;
+ return false;
+}
- if (!m_helpId.isEmpty() && !helpLinks.isEmpty()) {
- if (showF1) {
- // we need the original width without escape sequences
- const int width = QFontMetrics(QToolTip::font()).width(m_toolTip);
- m_toolTip = QString(QLatin1String("<table><tr><td valign=middle width=%2>%1</td>"
- "<td><img src=\":/cppeditor/images/f1.png\"></td></tr></table>"))
- .arg(Qt::escape(m_toolTip)).arg(width);
+QString CppHoverHandler::getDocContents()
+{
+ Q_ASSERT(m_matchingHelpCandidate >= 0);
+
+ QString contents;
+ const HelpCandidate &help = m_helpCandidates.at(m_matchingHelpCandidate);
+ QMap<QString, QUrl> helpLinks =
+ Core::HelpManager::instance()->linksForIdentifier(help.m_helpId);
+ foreach (const QUrl &url, helpLinks) {
+ // The help id might or might not be qualified. But anchors and marks are not qualified.
+ const QString &name = removeQualificationIfAny(help.m_helpId);
+ const QByteArray &html = Core::HelpManager::instance()->fileData(url);
+ switch (help.m_category) {
+ case HelpCandidate::Include:
+ contents = m_htmlDocExtractor.getClassOrNamespaceBrief(html, name);
+ break;
+ case HelpCandidate::ClassOrNamespace:
+ contents = m_htmlDocExtractor.getClassOrNamespaceDescription(html, name);
+ break;
+ case HelpCandidate::Function:
+ contents =
+ m_htmlDocExtractor.getFunctionDescription(html, help.m_markId, name);
+ break;
+ case HelpCandidate::Enum:
+ contents = m_htmlDocExtractor.getEnumDescription(html, name);
+ break;
+ case HelpCandidate::Typedef:
+ contents = m_htmlDocExtractor.getTypedefDescription(html, name);
+ break;
+ case HelpCandidate::Var:
+ contents = m_htmlDocExtractor.getVarDescription(html, name);
+ break;
+ case HelpCandidate::Macro:
+ contents = m_htmlDocExtractor.getMacroDescription(html, help.m_markId, name);
+ break;
+ default:
+ break;
}
- editor->setContextHelpId(m_helpId);
- } else if (!m_toolTip.isEmpty() && Qt::mightBeRichText(m_toolTip)) {
- m_toolTip = QString(QLatin1String("<nobr>%1</nobr>")).arg(Qt::escape(m_toolTip));
+
+ if (!contents.isEmpty())
+ break;
}
+ return contents;
+}
+
+void CppHoverHandler::resetMatchings()
+{
+ m_matchingHelpCandidate = -1;
+ m_helpCandidates.clear();
+ m_toolTip.clear();
+}
+
+TextEditor::BaseTextEditor *CppHoverHandler::baseTextEditor(TextEditor::ITextEditor *editor)
+{
+ return qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
}
diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cppeditor/cpphoverhandler.h
index 311b829223..84c58733d4 100644
--- a/src/plugins/cppeditor/cpphoverhandler.h
+++ b/src/plugins/cppeditor/cpphoverhandler.h
@@ -30,12 +30,21 @@
#ifndef CPPHOVERHANDLER_H
#define CPPHOVERHANDLER_H
+#include <utils/htmldocextractor.h>
+
#include <QtCore/QObject>
+#include <QtCore/QList>
+
+#include <cplusplus/CppDocument.h>
QT_BEGIN_NAMESPACE
class QPoint;
QT_END_NAMESPACE
+namespace CPlusPlus {
+class LookupItem;
+}
+
namespace Core {
class IEditor;
}
@@ -46,6 +55,7 @@ class CppModelManagerInterface;
namespace TextEditor {
class ITextEditor;
+class BaseTextEditor;
}
namespace CppEditor {
@@ -54,7 +64,6 @@ namespace Internal {
class CppHoverHandler : public QObject
{
Q_OBJECT
-
public:
CppHoverHandler(QObject *parent = 0);
@@ -66,11 +75,45 @@ private slots:
void editorOpened(Core::IEditor *editor);
private:
- void updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos);
+ struct HelpCandidate
+ {
+ enum Category {
+ ClassOrNamespace,
+ Enum,
+ Typedef,
+ Var,
+ Macro,
+ Include,
+ Function
+ };
+
+ HelpCandidate(const QString &helpId, const QString &markId, Category category) :
+ m_helpId(helpId), m_markId(markId), m_category(category)
+ {}
+ QString m_helpId;
+ QString m_markId;
+ Category m_category;
+ };
+
+ void resetMatchings();
+ void identifyMatch(TextEditor::ITextEditor *editor, int pos);
+ bool matchDiagnosticMessage(const CPlusPlus::Document::Ptr &document, unsigned line);
+ bool matchIncludeFile(const CPlusPlus::Document::Ptr &document, unsigned line);
+ bool matchMacroInUse(const CPlusPlus::Document::Ptr &document, unsigned pos);
+ void handleLookupItemMatch(const CPlusPlus::LookupItem &lookupItem,
+ const bool assignTooltip);
+
+ void evaluateHelpCandidates();
+ bool helpIdExists(const QString &helpId) const;
+ QString getDocContents();
+
+ static TextEditor::BaseTextEditor *baseTextEditor(TextEditor::ITextEditor *editor);
CppTools::CppModelManagerInterface *m_modelManager;
- QString m_helpId;
+ int m_matchingHelpCandidate;
+ QList<HelpCandidate> m_helpCandidates;
QString m_toolTip;
+ Utils::HtmlDocExtractor m_htmlDocExtractor;
};
} // namespace Internal
diff --git a/src/plugins/cppeditor/cppoutline.cpp b/src/plugins/cppeditor/cppoutline.cpp
index b6ee497c51..146b060ae6 100644
--- a/src/plugins/cppeditor/cppoutline.cpp
+++ b/src/plugins/cppeditor/cppoutline.cpp
@@ -4,10 +4,12 @@
#include <Symbol.h>
#include <coreplugin/ifile.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <cplusplus/OverviewModel.h>
-#include <QtGui/QVBoxLayout>
#include <QtCore/QDebug>
+#include <QtGui/QVBoxLayout>
+#include <QtCore/QTimer>
using namespace CppEditor::Internal;
@@ -29,9 +31,11 @@ CppOutlineTreeView::CppOutlineTreeView(QWidget *parent) :
setExpandsOnDoubleClick(false);
}
-CppOutlineFilterModel::CppOutlineFilterModel(QObject *parent) :
- QSortFilterProxyModel(parent)
+CppOutlineFilterModel::CppOutlineFilterModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent) :
+ QSortFilterProxyModel(parent),
+ m_sourceModel(sourceModel)
{
+ setSourceModel(m_sourceModel);
}
bool CppOutlineFilterModel::filterAcceptsRow(int sourceRow,
@@ -41,6 +45,12 @@ bool CppOutlineFilterModel::filterAcceptsRow(int sourceRow,
if (!sourceParent.isValid() && sourceRow == 0) {
return false;
}
+ // ignore generated symbols, e.g. by macro expansion (Q_OBJECT)
+ const QModelIndex sourceIndex = m_sourceModel->index(sourceRow, 0, sourceParent);
+ CPlusPlus::Symbol *symbol = m_sourceModel->symbolFromIndex(sourceIndex);
+ if (symbol && symbol->isGenerated())
+ return false;
+
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
@@ -49,8 +59,8 @@ CppOutlineWidget::CppOutlineWidget(CPPEditor *editor) :
TextEditor::IOutlineWidget(),
m_editor(editor),
m_treeView(new CppOutlineTreeView(this)),
- m_model(new CPlusPlus::OverviewModel(this)),
- m_proxyModel(new CppOutlineFilterModel(this)),
+ m_model(m_editor->outlineModel()),
+ m_proxyModel(new CppOutlineFilterModel(m_model, this)),
m_enableCursorSync(true),
m_blockCursorSync(false)
{
@@ -60,20 +70,13 @@ CppOutlineWidget::CppOutlineWidget(CPPEditor *editor) :
layout->addWidget(m_treeView);
setLayout(layout);
- m_proxyModel->setSourceModel(m_model);
m_treeView->setModel(m_proxyModel);
- CppTools::CppModelManagerInterface *modelManager = CppTools::CppModelManagerInterface::instance();
+ connect(m_model, SIGNAL(modelReset()), this, SLOT(modelUpdated()));
+ modelUpdated();
- connect(modelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
- this, SLOT(updateOutline(CPlusPlus::Document::Ptr)));
-
- if (modelManager->snapshot().contains(editor->file()->fileName())) {
- updateOutline(modelManager->snapshot().document(editor->file()->fileName()));
- }
-
- connect(m_editor, SIGNAL(cursorPositionChanged()),
- this, SLOT(updateSelectionInTree()));
+ connect(m_editor, SIGNAL(outlineModelIndexChanged(QModelIndex)),
+ this, SLOT(updateSelectionInTree(QModelIndex)));
connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(updateSelectionInText(QItemSelection)));
}
@@ -82,32 +85,19 @@ void CppOutlineWidget::setCursorSynchronization(bool syncWithCursor)
{
m_enableCursorSync = syncWithCursor;
if (m_enableCursorSync)
- updateSelectionInTree();
+ updateSelectionInTree(m_editor->outlineModelIndex());
}
-void CppOutlineWidget::updateOutline(CPlusPlus::Document::Ptr document)
+void CppOutlineWidget::modelUpdated()
{
- m_document = document;
- if (document && m_editor
- && (document->fileName() == m_editor->file()->fileName())
- && (document->editorRevision() == m_editor->editorRevision())) {
- if (debug)
- qDebug() << "CppOutline - rebuilding model";
- m_model->rebuild(document);
- m_treeView->expandAll();
- updateSelectionInTree();
- }
+ m_treeView->expandAll();
}
-void CppOutlineWidget::updateSelectionInTree()
+void CppOutlineWidget::updateSelectionInTree(const QModelIndex &index)
{
if (!syncCursor())
return;
- int line = m_editor->textCursor().blockNumber();
- int column = m_editor->textCursor().columnNumber();
-
- QModelIndex index = indexForPosition(QModelIndex(), line, column);
QModelIndex proxyIndex = m_proxyModel->mapFromSource(index);
m_blockCursorSync = true;
@@ -115,6 +105,7 @@ void CppOutlineWidget::updateSelectionInTree()
qDebug() << "CppOutline - updating selection due to cursor move";
m_treeView->selectionModel()->select(proxyIndex, QItemSelectionModel::ClearAndSelect);
+ m_treeView->scrollTo(proxyIndex);
m_blockCursorSync = false;
}
@@ -129,69 +120,21 @@ void CppOutlineWidget::updateSelectionInText(const QItemSelection &selection)
CPlusPlus::Symbol *symbol = m_model->symbolFromIndex(index);
if (symbol) {
m_blockCursorSync = true;
- unsigned line, column;
- m_document->translationUnit()->getPosition(symbol->startOffset(), &line, &column);
if (debug)
- qDebug() << "CppOutline - moving cursor to" << line << column - 1;
+ qDebug() << "CppOutline - moving cursor to" << symbol->line() << symbol->column() - 1;
+
+ Core::EditorManager *editorManager = Core::EditorManager::instance();
+ editorManager->cutForwardNavigationHistory();
+ editorManager->addCurrentPositionToNavigationHistory();
// line has to be 1 based, column 0 based!
- m_editor->gotoLine(line, column - 1);
+ m_editor->gotoLine(symbol->line(), symbol->column() - 1);
m_blockCursorSync = false;
}
}
}
-QModelIndex CppOutlineWidget::indexForPosition(const QModelIndex &rootIndex, int line, int column)
-{
- QModelIndex result = rootIndex;
-
- const int rowCount = m_model->rowCount(rootIndex);
- for (int row = 0; row < rowCount; ++row) {
- QModelIndex index = m_model->index(row, 0, rootIndex);
- CPlusPlus::Symbol *symbol = m_model->symbolFromIndex(index);
- if (symbol && positionInsideSymbol(line, column, symbol)) {
- // recurse to children
- result = indexForPosition(index, line, column);
- }
- }
-
- return result;
-}
-
-bool CppOutlineWidget::positionInsideSymbol(unsigned cursorLine, unsigned cursorColumn, CPlusPlus::Symbol *symbol) const
-{
- if (!m_document)
- return false;
- CPlusPlus::TranslationUnit *translationUnit = m_document->translationUnit();
-
- unsigned symbolStartLine = -1;
- unsigned symbolStartColumn = -1;
-
- translationUnit->getPosition(symbol->startOffset(), &symbolStartLine, &symbolStartColumn);
-
- // normalize to 0 based
- --symbolStartLine;
- --symbolStartColumn;
-
- if (symbolStartLine < cursorLine
- || (symbolStartLine == cursorLine && symbolStartColumn <= cursorColumn)) {
- unsigned symbolEndLine = -1;
- unsigned symbolEndColumn = -1;
- translationUnit->getPosition(symbol->endOffset(), &symbolEndLine, &symbolEndColumn);
-
- // normalize to 0 based
- --symbolEndLine;
- --symbolEndColumn;
-
- if (symbolEndLine > cursorLine
- || (symbolEndLine == cursorLine && symbolEndColumn >= cursorColumn)) {
- return true;
- }
- }
- return false;
-}
-
bool CppOutlineWidget::syncCursor()
{
return m_enableCursorSync && !m_blockCursorSync;
diff --git a/src/plugins/cppeditor/cppoutline.h b/src/plugins/cppeditor/cppoutline.h
index 96443811cc..3eea8b3c65 100644
--- a/src/plugins/cppeditor/cppoutline.h
+++ b/src/plugins/cppeditor/cppoutline.h
@@ -22,10 +22,12 @@ class CppOutlineFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
- CppOutlineFilterModel(QObject *parent);
+ CppOutlineFilterModel(CPlusPlus::OverviewModel *sourceModel, QObject *parent);
// QSortFilterProxyModel
bool filterAcceptsRow(int sourceRow,
const QModelIndex &sourceParent) const;
+private:
+ CPlusPlus::OverviewModel *m_sourceModel;
};
class CppOutlineWidget : public TextEditor::IOutlineWidget
@@ -38,13 +40,11 @@ public:
virtual void setCursorSynchronization(bool syncWithCursor);
private slots:
- void updateOutline(CPlusPlus::Document::Ptr document);
- void updateSelectionInTree();
+ void modelUpdated();
+ void updateSelectionInTree(const QModelIndex &index);
void updateSelectionInText(const QItemSelection &selection);
private:
- QModelIndex indexForPosition(const QModelIndex &rootIndex, int line, int column);
- bool positionInsideSymbol(unsigned cursorLine, unsigned cursorColumn, CPlusPlus::Symbol *symbol) const;
bool syncCursor();
private:
@@ -52,7 +52,6 @@ private:
CppOutlineTreeView *m_treeView;
CPlusPlus::OverviewModel *m_model;
CppOutlineFilterModel *m_proxyModel;
- CPlusPlus::Document::Ptr m_document;
bool m_enableCursorSync;
bool m_blockCursorSync;
diff --git a/src/plugins/cppeditor/cppplugin.cpp b/src/plugins/cppeditor/cppplugin.cpp
index c1dfeff08c..2b9e980f82 100644
--- a/src/plugins/cppeditor/cppplugin.cpp
+++ b/src/plugins/cppeditor/cppplugin.cpp
@@ -135,7 +135,7 @@ CppPlugin *CppPlugin::m_instance = 0;
CppPlugin::CppPlugin() :
m_actionHandler(0),
- m_sortedMethodOverview(false),
+ m_sortedOutline(false),
m_renameSymbolUnderCursorAction(0),
m_findUsagesAction(0),
m_updateCodeModelAction(0)
@@ -176,19 +176,19 @@ void CppPlugin::initializeEditor(CPPEditor *editor)
this, SLOT(quickFix(TextEditor::ITextEditable*)));
// method combo box sorting
- connect(this, SIGNAL(methodOverviewSortingChanged(bool)),
- editor, SLOT(setSortedMethodOverview(bool)));
+ connect(this, SIGNAL(outlineSortingChanged(bool)),
+ editor, SLOT(setSortedOutline(bool)));
}
-void CppPlugin::setSortedMethodOverview(bool sorted)
+void CppPlugin::setSortedOutline(bool sorted)
{
- m_sortedMethodOverview = sorted;
- emit methodOverviewSortingChanged(sorted);
+ m_sortedOutline = sorted;
+ emit outlineSortingChanged(sorted);
}
-bool CppPlugin::sortedMethodOverview() const
+bool CppPlugin::sortedOutline() const
{
- return m_sortedMethodOverview;
+ return m_sortedOutline;
}
CppQuickFixCollector *CppPlugin::quickFixCollector() const
@@ -308,21 +308,22 @@ bool CppPlugin::initialize(const QStringList & /*arguments*/, QString *errorMess
void CppPlugin::readSettings()
{
- m_sortedMethodOverview = Core::ICore::instance()->settings()->value("CppTools/SortedMethodOverview", false).toBool();
+ m_sortedOutline = Core::ICore::instance()->settings()->value("CppTools/SortedMethodOverview", false).toBool();
}
void CppPlugin::writeSettings()
{
- Core::ICore::instance()->settings()->setValue("CppTools/SortedMethodOverview", m_sortedMethodOverview);
+ Core::ICore::instance()->settings()->setValue("CppTools/SortedMethodOverview", m_sortedOutline);
}
void CppPlugin::extensionsInitialized()
{
}
-void CppPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag CppPlugin::aboutToShutdown()
{
writeSettings();
+ return SynchronousShutdown;
}
void CppPlugin::switchDeclarationDefinition()
diff --git a/src/plugins/cppeditor/cppplugin.h b/src/plugins/cppeditor/cppplugin.h
index c7d4b82c3c..517da5c2f9 100644
--- a/src/plugins/cppeditor/cppplugin.h
+++ b/src/plugins/cppeditor/cppplugin.h
@@ -60,20 +60,20 @@ public:
bool initialize(const QStringList &arguments, QString *error_message = 0);
void extensionsInitialized();
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
// Connect editor to settings changed signals.
void initializeEditor(CPPEditor *editor);
- bool sortedMethodOverview() const;
+ bool sortedOutline() const;
CppQuickFixCollector *quickFixCollector() const;
signals:
- void methodOverviewSortingChanged(bool sort);
+ void outlineSortingChanged(bool sort);
public slots:
- void setSortedMethodOverview(bool sorted);
+ void setSortedOutline(bool sorted);
private slots:
void switchDeclarationDefinition();
@@ -93,7 +93,7 @@ private:
static CppPlugin *m_instance;
TextEditor::TextEditorActionHandler *m_actionHandler;
- bool m_sortedMethodOverview;
+ bool m_sortedOutline;
QAction *m_renameSymbolUnderCursorAction;
QAction *m_findUsagesAction;
QAction *m_updateCodeModelAction;
diff --git a/src/plugins/cppeditor/cppquickfix.cpp b/src/plugins/cppeditor/cppquickfix.cpp
index 29dc9ed1b6..b717d63254 100644
--- a/src/plugins/cppeditor/cppquickfix.cpp
+++ b/src/plugins/cppeditor/cppquickfix.cpp
@@ -29,11 +29,13 @@
#include "cppquickfix.h"
#include "cppeditor.h"
+#include "cppdeclfromdef.h"
#include <cplusplus/ASTPath.h>
#include <cplusplus/CppDocument.h>
#include <cplusplus/ResolveExpression.h>
#include <cplusplus/Overview.h>
+#include <cplusplus/TypeOfExpression.h>
#include <TranslationUnit.h>
#include <ASTVisitor.h>
@@ -929,7 +931,7 @@ public:
// We need to do a QCA::translate, so we need a context.
// Use fully qualified class name:
Overview oo;
- foreach (const Name *n, LookupContext::fullyQualifiedName(function)) {
+ foreach (const Name *n, LookupContext::path(function)) {
if (!m_context.isEmpty())
m_context.append(QLatin1String("::"));
m_context.append(oo.prettyName(n));
@@ -1217,6 +1219,135 @@ public:
};
+/*
+ Adds missing case statements for "switch (enumVariable)"
+*/
+class CompleteSwitchCaseStatement: public CppQuickFixOperation
+{
+public:
+ CompleteSwitchCaseStatement(TextEditor::BaseTextEditor *editor)
+ : CppQuickFixOperation(editor)
+ {}
+
+ virtual QString description() const
+ {
+ return QApplication::translate("CppTools::QuickFix", "Complete Switch Statement");
+ }
+
+ virtual int match(const QList<AST *> &path)
+ {
+ if (path.isEmpty())
+ return -1; // nothing to do
+
+ // look for switch statement
+ for (int depth = path.size()-1; depth >= 0; --depth) {
+ AST *ast = path.at(depth);
+ SwitchStatementAST *switchStatement = ast->asSwitchStatement();
+ if (switchStatement) {
+ if (!isCursorOn(switchStatement->switch_token) || !switchStatement->statement)
+ return -1;
+ compoundStatement = switchStatement->statement->asCompoundStatement();
+ if (!compoundStatement) // we ignore pathologic case "switch (t) case A: ;"
+ return -1;
+ // look if the condition's type is an enum
+ if (Enum *e = conditionEnum(switchStatement)) {
+ // check the possible enum values
+ values.clear();
+ Overview prettyPrint;
+ for (unsigned i = 0; i < e->memberCount(); ++i) {
+ if (Declaration *decl = e->memberAt(i)->asDeclaration()) {
+ values << prettyPrint(decl->name());
+ }
+ }
+ // Get the used values
+ CaseStatementCollector caseValues(document()->translationUnit());
+ QStringList usedValues = caseValues(switchStatement);
+ // save the values that would be added
+ foreach (const QString &usedValue, usedValues)
+ values.removeAll(usedValue);
+ if (values.isEmpty())
+ return -1;
+ return depth;
+ }
+ return -1;
+ }
+ }
+
+ return -1;
+ }
+
+ virtual void createChanges()
+ {
+ ChangeSet changes;
+ int start = endOf(compoundStatement->lbrace_token);
+ changes.insert(start, QLatin1String("\ncase ")
+ + values.join(QLatin1String(":\nbreak;\ncase "))
+ + QLatin1String(":\nbreak;"));
+ refactoringChanges()->changeFile(fileName(), changes);
+ refactoringChanges()->reindent(fileName(), range(compoundStatement));
+ }
+
+protected:
+ Enum *conditionEnum(SwitchStatementAST *statement)
+ {
+ Block *block = statement->symbol;
+ Scope *scope = document()->scopeAt(block->line(), block->column());
+ TypeOfExpression typeOfExpression;
+ typeOfExpression.init(document(), snapshot());
+ const QList<LookupItem> results = typeOfExpression(statement->condition,
+ document(),
+ scope);
+ foreach (LookupItem result, results) {
+ FullySpecifiedType fst = result.type();
+ if (Enum *e = result.declaration()->type()->asEnumType())
+ return e;
+ if (NamedType *namedType = fst->asNamedType()) {
+ QList<Symbol *> candidates =
+ typeOfExpression.context().lookup(namedType->name(), scope);
+ foreach (Symbol *candidate, candidates) {
+ if (Enum *e = candidate->asEnum()) {
+ return e;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+ class CaseStatementCollector : public ASTVisitor
+ {
+ public:
+ CaseStatementCollector(TranslationUnit *unit) : ASTVisitor(unit) {}
+ QStringList operator ()(AST *ast)
+ {
+ values.clear();
+ foundCaseStatementLevel = false;
+ accept(ast);
+ return values;
+ }
+
+ bool preVisit(AST *ast) {
+ if (CaseStatementAST *cs = ast->asCaseStatement()) {
+ foundCaseStatementLevel = true;
+ if (SimpleNameAST *sm = cs->expression->asSimpleName()) {
+ Overview prettyPrint;
+ values << prettyPrint(sm->name);
+ }
+ return true;
+ } else if (foundCaseStatementLevel) {
+ return false;
+ }
+ return true;
+ }
+
+ bool foundCaseStatementLevel;
+ QStringList values;
+ };
+
+protected:
+ CompoundStatementAST *compoundStatement;
+ QStringList values;
+};
+
} // end of anonymous namespace
@@ -1415,6 +1546,8 @@ QList<TextEditor::QuickFixOperation::Ptr> CppQuickFixFactory::quickFixOperations
QSharedPointer<ConvertNumericToHex> convertNumericToHex(new ConvertNumericToHex(editor));
QSharedPointer<ConvertNumericToOctal> convertNumericToOctal(new ConvertNumericToOctal(editor));
QSharedPointer<ConvertNumericToDecimal> convertNumericToDecimal(new ConvertNumericToDecimal(editor));
+ QSharedPointer<CompleteSwitchCaseStatement> completeSwitchCaseStatement(new CompleteSwitchCaseStatement(editor));
+ QSharedPointer<DeclFromDef> declFromDef(new DeclFromDef(editor));
quickFixOperations.append(rewriteLogicalAndOp);
quickFixOperations.append(splitIfStatementOp);
@@ -1429,6 +1562,11 @@ QList<TextEditor::QuickFixOperation::Ptr> CppQuickFixFactory::quickFixOperations
quickFixOperations.append(convertNumericToHex);
quickFixOperations.append(convertNumericToOctal);
quickFixOperations.append(convertNumericToDecimal);
+ quickFixOperations.append(completeSwitchCaseStatement);
+
+#if 0
+ quickFixOperations.append(declFromDef);
+#endif
if (editor->mimeType() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)
quickFixOperations.append(wrapCString);
diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp
index 733671fa51..3f90f644c0 100644
--- a/src/plugins/cpptools/cppcodecompletion.cpp
+++ b/src/plugins/cpptools/cppcodecompletion.cpp
@@ -200,7 +200,7 @@ protected:
{ _item = newCompletionItem(name); }
virtual void visit(const QualifiedNameId *name)
- { _item = newCompletionItem(name->unqualifiedNameId()); }
+ { _item = newCompletionItem(name->name()); }
};
struct CompleteFunctionDeclaration
@@ -1723,7 +1723,33 @@ QList<TextEditor::CompletionItem> CppCodeCompletion::getCompletions()
return completionItems;
}
-void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
+bool CppCodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+ if (item.data.canConvert<QString>()) // snippet
+ return false;
+
+ if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT)
+ return typedChar == QLatin1Char('(')
+ || typedChar == QLatin1Char(',');
+
+ if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL)
+ return typedChar == QLatin1Char('/')
+ && item.text.endsWith(QLatin1Char('/'));
+
+ if (item.data.value<Symbol *>())
+ return typedChar == QLatin1Char(':')
+ || typedChar == QLatin1Char(';')
+ || typedChar == QLatin1Char('.')
+ || typedChar == QLatin1Char(',')
+ || typedChar == QLatin1Char('(');
+
+ if (item.data.canConvert<CompleteFunctionDeclaration>())
+ return typedChar == QLatin1Char('(');
+
+ return false;
+}
+
+void CppCodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
{
Symbol *symbol = 0;
@@ -1749,10 +1775,15 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
toInsert = item.text;
extraChars += QLatin1Char(')');
+
+ if (typedChar == QLatin1Char('(')) // Eat the opening parenthesis
+ typedChar = QChar();
} else if (m_completionOperator == T_STRING_LITERAL || m_completionOperator == T_ANGLE_STRING_LITERAL) {
toInsert = item.text;
if (!toInsert.endsWith(QLatin1Char('/')))
extraChars += QLatin1Char((m_completionOperator == T_ANGLE_STRING_LITERAL) ? '>' : '"');
+ else if (typedChar == QLatin1Char('/')) // Eat the slash
+ typedChar = QChar();
} else {
toInsert = item.text;
@@ -1768,7 +1799,7 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
if (! function->hasReturnType() && (function->identity() && !function->identity()->isDestructorNameId())) {
// Don't insert any magic, since the user might have just wanted to select the class
- } else if (function->templateParameterCount() != 0) {
+ } else if (function->templateParameterCount() != 0 && typedChar != QLatin1Char('(')) {
// If there are no arguments, then we need the template specification
if (function->argumentCount() == 0) {
extraChars += QLatin1Char('<');
@@ -1777,32 +1808,50 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
if (completionSettings().m_spaceAfterFunctionName)
extraChars += QLatin1Char(' ');
extraChars += QLatin1Char('(');
+ if (typedChar == QLatin1Char('('))
+ typedChar = QChar();
// If the function doesn't return anything, automatically place the semicolon,
// unless we're doing a scope completion (then it might be function definition).
- bool endWithSemicolon = function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON;
+ const QChar characterAtCursor = m_editor->characterAt(m_editor->position());
+ bool endWithSemicolon = typedChar == QLatin1Char(';')
+ || (function->returnType()->isVoidType() && m_completionOperator != T_COLON_COLON);
+ const QChar semicolon = typedChar.isNull() ? QLatin1Char(';') : typedChar;
+
+ if (endWithSemicolon && characterAtCursor == semicolon) {
+ endWithSemicolon = false;
+ typedChar = QChar();
+ }
// If the function takes no arguments, automatically place the closing parenthesis
if (item.duplicateCount == 0 && ! function->hasArguments()) {
extraChars += QLatin1Char(')');
- if (endWithSemicolon)
- extraChars += QLatin1Char(';');
+ if (endWithSemicolon) {
+ extraChars += semicolon;
+ typedChar = QChar();
+ }
} else if (autoParenthesesEnabled) {
const QChar lookAhead = m_editor->characterAt(m_editor->position() + 1);
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
extraChars += QLatin1Char(')');
--cursorOffset;
if (endWithSemicolon) {
- extraChars += QLatin1Char(';');
+ extraChars += semicolon;
--cursorOffset;
+ typedChar = QChar();
}
}
+ // TODO: When an opening parenthesis exists, the "semicolon" should really be
+ // inserted after the matching closing parenthesis.
}
}
}
}
if (autoInsertBrackets && item.data.canConvert<CompleteFunctionDeclaration>()) {
+ if (typedChar == QLatin1Char('('))
+ typedChar = QChar();
+
// everything from the closing parenthesis on are extra chars, to
// make sure an auto-inserted ")" gets replaced by ") const" if necessary
int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
@@ -1811,6 +1860,13 @@ void CppCodeCompletion::complete(const TextEditor::CompletionItem &item)
}
}
+ // Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
+ if (!typedChar.isNull()) {
+ extraChars += typedChar;
+ if (cursorOffset != 0)
+ --cursorOffset;
+ }
+
// Avoid inserting characters that are already there
for (int i = 0; i < extraChars.length(); ++i) {
const QChar a = extraChars.at(i);
@@ -1836,7 +1892,7 @@ bool CppCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
return false;
} else if (completionItems.count() == 1) {
- complete(completionItems.first());
+ complete(completionItems.first(), QChar());
return true;
} else if (m_completionOperator != T_LPAREN) {
return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
diff --git a/src/plugins/cpptools/cppcodecompletion.h b/src/plugins/cpptools/cppcodecompletion.h
index 513b038407..406d4a2f87 100644
--- a/src/plugins/cpptools/cppcodecompletion.h
+++ b/src/plugins/cpptools/cppcodecompletion.h
@@ -78,7 +78,8 @@ public:
int startCompletion(TextEditor::ITextEditable *editor);
void completions(QList<TextEditor::CompletionItem> *completions);
- void complete(const TextEditor::CompletionItem &item);
+ bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
+ void complete(const TextEditor::CompletionItem &item, QChar typedChar);
bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
void cleanup();
diff --git a/src/plugins/cpptools/cppcodeformatter.cpp b/src/plugins/cpptools/cppcodeformatter.cpp
index 45c79e783e..75fe53fda8 100644
--- a/src/plugins/cpptools/cppcodeformatter.cpp
+++ b/src/plugins/cpptools/cppcodeformatter.cpp
@@ -1,3 +1,32 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
#include "cppcodeformatter.h"
#include <Token.h>
@@ -828,7 +857,7 @@ int CodeFormatter::tokenizeBlock(const QTextBlock &block, bool *endedJoined)
*endedJoined = tokenize.endedJoined();
const int lexerState = tokenize.state();
- TextBlockUserData::setLexerState(block, lexerState);
+ BaseTextDocumentLayout::setLexerState(block, lexerState);
return lexerState;
}
@@ -913,12 +942,12 @@ bool QtStyleCodeFormatter::loadBlockData(const QTextBlock &block, BlockData *dat
void QtStyleCodeFormatter::saveLexerState(QTextBlock *block, int state) const
{
- TextBlockUserData::setLexerState(*block, state);
+ BaseTextDocumentLayout::setLexerState(*block, state);
}
int QtStyleCodeFormatter::loadLexerState(const QTextBlock &block) const
{
- return TextBlockUserData::lexerState(block);
+ return BaseTextDocumentLayout::lexerState(block);
}
void QtStyleCodeFormatter::onEnter(int newState, int *indentDepth, int *savedIndentDepth) const
diff --git a/src/plugins/cpptools/cppcodeformatter.h b/src/plugins/cpptools/cppcodeformatter.h
index bc615586fe..fb3db4997f 100644
--- a/src/plugins/cpptools/cppcodeformatter.h
+++ b/src/plugins/cpptools/cppcodeformatter.h
@@ -1,3 +1,32 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
#ifndef CPPCODEFORMATTER_H
#define CPPCODEFORMATTER_H
diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp
index 194b4ed790..d785bf1097 100644
--- a/src/plugins/cpptools/cppfindreferences.cpp
+++ b/src/plugins/cpptools/cppfindreferences.cpp
@@ -148,7 +148,7 @@ public:
CppFindReferences::CppFindReferences(CppTools::CppModelManagerInterface *modelManager)
: QObject(modelManager),
_modelManager(modelManager),
- _resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>())
+ _resultWindow(Find::SearchResultWindow::instance())
{
m_watcher.setPendingResultsLimit(1);
connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp
index dcff150363..9b84e8012b 100644
--- a/src/plugins/cpptools/cpptoolsplugin.cpp
+++ b/src/plugins/cpptools/cpptoolsplugin.cpp
@@ -160,8 +160,9 @@ void CppToolsPlugin::extensionsInitialized()
m_modelManager->setHeaderSuffixes(mimeType.suffixes());
}
-void CppToolsPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag CppToolsPlugin::aboutToShutdown()
{
+ return SynchronousShutdown;
}
void CppToolsPlugin::switchHeaderSource()
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 97e95b017f..532662b2ae 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -65,7 +65,7 @@ public:
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
CppModelManager *cppModelManager() { return m_modelManager; }
QString correspondingHeaderOrSource(const QString &fileName) const;
diff --git a/src/plugins/cpptools/searchsymbols.cpp b/src/plugins/cpptools/searchsymbols.cpp
index f66700bf30..99c2616489 100644
--- a/src/plugins/cpptools/searchsymbols.cpp
+++ b/src/plugins/cpptools/searchsymbols.cpp
@@ -96,10 +96,9 @@ bool SearchSymbols::visit(Function *symbol)
QString extraScope;
if (const Name *name = symbol->name()) {
- if (const QualifiedNameId *nameId = name->asQualifiedNameId()) {
- if (nameId->nameCount() > 1) {
- extraScope = overview.prettyName(nameId->nameAt(nameId->nameCount() - 2));
- }
+ if (const QualifiedNameId *q = name->asQualifiedNameId()) {
+ if (q->base())
+ extraScope = overview.prettyName(q->base());
}
}
QString fullScope = _scope;
diff --git a/src/plugins/debugger/cdb/breakpoint.cpp b/src/plugins/debugger/cdb/breakpoint.cpp
index 49a6550990..3f2525920c 100644
--- a/src/plugins/debugger/cdb/breakpoint.cpp
+++ b/src/plugins/debugger/cdb/breakpoint.cpp
@@ -257,9 +257,9 @@ static bool mapDeviceToDriveLetter(QString *s)
for (const TCHAR *driveLetter = driveLetters; *driveLetter; driveLetter++) {
szDrive[0] = *driveLetter; // Look up each device name
if (QueryDosDevice(szDrive, driveName, MAX_PATH)) {
- const QString deviceName = QString::fromUtf16(driveName);
+ const QString deviceName = QString::fromWCharArray(driveName);
if (s->startsWith(deviceName)) {
- s->replace(0, deviceName.size(), QString::fromUtf16(szDrive));
+ s->replace(0, deviceName.size(), QString::fromWCharArray(szDrive));
return true;
}
}
@@ -275,7 +275,7 @@ static bool mapDeviceToDriveLetter(QString *s)
static inline QString normalizeFileNameCaseHelper(const QString &f)
{
- HANDLE hFile = CreateFile(f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ HANDLE hFile = CreateFile((const wchar_t*)f.utf16(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return f;
// Get the file size. We need a non-empty file to map it.
@@ -305,7 +305,7 @@ static inline QString normalizeFileNameCaseHelper(const QString &f)
pszFilename[0] = 0;
// Get a file name of the form "/Device/HarddiskVolume1/file.cpp"
if (GetMappedFileName (GetCurrentProcess(), pMem, pszFilename, MAX_PATH)) {
- rc = QString::fromUtf16(pszFilename);
+ rc = QString::fromWCharArray(pszFilename);
if (!mapDeviceToDriveLetter(&rc))
rc.clear();
}
diff --git a/src/plugins/debugger/cdb/coreengine.cpp b/src/plugins/debugger/cdb/coreengine.cpp
index 324d470831..c84b7b9264 100644
--- a/src/plugins/debugger/cdb/coreengine.cpp
+++ b/src/plugins/debugger/cdb/coreengine.cpp
@@ -296,7 +296,7 @@ bool CoreEngine::init(const QString &dllEnginePath, QString *errorMessage)
*errorMessage = msgComFailed("GetImagePathWide", hr);
return false;
}
- m_baseImagePath = QString::fromUtf16(buf);
+ m_baseImagePath = QString::fromWCharArray(buf);
hr = lib.debugCreate( __uuidof(IDebugRegisters2), reinterpret_cast<void**>(&m_cif.debugRegisters));
if (FAILED(hr)) {
@@ -447,7 +447,7 @@ bool CoreEngine::startDebuggerWithExecutable(const QString &workingDirectory,
PCWSTR workingDirC = 0;
const QString workingDirN = workingDirectory.isEmpty() ? QString() : QDir::toNativeSeparators(workingDirectory);
if (!workingDirN.isEmpty())
- workingDirC = workingDirN.utf16();
+ workingDirC = (PCWSTR)workingDirN.utf16();
hr = m_cif.debugClient->CreateProcess2Wide(NULL,
reinterpret_cast<PWSTR>(const_cast<ushort *>(cmd.utf16())),
&dbgopts, sizeof(dbgopts),
@@ -820,7 +820,7 @@ quint64 CoreEngine::getSourceLineAddress(const QString &file,
{
ULONG64 rc = 0;
const HRESULT hr = m_cif.debugSymbols->GetOffsetByLineWide(line,
- const_cast<ushort*>(file.utf16()),
+ (wchar_t*)(file.utf16()),
&rc);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to determine address of %1:%2 : %3").
@@ -830,6 +830,11 @@ quint64 CoreEngine::getSourceLineAddress(const QString &file,
return rc;
}
+void CoreEngine::outputVersion()
+{
+ m_cif.debugControl->OutputVersionInformation(DEBUG_OUTCTL_ALL_CLIENTS);
+}
+
bool CoreEngine::autoDetectPath(QString *outPath,
QStringList *checkedDirectories /* = 0 */)
{
@@ -978,10 +983,10 @@ static void formatEventFilter(CIDebugControl *ctl, unsigned long start, unsigned
HRESULT hr = ctl->GetEventFilterTextWide(i, buffer, bufSize, 0);
if (SUCCEEDED(hr)) {
ULONG size;
- str << "- #" << i << " \"" << QString::fromUtf16(buffer) << '"';
+ str << "- #" << i << " \"" << QString::fromWCharArray(buffer) << '"';
hr = ctl->GetEventFilterCommandWide(i, buffer, bufSize, &size);
if (SUCCEEDED(hr) && size > 1)
- str << " command: '" << QString::fromUtf16(buffer) << '\'';
+ str << " command: '" << QString::fromWCharArray(buffer) << '\'';
if (isException) {
DEBUG_EXCEPTION_FILTER_PARAMETERS exceptionParameters;
hr = ctl->GetExceptionFilterParameters(1, 0, i, &exceptionParameters);
@@ -996,7 +1001,7 @@ static void formatEventFilter(CIDebugControl *ctl, unsigned long start, unsigned
if (exceptionParameters.SecondCommandSize) {
hr = ctl->GetExceptionFilterSecondCommandWide(i, buffer, bufSize, 0);
if (SUCCEEDED(hr))
- str << " 2nd-command '" << QString::fromUtf16(buffer) << '\'';
+ str << " 2nd-command '" << QString::fromWCharArray(buffer) << '\'';
}
}
} // isException
diff --git a/src/plugins/debugger/cdb/coreengine.h b/src/plugins/debugger/cdb/coreengine.h
index c73fc7e72d..c6e25e9ced 100644
--- a/src/plugins/debugger/cdb/coreengine.h
+++ b/src/plugins/debugger/cdb/coreengine.h
@@ -168,6 +168,9 @@ signals:
// feature of the engine.
void modulesLoaded();
+public slots:
+ void outputVersion();
+
protected:
virtual void timerEvent(QTimerEvent* te);
diff --git a/src/plugins/debugger/debuggeragents.cpp b/src/plugins/debugger/debuggeragents.cpp
index 7ce93ba1c9..2e6ded7373 100644
--- a/src/plugins/debugger/debuggeragents.cpp
+++ b/src/plugins/debugger/debuggeragents.cpp
@@ -50,6 +50,7 @@
#include <utils/qtcassert.h>
#include <QtCore/QDebug>
+#include <QtCore/QMetaObject>
#include <QtGui/QMessageBox>
#include <QtGui/QPlainTextEdit>
@@ -74,6 +75,8 @@ namespace Internal {
it handles communication between the engine and the bineditor.
*/
+namespace { const int DataRange = 1024 * 1024; }
+
MemoryViewAgent::MemoryViewAgent(DebuggerEngine *engine, quint64 addr)
: QObject(engine), m_engine(engine)
{
@@ -113,11 +116,15 @@ void MemoryViewAgent::createBinEditor(quint64 addr)
connect(editor->widget(),
SIGNAL(newRangeRequested(Core::IEditor *, quint64)), this,
SLOT(provideNewRange(Core::IEditor*,quint64)));
+ connect(editor->widget(), SIGNAL(startOfFileRequested(Core::IEditor *)),
+ this, SLOT(handleStartOfFileRequested(Core::IEditor*)));
+ connect(editor->widget(), SIGNAL(endOfFileRequested(Core::IEditor *)),
+ this, SLOT(handleEndOfFileRequested(Core::IEditor*)));
m_editors << editor;
editorManager->activateEditor(editor);
QMetaObject::invokeMethod(editor->widget(), "setNewWindowRequestAllowed");
QMetaObject::invokeMethod(editor->widget(), "setLazyData",
- Q_ARG(quint64, addr), Q_ARG(int, 1024 * 1024), Q_ARG(int, BinBlockSize));
+ Q_ARG(quint64, addr), Q_ARG(int, DataRange), Q_ARG(int, BinBlockSize));
} else {
DebuggerPlugin::instance()->showMessageBox(QMessageBox::Warning,
tr("No memory viewer available"),
@@ -147,10 +154,26 @@ void MemoryViewAgent::addLazyData(QObject *editorToken, quint64 addr,
void MemoryViewAgent::provideNewRange(Core::IEditor *editor, quint64 address)
{
QMetaObject::invokeMethod(editor->widget(), "setLazyData",
- Q_ARG(quint64, address), Q_ARG(int, 1024 * 1024),
+ Q_ARG(quint64, address), Q_ARG(int, DataRange),
Q_ARG(int, BinBlockSize));
}
+// Since we are not dealing with files, we take these signals to mean
+// "move to start/end of range". This seems to make more sense than
+// jumping to the start or end of the address space, respectively.
+void MemoryViewAgent::handleStartOfFileRequested(Core::IEditor *editor)
+{
+ QMetaObject::invokeMethod(editor->widget(),
+ "setCursorPosition", Q_ARG(int, 0));
+}
+
+void MemoryViewAgent::handleEndOfFileRequested(Core::IEditor *editor)
+{
+ QMetaObject::invokeMethod(editor->widget(),
+ "setCursorPosition", Q_ARG(int, DataRange - 1));
+}
+
+
///////////////////////////////////////////////////////////////////////
//
diff --git a/src/plugins/debugger/debuggeragents.h b/src/plugins/debugger/debuggeragents.h
index 85eab2f353..396b06fad5 100644
--- a/src/plugins/debugger/debuggeragents.h
+++ b/src/plugins/debugger/debuggeragents.h
@@ -63,6 +63,8 @@ private:
Q_SLOT void createBinEditor(quint64 startAddr);
Q_SLOT void fetchLazyData(Core::IEditor *, quint64 block, bool sync);
Q_SLOT void provideNewRange(Core::IEditor *editor, quint64 address);
+ Q_SLOT void handleStartOfFileRequested(Core::IEditor *editor);
+ Q_SLOT void handleEndOfFileRequested(Core::IEditor *editor);
QList<QPointer<Core::IEditor> > m_editors;
QPointer<DebuggerEngine> m_engine;
diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp
index 3795f73688..b0ced81a84 100644
--- a/src/plugins/debugger/debuggerdialogs.cpp
+++ b/src/plugins/debugger/debuggerdialogs.cpp
@@ -207,6 +207,8 @@ void AttachCoreDialog::setCoreFile(const QString &fileName)
//
///////////////////////////////////////////////////////////////////////
+#ifndef Q_OS_WIN
+
static bool isUnixProcessId(const QString &procname)
{
for (int i = 0; i != procname.size(); ++i)
@@ -288,6 +290,7 @@ static QList<ProcData> unixProcessList()
}
return rc;
}
+#endif // Q_OS_WIN
static QList<ProcData> processList()
{
diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h
index 0f7ba09523..c542087c25 100644
--- a/src/plugins/debugger/debuggerengine.h
+++ b/src/plugins/debugger/debuggerengine.h
@@ -87,7 +87,7 @@ public:
QString dumperLibrary;
QStringList dumperLibraryLocations;
- Core::SshServerInfo sshserver;
+ Core::SshConnectionParameters connParams;
DebuggerStartMode startMode;
};
diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp
index 7e847eab48..c1b2a7fbcb 100644
--- a/src/plugins/debugger/debuggerplugin.cpp
+++ b/src/plugins/debugger/debuggerplugin.cpp
@@ -2480,13 +2480,14 @@ void DebuggerPlugin::clearCppCodeModelSnapshot()
d->m_codeModelSnapshot = CPlusPlus::Snapshot();
}
-void DebuggerPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag DebuggerPlugin::aboutToShutdown()
{
writeSettings();
if (d->m_uiSwitcher)
d->m_uiSwitcher->aboutToShutdown();
//if (d->m_engine)
// d->m_engine->shutdown();
+ return SynchronousShutdown;
}
void DebuggerPlugin::showMessage(const QString &msg, int channel, int timeout)
diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h
index d3fadff4c6..b183c3c855 100644
--- a/src/plugins/debugger/debuggerplugin.h
+++ b/src/plugins/debugger/debuggerplugin.h
@@ -121,7 +121,7 @@ private:
friend class Internal::DebuggerListener
;
bool initialize(const QStringList &arguments, QString *errorMessage);
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
void extensionsInitialized();
void remoteCommand(const QStringList &options, const QStringList &arguments);
diff --git a/src/plugins/debugger/gdb/remotegdbprocess.cpp b/src/plugins/debugger/gdb/remotegdbprocess.cpp
index fb7e2d0281..7288f44c7b 100644
--- a/src/plugins/debugger/gdb/remotegdbprocess.cpp
+++ b/src/plugins/debugger/gdb/remotegdbprocess.cpp
@@ -33,14 +33,15 @@
#include <ctype.h>
+using namespace Core;
+
namespace Debugger {
namespace Internal {
-RemoteGdbProcess::RemoteGdbProcess(const Core::SshServerInfo &server,
+RemoteGdbProcess::RemoteGdbProcess(const Core::SshConnectionParameters &connParams,
RemotePlainGdbAdapter *adapter, QObject *parent)
- : AbstractGdbProcess(parent), m_serverInfo(server), m_adapter(adapter)
+ : AbstractGdbProcess(parent), m_connParams(connParams), m_adapter(adapter)
{
-
}
QByteArray RemoteGdbProcess::readAllStandardOutput()
@@ -59,68 +60,122 @@ QByteArray RemoteGdbProcess::readAllStandardError()
void RemoteGdbProcess::start(const QString &cmd, const QStringList &args)
{
- m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo);
- m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
- m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
m_command = cmd;
m_cmdArgs = args;
- m_errOutputConn->start();
- m_appOutputConn->start();
- m_gdbConn->start();
- }
+ m_gdbStarted = false;
+ m_error.clear();
+ m_conn = SshConnection::create();
+ connect(m_conn.data(), SIGNAL(connected()), this, SLOT(handleConnected()));
+ connect(m_conn.data(), SIGNAL(error(SshError)), this,
+ SLOT(handleConnectionError()));
+ m_conn->connectToHost(m_connParams);
+}
-bool RemoteGdbProcess::waitForStarted()
+void RemoteGdbProcess::handleConnected()
{
- if (!waitForInputReady(m_appOutputConn))
- return false;
- if (!sendAndWaitForEcho(m_appOutputConn, readerCmdLine(AppOutputFile)))
- return false;
- if (!waitForInputReady(m_errOutputConn))
- return false;
- if (!sendAndWaitForEcho(m_errOutputConn, readerCmdLine(ErrOutputFile)))
- return false;
- if (!waitForInputReady(m_gdbConn))
- return false;
- connect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleAppOutput()));
- connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleErrOutput()));
- connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleGdbOutput()));
- m_gdbStarted = false;
- m_gdbCmdLine = "stty -echo && DISPLAY=:0.0 " + m_command.toUtf8() + ' '
+ m_fifoCreator = m_conn->createRemoteProcess( "rm -f "
+ + AppOutputFile + " && mkfifo " + AppOutputFile);
+ connect(m_fifoCreator.data(), SIGNAL(closed(int)), this,
+ SLOT(handleFifoCreationFinished(int)));
+ m_fifoCreator->start();
+}
+
+void RemoteGdbProcess::handleConnectionError()
+{
+ emitErrorExit(tr("Connection could not be established."));
+}
+
+void RemoteGdbProcess::handleFifoCreationFinished(int exitStatus)
+{
+ if (exitStatus != SshRemoteProcess::ExitedNormally) {
+ emitErrorExit(tr("Could not create FIFO."));
+ } else {
+ m_appOutputReader = m_conn->createRemoteProcess("cat " + AppOutputFile);
+ connect(m_appOutputReader.data(), SIGNAL(started()), this,
+ SLOT(handleAppOutputReaderStarted()));
+ connect(m_appOutputReader.data(), SIGNAL(closed(int)), this,
+ SLOT(handleAppOutputReaderFinished(int)));
+ m_appOutputReader->start();
+ }
+}
+
+void RemoteGdbProcess::handleAppOutputReaderStarted()
+{
+ connect(m_appOutputReader.data(), SIGNAL(outputAvailable(QByteArray)),
+ this, SLOT(handleAppOutput(QByteArray)));
+ QByteArray cmdLine = "DISPLAY=:0.0 " + m_command.toUtf8() + ' '
+ m_cmdArgs.join(QLatin1String(" ")).toUtf8()
- + " -tty=" + AppOutputFile + " 2>" + ErrOutputFile + '\n';
+ + " -tty=" + AppOutputFile;
if (!m_wd.isEmpty())
- m_gdbCmdLine.prepend("cd " + m_wd.toUtf8() + " && ");
- if (sendInput(m_gdbCmdLine) != m_gdbCmdLine.count())
- return false;
+ cmdLine.prepend("cd " + m_wd.toUtf8() + " && ");
+ m_gdbProc = m_conn->createRemoteProcess(cmdLine);
+ connect(m_gdbProc.data(), SIGNAL(started()), this,
+ SLOT(handleGdbStarted()));
+ connect(m_gdbProc.data(), SIGNAL(closed(int)), this,
+ SLOT(handleGdbFinished(int)));
+ connect(m_gdbProc.data(), SIGNAL(outputAvailable(QByteArray)), this,
+ SLOT(handleGdbOutput(QByteArray)));
+ connect(m_gdbProc.data(), SIGNAL(errorOutputAvailable(QByteArray)), this,
+ SLOT(handleErrOutput(QByteArray)));
+ m_gdbProc->start();
+}
+
+void RemoteGdbProcess::handleAppOutputReaderFinished(int exitStatus)
+{
+ if (exitStatus != SshRemoteProcess::ExitedNormally)
+ emitErrorExit(tr("Application output reader unexpectedly finished."));
+}
- return true;
+void RemoteGdbProcess::handleGdbStarted()
+{
+ m_gdbStarted = true;
+}
+
+void RemoteGdbProcess::handleGdbFinished(int exitStatus)
+{
+ switch (exitStatus) {
+ case SshRemoteProcess::FailedToStart:
+ emitErrorExit(tr("Remote gdb failed to start."));
+ break;
+ case SshRemoteProcess::KilledBySignal:
+ emitErrorExit(tr("Remote gdb crashed."));
+ break;
+ case SshRemoteProcess::ExitedNormally:
+ emit finished(m_gdbProc->exitCode(), QProcess::NormalExit);
+ break;
+ }
+ disconnect(m_conn.data(), 0, this, 0);
+ m_gdbProc = SshRemoteProcess::Ptr();
+ m_appOutputReader = SshRemoteProcess::Ptr();
+ m_conn->disconnectFromHost();
+}
+
+bool RemoteGdbProcess::waitForStarted()
+{
+ return m_error.isEmpty();
}
qint64 RemoteGdbProcess::write(const QByteArray &data)
{
- if (!m_gdbStarted || !m_inputToSend.isEmpty() || !m_lastSeqNr.isEmpty()) {
+ if (!m_gdbStarted || !m_inputToSend.isEmpty() || !m_lastSeqNr.isEmpty())
m_inputToSend.enqueue(data);
- return data.size();
- } else {
- return sendInput(data);
- }
+ else
+ sendInput(data);
+ return data.size();
}
void RemoteGdbProcess::kill()
{
- stopReaders();
- Core::InteractiveSshConnection::Ptr controlConn
- = Core::InteractiveSshConnection::create(m_serverInfo);
- if (!controlConn->hasError()) {
- if (controlConn->start())
- controlConn->sendInput("pkill -x gdb\r\n");
- }
+ SshRemoteProcess::Ptr killProc
+ = m_conn->createRemoteProcess("pkill -SIGKILL -x gdb");
+ killProc->start();
+}
- m_gdbConn->quit();
- emit finished(0, QProcess::CrashExit);
+void RemoteGdbProcess::interruptInferior()
+{
+ SshRemoteProcess::Ptr intProc
+ = m_conn->createRemoteProcess("pkill -x -SIGINT gdb");
+ intProc->start();
}
QProcess::ProcessState RemoteGdbProcess::state() const
@@ -130,35 +185,19 @@ QProcess::ProcessState RemoteGdbProcess::state() const
QString RemoteGdbProcess::errorString() const
{
- return m_gdbConn ? m_gdbConn->error() : QString();
+ return m_error;
}
-void RemoteGdbProcess::handleGdbOutput()
+void RemoteGdbProcess::handleGdbOutput(const QByteArray &output)
{
- m_currentGdbOutput
- += removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0));
+ // TODO: Carriage return removal still necessary?
+ m_currentGdbOutput += removeCarriageReturn(output);
#if 0
qDebug("%s: complete unread output is '%s'", Q_FUNC_INFO, m_currentGdbOutput.data());
#endif
- if (checkForGdbExit(m_currentGdbOutput)) {
- m_currentGdbOutput.clear();
- return;
- }
-
if (!m_currentGdbOutput.endsWith('\n'))
return;
- if (!m_gdbStarted) {
- const int index = m_currentGdbOutput.indexOf(m_gdbCmdLine);
- if (index != -1)
- m_currentGdbOutput.remove(index, m_gdbCmdLine.size());
- // Note: We can't guarantee that we will match the command line,
- // because the remote terminal sometimes inserts control characters.
- // Otherwise we could set m_gdbStarted here.
- }
-
- m_gdbStarted = true;
-
if (m_currentGdbOutput.contains(m_lastSeqNr + '^'))
m_lastSeqNr.clear();
@@ -187,7 +226,7 @@ QProcessEnvironment RemoteGdbProcess::processEnvironment() const
void RemoteGdbProcess::setProcessEnvironment(const QProcessEnvironment & /* env */)
{
- // TODO: Do something.
+ // TODO: Do something. (if remote process exists: set, otherwise queue)
}
void RemoteGdbProcess::setEnvironment(const QStringList & /* env */)
@@ -211,48 +250,27 @@ int RemoteGdbProcess::findAnchor(const QByteArray &data) const
return -1;
}
-qint64 RemoteGdbProcess::sendInput(const QByteArray &data)
+void RemoteGdbProcess::sendInput(const QByteArray &data)
{
int pos;
for (pos = 0; pos < data.size(); ++pos)
if (!isdigit(data.at(pos)))
break;
m_lastSeqNr = data.left(pos);
- return m_gdbConn->sendInput(data) ? data.size() : 0;
+ m_gdbProc->sendInput(data);
}
-void RemoteGdbProcess::handleAppOutput()
+void RemoteGdbProcess::handleAppOutput(const QByteArray &output)
{
- m_adapter->handleApplicationOutput(m_appOutputConn->waitForRemoteOutput(0));
+ m_adapter->handleApplicationOutput(output);
}
-void RemoteGdbProcess::handleErrOutput()
+void RemoteGdbProcess::handleErrOutput(const QByteArray &output)
{
- m_errorOutput += m_errOutputConn->waitForRemoteOutput(0);
+ m_errorOutput += output;
emit readyReadStandardError();
}
-void RemoteGdbProcess::stopReaders()
-{
- if (m_appOutputConn) {
- disconnect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleAppOutput()));
- m_appOutputConn->sendInput(CtrlC);
- m_appOutputConn->quit();
- }
- if (m_errOutputConn) {
- disconnect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleErrOutput()));
- m_errOutputConn->sendInput(CtrlC);
- m_errOutputConn->quit();
- }
-}
-
-QByteArray RemoteGdbProcess::readerCmdLine(const QByteArray &file)
-{
- return "rm -f " + file + " && mkfifo " + file + " && cat " + file + "\r\n";
-}
-
QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data)
{
QByteArray output;
@@ -264,48 +282,16 @@ QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data)
return output;
}
-bool RemoteGdbProcess::checkForGdbExit(QByteArray &output)
+void RemoteGdbProcess::emitErrorExit(const QString &error)
{
- const QByteArray exitString("^exit");
- const int exitPos = output.indexOf(exitString);
- if (exitPos == -1)
- return false;
-
- emit finished(0, QProcess::NormalExit);
- disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleGdbOutput()));
- output.remove(exitPos + exitString.size(), output.size());
- stopReaders();
- return true;
-}
-
-bool RemoteGdbProcess::waitForInputReady(Core::InteractiveSshConnection::Ptr &conn)
-{
- if (conn->waitForRemoteOutput(m_serverInfo.timeout).isEmpty())
- return false;
- while (!conn->waitForRemoteOutput(100).isEmpty())
- ;
- return true;
-}
-
-bool RemoteGdbProcess::sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn,
- const QByteArray &cmdLine)
-{
- conn->sendInput(cmdLine);
- QByteArray allOutput;
- while (!allOutput.endsWith(cmdLine)) {
- const QByteArray curOutput = conn->waitForRemoteOutput(100);
- if (curOutput.isEmpty())
- return false;
- allOutput += curOutput;
+ if (m_error.isEmpty()) {
+ m_error = error;
+ emit finished(-1, QProcess::CrashExit);
}
- return true;
}
-
const QByteArray RemoteGdbProcess::CtrlC = QByteArray(1, 0x3);
const QByteArray RemoteGdbProcess::AppOutputFile("app_output");
-const QByteArray RemoteGdbProcess::ErrOutputFile("err_output");
} // namespace Internal
} // namespace Debugger
diff --git a/src/plugins/debugger/gdb/remotegdbprocess.h b/src/plugins/debugger/gdb/remotegdbprocess.h
index 1008b334da..9a9679b695 100644
--- a/src/plugins/debugger/gdb/remotegdbprocess.h
+++ b/src/plugins/debugger/gdb/remotegdbprocess.h
@@ -33,6 +33,7 @@
#include "abstractgdbprocess.h"
#include <coreplugin/ssh/sshconnection.h>
+#include <coreplugin/ssh/sshremoteprocess.h>
#include <QtCore/QByteArray>
#include <QtCore/QQueue>
@@ -46,7 +47,7 @@ class RemoteGdbProcess : public AbstractGdbProcess
{
Q_OBJECT
public:
- RemoteGdbProcess(const Core::SshServerInfo &server,
+ RemoteGdbProcess(const Core::SshConnectionParameters &server,
RemotePlainGdbAdapter *adapter, QObject *parent = 0);
virtual QByteArray readAllStandardOutput();
@@ -65,32 +66,37 @@ public:
virtual void setEnvironment(const QStringList &env);
virtual void setWorkingDirectory(const QString &dir);
+ void interruptInferior();
+
static const QByteArray CtrlC;
private slots:
- void handleGdbOutput();
- void handleAppOutput();
- void handleErrOutput();
+ void handleConnected();
+ void handleConnectionError();
+ void handleFifoCreationFinished(int exitStatus);
+ void handleAppOutputReaderStarted();
+ void handleAppOutputReaderFinished(int exitStatus);
+ void handleGdbStarted();
+ void handleGdbFinished(int exitStatus);
+ void handleGdbOutput(const QByteArray &output);
+ void handleAppOutput(const QByteArray &output);
+ void handleErrOutput(const QByteArray &output);
private:
static QByteArray readerCmdLine(const QByteArray &file);
int findAnchor(const QByteArray &data) const;
- qint64 sendInput(const QByteArray &data);
- void stopReaders();
+ void sendInput(const QByteArray &data);
QByteArray removeCarriageReturn(const QByteArray &data);
- bool checkForGdbExit(QByteArray &output);
- bool sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn,
- const QByteArray &cmdLine);
- bool waitForInputReady(Core::InteractiveSshConnection::Ptr &conn);
+ void emitErrorExit(const QString &error);
static const QByteArray AppOutputFile;
- static const QByteArray ErrOutputFile;
- Core::SshServerInfo m_serverInfo;
- Core::InteractiveSshConnection::Ptr m_gdbConn;
- Core::InteractiveSshConnection::Ptr m_appOutputConn;
- Core::InteractiveSshConnection::Ptr m_errOutputConn;
+ Core::SshConnectionParameters m_connParams;
+ Core::SshConnection::Ptr m_conn;
+ Core::SshRemoteProcess::Ptr m_gdbProc;
+ Core::SshRemoteProcess::Ptr m_appOutputReader;
+ Core::SshRemoteProcess::Ptr m_fifoCreator;
QByteArray m_gdbOutput;
QByteArray m_errorOutput;
QString m_command;
@@ -99,7 +105,7 @@ private:
QQueue<QByteArray> m_inputToSend;
QByteArray m_currentGdbOutput;
QByteArray m_lastSeqNr;
- QByteArray m_gdbCmdLine;
+ QString m_error;
bool m_gdbStarted;
RemotePlainGdbAdapter *m_adapter;
diff --git a/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
index 2f51918883..88946c1136 100644
--- a/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
+++ b/src/plugins/debugger/gdb/remoteplaingdbadapter.cpp
@@ -40,7 +40,7 @@ namespace Internal {
RemotePlainGdbAdapter::RemotePlainGdbAdapter(GdbEngine *engine,
QObject *parent)
: AbstractPlainGdbAdapter(engine, parent),
- m_gdbProc(engine->startParameters().sshserver, this)
+ m_gdbProc(engine->startParameters().connParams, this)
{
}
@@ -60,7 +60,7 @@ void RemotePlainGdbAdapter::startAdapter()
void RemotePlainGdbAdapter::interruptInferior()
{
- m_gdbProc.write(RemoteGdbProcess::CtrlC);
+ m_gdbProc.interruptInferior();
}
QByteArray RemotePlainGdbAdapter::execFilePath() const
diff --git a/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
index a3b9f7624d..f9bf949cbc 100644
--- a/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
+++ b/src/plugins/debugger/gdb/tcftrkgdbadapter.cpp
@@ -270,6 +270,7 @@ void TcfTrkGdbAdapter::tcftrkEvent(const tcftrk::TcfTrkEvent &e)
switch (e.type()) {
case tcftrk::TcfTrkEvent::LocatorHello:
+ m_trkDevice->sendLoggingAddListenerCommand(TcfTrkCallback());
startGdb(); // Commands are only accepted after hello
break;
case tcftrk::TcfTrkEvent::RunControlModuleLoadSuspended: // A module was loaded
@@ -311,6 +312,9 @@ void TcfTrkGdbAdapter::tcftrkEvent(const tcftrk::TcfTrkEvent &e)
Symbian::RegisterCount);
}
break;
+ case tcftrk::TcfTrkEvent::LoggingWriteEvent: // TODO: Not tested yet.
+ showMessage(e.toString(), AppOutput);
+ break;
default:
break;
}
diff --git a/src/plugins/debugger/shared/sharedlibraryinjector.cpp b/src/plugins/debugger/shared/sharedlibraryinjector.cpp
index c83c2a402e..04c39071f5 100644
--- a/src/plugins/debugger/shared/sharedlibraryinjector.cpp
+++ b/src/plugins/debugger/shared/sharedlibraryinjector.cpp
@@ -50,7 +50,7 @@ template <class SymbolType>
inline bool resolveSymbol(const char *libraryName, HMODULE libraryHandle, const char *symbolName, SymbolType *s, QString *errorMessage)
{
*s = 0;
- FARPROC WINAPI vs = ::GetProcAddress(libraryHandle, symbolName);
+ FARPROC vs = ::GetProcAddress(libraryHandle, symbolName);
if (vs == 0) {
*errorMessage = QString::fromLatin1("Unable to resolve '%2' in '%1'.").arg(QString::fromAscii(symbolName), QString::fromAscii(libraryName));
return false;
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index be26c36f2c..4f4c2f542d 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -693,8 +693,8 @@ QString decodeData(const QByteArray &ba, int encoding)
decodedBa[i + 1] = c;
}
//qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
- return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
- (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
+ return doubleQuote + QString::fromUtf16(reinterpret_cast<const ushort *>
+ (decodedBa.data()), decodedBa.size() / 2) + doubleQuote;
}
}
qDebug() << "ENCODING ERROR: " << encoding;
diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp
index f5a44b0602..d4bde7f222 100644
--- a/src/plugins/fakevim/fakevimhandler.cpp
+++ b/src/plugins/fakevim/fakevimhandler.cpp
@@ -149,19 +149,21 @@ enum Mode
enum SubMode
{
NoSubMode,
- ChangeSubMode, // Used for c
- DeleteSubMode, // Used for d
- FilterSubMode, // Used for !
- IndentSubMode, // Used for =
- RegisterSubMode, // Used for "
- ShiftLeftSubMode, // Used for <
- ShiftRightSubMode, // Used for >
- TransformSubMode, // Used for ~/gu/gU
- WindowSubMode, // Used for Ctrl-w
- YankSubMode, // Used for y
- ZSubMode, // Used for z
- CapitalZSubMode, // Used for Z
- ReplaceSubMode, // Used for r
+ ChangeSubMode, // Used for c
+ DeleteSubMode, // Used for d
+ FilterSubMode, // Used for !
+ IndentSubMode, // Used for =
+ RegisterSubMode, // Used for "
+ ShiftLeftSubMode, // Used for <
+ ShiftRightSubMode, // Used for >
+ TransformSubMode, // Used for ~/gu/gU
+ WindowSubMode, // Used for Ctrl-w
+ YankSubMode, // Used for y
+ ZSubMode, // Used for z
+ CapitalZSubMode, // Used for Z
+ ReplaceSubMode, // Used for r
+ OpenSquareSubMode, // Used for [
+ CloseSquareSubMode, // Used for ]
};
/*! A \e SubSubMode is used for things that require one more data item
@@ -232,6 +234,11 @@ struct Column
int logical; // Column on screen.
};
+QDebug operator<<(QDebug ts, const Column &col)
+{
+ return ts << "(p: " << col.physical << ", l: " << col.logical << ")";
+}
+
struct CursorPosition
{
// for jump history
@@ -292,7 +299,7 @@ QDebug operator<<(QDebug ts, const Range &range)
ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
- : cmd(c), hasBang(false), args(a), range(r)
+ : cmd(c), hasBang(false), args(a), range(r), count(1)
{}
QDebug operator<<(QDebug ts, const ExCommand &cmd)
@@ -600,12 +607,15 @@ public:
EventResult handleCommandMode(const Input &);
EventResult handleRegisterMode(const Input &);
EventResult handleExMode(const Input &);
+ EventResult handleOpenSquareSubMode(const Input &);
+ EventResult handleCloseSquareSubMode(const Input &);
EventResult handleSearchSubSubMode(const Input &);
EventResult handleCommandSubSubMode(const Input &);
void finishMovement(const QString &dotCommand = QString());
void finishMovement(const QString &dotCommand, int count);
void resetCommandMode();
void search(const SearchData &sd);
+ void searchBalanced(bool forward, QChar needle, QChar other);
void highlightMatches(const QString &needle);
void stopIncrementalFind();
@@ -1680,6 +1690,34 @@ EventResult FakeVimHandler::Private::handleCommandSubSubMode(const Input &input)
return handled;
}
+EventResult FakeVimHandler::Private::handleOpenSquareSubMode(const Input &input)
+{
+ EventResult handled = EventHandled;
+ m_submode = NoSubMode;
+ if (input.is('{')) {
+ searchBalanced(false, '{', '}');
+ } else if (input.is('(')) {
+ searchBalanced(false, '(', ')');
+ } else {
+ handled = EventUnhandled;
+ }
+ return handled;
+}
+
+EventResult FakeVimHandler::Private::handleCloseSquareSubMode(const Input &input)
+{
+ EventResult handled = EventHandled;
+ m_submode = NoSubMode;
+ if (input.is('}')) {
+ searchBalanced(true, '}', '{');
+ } else if (input.is(')')) {
+ searchBalanced(true, ')', '(');
+ } else {
+ handled = EventUnhandled;
+ }
+ return handled;
+}
+
EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
{
EventResult handled = EventHandled;
@@ -1698,6 +1736,10 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
}
} else if (m_subsubmode != NoSubSubMode) {
handleCommandSubSubMode(input);
+ } else if (m_submode == OpenSquareSubMode) {
+ handled = handleOpenSquareSubMode(input);
+ } else if (m_submode == CloseSquareSubMode) {
+ handled = handleCloseSquareSubMode(input);
} else if (m_submode == WindowSubMode) {
emit q->windowCommandRequested(input.key());
m_submode = NoSubMode;
@@ -2035,22 +2077,25 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
m_opcount = m_mvcount;
m_mvcount.clear();
m_submode = DeleteSubMode;
- } else if ((input.is('d') || input.is('x')) && isVisualCharMode()) {
- leaveVisualMode();
- m_submode = DeleteSubMode;
- finishMovement();
- } else if ((input.is('d') || input.is('x')) && isVisualLineMode()) {
- leaveVisualMode();
- m_rangemode = RangeLineMode;
- yankText(currentRange(), m_register);
- removeText(currentRange());
- handleStartOfLine();
- } else if ((input.is('d') || input.is('x')) && isVisualBlockMode()) {
- leaveVisualMode();
- m_rangemode = RangeBlockMode;
- yankText(currentRange(), m_register);
- removeText(currentRange());
- setPosition(qMin(position(), anchor()));
+ } else if ((input.is('d') || input.is('x') || input.isKey(Key_Delete))
+ && isVisualMode()) {
+ if (isVisualCharMode()) {
+ leaveVisualMode();
+ m_submode = DeleteSubMode;
+ finishMovement();
+ } else if (isVisualLineMode()) {
+ leaveVisualMode();
+ m_rangemode = RangeLineMode;
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
+ handleStartOfLine();
+ } else if (isVisualBlockMode()) {
+ leaveVisualMode();
+ m_rangemode = RangeBlockMode;
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
+ setPosition(qMin(position(), anchor()));
+ }
} else if (input.is('D') && isNoVisualMode()) {
if (atEndOfLine())
moveLeft();
@@ -2170,7 +2215,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
setCursorPosition(m_jumpListRedo.last());
m_jumpListRedo.pop_back();
}
- } else if (input.is('j') || input.isKey(Key_Down)) {
+ } else if (input.is('j') || input.isKey(Key_Down)
+ || input.isControl('j') || input.isControl('n')) {
m_movetype = MoveLineWise;
setAnchor();
moveDown(count());
@@ -2198,7 +2244,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
}
endEditBlock();
finishMovement();
- } else if (input.is('k') || input.isKey(Key_Up)) {
+ } else if (input.is('k') || input.isKey(Key_Up) || input.isControl('p')) {
m_movetype = MoveLineWise;
setAnchor();
moveUp(count());
@@ -2472,6 +2518,10 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
else if (input.is('U'))
m_subsubmode = UpCaseSubSubMode;
finishMovement();
+ } else if (input.is('[')) {
+ m_submode = OpenSquareSubMode;
+ } else if (input.is(']')) {
+ m_submode = CloseSquareSubMode;
} else if (input.isKey(Key_PageDown) || input.isControl('f')) {
moveDown(count() * (linesOnScreen() - 2) - cursorLineOnScreen());
scrollToLine(cursorLine());
@@ -2486,6 +2536,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
setAnchor();
moveRight(qMin(1, rightDist()));
removeText(currentRange());
+ if (atEndOfLine())
+ moveLeft();
} else if (input.isKey(Key_BracketLeft) || input.isKey(Key_BracketRight)) {
} else if (input.isControl(Key_BracketRight)) {
@@ -2624,11 +2676,11 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
if (col.logical <= ind.logical && col.logical
&& startsWithWhitespace(data, col.physical)) {
const int ts = config(ConfigTabStop).toInt();
- const int newcol = col.logical - 1 - (col.logical - 1) % ts;
- data.remove(0, col.physical);
- setLineContents(line, tabExpand(newcol).append(data));
+ const int newl = col.logical - 1 - (col.logical - 1) % ts;
+ const QString prefix = tabExpand(newl);
+ setLineContents(line, prefix + data.mid(col.physical));
moveToStartOfLine();
- moveRight(newcol);
+ moveRight(prefix.size());
m_lastInsertion.clear(); // FIXME
} else {
m_tc.deletePreviousChar();
@@ -2649,14 +2701,18 @@ EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
removeAutomaticIndentation();
moveUp(count() * (linesOnScreen() - 2));
m_lastInsertion.clear();
- } else if (input.isKey(Key_Tab) && hasConfig(ConfigExpandTab)) {
+ } else if (input.isKey(Key_Tab)) {
m_justAutoIndented = 0;
- const int ts = config(ConfigTabStop).toInt();
- const int col = logicalCursorColumn();
- QString str = QString(ts - col % ts, ' ');
- m_lastInsertion.append(str);
- insertText(str);
- setTargetColumn();
+ if (hasConfig(ConfigExpandTab)) {
+ const int ts = config(ConfigTabStop).toInt();
+ const int col = logicalCursorColumn();
+ QString str = QString(ts - col % ts, ' ');
+ m_lastInsertion.append(str);
+ insertText(str);
+ setTargetColumn();
+ } else {
+ insertInInsertMode(input.raw());
+ }
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
int shift = config(ConfigShiftWidth).toInt();
@@ -3393,7 +3449,7 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
if (line.startsWith(QLatin1Char('%')))
line = "1,$" + line.mid(1);
- int beginLine = readLineCode(line);
+ const int beginLine = readLineCode(line);
int endLine = -1;
if (line.startsWith(',')) {
line = line.mid(1);
@@ -3411,6 +3467,8 @@ void FakeVimHandler::Private::handleExCommand(const QString &line0)
cmd.hasBang = arg0.endsWith('!');
if (cmd.hasBang)
cmd.cmd.chop(1);
+ if (beginLine != -1)
+ cmd.count = beginLine;
//qDebug() << "CMD: " << cmd;
enterCommandMode();
@@ -3459,6 +3517,40 @@ static void vimPatternToQtPattern(QString *needle, QTextDocument::FindFlags *fla
//qDebug() << "NEEDLE " << needle0 << needle;
}
+void FakeVimHandler::Private::searchBalanced(bool forward, QChar needle, QChar other)
+{
+ int level = 1;
+ int pos = m_tc.position();
+ const int npos = forward ? lastPositionInDocument() : 0;
+ QTextDocument *doc = m_tc.document();
+ while (true) {
+ if (forward)
+ ++pos;
+ else
+ --pos;
+ if (pos == npos)
+ return;
+ QChar c = doc->characterAt(pos);
+ if (c == other)
+ ++level;
+ else if (c == needle)
+ --level;
+ if (level == 0) {
+ const int oldLine = cursorLine() - cursorLineOnScreen();
+ m_tc.setPosition(pos, MoveAnchor);
+ m_tc.clearSelection();
+ EDITOR(setTextCursor(m_tc));
+ // Making this unconditional feels better, but is not "vim like".
+ if (oldLine != cursorLine() - cursorLineOnScreen())
+ scrollToLine(cursorLine() - linesOnScreen() / 2);
+ setTargetColumn();
+ updateSelection();
+ recordJump();
+ return;
+ }
+ }
+}
+
void FakeVimHandler::Private::search(const SearchData &sd)
{
if (sd.needle.isEmpty())
@@ -3613,13 +3705,13 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat)
if (hasConfig(ConfigStartOfLine))
targetPos = firstPositionInLine(beginLine);
- int len = config(ConfigShiftWidth).toInt() * repeat;
- QString indent(len, ' ');
-
+ const int sw = config(ConfigShiftWidth).toInt();
beginEditBlock(targetPos);
for (int line = beginLine; line <= endLine; ++line) {
- setPosition(firstPositionInLine(line));
- m_tc.insertText(indent);
+ QString data = lineContents(line);
+ const Column col = indentation(data);
+ data = tabExpand(col.logical + sw * repeat) + data.mid(col.physical);
+ setLineContents(line, data);
}
endEditBlock();
@@ -4301,10 +4393,12 @@ void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
{
QTextBlock block = m_tc.document()->findBlockByNumber(line - 1);
QTextCursor tc = m_tc;
- tc.setPosition(block.position());
- tc.setPosition(block.position() + block.length() - 1, KeepAnchor);
+ const int begin = block.position();
+ const int len = block.length();
+ tc.setPosition(begin);
+ tc.setPosition(begin + len - 1, KeepAnchor);
tc.removeSelectedText();
- fixMarks(block.position(), block.length() - contents.size());
+ fixMarks(begin, contents.size() + 1 - len);
tc.insertText(contents);
}
diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h
index f5b0db1c04..6db23a2d46 100644
--- a/src/plugins/fakevim/fakevimhandler.h
+++ b/src/plugins/fakevim/fakevimhandler.h
@@ -60,7 +60,7 @@ struct Range
struct ExCommand
{
- ExCommand() : hasBang(false) {}
+ ExCommand() : hasBang(false), count(1) {}
ExCommand(const QString &cmd, const QString &args = QString(),
const Range &range = Range());
@@ -68,6 +68,7 @@ struct ExCommand
bool hasBang;
QString args;
Range range;
+ int count;
};
class FakeVimHandler : public QObject
diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp
index 0ecdcdc4fa..fc313bac38 100644
--- a/src/plugins/fakevim/fakevimplugin.cpp
+++ b/src/plugins/fakevim/fakevimplugin.cpp
@@ -102,8 +102,6 @@ const char * const SETTINGS_CATEGORY = "D.FakeVim";
const char * const SETTINGS_CATEGORY_FAKEVIM_ICON = ":/core/images/category_fakevim.png";
const char * const SETTINGS_ID = "A.General";
const char * const SETTINGS_EX_CMDS_ID = "B.ExCommands";
-const char * const CMD_FILE_NEXT = "FakeVim.SwitchFileNext";
-const char * const CMD_FILE_PREV = "FakeVim.SwitchFilePrev";
} // namespace Constants
} // namespace FakeVim
@@ -119,6 +117,7 @@ namespace FakeVim {
namespace Internal {
typedef QMap<QString, QRegExp> CommandMap;
+typedef QLatin1String _;
class FakeVimOptionPage : public Core::IOptionsPage
{
@@ -128,11 +127,12 @@ public:
FakeVimOptionPage() {}
// IOptionsPage
- QString id() const { return QLatin1String(Constants::SETTINGS_ID); }
+ QString id() const { return _(Constants::SETTINGS_ID); }
QString displayName() const { return tr("General"); }
- QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
+ QString category() const { return _(Constants::SETTINGS_CATEGORY); }
QString displayCategory() const { return tr("FakeVim"); }
- QIcon categoryIcon() const { return QIcon(QLatin1String(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON)); }
+ QIcon categoryIcon() const
+ { return QIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON)); }
QWidget *createPage(QWidget *parent);
void apply() { m_group.apply(ICore::instance()->settings()); }
@@ -238,7 +238,7 @@ void FakeVimOptionPage::setQtStyle()
m_ui.checkBoxAutoIndent->setChecked(true);
m_ui.checkBoxSmartIndent->setChecked(true);
m_ui.checkBoxIncSearch->setChecked(true);
- m_ui.lineEditBackspace->setText(QLatin1String("indent,eol,start"));
+ m_ui.lineEditBackspace->setText(_("indent,eol,start"));
}
void FakeVimOptionPage::setPlainStyle()
@@ -275,7 +275,7 @@ struct CommandItem
QTreeWidgetItem *m_item;
};
-Q_DECLARE_METATYPE(CommandItem*);
+Q_DECLARE_METATYPE(CommandItem *);
namespace FakeVim {
namespace Internal {
@@ -288,9 +288,9 @@ public:
FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
// IOptionsPage
- QString id() const { return QLatin1String(Constants::SETTINGS_EX_CMDS_ID); }
+ QString id() const { return _(Constants::SETTINGS_EX_CMDS_ID); }
QString displayName() const { return tr("Ex Command Mapping"); }
- QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
+ QString category() const { return _(Constants::SETTINGS_CATEGORY); }
QString displayCategory() const { return tr("FakeVim"); }
QIcon categoryIcon() const { return QIcon(); } // TODO: Icon for FakeVim
@@ -512,9 +512,8 @@ private slots:
void handleDelayedQuitAll(bool forced);
void handleDelayedQuit(bool forced, Core::IEditor *editor);
- void switchFile(bool previous);
- void switchFileNext();
- void switchFilePrev();
+ void switchToFile(int n);
+ int currentFile() const;
signals:
void delayedQuitRequested(bool forced, Core::IEditor *editor);
@@ -525,6 +524,8 @@ private:
FakeVimOptionPage *m_fakeVimOptionsPage;
FakeVimExCommandsPage *m_fakeVimExCommandsPage;
QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
+ QPointer<EditorManager> m_editorManager;
+ EditorManager *editorManager() const { return m_editorManager; }
void triggerAction(const QString &code);
void setActionChecked(const QString &code, bool check);
@@ -546,10 +547,6 @@ FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
q = plugin;
m_fakeVimOptionsPage = 0;
m_fakeVimExCommandsPage = 0;
- defaultExCommandMap()[Constants::CMD_FILE_NEXT] =
- QRegExp("^n(ext)?!?( (.*))?$");
- defaultExCommandMap()[Constants::CMD_FILE_PREV] =
- QRegExp("^(N(ext)?|prev(ious)?)!?( (.*))?$");
defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
QRegExp("^A$");
defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
@@ -560,7 +557,7 @@ FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
QRegExp("^tag?$");
defaultExCommandMap()[Core::Constants::GO_BACK] =
QRegExp("^pop?$");
- defaultExCommandMap()[QLatin1String("QtCreator.Locate")] =
+ defaultExCommandMap()[_("QtCreator.Locate")] =
QRegExp("^e$");
}
@@ -584,6 +581,7 @@ void FakeVimPluginPrivate::aboutToShutdown()
bool FakeVimPluginPrivate::initialize()
{
+ m_editorManager = Core::ICore::instance()->editorManager();
Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
QTC_ASSERT(actionManager, return false);
@@ -607,10 +605,9 @@ bool FakeVimPluginPrivate::initialize()
advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
// EditorManager
- QObject *editorManager = Core::ICore::instance()->editorManager();
- connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
+ connect(editorManager(), SIGNAL(editorAboutToClose(Core::IEditor*)),
this, SLOT(editorAboutToClose(Core::IEditor*)));
- connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
+ connect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
this, SLOT(editorOpened(Core::IEditor*)));
connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
@@ -618,16 +615,6 @@ bool FakeVimPluginPrivate::initialize()
connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
this, SLOT(maybeReadVimRc()));
- QAction *switchFileNextAction = new QAction(tr("Switch to next file"), this);
- cmd = actionManager->registerAction(switchFileNextAction, Constants::CMD_FILE_NEXT, globalcontext);
- cmd->setAttribute(Command::CA_Hide);
- connect(switchFileNextAction, SIGNAL(triggered()), this, SLOT(switchFileNext()));
-
- QAction *switchFilePrevAction = new QAction(tr("Switch to previous file"), this);
- cmd = actionManager->registerAction(switchFilePrevAction, Constants::CMD_FILE_PREV, globalcontext);
- cmd->setAttribute(Command::CA_Hide);
- connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));
-
// Delayed operations.
connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
@@ -645,7 +632,7 @@ static const char *idKey = "Command";
void FakeVimPluginPrivate::writeSettings(QSettings *settings)
{
- settings->beginWriteArray(QLatin1String(exCommandMapGroup));
+ settings->beginWriteArray(_(exCommandMapGroup));
int count = 0;
typedef CommandMap::const_iterator Iterator;
@@ -657,8 +644,8 @@ void FakeVimPluginPrivate::writeSettings(QSettings *settings)
if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
|| (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
settings->setArrayIndex(count);
- settings->setValue(QLatin1String(idKey), id);
- settings->setValue(QLatin1String(reKey), re.pattern());
+ settings->setValue(_(idKey), id);
+ settings->setValue(_(reKey), re.pattern());
++count;
}
}
@@ -670,11 +657,11 @@ void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
exCommandMap() = defaultExCommandMap();
- int size = settings->beginReadArray(QLatin1String(exCommandMapGroup));
+ int size = settings->beginReadArray(_(exCommandMapGroup));
for (int i = 0; i < size; ++i) {
settings->setArrayIndex(i);
- const QString id = settings->value(QLatin1String(idKey)).toString();
- const QString re = settings->value(QLatin1String(reKey)).toString();
+ const QString id = settings->value(_(idKey)).toString();
+ const QString re = settings->value(_(reKey)).toString();
exCommandMap()[id] = QRegExp(re);
}
settings->endArray();
@@ -702,8 +689,8 @@ void FakeVimPluginPrivate::maybeReadVimRc()
void FakeVimPluginPrivate::showSettingsDialog()
{
Core::ICore::instance()->showOptionsDialog(
- QLatin1String(Constants::SETTINGS_CATEGORY),
- QLatin1String(Constants::SETTINGS_ID));
+ _(Constants::SETTINGS_CATEGORY),
+ _(Constants::SETTINGS_ID));
}
void FakeVimPluginPrivate::triggerAction(const QString &code)
@@ -873,15 +860,15 @@ void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
if (Find::FindPlugin::instance())
Find::FindPlugin::instance()->setUseFakeVim(on);
if (on) {
- Core::EditorManager::instance()->showEditorStatusBar(
- QLatin1String(Constants::MINI_BUFFER),
+ editorManager()->showEditorStatusBar(
+ _(Constants::MINI_BUFFER),
"vi emulation mode. Type :q to leave. Use , Ctrl-R to trigger run.",
tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
foreach (Core::IEditor *editor, m_editorToHandler.keys())
m_editorToHandler[editor]->setupWidget();
} else {
- Core::EditorManager::instance()->hideEditorStatusBar(
- QLatin1String(Constants::MINI_BUFFER));
+ editorManager()->hideEditorStatusBar(
+ _(Constants::MINI_BUFFER));
TextEditor::TabSettings ts =
TextEditor::TextEditorSettings::instance()->tabSettings();
foreach (Core::IEditor *editor, m_editorToHandler.keys())
@@ -912,7 +899,7 @@ void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
{
using namespace Core;
- //qDebug() << "PLUGIN HANDLE: " << cmd.cmd;
+ //qDebug() << "PLUGIN HANDLE: " << cmd.cmd << cmd.count;
*handled = false;
@@ -920,8 +907,7 @@ void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
if (!handler)
return;
- EditorManager *editorManager = EditorManager::instance();
- QTC_ASSERT(editorManager, return);
+ QTC_ASSERT(editorManager(), return);
*handled = true;
if (cmd.cmd == "w" || cmd.cmd == "write") {
@@ -980,6 +966,20 @@ void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
setActionChecked(Find::Constants::CASE_SENSITIVE, true);
*handled = false; // Let the handler see it as well.
}
+ } else if (cmd.cmd == "n" || cmd.cmd == "next") {
+ // :n[ext]
+ switchToFile(currentFile() + cmd.count);
+ } else if (cmd.cmd == "prev" || cmd.cmd == "previous"
+ || cmd.cmd == "N" || cmd.cmd == "Next") {
+ // :prev[ious]
+ switchToFile(currentFile() - cmd.count);
+ } else if (cmd.cmd == "bn" || cmd.cmd == "bnext") {
+ // :bn[ext]
+ switchToFile(currentFile() + cmd.count);
+ } else if (cmd.cmd == "bp" || cmd.cmd == "bprevious"
+ || cmd.cmd == "bN" || cmd.cmd == "bNext") {
+ // :bp[revious]
+ switchToFile(currentFile() - cmd.count);
} else {
// Check whether one of the configure commands matches.
typedef CommandMap::const_iterator Iterator;
@@ -1027,7 +1027,7 @@ void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
if (undoFakeEOL)
cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
if (match == TextEditor::TextBlockUserData::NoMatch) {
- // backward matching is according to the character before the cursor
+ // Backward matching is according to the character before the cursor.
bool undoMove = false;
if (!cursor->atBlockEnd()) {
cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
@@ -1102,8 +1102,8 @@ void FakeVimPluginPrivate::quitFakeVim()
void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
{
//qDebug() << "SHOW COMMAND BUFFER" << contents;
- Core::EditorManager::instance()->showEditorStatusBar(
- QLatin1String(Constants::MINI_BUFFER), contents,
+ editorManager()->showEditorStatusBar(
+ _(Constants::MINI_BUFFER), contents,
tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
}
@@ -1122,24 +1122,22 @@ void FakeVimPluginPrivate::changeSelection
bt->setExtraSelections(BaseTextEditor::FakeVimSelection, selection);
}
-void FakeVimPluginPrivate::switchFile(bool previous)
+int FakeVimPluginPrivate::currentFile() const
{
- Core::EditorManager *em = Core::EditorManager::instance();
- Core::OpenEditorsModel *model = em->openedEditorsModel();
+ Core::OpenEditorsModel *model = editorManager()->openedEditorsModel();
IEditor *cur = Core::EditorManager::instance()->currentEditor();
- int curIdx = model->indexOf(cur).row();
- int nIdx = (curIdx + model->rowCount() + (previous ? -1 : 1)) % model->rowCount();
- em->activateEditor(model->index(nIdx, 0), 0);
-}
-
-void FakeVimPluginPrivate::switchFileNext()
-{
- switchFile(false);
+ return model->indexOf(cur).row();
}
-void FakeVimPluginPrivate::switchFilePrev()
+void FakeVimPluginPrivate::switchToFile(int n)
{
- switchFile(true);
+ Core::OpenEditorsModel *model = editorManager()->openedEditorsModel();
+ int size = model->rowCount();
+ QTC_ASSERT(size, return);
+ n = n % size;
+ if (n < 0)
+ n += size;
+ editorManager()->activateEditor(model->index(n, 0), 0);
}
CommandMap &FakeVimExCommandsPage::exCommandMap()
@@ -1175,9 +1173,10 @@ bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessa
return d->initialize();
}
-void FakeVimPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag FakeVimPlugin::aboutToShutdown()
{
d->aboutToShutdown();
+ return SynchronousShutdown;
}
void FakeVimPlugin::extensionsInitialized()
diff --git a/src/plugins/fakevim/fakevimplugin.h b/src/plugins/fakevim/fakevimplugin.h
index a40bb9a9f2..b9e38375e6 100644
--- a/src/plugins/fakevim/fakevimplugin.h
+++ b/src/plugins/fakevim/fakevimplugin.h
@@ -50,7 +50,7 @@ public:
private:
// implementation of ExtensionSystem::IPlugin
bool initialize(const QStringList &arguments, QString *error_message);
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
void extensionsInitialized();
private:
diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp
index 86dd69e33c..80e30044ce 100644
--- a/src/plugins/find/basetextfind.cpp
+++ b/src/plugins/find/basetextfind.cpp
@@ -153,8 +153,15 @@ IFindSupport::Result BaseTextFind::findStep(const QString &txt, IFindSupport::Fi
return found ? Found : NotFound;
}
-bool BaseTextFind::replaceStep(const QString &before, const QString &after,
- IFindSupport::FindFlags findFlags)
+void BaseTextFind::replace(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags)
+{
+ QTextCursor cursor = replaceInternal(before, after, findFlags);
+ setTextCursor(cursor);
+}
+
+QTextCursor BaseTextFind::replaceInternal(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags)
{
QTextCursor cursor = textCursor();
bool usesRegExp = (findFlags & IFindSupport::FindRegularExpression);
@@ -169,6 +176,13 @@ bool BaseTextFind::replaceStep(const QString &before, const QString &after,
if ((findFlags&IFindSupport::FindBackward) != 0)
cursor.setPosition(start);
}
+ return cursor;
+}
+
+bool BaseTextFind::replaceStep(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags)
+{
+ QTextCursor cursor = replaceInternal(before, after, findFlags);
return find(before, findFlags, cursor);
}
diff --git a/src/plugins/find/basetextfind.h b/src/plugins/find/basetextfind.h
index c62f5a7282..e2bca51127 100644
--- a/src/plugins/find/basetextfind.h
+++ b/src/plugins/find/basetextfind.h
@@ -60,6 +60,8 @@ public:
Result findIncremental(const QString &txt, IFindSupport::FindFlags findFlags);
Result findStep(const QString &txt, IFindSupport::FindFlags findFlags);
+ void replace(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags);
bool replaceStep(const QString &before, const QString &after,
IFindSupport::FindFlags findFlags);
int replaceAll(const QString &before, const QString &after,
@@ -76,6 +78,8 @@ private:
bool find(const QString &txt,
IFindSupport::FindFlags findFlags,
QTextCursor start);
+ QTextCursor replaceInternal(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags);
QTextCursor textCursor() const;
void setTextCursor(const QTextCursor&);
diff --git a/src/plugins/find/currentdocumentfind.cpp b/src/plugins/find/currentdocumentfind.cpp
index 18c625b547..d378823bcb 100644
--- a/src/plugins/find/currentdocumentfind.cpp
+++ b/src/plugins/find/currentdocumentfind.cpp
@@ -118,6 +118,13 @@ IFindSupport::Result CurrentDocumentFind::findStep(const QString &txt, IFindSupp
return m_currentFind->findStep(txt, findFlags);
}
+void CurrentDocumentFind::replace(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags)
+{
+ QTC_ASSERT(m_currentFind, return);
+ m_currentFind->replace(before, after, findFlags);
+}
+
bool CurrentDocumentFind::replaceStep(const QString &before, const QString &after,
IFindSupport::FindFlags findFlags)
{
diff --git a/src/plugins/find/currentdocumentfind.h b/src/plugins/find/currentdocumentfind.h
index f104ef5f4b..f199854c53 100644
--- a/src/plugins/find/currentdocumentfind.h
+++ b/src/plugins/find/currentdocumentfind.h
@@ -56,6 +56,8 @@ public:
void highlightAll(const QString &txt, IFindSupport::FindFlags findFlags);
IFindSupport::Result findIncremental(const QString &txt, IFindSupport::FindFlags findFlags);
IFindSupport::Result findStep(const QString &txt, IFindSupport::FindFlags findFlags);
+ void replace(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags);
bool replaceStep(const QString &before, const QString &after,
IFindSupport::FindFlags findFlags);
int replaceAll(const QString &before, const QString &after,
diff --git a/src/plugins/find/find.pro b/src/plugins/find/find.pro
index bfdb979332..d0f67a405c 100644
--- a/src/plugins/find/find.pro
+++ b/src/plugins/find/find.pro
@@ -27,7 +27,8 @@ SOURCES += findtoolwindow.cpp \
searchresulttreeitems.cpp \
searchresulttreemodel.cpp \
searchresulttreeview.cpp \
- searchresultwindow.cpp
+ searchresultwindow.cpp \
+ ifindfilter.cpp
FORMS += findwidget.ui \
finddialog.ui
RESOURCES += find.qrc
diff --git a/src/plugins/find/findplugin.cpp b/src/plugins/find/findplugin.cpp
index f5d22a9128..900020c765 100644
--- a/src/plugins/find/findplugin.cpp
+++ b/src/plugins/find/findplugin.cpp
@@ -143,12 +143,13 @@ void FindPlugin::extensionsInitialized()
readSettings();
}
-void FindPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag FindPlugin::aboutToShutdown()
{
d->m_findToolBar->setVisible(false);
d->m_findToolBar->setParent(0);
d->m_currentDocumentFind->removeConnections();
writeSettings();
+ return SynchronousShutdown;
}
void FindPlugin::filterChanged()
@@ -208,7 +209,8 @@ void FindPlugin::setupMenu()
mfindadvanced->menu()->setTitle(tr("Advanced Find"));
mfind->addMenu(mfindadvanced, Constants::G_FIND_FILTERS);
d->m_openFindDialog = new QAction(tr("Open Advanced Find..."), this);
- cmd = am->registerAction(d->m_openFindDialog, QLatin1String("Find.Dialog"), globalcontext);
+ d->m_openFindDialog->setIconText(tr("Advanced..."));
+ cmd = am->registerAction(d->m_openFindDialog, Constants::ADVANCED_FIND, globalcontext);
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F")));
mfindadvanced->addAction(cmd);
connect(d->m_openFindDialog, SIGNAL(triggered()), this, SLOT(openFindFilter()));
@@ -226,7 +228,7 @@ void FindPlugin::setupFilterMenuItems()
d->m_filterActions.clear();
bool haveEnabledFilters = false;
foreach (IFindFilter *filter, findInterfaces) {
- QAction *action = new QAction(QLatin1String(" ") + filter->name(), this);
+ QAction *action = new QAction(QLatin1String(" ") + filter->displayName(), this);
bool isEnabled = filter->isEnabled();
if (isEnabled)
haveEnabledFilters = true;
diff --git a/src/plugins/find/findplugin.h b/src/plugins/find/findplugin.h
index ef0e0d7356..d643be94fc 100644
--- a/src/plugins/find/findplugin.h
+++ b/src/plugins/find/findplugin.h
@@ -67,7 +67,7 @@ public:
// IPlugin
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
QTextDocument::FindFlags findFlags() const;
void updateFindCompletion(const QString &text);
diff --git a/src/plugins/find/findtoolbar.cpp b/src/plugins/find/findtoolbar.cpp
index 2dc9323683..6b0118759e 100644
--- a/src/plugins/find/findtoolbar.cpp
+++ b/src/plugins/find/findtoolbar.cpp
@@ -71,7 +71,9 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen
m_enterFindStringAction(0),
m_findNextAction(0),
m_findPreviousAction(0),
+ m_replaceAction(0),
m_replaceNextAction(0),
+ m_replacePreviousAction(0),
m_casesensitiveIcon(":/find/images/casesensitively.png"),
m_regexpIcon(":/find/images/regexp.png"),
m_wholewordsIcon(":/find/images/wholewords.png"),
@@ -138,6 +140,8 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen
Core::ActionContainer *mfind = am->actionContainer(Constants::M_FIND);
Core::Command *cmd;
+ m_ui.advancedButton->setDefaultAction(am->command(Constants::ADVANCED_FIND)->action());
+
QIcon icon = QIcon::fromTheme(QLatin1String("edit-find-replace"));
m_findInDocumentAction = new QAction(icon, tr("Find/Replace"), this);
cmd = am->registerAction(m_findInDocumentAction, Constants::FIND_IN_DOCUMENT, globalcontext);
@@ -168,7 +172,15 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen
connect(m_findPreviousAction, SIGNAL(triggered()), this, SLOT(invokeFindPrevious()));
m_ui.findPreviousButton->setDefaultAction(cmd->action());
- m_replaceNextAction = new QAction(tr("Replace && Find Next"), this);
+ m_replaceAction = new QAction(tr("Replace"), this);
+ cmd = am->registerAction(m_replaceAction, Constants::REPLACE, globalcontext);
+ cmd->setDefaultKeySequence(QKeySequence());
+ mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
+ connect(m_replaceAction, SIGNAL(triggered()), this, SLOT(invokeReplace()));
+ m_ui.replaceButton->setDefaultAction(cmd->action());
+
+ m_replaceNextAction = new QAction(tr("Replace && Find"), this);
+ m_replaceNextAction->setIconText(tr("Replace && Find")); // work around bug in Qt that kills ampersands in tool button
cmd = am->registerAction(m_replaceNextAction, Constants::REPLACE_NEXT, globalcontext);
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+=")));
mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
@@ -181,7 +193,6 @@ FindToolBar::FindToolBar(FindPlugin *plugin, CurrentDocumentFind *currentDocumen
//cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+=")));
mfind->addAction(cmd, Constants::G_FIND_ACTIONS);
connect(m_replacePreviousAction, SIGNAL(triggered()), this, SLOT(invokeReplacePrevious()));
- m_ui.replacePreviousButton->setDefaultAction(cmd->action());
m_replaceAllAction = new QAction(tr("Replace All"), this);
cmd = am->registerAction(m_replaceAllAction, Constants::REPLACE_ALL, globalcontext);
@@ -315,6 +326,7 @@ void FindToolBar::updateToolBar()
m_findNextAction->setEnabled(enabled);
m_findPreviousAction->setEnabled(enabled);
+ m_replaceAction->setEnabled(replaceEnabled);
m_replaceNextAction->setEnabled(replaceEnabled);
m_replacePreviousAction->setEnabled(replaceEnabled);
m_replaceAllAction->setEnabled(replaceEnabled);
@@ -332,9 +344,10 @@ void FindToolBar::updateToolBar()
m_ui.replaceLabel->setEnabled(replaceEnabled);
m_ui.replaceEdit->setVisible(replaceEnabled);
m_ui.replaceLabel->setVisible(replaceEnabled);
- m_ui.replacePreviousButton->setVisible(replaceEnabled);
+ m_ui.replaceButton->setVisible(replaceEnabled);
m_ui.replaceNextButton->setVisible(replaceEnabled);
m_ui.replaceAllButton->setVisible(replaceEnabled);
+ m_ui.advancedButton->setVisible(replaceEnabled);
layout()->invalidate();
if (!replaceEnabled && enabled && replaceFocus)
@@ -433,6 +446,16 @@ void FindToolBar::invokeFindIncremental()
}
}
+void FindToolBar::invokeReplace()
+{
+ setFindFlag(IFindSupport::FindBackward, false);
+ if (m_currentDocumentFind->isEnabled() && m_currentDocumentFind->supportsReplace()) {
+ m_plugin->updateFindCompletion(getFindText());
+ m_plugin->updateReplaceCompletion(getReplaceText());
+ m_currentDocumentFind->replace(getFindText(), getReplaceText(), effectiveFindFlags());
+ }
+}
+
void FindToolBar::invokeReplaceNext()
{
setFindFlag(IFindSupport::FindBackward, false);
diff --git a/src/plugins/find/findtoolbar.h b/src/plugins/find/findtoolbar.h
index ab8d8a4132..92abe7e475 100644
--- a/src/plugins/find/findtoolbar.h
+++ b/src/plugins/find/findtoolbar.h
@@ -67,6 +67,7 @@ private slots:
void invokeFindNext();
void invokeFindPrevious();
void invokeFindStep();
+ void invokeReplace();
void invokeReplaceNext();
void invokeReplacePrevious();
void invokeReplaceStep();
@@ -120,6 +121,7 @@ private:
QAction *m_enterFindStringAction;
QAction *m_findNextAction;
QAction *m_findPreviousAction;
+ QAction *m_replaceAction;
QAction *m_replaceNextAction;
QAction *m_replacePreviousAction;
QAction *m_replaceAllAction;
diff --git a/src/plugins/find/findtoolwindow.cpp b/src/plugins/find/findtoolwindow.cpp
index 5b4bf7d35e..63993c13cd 100644
--- a/src/plugins/find/findtoolwindow.cpp
+++ b/src/plugins/find/findtoolwindow.cpp
@@ -98,7 +98,7 @@ void FindToolWindow::setFindFilters(const QList<IFindFilter *> &filters)
m_ui.filterList->clear();
QStringList names;
foreach (IFindFilter *filter, filters) {
- names << filter->name();
+ names << filter->displayName();
m_configWidgets.append(filter->createConfigWidget());
}
m_ui.filterList->addItems(names);
diff --git a/src/plugins/find/findwidget.ui b/src/plugins/find/findwidget.ui
index 0e9b1cafe7..2457dbc698 100644
--- a/src/plugins/find/findwidget.ui
+++ b/src/plugins/find/findwidget.ui
@@ -100,7 +100,7 @@
</widget>
</item>
<item row="1" column="1">
- <widget class="Utils::FilterLineEdit" name="replaceEdit"/>
+ <widget class="Utils::FilterLineEdit" name="replaceEdit"/>
</item>
<item row="1" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
@@ -108,10 +108,16 @@
<number>3</number>
</property>
<item>
- <widget class="QToolButton" name="replacePreviousButton">
+ <widget class="QToolButton" name="replaceButton">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
+ <property name="text">
+ <string>Replace</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
<property name="arrowType">
<enum>Qt::LeftArrow</enum>
</property>
@@ -125,6 +131,12 @@
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
+ <property name="text">
+ <string>Replace &amp;&amp; Find</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
<property name="arrowType">
<enum>Qt::RightArrow</enum>
</property>
@@ -136,7 +148,7 @@
<font/>
</property>
<property name="text">
- <string>All</string>
+ <string>Replace All</string>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
@@ -156,6 +168,16 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QToolButton" name="advancedButton">
+ <property name="text">
+ <string>Advanced...</string>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextOnly</enum>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
diff --git a/src/plugins/find/ifindfilter.cpp b/src/plugins/find/ifindfilter.cpp
new file mode 100644
index 0000000000..d2eef03065
--- /dev/null
+++ b/src/plugins/find/ifindfilter.cpp
@@ -0,0 +1,174 @@
+#include "ifindfilter.h"
+
+/*!
+ \class Find::IFindFilter
+ \brief The IFindFilter class is the base class for find implementations
+ that are invoked via the \gui{Edit -> Find/Replace -> Advanced Find}
+ dialog.
+
+ Implementations of this class add an additional "Scope" to the Advanced
+ Find dialog. That can be any search that requires the user to provide
+ a text based search term (potentially with find flags like
+ searching case sensitively or using regular expressions). Existing
+ scopes are e.g. "All Projects" (i.e. search in all files in all projects)
+ and "Files on File System" where the user provides a directory and file
+ patterns to search.
+
+ To make your find scope available to the user, you need to implement this
+ class, and register an instance of your subclass in the plugin manager.
+
+ A common way to present the search results to the user, is to use the
+ shared \gui{Search Results} panel.
+
+ If you want to implement a find filter that is doing a file based text
+ search, you should use Find::BaseFileFind, which already implements all
+ the details for this kind of search, only requiring you to provide an
+ iterator over the file names of the files that should be searched.
+
+ If you want to implement a more specialized find filter, you'll need
+ to
+ \list
+ \o Start your search in a separate thread
+ \o Make this known to the Core::ProgressManager, for a progress bar
+ and the ability to cancel the search
+ \o Interface with the shared \gui{Search Results} panel, to show
+ the search results, handle the event that the user click on one
+ of the search result items, and possible handle a global replace
+ of all or some of the search result items.
+ \endlist
+
+ Luckily QtConcurrent and the search result panel provide the frameworks
+ that make it relatively easy to implement,
+ while ensuring a common way for the user.
+
+ The common pattern is roughly this:
+
+ Implement the actual search within a QtConcurrent based method, i.e.
+ a method that takes a \c{QFutureInterface<MySearchResult> &future}
+ as the first parameter and the other information needed for the search
+ as additional parameters. It should set useful progress information
+ on the QFutureInterface, regularly check for \c{future.isPaused()}
+ and \c{future.isCanceled()}, and report the search results
+ (possibly in chunks) via \c{future.reportResult}.
+
+ In the find filter's find/replaceAll method, get the shared
+ \gui{Search Results} window, initiate a new search and connect the
+ signals for handling selection of results and the replace action
+ (see the Find::SearchResultWindow class for details).
+ Start your search implementation via the corresponding QtConcurrent
+ methods. Add the returned QFuture object to the Core::ProgressManager.
+ Use a QFutureWatcher on the returned QFuture object to receive a signal
+ when your search implementation reports search results, and add these
+ to the shared \gui{Search Results} window.
+*/
+
+/*!
+ \fn IFindFilter::~IFindFilter()
+ \internal
+*/
+
+/*!
+ \fn QString IFindFilter::id() const
+ \brief Unique string identifier for this find filter.
+
+ Usually should be something like "MyPlugin.MyFindFilter".
+*/
+
+/*!
+ \fn QString IFindFilter::displayName() const
+ \brief The name of the find filter/scope as presented to the user.
+
+ This is the name that e.g. appears in the scope selection combo box.
+ Always return a translatable string (i.e. use tr() for the return value).
+*/
+
+/*!
+ \fn bool IFindFilter::isEnabled() const
+ \brief Returns if the user should be able to select this find filter
+ at the moment.
+
+ This is used e.g. for the "Current Projects" scope - if the user hasn't
+ opened a project, the scope is disabled.
+
+ \sa changed()
+*/
+
+/*!
+ \fn QKeySequence IFindFilter::defaultShortcut() const
+ \brief Returns the shortcut that can be used to open the advanced find
+ dialog with this filter/scope preselected.
+
+ Usually return an empty shortcut here, the user can still choose and
+ assign a specific shortcut to this find scope via the preferences.
+*/
+
+/*!
+ \fn bool IFindFilter::isReplaceSupported() const
+ \brief Returns if the find filter supports search and replace.
+
+ The default value is false, override this method to return true, if
+ your find filter supports global search and replace.
+*/
+
+/*!
+ \fn void IFindFilter::findAll(const QString &txt, QTextDocument::FindFlags findFlags)
+ \brief This method is called when the user selected this find scope and
+ initiated a search.
+
+ You should start a thread which actually performs the search for \a txt
+ using the given \a findFlags
+ (add it to Core::ProgressManager for a progress bar!) and presents the
+ search results to the user (using the \gui{Search Results} output pane).
+ For more information see the descriptions of this class,
+ Core::ProgressManager, and Find::SearchResultWindow.
+
+ \sa replaceAll()
+ \sa Core::ProgressManager
+ \sa Find::SearchResultWindow
+*/
+
+/*!
+ \fn void IFindFilter::replaceAll(const QString &txt, QTextDocument::FindFlags findFlags)
+ \brief Override this method if you want to support search and replace.
+
+ This method is called when the user selected this find scope and
+ initiated a search and replace.
+ The default implementation does nothing.
+
+ You should start a thread which actually performs the search for \a txt
+ using the given \a findFlags
+ (add it to Core::ProgressManager for a progress bar!) and presents the
+ search results to the user (using the \gui{Search Results} output pane).
+ For more information see the descriptions of this class,
+ Core::ProgressManager, and Find::SearchResultWindow.
+
+ \sa findAll()
+ \sa Core::ProgressManager
+ \sa Find::SearchResultWindow
+*/
+
+/*!
+ \fn QWidget *IFindFilter::createConfigWidget()
+ \brief Return a widget that contains additional controls for options
+ for this find filter.
+
+ The widget will be shown below the common options in the Advanced Find
+ dialog. It will be reparented and deleted by the find plugin.
+*/
+
+/*!
+ \fn void IFindFilter::writeSettings(QSettings *settings)
+ \brief Called at shutdown to write the state of the additional options
+ for this find filter to the \a settings.
+*/
+
+/*!
+ \fn void IFindFilter::readSettings(QSettings *settings)
+ \brief Called at startup to read the state of the additional options
+ for this find filter from the \a settings.
+*/
+
+/*!
+ \fn void IFindFilter::changed()
+ \brief Signals that the enabled state of this find filter has changed.
+*/
diff --git a/src/plugins/find/ifindfilter.h b/src/plugins/find/ifindfilter.h
index 8f7967e933..c063b55329 100644
--- a/src/plugins/find/ifindfilter.h
+++ b/src/plugins/find/ifindfilter.h
@@ -50,7 +50,7 @@ public:
virtual ~IFindFilter() {}
virtual QString id() const = 0;
- virtual QString name() const = 0;
+ virtual QString displayName() const = 0;
virtual bool isEnabled() const = 0;
virtual QKeySequence defaultShortcut() const = 0;
virtual bool isReplaceSupported() const { return false; }
diff --git a/src/plugins/find/ifindsupport.h b/src/plugins/find/ifindsupport.h
index 3d7900f051..e0293474bb 100644
--- a/src/plugins/find/ifindsupport.h
+++ b/src/plugins/find/ifindsupport.h
@@ -65,6 +65,8 @@ public:
virtual void highlightAll(const QString &txt, FindFlags findFlags);
virtual Result findIncremental(const QString &txt, FindFlags findFlags) = 0;
virtual Result findStep(const QString &txt, FindFlags findFlags) = 0;
+ virtual void replace(const QString &before, const QString &after,
+ FindFlags findFlags) = 0;
virtual bool replaceStep(const QString &before, const QString &after,
FindFlags findFlags) = 0;
virtual int replaceAll(const QString &before, const QString &after,
diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp
index 45b69f4d75..f6566b5a77 100644
--- a/src/plugins/find/searchresultwindow.cpp
+++ b/src/plugins/find/searchresultwindow.cpp
@@ -160,6 +160,14 @@ namespace Internal {
return IFindSupport::NotFound;
}
+ void replace(const QString &before, const QString &after,
+ IFindSupport::FindFlags findFlags)
+ {
+ Q_UNUSED(before)
+ Q_UNUSED(after)
+ Q_UNUSED(findFlags)
+ }
+
bool replaceStep(const QString &before, const QString &after,
IFindSupport::FindFlags findFlags)
{
@@ -211,8 +219,88 @@ namespace Internal {
using namespace Find::Internal;
+/*!
+ \enum Find::SearchResultWindow::SearchMode
+ Specifies if a search should show the replace UI or not.
+
+ \value SearchOnly
+ The search doesn't support replace.
+ \value SearchAndReplace
+ The search supports replace, so show the UI for it.
+*/
+
+/*!
+ \class Find::SearchResult
+ \brief Reports user interaction like activation of a search result item.
+
+ Whenever a new search is initiated via startNewSearch, an instance of this
+ class is returned to provide the initiator with the hooks for handling user
+ interaction.
+*/
+
+/*!
+ \fn void SearchResult::activated(const Find::SearchResultItem &item)
+ \brief Sent if the user activated (e.g. double-clicked) a search result
+ \a item.
+*/
+
+/*!
+ \fn void SearchResult::replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems)
+ \brief Sent when the user initiated a replace, e.g. by pressing the replace
+ all button.
+
+ The signal reports the text to use for replacement in \a replaceText,
+ and the list of search result items that were selected by the user
+ in \a checkedItems.
+ The handler of this signal should apply the replace only on the selected
+ items.
+*/
+
+/*!
+ \class Find::SearchResultWindow
+ \brief The SearchResultWindow class is the implementation of a commonly
+ shared \gui{Search Results} output pane. Use it to show search results
+ to a user.
+
+ Whenever you want to show the user a list of search results, or want
+ to present UI for a global search and replace, use the single instance
+ of this class.
+
+ Except for being an implementation of a output pane, the
+ SearchResultWindow has a few methods and one enum that allows other
+ plugins to show their search results and hook into the user actions for
+ selecting an entry and performing a global replace.
+
+ Whenever you start a search, call startNewSearch(SearchMode) to initialize
+ the search result window. The parameter determines if the GUI for
+ replacing should be shown.
+ The method returns a SearchResult object that is your
+ hook into the signals from user interaction for this search.
+ When you produce search results, call addResults or addResult to add them
+ to the search result window.
+ After the search has finished call finishSearch to inform the search
+ result window about it.
+
+ After that you get activated signals via your SearchResult instance when
+ the user selects a search result item, and, if you started the search
+ with the SearchAndReplace option, the replaceButtonClicked signal
+ when the user requests a replace.
+*/
+
+/*!
+ \fn QString SearchResultWindow::displayName() const
+ \internal
+*/
+
+SearchResultWindow *SearchResultWindow::m_instance = 0;
+
+/*!
+ \fn SearchResultWindow::SearchResultWindow()
+ \internal
+*/
SearchResultWindow::SearchResultWindow() : d(new SearchResultWindowPrivate)
{
+ m_instance = this;
d->m_widget = new QStackedWidget;
d->m_widget->setWindowTitle(displayName());
@@ -260,6 +348,10 @@ SearchResultWindow::SearchResultWindow() : d(new SearchResultWindowPrivate)
setShowReplaceUI(false);
}
+/*!
+ \fn SearchResultWindow::~SearchResultWindow()
+ \internal
+*/
SearchResultWindow::~SearchResultWindow()
{
writeSettings();
@@ -271,16 +363,38 @@ SearchResultWindow::~SearchResultWindow()
delete d;
}
+/*!
+ \fn SearchResultWindow *SearchResultWindow::instance()
+ \brief Returns the single shared instance of the Search Results window.
+*/
+SearchResultWindow *SearchResultWindow::instance()
+{
+ return m_instance;
+}
+
+/*!
+ \fn void SearchResultWindow::setTextToReplace(const QString &textToReplace)
+ \brief Sets the value in the UI element that allows the user to type
+ the text that should replace text in search results to \a textToReplace.
+*/
void SearchResultWindow::setTextToReplace(const QString &textToReplace)
{
d->m_replaceTextEdit->setText(textToReplace);
}
+/*!
+ \fn QString SearchResultWindow::textToReplace() const
+ \brief Returns the text that should replace the text in search results.
+*/
QString SearchResultWindow::textToReplace() const
{
return d->m_replaceTextEdit->text();
}
+/*!
+ \fn void SearchResultWindow::setShowReplaceUI(bool show)
+ \internal
+*/
void SearchResultWindow::setShowReplaceUI(bool show)
{
d->m_searchResultTreeView->model()->setShowReplaceUI(show);
@@ -290,6 +404,10 @@ void SearchResultWindow::setShowReplaceUI(bool show)
d->m_isShowingReplaceUI = show;
}
+/*!
+ \fn void SearchResultWindow::handleReplaceButton()
+ \internal
+*/
void SearchResultWindow::handleReplaceButton()
{
QTC_ASSERT(d->m_currentSearch, return);
@@ -299,6 +417,10 @@ void SearchResultWindow::handleReplaceButton()
d->m_currentSearch->replaceButtonClicked(d->m_replaceTextEdit->text(), checkedItems());
}
+/*!
+ \fn QList<SearchResultItem> SearchResultWindow::checkedItems() const
+ \internal
+*/
QList<SearchResultItem> SearchResultWindow::checkedItems() const
{
QList<SearchResultItem> result;
@@ -318,20 +440,42 @@ QList<SearchResultItem> SearchResultWindow::checkedItems() const
return result;
}
+/*!
+ \fn void SearchResultWindow::visibilityChanged(bool)
+ \internal
+*/
void SearchResultWindow::visibilityChanged(bool /*visible*/)
{
}
+/*!
+ \fn QWidget *SearchResultWindow::outputWidget(QWidget *)
+ \internal
+*/
QWidget *SearchResultWindow::outputWidget(QWidget *)
{
return d->m_widget;
}
+/*!
+ \fn QList<QWidget*> SearchResultWindow::toolBarWidgets() const
+ \internal
+*/
QList<QWidget*> SearchResultWindow::toolBarWidgets() const
{
return QList<QWidget*>() << d->m_expandCollapseButton << d->m_replaceLabel << d->m_replaceTextEdit << d->m_replaceButton;
}
+/*!
+ \fn SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndReplace)
+ \brief Tells the search results window to start a new search.
+
+ This will clear the contents of the previous search and initialize the UI
+ with regard to showing the replace UI or not (depending on the search mode
+ in \a searchOrSearchAndReplace).
+ Returns a SearchResult object that is used for signaling user interaction
+ with the results of this search.
+*/
SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndReplace)
{
clearContents();
@@ -341,6 +485,11 @@ SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndRep
return d->m_currentSearch;
}
+/*!
+ \fn void SearchResultWindow::finishSearch()
+ \brief Notifies the search result window that the current search
+ has finished, and the UI should reflect that.
+*/
void SearchResultWindow::finishSearch()
{
if (d->m_items.count()) {
@@ -350,6 +499,10 @@ void SearchResultWindow::finishSearch()
}
}
+/*!
+ \fn void SearchResultWindow::clearContents()
+ \brief Clears the current contents in the search result window.
+*/
void SearchResultWindow::clearContents()
{
d->m_replaceTextEdit->setEnabled(false);
@@ -361,6 +514,10 @@ void SearchResultWindow::clearContents()
navigateStateChanged();
}
+/*!
+ \fn void SearchResultWindow::showNoMatchesFound()
+ \internal
+*/
void SearchResultWindow::showNoMatchesFound()
{
d->m_replaceTextEdit->setEnabled(false);
@@ -368,26 +525,47 @@ void SearchResultWindow::showNoMatchesFound()
d->m_widget->setCurrentWidget(d->m_noMatchesFoundDisplay);
}
+/*!
+ \fn bool SearchResultWindow::isEmpty() const
+ Returns if the search result window currently doesn't show any results.
+*/
bool SearchResultWindow::isEmpty() const
{
return (d->m_searchResultTreeView->model()->rowCount() < 1);
}
+/*!
+ \fn int SearchResultWindow::numberOfResults() const
+ Returns the number of search results currently shown in the search
+ results window.
+*/
int SearchResultWindow::numberOfResults() const
{
return d->m_items.count();
}
+/*!
+ \fn bool SearchResultWindow::hasFocus()
+ \internal
+*/
bool SearchResultWindow::hasFocus()
{
return d->m_searchResultTreeView->hasFocus() || (d->m_isShowingReplaceUI && d->m_replaceTextEdit->hasFocus());
}
+/*!
+ \fn bool SearchResultWindow::canFocus()
+ \internal
+*/
bool SearchResultWindow::canFocus()
{
return !d->m_items.isEmpty();
}
+/*!
+ \fn void SearchResultWindow::setFocus()
+ \internal
+*/
void SearchResultWindow::setFocus()
{
if (!d->m_items.isEmpty()) {
@@ -406,17 +584,37 @@ void SearchResultWindow::setFocus()
}
}
+/*!
+ \fn void SearchResultWindow::setTextEditorFont(const QFont &font)
+ \internal
+*/
void SearchResultWindow::setTextEditorFont(const QFont &font)
{
d->m_searchResultTreeView->setTextEditorFont(font);
}
+/*!
+ \fn void SearchResultWindow::handleJumpToSearchResult(int index, bool)
+ \internal
+*/
void SearchResultWindow::handleJumpToSearchResult(int index, bool /* checked */)
{
QTC_ASSERT(d->m_currentSearch, return);
d->m_currentSearch->activated(d->m_items.at(index));
}
+/*!
+ \fn void SearchResultWindow::addResult(const QString &fileName, int lineNumber, const QString &rowText, int searchTermStart, int searchTermLength, const QVariant &userData)
+ \brief Adds a single result line to the search results.
+
+ The \a fileName, \a lineNumber and \a rowText are shown in the result line.
+ \a searchTermStart and \a searchTermLength specify the region that
+ should be visually marked (string position and length in \a rowText).
+ You can attach arbitrary \a userData to the search result, which can
+ be used e.g. when reacting to the signals of the SearchResult for your search.
+
+ \sa addResults()
+*/
void SearchResultWindow::addResult(const QString &fileName, int lineNumber, const QString &rowText,
int searchTermStart, int searchTermLength, const QVariant &userData)
{
@@ -430,6 +628,13 @@ void SearchResultWindow::addResult(const QString &fileName, int lineNumber, cons
addResults(QList<SearchResultItem>() << item);
}
+/*!
+ \fn void SearchResultWindow::addResults(QList<SearchResultItem> &items)
+ \brief Adds all of the given search result \a items to the search
+ results window.
+
+ \sa addResult()
+*/
void SearchResultWindow::addResults(QList<SearchResultItem> &items)
{
int index = d->m_items.size();
@@ -452,6 +657,10 @@ void SearchResultWindow::addResults(QList<SearchResultItem> &items)
}
}
+/*!
+ \fn void SearchResultWindow::handleExpandCollapseToolButton(bool checked)
+ \internal
+*/
void SearchResultWindow::handleExpandCollapseToolButton(bool checked)
{
d->m_searchResultTreeView->setAutoExpandResults(checked);
@@ -461,6 +670,10 @@ void SearchResultWindow::handleExpandCollapseToolButton(bool checked)
d->m_searchResultTreeView->collapseAll();
}
+/*!
+ \fn void SearchResultWindow::readSettings()
+ \internal
+*/
void SearchResultWindow::readSettings()
{
QSettings *s = Core::ICore::instance()->settings();
@@ -471,6 +684,10 @@ void SearchResultWindow::readSettings()
}
}
+/*!
+ \fn void SearchResultWindow::writeSettings()
+ \internal
+*/
void SearchResultWindow::writeSettings()
{
QSettings *s = Core::ICore::instance()->settings();
@@ -481,21 +698,37 @@ void SearchResultWindow::writeSettings()
}
}
+/*!
+ \fn int SearchResultWindow::priorityInStatusBar() const
+ \internal
+*/
int SearchResultWindow::priorityInStatusBar() const
{
return 80;
}
+/*!
+ \fn bool SearchResultWindow::canNext()
+ \internal
+*/
bool SearchResultWindow::canNext()
{
return d->m_items.count() > 0;
}
+/*!
+ \fn bool SearchResultWindow::canPrevious()
+ \internal
+*/
bool SearchResultWindow::canPrevious()
{
return d->m_items.count() > 0;
}
+/*!
+ \fn void SearchResultWindow::goToNext()
+ \internal
+*/
void SearchResultWindow::goToNext()
{
if (d->m_items.count() == 0)
@@ -506,6 +739,11 @@ void SearchResultWindow::goToNext()
d->m_searchResultTreeView->emitJumpToSearchResult(idx);
}
}
+
+/*!
+ \fn void SearchResultWindow::goToPrev()
+ \internal
+*/
void SearchResultWindow::goToPrev()
{
if (!d->m_searchResultTreeView->model()->rowCount())
@@ -517,6 +755,10 @@ void SearchResultWindow::goToPrev()
}
}
+/*!
+ \fn bool SearchResultWindow::canNavigate()
+ \internal
+*/
bool SearchResultWindow::canNavigate()
{
return true;
diff --git a/src/plugins/find/searchresultwindow.h b/src/plugins/find/searchresultwindow.h
index 47a80c2468..92e58cebbb 100644
--- a/src/plugins/find/searchresultwindow.h
+++ b/src/plugins/find/searchresultwindow.h
@@ -82,6 +82,7 @@ public:
SearchResultWindow();
virtual ~SearchResultWindow();
+ static SearchResultWindow *instance();
QWidget *outputWidget(QWidget *);
QList<QWidget*> toolBarWidgets() const;
@@ -129,6 +130,7 @@ private:
QList<SearchResultItem> checkedItems() const;
Internal::SearchResultWindowPrivate *d;
+ static SearchResultWindow *m_instance;
};
} // namespace Find
diff --git a/src/plugins/find/textfindconstants.h b/src/plugins/find/textfindconstants.h
index a5e56e2297..69f6a2428e 100644
--- a/src/plugins/find/textfindconstants.h
+++ b/src/plugins/find/textfindconstants.h
@@ -40,11 +40,13 @@ const char * const G_FIND_FILTERS = "Find.FindMenu.Filters";
const char * const G_FIND_FLAGS = "Find.FindMenu.Flags";
const char * const G_FIND_ACTIONS = "Find.FindMenu.Actions";
+const char * const ADVANCED_FIND = "Find.Dialog";
const char * const FIND = "Find.FindReplace";
const char * const FIND_IN_DOCUMENT = "Find.FindInCurrentDocument";
const char * const FIND_NEXT = "Find.FindNext";
const char * const FIND_PREVIOUS = "Find.FindPrevious";
const char * const FIND_ALL = "Find.FindAll";
+const char * const REPLACE = "Find.Replace";
const char * const REPLACE_NEXT = "Find.ReplaceNext";
const char * const REPLACE_PREVIOUS = "Find.ReplacePrevious";
const char * const REPLACE_ALL = "Find.ReplaceAll";
diff --git a/src/plugins/help/helpfindsupport.h b/src/plugins/help/helpfindsupport.h
index 4292381b17..f317f5aa9f 100644
--- a/src/plugins/help/helpfindsupport.h
+++ b/src/plugins/help/helpfindsupport.h
@@ -58,6 +58,8 @@ public:
Result findIncremental(const QString &txt, Find::IFindSupport::FindFlags findFlags);
Result findStep(const QString &txt, Find::IFindSupport::FindFlags findFlags);
+ void replace(const QString &, const QString &,
+ Find::IFindSupport::FindFlags ) { }
bool replaceStep(const QString &, const QString &,
Find::IFindSupport::FindFlags ) { return false; }
int replaceAll(const QString &, const QString &,
@@ -85,6 +87,8 @@ public:
Result findIncremental(const QString &txt, Find::IFindSupport::FindFlags findFlags);
Result findStep(const QString &txt, Find::IFindSupport::FindFlags findFlags);
+ void replace(const QString &, const QString &,
+ Find::IFindSupport::FindFlags ) { }
bool replaceStep(const QString &, const QString &,
Find::IFindSupport::FindFlags ) { return false; }
int replaceAll(const QString &, const QString &,
diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp
index f5259658e6..718436afbf 100644
--- a/src/plugins/help/helpplugin.cpp
+++ b/src/plugins/help/helpplugin.cpp
@@ -79,6 +79,12 @@
#include <QtHelp/QHelpEngine>
+#if !defined(QT_NO_WEBKIT)
+#include <QtWebKit/QWebElement>
+#include <QtWebKit/QWebElementCollection>
+#include <QtWebKit/QWebFrame>
+#endif
+
using namespace Core::Constants;
using namespace Help::Internal;
@@ -332,10 +338,11 @@ void HelpPlugin::extensionsInitialized()
helpManager->registerDocumentation(filesToRegister);
}
-void HelpPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
{
if (m_sideBar)
m_sideBar->saveSettings(m_core->settings(), QLatin1String("HelpSideBar"));
+ return SynchronousShutdown;
}
void HelpPlugin::setupUi()
@@ -688,13 +695,11 @@ void HelpPlugin::activateContext()
} else if (m_core->modeManager()->currentMode() == m_mode)
return;
- QString id;
- QMap<QString, QUrl> links;
-
// Find out what to show
+ QMap<QString, QUrl> links;
if (IContext *context = m_core->currentContextObject()) {
- id = context->contextHelpId();
- links = Core::HelpManager::instance()->linksForIdentifier(id);
+ m_idFromContext = context->contextHelpId();
+ links = Core::HelpManager::instance()->linksForIdentifier(m_idFromContext);
}
if (HelpViewer* viewer = viewerForContextMode()) {
@@ -702,16 +707,26 @@ void HelpPlugin::activateContext()
// No link found or no context object
viewer->setHtml(tr("<html><head><title>No Documentation</title>"
"</head><body><br/><center><b>%1</b><br/>No documentation "
- "available.</center></body></html>").arg(id));
+ "available.</center></body></html>").arg(m_idFromContext));
viewer->setSource(QUrl());
} else {
const QUrl &source = *links.begin();
- if (viewer->source() != source)
+ const QUrl &oldSource = viewer->source();
+ if (source != oldSource) {
+#if !defined(QT_NO_WEBKIT)
+ viewer->stop();
+#endif
viewer->setSource(source);
+ }
viewer->setFocus();
+ connect(viewer, SIGNAL(loadFinished(bool)), this,
+ SLOT(highlightSearchTerms()));
+
+ if (source.toString().remove(source.fragment())
+ == oldSource.toString().remove(oldSource.fragment())) {
+ highlightSearchTerms();
+ }
}
- if (viewer != m_helpViewerForSideBar)
- activateHelpMode();
}
}
@@ -819,6 +834,45 @@ void HelpPlugin::addBookmark()
manager->showBookmarkDialog(m_centralWidget, viewer->title(), url);
}
+void HelpPlugin::highlightSearchTerms()
+{
+ if (HelpViewer* viewer = viewerForContextMode()) {
+ disconnect(viewer, SIGNAL(loadFinished(bool)), this,
+ SLOT(highlightSearchTerms()));
+
+#if !defined(QT_NO_WEBKIT)
+ const QString &attrValue = m_idFromContext.mid(m_idFromContext
+ .lastIndexOf(QChar(':')) + 1);
+ if (attrValue.isEmpty())
+ return;
+
+ const QWebElement &document = viewer->page()->mainFrame()->documentElement();
+ const QWebElementCollection &collection = document.findAll(QLatin1String("h3.fn a"));
+
+ const QLatin1String property("background-color");
+ foreach (const QWebElement &element, collection) {
+ const QString &name = element.attribute(QLatin1String("name"));
+ if (name.isEmpty())
+ continue;
+
+ if (m_oldAttrValue == name) {
+ QWebElement parent = element.parent();
+ parent.setStyleProperty(property, m_styleProperty);
+ }
+
+ if (attrValue == name) {
+ QWebElement parent = element.parent();
+ m_styleProperty = parent.styleProperty(property,
+ QWebElement::InlineStyle);
+ parent.setStyleProperty(property, QLatin1String("yellow"));
+ }
+ }
+ m_oldAttrValue = attrValue;
+#endif
+ viewer->findText(m_idFromContext, 0, false, true);
+ }
+}
+
void HelpPlugin::handleHelpRequest(const QUrl &url)
{
if (HelpViewer::launchWithExternalApp(url))
diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h
index a5099db7f7..b97b61c2b6 100644
--- a/src/plugins/help/helpplugin.h
+++ b/src/plugins/help/helpplugin.h
@@ -70,7 +70,7 @@ public:
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
private slots:
void modeChanged(Core::IMode *mode);
@@ -98,6 +98,7 @@ private slots:
void updateCloseButton();
void setupHelpEngineIfNeeded();
+ void highlightSearchTerms();
void handleHelpRequest(const QUrl &url);
private:
@@ -134,6 +135,10 @@ private:
Core::MiniSplitter *m_splitter;
QToolButton *m_closeButton;
+
+ QString m_oldAttrValue;
+ QString m_styleProperty;
+ QString m_idFromContext;
};
} // namespace Internal
diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp
index 745f6ab1a6..7cf1f8712d 100644
--- a/src/plugins/perforce/perforcesettings.cpp
+++ b/src/plugins/perforce/perforcesettings.cpp
@@ -46,6 +46,7 @@ static const char *portKeyC = "Port";
static const char *clientKeyC = "Client";
static const char *userKeyC = "User";
static const char *promptToSubmitKeyC = "PromptForSubmit";
+static const char *autoOpenKeyC = "PromptToOpen";
static const char *timeOutKeyC = "TimeOut";
static const char *logCountKeyC = "LogCount";
@@ -68,7 +69,8 @@ Settings::Settings() :
logCount(defaultLogCount),
defaultEnv(true),
timeOutS(defaultTimeOutS),
- promptToSubmit(true)
+ promptToSubmit(true),
+ autoOpen(true)
{
}
@@ -78,7 +80,8 @@ bool Settings::equals(const Settings &rhs) const
&& logCount == rhs.logCount
&& p4Command == rhs.p4Command && p4Port == rhs.p4Port
&& p4Client == rhs.p4Client && p4User == rhs.p4User
- && timeOutS == rhs.timeOutS && promptToSubmit == rhs.promptToSubmit;
+ && timeOutS == rhs.timeOutS && promptToSubmit == rhs.promptToSubmit
+ && autoOpen == rhs.autoOpen;
};
QStringList Settings::commonP4Arguments() const
@@ -115,6 +118,7 @@ void PerforceSettings::fromSettings(QSettings *settings)
m_settings.timeOutS = settings->value(QLatin1String(timeOutKeyC), defaultTimeOutS).toInt();
m_settings.promptToSubmit = settings->value(QLatin1String(promptToSubmitKeyC), true).toBool();
m_settings.logCount = settings->value(QLatin1String(logCountKeyC), int(defaultLogCount)).toInt();
+ m_settings.autoOpen = settings->value(QLatin1String(autoOpenKeyC), true).toBool();
settings->endGroup();
}
@@ -129,6 +133,7 @@ void PerforceSettings::toSettings(QSettings *settings) const
settings->setValue(QLatin1String(timeOutKeyC), m_settings.timeOutS);
settings->setValue(QLatin1String(promptToSubmitKeyC), m_settings.promptToSubmit);
settings->setValue(QLatin1String(logCountKeyC), m_settings.logCount);
+ settings->setValue(QLatin1String(autoOpenKeyC), m_settings.autoOpen);
settings->endGroup();
}
@@ -180,6 +185,16 @@ void PerforceSettings::setPromptToSubmit(bool p)
m_settings.promptToSubmit = p;
}
+bool PerforceSettings::autoOpen() const
+{
+ return m_settings.autoOpen;
+}
+
+void PerforceSettings::setAutoOpen(bool b)
+{
+ m_settings.autoOpen = b;
+}
+
QString PerforceSettings::topLevel() const
{
return m_topLevel;
diff --git a/src/plugins/perforce/perforcesettings.h b/src/plugins/perforce/perforcesettings.h
index 803645237f..cdb1ee0675 100644
--- a/src/plugins/perforce/perforcesettings.h
+++ b/src/plugins/perforce/perforcesettings.h
@@ -61,6 +61,7 @@ struct Settings {
bool defaultEnv;
int timeOutS;
bool promptToSubmit;
+ bool autoOpen;
};
inline bool operator==(const Settings &s1, const Settings &s2) { return s1.equals(s2); }
@@ -122,6 +123,8 @@ public:
bool defaultEnv() const;
bool promptToSubmit() const;
void setPromptToSubmit(bool p);
+ bool autoOpen() const;
+ void setAutoOpen(bool p);
// Return basic arguments, including -d and server connection parameters.
QStringList commonP4Arguments(const QString &workingDir) const;
diff --git a/src/plugins/perforce/perforceversioncontrol.cpp b/src/plugins/perforce/perforceversioncontrol.cpp
index 2dc414f59c..0f0a5e8190 100644
--- a/src/plugins/perforce/perforceversioncontrol.cpp
+++ b/src/plugins/perforce/perforceversioncontrol.cpp
@@ -70,6 +70,14 @@ bool PerforceVersionControl::vcsOpen(const QString &fileName)
return m_plugin->vcsOpen(fi.absolutePath(), fi.fileName());
}
+Core::IVersionControl::SettingsFlags PerforceVersionControl::settingsFlags() const
+{
+ SettingsFlags rc;
+ if (m_plugin->settings().autoOpen())
+ rc|= AutoOpen;
+ return rc;
+};
+
bool PerforceVersionControl::vcsAdd(const QString &fileName)
{
const QFileInfo fi(fileName);
diff --git a/src/plugins/perforce/perforceversioncontrol.h b/src/plugins/perforce/perforceversioncontrol.h
index 1e14cd2810..b3a01f7a6c 100644
--- a/src/plugins/perforce/perforceversioncontrol.h
+++ b/src/plugins/perforce/perforceversioncontrol.h
@@ -50,6 +50,7 @@ public:
virtual bool supportsOperation(Operation operation) const;
virtual bool vcsOpen(const QString &fileName);
+ virtual SettingsFlags settingsFlags() const;
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
virtual bool vcsMove(const QString &from, const QString &to);
diff --git a/src/plugins/perforce/settingspage.cpp b/src/plugins/perforce/settingspage.cpp
index 5bdfe0f547..47fa6c9d24 100644
--- a/src/plugins/perforce/settingspage.cpp
+++ b/src/plugins/perforce/settingspage.cpp
@@ -85,6 +85,7 @@ Settings SettingsPageWidget::settings() const
settings.timeOutS = m_ui.timeOutSpinBox->value();
settings.logCount = m_ui.logCountSpinBox->value();
settings.promptToSubmit = m_ui.promptToSubmitCheckBox->isChecked();
+ settings.autoOpen = m_ui.autoOpenCheckBox->isChecked();
return settings;
}
@@ -98,6 +99,7 @@ void SettingsPageWidget::setSettings(const PerforceSettings &s)
m_ui.logCountSpinBox->setValue(s.logCount());
m_ui.timeOutSpinBox->setValue(s.timeOutS());
m_ui.promptToSubmitCheckBox->setChecked(s.promptToSubmit());
+ m_ui.autoOpenCheckBox->setChecked(s.autoOpen());
}
void SettingsPageWidget::setStatusText(const QString &t)
diff --git a/src/plugins/perforce/settingspage.ui b/src/plugins/perforce/settingspage.ui
index 6f27041508..e62622038b 100644
--- a/src/plugins/perforce/settingspage.ui
+++ b/src/plugins/perforce/settingspage.ui
@@ -2,14 +2,6 @@
<ui version="4.0">
<class>Perforce::Internal::SettingsPage</class>
<widget class="QWidget" name="Perforce::Internal::SettingsPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>437</width>
- <height>407</height>
- </rect>
- </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="configGroupBox">
@@ -25,7 +17,7 @@
</widget>
</item>
<item row="0" column="1">
- <widget class="Utils::PathChooser" name="pathChooser"/>
+ <widget class="Utils::PathChooser" name="pathChooser" native="true"/>
</item>
</layout>
</widget>
@@ -134,6 +126,13 @@
</property>
</widget>
</item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QCheckBox" name="autoOpenCheckBox">
+ <property name="text">
+ <string>Automatically open files when editing</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp
index abb1b90b23..78823bda17 100644
--- a/src/plugins/projectexplorer/allprojectsfind.cpp
+++ b/src/plugins/projectexplorer/allprojectsfind.cpp
@@ -60,7 +60,7 @@ QString AllProjectsFind::id() const
return QLatin1String("All Projects");
}
-QString AllProjectsFind::name() const
+QString AllProjectsFind::displayName() const
{
return tr("All Projects");
}
diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h
index 2f78757c15..d4710a7381 100644
--- a/src/plugins/projectexplorer/allprojectsfind.h
+++ b/src/plugins/projectexplorer/allprojectsfind.h
@@ -51,7 +51,7 @@ public:
AllProjectsFind(ProjectExplorerPlugin *plugin, Find::SearchResultWindow *resultWindow);
QString id() const;
- QString name() const;
+ QString displayName() const;
bool isEnabled() const;
QKeySequence defaultShortcut() const;
diff --git a/src/plugins/projectexplorer/buildconfigdialog.cpp b/src/plugins/projectexplorer/buildconfigdialog.cpp
index 5e3a58fbe1..32e018ec45 100644
--- a/src/plugins/projectexplorer/buildconfigdialog.cpp
+++ b/src/plugins/projectexplorer/buildconfigdialog.cpp
@@ -31,6 +31,7 @@
#include "project.h"
#include "runconfiguration.h"
#include "buildconfiguration.h"
+#include "target.h"
#include <QtGui/QVBoxLayout>
#include <QtGui/QPushButton>
diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp
index dc58861cab..c94f5452cf 100644
--- a/src/plugins/projectexplorer/buildmanager.cpp
+++ b/src/plugins/projectexplorer/buildmanager.cpp
@@ -411,27 +411,43 @@ bool BuildManager::buildQueueAppend(QList<BuildStep *> steps)
return true;
}
-void BuildManager::buildProjects(const QList<BuildConfiguration *> &configurations)
+bool BuildManager::buildProjects(const QList<BuildConfiguration *> &configurations)
{
QList<BuildStep *> steps;
- foreach(BuildConfiguration *bc, configurations) {
+ foreach(BuildConfiguration *bc, configurations)
steps.append(bc->steps(BuildStep::Build));
- // TODO: Verify that this is indeed what we want.
- steps.append(bc->steps(BuildStep::Deploy));
+
+ bool success = buildQueueAppend(steps);
+ if (!success) {
+ m_outputWindow->popup(false);
+ return false;
}
+ if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
+ m_outputWindow->popup(false);
+ startBuildQueue();
+ return true;
+}
+
+bool BuildManager::deployProjects(const QList<BuildConfiguration *> &configurations)
+{
+ QList<BuildStep *> steps;
+ foreach(BuildConfiguration *bc, configurations)
+ steps.append(bc->steps(BuildStep::Deploy));
+
bool success = buildQueueAppend(steps);
if (!success) {
m_outputWindow->popup(false);
- return;
+ return false;
}
if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
m_outputWindow->popup(false);
startBuildQueue();
+ return true;
}
-void BuildManager::cleanProjects(const QList<BuildConfiguration *> &configurations)
+bool BuildManager::cleanProjects(const QList<BuildConfiguration *> &configurations)
{
QList<BuildStep *> steps;
foreach(BuildConfiguration *bc, configurations)
@@ -440,22 +456,28 @@ void BuildManager::cleanProjects(const QList<BuildConfiguration *> &configuratio
bool success = buildQueueAppend(steps);
if (!success) {
m_outputWindow->popup(false);
- return;
+ return false;
}
if (ProjectExplorerPlugin::instance()->projectExplorerSettings().showCompilerOutput)
m_outputWindow->popup(false);
startBuildQueue();
+ return true;
+}
+
+bool BuildManager::buildProject(BuildConfiguration *configuration)
+{
+ return buildProjects(QList<BuildConfiguration *>() << configuration);
}
-void BuildManager::buildProject(BuildConfiguration *configuration)
+bool BuildManager::deployProject(BuildConfiguration *configuration)
{
- buildProjects(QList<BuildConfiguration *>() << configuration);
+ return deployProjects(QList<BuildConfiguration *>() << configuration);
}
-void BuildManager::cleanProject(BuildConfiguration *configuration)
+bool BuildManager::cleanProject(BuildConfiguration *configuration)
{
- cleanProjects(QList<BuildConfiguration *>() << configuration);
+ return cleanProjects(QList<BuildConfiguration *>() << configuration);
}
void BuildManager::appendStep(BuildStep *step)
diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h
index 45b2218d4c..0423d9e807 100644
--- a/src/plugins/projectexplorer/buildmanager.h
+++ b/src/plugins/projectexplorer/buildmanager.h
@@ -72,10 +72,12 @@ public:
bool tasksAvailable() const;
- void buildProject(BuildConfiguration *bc);
- void buildProjects(const QList<BuildConfiguration *> &configurations);
- void cleanProject(BuildConfiguration *configuration);
- void cleanProjects(const QList<BuildConfiguration *> &configurations);
+ bool buildProject(BuildConfiguration *bc);
+ bool buildProjects(const QList<BuildConfiguration *> &configurations);
+ bool deployProject(BuildConfiguration *bc);
+ bool deployProjects(const QList<BuildConfiguration *> &configurations);
+ bool cleanProject(BuildConfiguration *configuration);
+ bool cleanProjects(const QList<BuildConfiguration *> &configurations);
bool isBuilding(Project *p);
bool isBuilding(BuildStep *step);
diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp
index 95e12558ad..6424438b7f 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.cpp
+++ b/src/plugins/projectexplorer/compileoutputwindow.cpp
@@ -38,6 +38,7 @@
#include <QtGui/QKeyEvent>
#include <QtGui/QIcon>
+#include <QtGui/QTextCharFormat>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QTextEdit>
@@ -53,14 +54,14 @@ const int MAX_LINECOUNT = 10000;
CompileOutputWindow::CompileOutputWindow(BuildManager * /*bm*/)
{
- m_textEdit = new QPlainTextEdit();
- m_textEdit->setWindowTitle(tr("Compile Output"));
- m_textEdit->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
- m_textEdit->setReadOnly(true);
- m_textEdit->setFrameStyle(QFrame::NoFrame);
+ m_outputWindow = new OutputWindow();
+ m_outputWindow->setWindowTitle(tr("Compile Output"));
+ m_outputWindow->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
+ m_outputWindow->setReadOnly(true);
+
Aggregation::Aggregate *agg = new Aggregation::Aggregate;
- agg->add(m_textEdit);
- agg->add(new Find::BaseTextFind(m_textEdit));
+ agg->add(m_outputWindow);
+ agg->add(new Find::BaseTextFind(m_outputWindow));
qRegisterMetaType<QTextCharFormat>("QTextCharFormat");
@@ -76,7 +77,7 @@ CompileOutputWindow::~CompileOutputWindow()
bool CompileOutputWindow::hasFocus()
{
- return m_textEdit->hasFocus();
+ return m_outputWindow->hasFocus();
}
bool CompileOutputWindow::canFocus()
@@ -86,50 +87,28 @@ bool CompileOutputWindow::canFocus()
void CompileOutputWindow::setFocus()
{
- m_textEdit->setFocus();
+ m_outputWindow->setFocus();
}
QWidget *CompileOutputWindow::outputWidget(QWidget *)
{
- return m_textEdit;
+ return m_outputWindow;
}
void CompileOutputWindow::appendText(const QString &text, const QTextCharFormat &textCharFormat)
{
- if (m_textEdit->document()->blockCount() > MAX_LINECOUNT)
- return;
- bool shouldScroll = (m_textEdit->verticalScrollBar()->value() ==
- m_textEdit->verticalScrollBar()->maximum());
- QString textWithNewline = text;
- if (!textWithNewline.endsWith("\n"))
- textWithNewline.append("\n");
- QTextCursor cursor = QTextCursor(m_textEdit->document());
- cursor.movePosition(QTextCursor::End);
- cursor.beginEditBlock();
- cursor.insertText(textWithNewline, textCharFormat);
-
- if (m_textEdit->document()->blockCount() > MAX_LINECOUNT) {
- QTextCharFormat tmp;
- tmp.setFontWeight(QFont::Bold);
- cursor.insertText(tr("Additional output omitted\n"), tmp);
- }
-
- cursor.endEditBlock();
-
- if (shouldScroll)
- m_textEdit->setTextCursor(cursor);
+ m_outputWindow->appendText(text, textCharFormat, MAX_LINECOUNT);
}
void CompileOutputWindow::clearContents()
{
- m_textEdit->clear();
+ m_outputWindow->clear();
m_taskPositions.clear();
}
-void CompileOutputWindow::visibilityChanged(bool b)
+void CompileOutputWindow::visibilityChanged(bool)
{
- if (b)
- m_textEdit->verticalScrollBar()->setValue(m_textEdit->verticalScrollBar()->maximum());
+
}
int CompileOutputWindow::priorityInStatusBar() const
@@ -164,7 +143,7 @@ bool CompileOutputWindow::canNavigate()
void CompileOutputWindow::registerPositionOf(const Task &task)
{
- int blocknumber = m_textEdit->blockCount();
+ int blocknumber = m_outputWindow->blockCount();
if (blocknumber > MAX_LINECOUNT)
return;
m_taskPositions.insert(task.taskId, blocknumber - 1);
@@ -178,7 +157,7 @@ bool CompileOutputWindow::knowsPositionOf(const Task &task)
void CompileOutputWindow::showPositionOf(const Task &task)
{
int position = m_taskPositions.value(task.taskId);
- QTextCursor newCursor(m_textEdit->document()->findBlockByNumber(position));
+ QTextCursor newCursor(m_outputWindow->document()->findBlockByNumber(position));
newCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
- m_textEdit->setTextCursor(newCursor);
+ m_outputWindow->setTextCursor(newCursor);
}
diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h
index 2065af56a8..5b4e7b8adb 100644
--- a/src/plugins/projectexplorer/compileoutputwindow.h
+++ b/src/plugins/projectexplorer/compileoutputwindow.h
@@ -30,15 +30,14 @@
#ifndef COMPILEOUTPUTWINDOW_H
#define COMPILEOUTPUTWINDOW_H
+#include "outputwindow.h"
#include <coreplugin/ioutputpane.h>
#include <QtCore/QHash>
-#include <QtGui/QColor>
-#include <QtGui/QTextCharFormat>
-
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
+class QTextCharFormat;
QT_END_NAMESPACE
namespace ProjectExplorer {
@@ -80,7 +79,7 @@ public:
void showPositionOf(const Task &task);
private:
- QPlainTextEdit *m_textEdit;
+ OutputWindow *m_outputWindow;
QHash<unsigned int, int> m_taskPositions;
ShowOutputTaskHandler * m_handler;
};
diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp
index 7256a8603b..ef9ec44a9b 100644
--- a/src/plugins/projectexplorer/currentprojectfind.cpp
+++ b/src/plugins/projectexplorer/currentprojectfind.cpp
@@ -60,7 +60,7 @@ QString CurrentProjectFind::id() const
return QLatin1String("Current Project");
}
-QString CurrentProjectFind::name() const
+QString CurrentProjectFind::displayName() const
{
return tr("Current Project");
}
diff --git a/src/plugins/projectexplorer/currentprojectfind.h b/src/plugins/projectexplorer/currentprojectfind.h
index 2e98e49e1b..5ee35a83c8 100644
--- a/src/plugins/projectexplorer/currentprojectfind.h
+++ b/src/plugins/projectexplorer/currentprojectfind.h
@@ -54,7 +54,7 @@ public:
CurrentProjectFind(ProjectExplorerPlugin *plugin, Find::SearchResultWindow *resultWindow);
QString id() const;
- QString name() const;
+ QString displayName() const;
bool isEnabled() const;
QKeySequence defaultShortcut() const;
diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
index de2894ac8c..43a3af6a73 100644
--- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
+++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp
@@ -38,6 +38,7 @@
#include <utils/detailswidget.h>
#include <utils/pathchooser.h>
+#include <QtCore/QDir>
#include <QtGui/QCheckBox>
#include <QtGui/QComboBox>
#include <QtGui/QDialog>
diff --git a/src/plugins/projectexplorer/customwizard/customwizard.cpp b/src/plugins/projectexplorer/customwizard/customwizard.cpp
index b9c8e7e18a..9ec1483b21 100644
--- a/src/plugins/projectexplorer/customwizard/customwizard.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizard.cpp
@@ -155,11 +155,10 @@ static inline bool createFile(Internal::CustomWizardFile cwFile,
return false;
}
// Field replacement on contents
- QString contents = QString::fromLocal8Bit(file.readAll());
- if (!contents.isEmpty() && !fm.isEmpty())
- Internal::CustomWizardContext::replaceFields(fm, &contents);
+ const QString contentsIn = QString::fromLocal8Bit(file.readAll());
+
Core::GeneratedFile generatedFile;
- generatedFile.setContents(contents);
+ generatedFile.setContents(Internal::CustomWizardContext::processFile(fm, contentsIn));
generatedFile.setPath(targetPath);
Core::GeneratedFile::Attributes attributes = 0;
if (cwFile.openEditor)
diff --git a/src/plugins/projectexplorer/customwizard/customwizard.pri b/src/plugins/projectexplorer/customwizard/customwizard.pri
index 61475f3ae4..ac8bf2ea26 100644
--- a/src/plugins/projectexplorer/customwizard/customwizard.pri
+++ b/src/plugins/projectexplorer/customwizard/customwizard.pri
@@ -1,7 +1,9 @@
INCLUDEPATH *= $$PWD
HEADERS += $$PWD/customwizard.h \
$$PWD/customwizardparameters.h \
- $$PWD/customwizardpage.h
+ $$PWD/customwizardpage.h \
+ customwizard/customwizardpreprocessor.h
SOURCES += $$PWD/customwizard.cpp \
$$PWD/customwizardparameters.cpp \
- $$PWD/customwizardpage.cpp
+ $$PWD/customwizardpage.cpp \
+ customwizard/customwizardpreprocessor.cpp
diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
index 487c3aef37..d821ed5203 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
+++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp
@@ -28,6 +28,7 @@
**************************************************************************/
#include "customwizardparameters.h"
+#include "customwizardpreprocessor.h"
#include <coreplugin/mimedatabase.h>
#include <coreplugin/icore.h>
@@ -644,5 +645,24 @@ void CustomWizardContext::reset()
mdb->preferredSuffixByType(QLatin1String(CppTools::Constants::CPP_HEADER_MIMETYPE)));
}
+QString CustomWizardContext::processFile(const FieldReplacementMap &fm, QString in)
+{
+
+ if (in.isEmpty())
+ return in;
+
+ if (!fm.isEmpty())
+ replaceFields(fm, &in);
+
+ QString out;
+ QString errorMessage;
+ if (!customWizardPreprocess(in, &out, &errorMessage)) {
+ qWarning("Error preprocessing custom widget file: %s\nFile:\n%s",
+ qPrintable(errorMessage), qPrintable(in));
+ return QString();
+ }
+ return out;
+}
+
} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.h b/src/plugins/projectexplorer/customwizard/customwizardparameters.h
index e5c26abf3f..5d92843f04 100644
--- a/src/plugins/projectexplorer/customwizard/customwizardparameters.h
+++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.h
@@ -105,6 +105,7 @@ struct CustomWizardContext {
// %Field:l% -> lower case replacement, 'u' upper case,
// 'c' capitalize first letter.
static void replaceFields(const FieldReplacementMap &fm, QString *s);
+ static QString processFile(const FieldReplacementMap &fm, QString in);
FieldReplacementMap baseReplacements;
};
diff --git a/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.cpp b/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.cpp
new file mode 100644
index 0000000000..2d0a279041
--- /dev/null
+++ b/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.cpp
@@ -0,0 +1,268 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "customwizardpreprocessor.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QStringList>
+#include <QtCore/QStack>
+#include <QtCore/QRegExp>
+#include <QtCore/QDebug>
+
+#include <QtScript/QScriptEngine>
+#include <QtScript/QScriptValue>
+
+namespace ProjectExplorer {
+namespace Internal {
+
+enum { debug = 0 };
+
+// Preprocessor: Conditional section type.
+enum PreprocessorSection {
+ IfSection,
+ ElsifSection,
+ ElseSection,
+ EndifSection,
+ OtherSection
+};
+
+// Preprocessor: Section stack entry containing nested '@if' section
+// state.
+struct PreprocessStackEntry {
+ PreprocessStackEntry(PreprocessorSection section = OtherSection,
+ bool parentEnabled = true,
+ bool condition = false,
+ bool anyIfClauseMatched = false);
+
+ PreprocessorSection section;
+ bool parentEnabled;
+ bool condition; // Current 'if/elsif' section is enabled.
+ bool anyIfClauseMatched; // Determines if 'else' triggers
+};
+
+PreprocessStackEntry::PreprocessStackEntry(PreprocessorSection s,
+ bool p, bool c, bool a) :
+ section(s), parentEnabled(p), condition(c), anyIfClauseMatched(a)
+{
+}
+
+// Context for preprocessing.
+class PreprocessContext {
+public:
+ PreprocessContext();
+ bool process(const QString &in, QString *out, QString *errorMessage);
+
+private:
+ void reset();
+ bool evaluateExpression(const QString &expression, bool *result, QString *errorMessage);
+ PreprocessorSection preprocessorLine(const QString & in, QString *ifExpression) const;
+
+ mutable QRegExp m_ifPattern;
+ mutable QRegExp m_elsifPattern;
+ const QRegExp m_elsePattern;
+ const QRegExp m_endifPattern;
+
+ QStack<PreprocessStackEntry> m_sectionStack;
+ QScriptEngine m_scriptEngine;
+};
+
+PreprocessContext::PreprocessContext() :
+ // Cut out expression for 'if/elsif '
+ m_ifPattern(QLatin1String("^([\\s]*@[\\s]*if[\\s]*)(.*)$")),
+ m_elsifPattern(QLatin1String("^([\\s]*@[\\s]*elsif[\\s]*)(.*)$")),
+ m_elsePattern(QLatin1String("^[\\s]*@[\\s]*else.*$")),
+ m_endifPattern(QLatin1String("^[\\s]*@[\\s]*endif.*$"))
+{
+ QTC_ASSERT(m_ifPattern.isValid() && m_elsifPattern.isValid()
+ && m_elsePattern.isValid() &&m_endifPattern.isValid(), return);
+}
+
+void PreprocessContext::reset()
+{
+ m_sectionStack.clear(); // Add a default, enabled section.
+ m_sectionStack.push(PreprocessStackEntry(OtherSection, true, true));
+}
+
+// Determine type of line and return enumeration, cut out
+// expression for '@if/@elsif'.
+PreprocessorSection PreprocessContext::preprocessorLine(const QString &in,
+ QString *ifExpression) const
+{
+ if (m_ifPattern.exactMatch(in)) {
+ *ifExpression = m_ifPattern.cap(2).trimmed();
+ return IfSection;
+ }
+ if (m_elsifPattern.exactMatch(in)) {
+ *ifExpression = m_elsifPattern.cap(2).trimmed();
+ return ElsifSection;
+ }
+
+ ifExpression->clear();
+
+ if (m_elsePattern.exactMatch(in))
+ return ElseSection;
+ if (m_endifPattern.exactMatch(in))
+ return EndifSection;
+ return OtherSection;
+}
+
+// Evaluate an expression within an 'if'/'elsif' to a bool via QScript
+bool PreprocessContext::evaluateExpression(const QString &expression,
+ bool *result,
+ QString *errorMessage)
+{
+ errorMessage->clear();
+ *result = false;
+ m_scriptEngine.clearExceptions();
+ const QScriptValue value = m_scriptEngine.evaluate(expression);
+ if (m_scriptEngine.hasUncaughtException()) {
+ *errorMessage = QString::fromLatin1("Error in '%1': %2").
+ arg(expression, m_scriptEngine.uncaughtException().toString());
+ return false;
+ }
+ // Try to convert to bool, be that an int or whatever.
+ if (value.isBool()) {
+ *result = value.toBool();
+ return true;
+ }
+ if (value.isNumber()) {
+ *result = !qFuzzyCompare(value.toNumber(), 0);
+ return true;
+ }
+ if (value.isString()) {
+ *result = !value.toString().isEmpty();
+ return true;
+ }
+ *errorMessage = QString::fromLatin1("Cannot convert result of '%1' ('%2'to bool.").
+ arg(expression, value.toString());
+ return false;
+}
+
+static inline QString msgEmptyStack(int line)
+{
+ return QString::fromLatin1("Unmatched '@endif' at line %1.").arg(line);
+}
+
+bool PreprocessContext::process(const QString &in, QString *out, QString *errorMessage)
+{
+ out->clear();
+ if (in.isEmpty())
+ return true;
+
+ out->reserve(in.size());
+ reset();
+
+ const QChar newLine = QLatin1Char('\n');
+ const QStringList lines = in.split(newLine, QString::KeepEmptyParts);
+ const int lineCount = lines.size();
+ for (int l = 0; l < lineCount; l++) {
+ // Check for element of the stack (be it dummy, else something is wrong).
+ if (m_sectionStack.isEmpty()) {
+ *errorMessage = msgEmptyStack(l);
+ return false;
+ }
+ QString expression;
+ bool expressionValue = false;
+ PreprocessStackEntry &top = m_sectionStack.back();
+
+ switch (preprocessorLine(lines.at(l), &expression)) {
+ case IfSection:
+ // '@If': Push new section
+ if (top.parentEnabled) {
+ if (!evaluateExpression(expression, &expressionValue, errorMessage)) {
+ *errorMessage = QString::fromLatin1("Error in @if at %1: %2").
+ arg(l + 1).arg(*errorMessage);
+ return false;
+ }
+ }
+ if (debug)
+ qDebug("'%s' : expr='%s' -> %d", qPrintable(lines.at(l)), qPrintable(expression), expressionValue);
+ m_sectionStack.push(PreprocessStackEntry(IfSection,
+ top.condition, expressionValue, expressionValue));
+ break;
+ case ElsifSection: // '@elsif': Check condition.
+ if (top.section != IfSection && top.section != ElsifSection) {
+ *errorMessage = QString::fromLatin1("No preceding @if found for @elsif at %1").
+ arg(l + 1);
+ return false;
+ }
+ if (top.parentEnabled) {
+ if (!evaluateExpression(expression, &expressionValue, errorMessage)) {
+ *errorMessage = QString::fromLatin1("Error in @elsif at %1: %2").
+ arg(l + 1).arg(*errorMessage);
+ return false;
+ }
+ }
+ if (debug)
+ qDebug("'%s' : expr='%s' -> %d", qPrintable(lines.at(l)), qPrintable(expression), expressionValue);
+ top.section = ElsifSection;
+ // ignore consecutive '@elsifs' once something matched
+ if (top.anyIfClauseMatched) {
+ top.condition = false;
+ } else {
+ if ( (top.condition = expressionValue) )
+ top.anyIfClauseMatched = true;
+ }
+ break;
+ case ElseSection: // '@else': Check condition.
+ if (top.section != IfSection && top.section != ElsifSection) {
+ *errorMessage = QString::fromLatin1("No preceding @if/@elsif found for @else at %1").
+ arg(l + 1);
+ return false;
+ }
+ expressionValue = top.parentEnabled && !top.anyIfClauseMatched;
+ if (debug)
+ qDebug("%s -> %d", qPrintable(lines.at(l)), expressionValue);
+ top.section = ElseSection;
+ top.condition = expressionValue;
+ break;
+ case EndifSection: // '@endif': Discard section.
+ m_sectionStack.pop();
+ break;
+ case OtherSection: // Rest: Append according to current condition.
+ if (top.condition) {
+ out->append(lines.at(l));
+ out->append(newLine);
+ }
+ break;
+ } // switch section
+
+ } // for lines
+ return true;
+}
+
+bool customWizardPreprocess(const QString &in, QString *out, QString *errorMessage)
+{
+ PreprocessContext context;
+ return context.process(in, out, errorMessage);
+}
+
+} // namespace Internal
+} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.h b/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.h
new file mode 100644
index 0000000000..be84a04f72
--- /dev/null
+++ b/src/plugins/projectexplorer/customwizard/customwizardpreprocessor.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef CUSTOMWIZARDPREPROCESSOR_H
+#define CUSTOMWIZARDPREPROCESSOR_H
+
+#include <QtCore/QString>
+
+namespace ProjectExplorer {
+namespace Internal {
+
+/* Preprocess a string using simple syntax: \code
+
+Text
+@if <JavaScript-expression>
+Bla...
+@elsif <JavaScript-expression2>
+Blup
+@endif
+\endcode
+
+* JavaScript-expressions must evaluate to integers or boolean, like
+* '2 == 1 + 1', '"a" == "a"'. The variables of the custom wizard will be
+* expanded beforem, so , "%VAR%" should be used for strings and %VAR% for integers.
+*/
+
+bool customWizardPreprocess(const QString &in, QString *out, QString *errorMessage);
+
+} // namespace Internal
+} // namespace ProjectExplorer
+
+#endif // CUSTOMWIZARDPREPROCESSOR_H
diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
index 3bbc97bf28..315ed6f1da 100644
--- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp
+++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp
@@ -30,6 +30,7 @@
#include "miniprojecttargetselector.h"
#include "buildconfigurationmodel.h"
#include "runconfigurationmodel.h"
+#include "target.h"
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
diff --git a/src/plugins/projectexplorer/outputformatter.cpp b/src/plugins/projectexplorer/outputformatter.cpp
index 69bb04b72e..9cf37e3103 100644
--- a/src/plugins/projectexplorer/outputformatter.cpp
+++ b/src/plugins/projectexplorer/outputformatter.cpp
@@ -37,8 +37,8 @@
using namespace ProjectExplorer;
using namespace TextEditor;
-OutputFormatter::OutputFormatter(QObject *parent)
- : QObject(parent)
+OutputFormatter::OutputFormatter()
+ : QObject()
, m_formats(0)
{
initFormats();
@@ -58,7 +58,6 @@ QPlainTextEdit *OutputFormatter::plainTextEdit() const
void OutputFormatter::setPlainTextEdit(QPlainTextEdit *plainText)
{
m_plainTextEdit = plainText;
- setParent(m_plainTextEdit);
}
void OutputFormatter::appendApplicationOutput(const QString &text, bool onStdErr)
@@ -83,15 +82,6 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
cursor.insertText(text, format);
}
-void OutputFormatter::mousePressEvent(QMouseEvent * /*e*/)
-{}
-
-void OutputFormatter::mouseReleaseEvent(QMouseEvent * /*e*/)
-{}
-
-void OutputFormatter::mouseMoveEvent(QMouseEvent * /*e*/)
-{}
-
void OutputFormatter::initFormats()
{
FontSettings fs = TextEditorSettings::instance()->fontSettings();
@@ -117,3 +107,8 @@ void OutputFormatter::initFormats()
m_formats[StdErrFormat].setFont(font);
m_formats[StdErrFormat].setForeground(QColor(200, 0, 0));
}
+
+void OutputFormatter::handleLink(const QString &href)
+{
+ Q_UNUSED(href);
+}
diff --git a/src/plugins/projectexplorer/outputformatter.h b/src/plugins/projectexplorer/outputformatter.h
index effff7725a..71737a9e7c 100644
--- a/src/plugins/projectexplorer/outputformatter.h
+++ b/src/plugins/projectexplorer/outputformatter.h
@@ -46,7 +46,7 @@ class PROJECTEXPLORER_EXPORT OutputFormatter: public QObject
Q_OBJECT
public:
- OutputFormatter(QObject *parent = 0);
+ OutputFormatter();
virtual ~OutputFormatter();
QPlainTextEdit *plainTextEdit() const;
@@ -55,9 +55,7 @@ public:
virtual void appendApplicationOutput(const QString &text, bool onStdErr);
virtual void appendMessage(const QString &text, bool isError);
- virtual void mousePressEvent(QMouseEvent *e);
- virtual void mouseReleaseEvent(QMouseEvent *e);
- virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void handleLink(const QString &href);
protected:
enum Format {
diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp
index 91d0cab951..99a8d34d83 100644
--- a/src/plugins/projectexplorer/outputwindow.cpp
+++ b/src/plugins/projectexplorer/outputwindow.cpp
@@ -33,6 +33,7 @@
#include "projectexplorersettings.h"
#include "runconfiguration.h"
#include "session.h"
+#include "outputformatter.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
@@ -43,6 +44,8 @@
#include <coreplugin/icontext.h>
#include <find/basetextfind.h>
#include <aggregation/aggregate.h>
+#include <texteditor/basetexteditor.h>
+#include <projectexplorer/project.h>
#include <QtGui/QIcon>
#include <QtGui/QScrollBar>
@@ -56,6 +59,7 @@
#include <QtGui/QVBoxLayout>
#include <QtGui/QTabWidget>
#include <QtGui/QToolButton>
+#include <QtGui/QShowEvent>
using namespace ProjectExplorer::Internal;
using namespace ProjectExplorer;
@@ -209,7 +213,7 @@ void OutputPane::createNewOutputWindow(RunControl *rc)
OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i));
ow->grayOutOldContent();
ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum());
- ow->setFormatter(rc->createOutputFormatter(ow));
+ ow->setFormatter(rc->outputFormatter());
m_outputWindows.insert(rc, ow);
found = true;
break;
@@ -217,7 +221,9 @@ void OutputPane::createNewOutputWindow(RunControl *rc)
}
if (!found) {
OutputWindow *ow = new OutputWindow(m_tabWidget);
- ow->setFormatter(rc->createOutputFormatter(ow));
+ ow->setWindowTitle(tr("Application Output Window"));
+ ow->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
+ ow->setFormatter(rc->outputFormatter());
Aggregation::Aggregate *agg = new Aggregation::Aggregate;
agg->add(ow);
agg->add(new Find::BaseTextFind(ow));
@@ -372,13 +378,14 @@ bool OutputPane::canNavigate()
OutputWindow::OutputWindow(QWidget *parent)
: QPlainTextEdit(parent)
+ , m_formatter(0)
, m_enforceNewline(false)
, m_scrollToBottom(false)
+ , m_linksActive(true)
+ , m_mousePressed(false)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
//setCenterOnScroll(false);
- setWindowTitle(tr("Application Output Window"));
- setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
setFrameShape(QFrame::NoFrame);
setMouseTracking(true);
@@ -427,6 +434,42 @@ OutputWindow::~OutputWindow()
delete m_outputWindowContext;
}
+void OutputWindow::mousePressEvent(QMouseEvent * e)
+{
+ m_mousePressed = true;
+ QPlainTextEdit::mousePressEvent(e);
+}
+
+
+void OutputWindow::mouseReleaseEvent(QMouseEvent *e)
+{
+ m_mousePressed = false;
+
+ if (!m_linksActive) {
+ // Mouse was released, activate links again
+ m_linksActive = true;
+ return;
+ }
+
+ const QString href = anchorAt(e->pos());
+ if (m_formatter)
+ m_formatter->handleLink(href);
+ QPlainTextEdit::mousePressEvent(e);
+}
+
+void OutputWindow::mouseMoveEvent(QMouseEvent *e)
+{
+ // Cursor was dragged to make a selection, deactivate links
+ if (m_mousePressed && textCursor().hasSelection())
+ m_linksActive = false;
+
+ if (!m_linksActive || anchorAt(e->pos()).isEmpty())
+ viewport()->setCursor(Qt::IBeamCursor);
+ else
+ viewport()->setCursor(Qt::PointingHandCursor);
+ QPlainTextEdit::mouseMoveEvent(e);
+}
+
OutputFormatter *OutputWindow::formatter() const
{
return m_formatter;
@@ -514,6 +557,28 @@ void OutputWindow::appendMessage(const QString &out, bool isError)
enableUndoRedo();
}
+// TODO rename
+void OutputWindow::appendText(const QString &text, const QTextCharFormat &format, int maxLineCount)
+{
+ if (document()->blockCount() > maxLineCount)
+ return;
+ const bool atBottom = isScrollbarAtBottom();
+ QTextCursor cursor = QTextCursor(document());
+ cursor.movePosition(QTextCursor::End);
+ cursor.beginEditBlock();
+ cursor.insertText(doNewlineEnfocement(text), format);
+
+ if (document()->blockCount() > maxLineCount) {
+ QTextCharFormat tmp;
+ tmp.setFontWeight(QFont::Bold);
+ cursor.insertText(tr("Additional output omitted\n"), tmp);
+ }
+
+ cursor.endEditBlock();
+ if (atBottom)
+ scrollToBottom();
+}
+
bool OutputWindow::isScrollbarAtBottom() const
{
return verticalScrollBar()->value() == verticalScrollBar()->maximum();
@@ -552,21 +617,3 @@ void OutputWindow::enableUndoRedo()
setMaximumBlockCount(0);
setUndoRedoEnabled(true);
}
-
-void OutputWindow::mousePressEvent(QMouseEvent *e)
-{
- QPlainTextEdit::mousePressEvent(e);
- m_formatter->mousePressEvent(e);
-}
-
-void OutputWindow::mouseReleaseEvent(QMouseEvent *e)
-{
- QPlainTextEdit::mouseReleaseEvent(e);
- m_formatter->mouseReleaseEvent(e);
-}
-
-void OutputWindow::mouseMoveEvent(QMouseEvent *e)
-{
- QPlainTextEdit::mouseMoveEvent(e);
- m_formatter->mouseMoveEvent(e);
-}
diff --git a/src/plugins/projectexplorer/outputwindow.h b/src/plugins/projectexplorer/outputwindow.h
index 0646c16f7e..4cc3eecb03 100644
--- a/src/plugins/projectexplorer/outputwindow.h
+++ b/src/plugins/projectexplorer/outputwindow.h
@@ -31,12 +31,9 @@
#define OUTPUTWINDOW_H
#include <coreplugin/ioutputpane.h>
-#include <projectexplorer/outputformatter.h>
-#include <QtCore/QObject>
#include <QtCore/QHash>
#include <QtGui/QIcon>
-#include <QtGui/QShowEvent>
#include <QtGui/QPlainTextEdit>
QT_BEGIN_NAMESPACE
@@ -50,8 +47,9 @@ namespace Core {
}
namespace ProjectExplorer {
-
+class OutputFormatter;
class RunControl;
+class Project;
namespace Constants {
const char * const C_APP_OUTPUT = "Application Output";
@@ -137,29 +135,38 @@ public:
void appendApplicationOutput(const QString &out, bool onStdErr);
void appendApplicationOutputInline(const QString &out, bool onStdErr);
void appendMessage(const QString &out, bool isError);
+ /// appends a \p text using \p format without using formater
+ void appendText(const QString &text, const QTextCharFormat &format, int maxLineCount);
void grayOutOldContent();
void showEvent(QShowEvent *);
-protected:
- void mousePressEvent(QMouseEvent *e);
- void mouseReleaseEvent(QMouseEvent *e);
- void mouseMoveEvent(QMouseEvent *e);
+ void clear()
+ {
+ m_enforceNewline = false;
+ QPlainTextEdit::clear();
+ }
+protected:
bool isScrollbarAtBottom() const;
void scrollToBottom();
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+
private:
void enableUndoRedo();
QString doNewlineEnfocement(const QString &out);
-private:
Core::BaseContext *m_outputWindowContext;
OutputFormatter *m_formatter;
bool m_enforceNewline;
bool m_scrollToBottom;
+ bool m_linksActive;
+ bool m_mousePressed;
};
} // namespace Internal
diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h
index 01b4569cb3..f88dcabd76 100644
--- a/src/plugins/projectexplorer/project.h
+++ b/src/plugins/projectexplorer/project.h
@@ -31,7 +31,6 @@
#define PROJECT_H
#include "projectexplorer_export.h"
-#include "target.h"
#include <QtCore/QObject>
#include <QtCore/QSet>
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index ad0cebcde4..aa6aa70e2b 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -97,6 +97,7 @@
#include <utils/consoleprocess.h>
#include <utils/qtcassert.h>
#include <utils/parameteraction.h>
+#include <utils/stringutils.h>
#include <QtCore/QtPlugin>
#include <QtCore/QDateTime>
@@ -146,6 +147,10 @@ struct ProjectExplorerPluginPrivate {
Utils::ParameterAction *m_rebuildActionContextMenu;
QAction *m_rebuildSessionAction;
QAction *m_cleanProjectOnlyAction;
+ QAction *m_deployProjectOnlyAction;
+ Utils::ParameterAction *m_deployAction;
+ Utils::ParameterAction *m_deployActionContextMenu;
+ QAction *m_deploySessionAction;
Utils::ParameterAction *m_cleanAction;
Utils::ParameterAction *m_cleanActionContextMenu;
QAction *m_cleanSessionAction;
@@ -312,13 +317,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
ProcessStepFactory *processStepFactory = new ProcessStepFactory;
addAutoReleasedObject(processStepFactory);
- ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
AllProjectsFind *allProjectsFind = new AllProjectsFind(this,
- pm->getObject<Find::SearchResultWindow>());
+ Find::SearchResultWindow::instance());
addAutoReleasedObject(allProjectsFind);
CurrentProjectFind *currentProjectFind = new CurrentProjectFind(this,
- pm->getObject<Find::SearchResultWindow>());
+ Find::SearchResultWindow::instance());
addAutoReleasedObject(currentProjectFind);
addAutoReleasedObject(new LocalApplicationRunControlFactory);
@@ -569,6 +573,12 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
mbuild->addAction(cmd, Constants::G_BUILD_SESSION);
msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD);
+ // deploy session
+ d->m_deploySessionAction = new QAction(tr("Deploy All"), this);
+ cmd = am->registerAction(d->m_deploySessionAction, Constants::DEPLOYSESSION, globalcontext);
+ mbuild->addAction(cmd, Constants::G_BUILD_SESSION);
+ msessionContextMenu->addAction(cmd, Constants::G_SESSION_BUILD);
+
// clean session
QIcon cleanIcon(Constants::ICON_CLEAN);
cleanIcon.addFile(Constants::ICON_CLEAN_SMALL);
@@ -594,6 +604,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
cmd->setDefaultText(d->m_rebuildAction->text());
mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
+ // deploy action
+ d->m_deployAction = new Utils::ParameterAction(tr("Deploy Project"), tr("Deploy Project \"%1\""),
+ Utils::ParameterAction::AlwaysEnabled, this);
+ cmd = am->registerAction(d->m_deployAction, Constants::DEPLOY, globalcontext);
+ cmd->setAttribute(Core::Command::CA_UpdateText);
+ cmd->setDefaultText(d->m_deployAction->text());
+ mbuild->addAction(cmd, Constants::G_BUILD_PROJECT);
+
// clean action
d->m_cleanAction = new Utils::ParameterAction(tr("Clean Project"), tr("Clean Project \"%1\""),
Utils::ParameterAction::AlwaysEnabled, this);
@@ -618,6 +636,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
cmd->setDefaultText(d->m_rebuildActionContextMenu->text());
mproject->addAction(cmd, Constants::G_PROJECT_BUILD);
+ // deploy action (context menu)
+ d->m_deployActionContextMenu = new Utils::ParameterAction(tr("Deploy Project"), tr("Deploy Project \"%1\""),
+ Utils::ParameterAction::AlwaysEnabled, this);
+ cmd = am->registerAction(d->m_rebuildActionContextMenu, Constants::DEPLOYCM, globalcontext);
+ cmd->setAttribute(Core::Command::CA_UpdateText);
+ cmd->setDefaultText(d->m_deployActionContextMenu->text());
+ mproject->addAction(cmd, Constants::G_PROJECT_BUILD);
+
// clean action (context menu)
d->m_cleanActionContextMenu = new Utils::ParameterAction(tr("Clean Project"), tr("Clean Project \"%1\""),
Utils::ParameterAction::AlwaysEnabled, this);
@@ -634,6 +660,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
d->m_rebuildProjectOnlyAction = new QAction(tr("Rebuild Without Dependencies"), this);
cmd = am->registerAction(d->m_rebuildProjectOnlyAction, Constants::REBUILDPROJECTONLY, globalcontext);
+ // deploy without dependencies action
+ d->m_deployProjectOnlyAction = new QAction(tr("Deploy Without Dependencies"), this);
+ cmd = am->registerAction(d->m_deployProjectOnlyAction, Constants::DEPLOYPROJECTONLY, globalcontext);
+
// clean without dependencies action
d->m_cleanProjectOnlyAction = new QAction(tr("Clean Without Dependencies"), this);
cmd = am->registerAction(d->m_cleanProjectOnlyAction, Constants::CLEANPROJECTONLY, globalcontext);
@@ -748,7 +778,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
}
if (QSettings *s = core->settings()) {
- d->m_projectExplorerSettings.buildBeforeRun = s->value("ProjectExplorer/Settings/BuildBeforeRun", true).toBool();
+ d->m_projectExplorerSettings.buildBeforeDeploy = s->value("ProjectExplorer/Settings/BuildBeforeDeploy", true).toBool();
+ d->m_projectExplorerSettings.deployBeforeRun = s->value("ProjectExplorer/Settings/DeployBeforeRun", true).toBool();
d->m_projectExplorerSettings.saveBeforeBuild = s->value("ProjectExplorer/Settings/SaveBeforeBuild", false).toBool();
d->m_projectExplorerSettings.showCompilerOutput = s->value("ProjectExplorer/Settings/ShowCompilerOutput", false).toBool();
d->m_projectExplorerSettings.cleanOldAppOutput = s->value("ProjectExplorer/Settings/CleanOldAppOutput", false).toBool();
@@ -771,6 +802,10 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
connect(d->m_rebuildAction, SIGNAL(triggered()), this, SLOT(rebuildProject()));
connect(d->m_rebuildActionContextMenu, SIGNAL(triggered()), this, SLOT(rebuildProjectContextMenu()));
connect(d->m_rebuildSessionAction, SIGNAL(triggered()), this, SLOT(rebuildSession()));
+ connect(d->m_deployProjectOnlyAction, SIGNAL(triggered()), this, SLOT(deployProjectOnly()));
+ connect(d->m_deployAction, SIGNAL(triggered()), this, SLOT(deployProject()));
+ connect(d->m_deployActionContextMenu, SIGNAL(triggered()), this, SLOT(deployProjectContextMenu()));
+ connect(d->m_deploySessionAction, SIGNAL(triggered()), this, SLOT(deploySession()));
connect(d->m_cleanProjectOnlyAction, SIGNAL(triggered()), this, SLOT(cleanProjectOnly()));
connect(d->m_cleanAction, SIGNAL(triggered()), this, SLOT(cleanProject()));
connect(d->m_cleanActionContextMenu, SIGNAL(triggered()), this, SLOT(cleanProjectContextMenu()));
@@ -909,12 +944,13 @@ void ProjectExplorerPlugin::extensionsInitialized()
d->m_buildManager->extensionsInitialized();
}
-void ProjectExplorerPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
{
d->m_proWindow->aboutToShutdown(); // disconnect from session
d->m_session->clear();
d->m_projectsMode = 0;
// d->m_proWindow->saveConfigChanges();
+ return SynchronousShutdown;
}
void ProjectExplorerPlugin::newProject()
@@ -998,7 +1034,8 @@ void ProjectExplorerPlugin::savePersistentSettings()
s->setValue("ProjectExplorer/RecentProjects/FileNames", fileNames);
s->setValue("ProjectExplorer/RecentProjects/DisplayNames", displayNames);
- s->setValue("ProjectExplorer/Settings/BuildBeforeRun", d->m_projectExplorerSettings.buildBeforeRun);
+ s->setValue("ProjectExplorer/Settings/BuildBeforeDeploy", d->m_projectExplorerSettings.buildBeforeDeploy);
+ s->setValue("ProjectExplorer/Settings/DeployBeforeRun", d->m_projectExplorerSettings.deployBeforeRun);
s->setValue("ProjectExplorer/Settings/SaveBeforeBuild", d->m_projectExplorerSettings.saveBeforeBuild);
s->setValue("ProjectExplorer/Settings/ShowCompilerOutput", d->m_projectExplorerSettings.showCompilerOutput);
s->setValue("ProjectExplorer/Settings/CleanOldAppOutput", d->m_projectExplorerSettings.cleanOldAppOutput);
@@ -1261,7 +1298,6 @@ void ProjectExplorerPlugin::executeRunConfiguration(RunConfiguration *runConfigu
RunControl *control = runControlFactory->create(runConfiguration, runMode);
startRunControl(control, runMode);
}
-
}
void ProjectExplorerPlugin::startRunControl(RunControl *runControl, const QString &runMode)
@@ -1362,19 +1398,18 @@ void ProjectExplorerPlugin::updateActions()
if (debug)
qDebug() << "ProjectExplorerPlugin::updateActions";
- Project *startupProject = session()->startupProject();
- bool enableBuildActions = startupProject
- && ! (d->m_buildManager->isBuilding(startupProject))
- && hasBuildSettings(startupProject);
+ Project *project = startupProject();
+ bool enableBuildActions = project
+ && ! (d->m_buildManager->isBuilding(project))
+ && hasBuildSettings(project);
bool enableBuildActionsContextMenu = d->m_currentProject
&& ! (d->m_buildManager->isBuilding(d->m_currentProject))
&& hasBuildSettings(d->m_currentProject);
-
bool hasProjects = !d->m_session->projects().isEmpty();
bool building = d->m_buildManager->isBuilding();
- QString projectName = startupProject ? startupProject->displayName() : QString();
+ QString projectName = project ? project->displayName() : QString();
QString projectNameContextMenu = d->m_currentProject ? d->m_currentProject->displayName() : QString();
if (debug)
@@ -1411,7 +1446,7 @@ void ProjectExplorerPlugin::updateActions()
d->m_projectSelectorAction->setEnabled(!session()->projects().isEmpty());
d->m_projectSelectorActionMenu->setEnabled(!session()->projects().isEmpty());
- updateRunActions();
+ updateDeployActions();
}
// NBS TODO check projectOrder()
@@ -1575,6 +1610,54 @@ void ProjectExplorerPlugin::rebuildSession()
}
}
+void ProjectExplorerPlugin::deployProjectOnly()
+{
+ if (!saveModifiedFiles())
+ return;
+ d->m_buildManager->deployProject(session()->startupProject()->activeTarget()->activeBuildConfiguration());
+}
+
+void ProjectExplorerPlugin::deployProject()
+{
+ if (!saveModifiedFiles())
+ return;
+
+ const QList<Project *> &projects = d->m_session->projectOrder(session()->startupProject());
+ QList<BuildConfiguration *> configurations;
+ foreach (Project *pro, projects)
+ if (pro->activeTarget()->activeBuildConfiguration())
+ configurations << pro->activeTarget()->activeBuildConfiguration();
+
+ d->m_buildManager->deployProjects(configurations);
+}
+
+void ProjectExplorerPlugin::deployProjectContextMenu()
+{
+ if (!saveModifiedFiles())
+ return;
+
+ QList<BuildConfiguration *> configurations;
+ foreach (Project *pro, d->m_session->projectOrder(d->m_currentProject))
+ if (pro->activeTarget()->activeBuildConfiguration())
+ configurations << pro->activeTarget()->activeBuildConfiguration();
+
+ d->m_buildManager->deployProjects(configurations);
+}
+
+void ProjectExplorerPlugin::deploySession()
+{
+ if (!saveModifiedFiles())
+ return;
+
+ const QList<Project *> & projects = d->m_session->projectOrder();
+ QList<BuildConfiguration *> configurations;
+ foreach (Project *pro, projects)
+ if (pro->activeTarget()->activeBuildConfiguration())
+ configurations << pro->activeTarget()->activeBuildConfiguration();
+
+ d->m_buildManager->deployProjects(configurations);
+}
+
void ProjectExplorerPlugin::cleanProjectOnly()
{
if (debug)
@@ -1648,34 +1731,58 @@ bool ProjectExplorerPlugin::hasBuildSettings(Project *pro)
return false;
}
+bool ProjectExplorerPlugin::hasDeploySettings(Project *pro)
+{
+ const QList<Project *> & projects = d->m_session->projectOrder(pro);
+ foreach(Project *project, projects)
+ if (project->activeTarget()->activeBuildConfiguration() &&
+ !project->activeTarget()->activeBuildConfiguration()->steps(BuildStep::Deploy).isEmpty())
+ return true;
+ return false;
+}
+
void ProjectExplorerPlugin::runProjectImpl(Project *pro, QString mode)
{
if (!pro)
return;
- if (d->m_projectExplorerSettings.buildBeforeRun && hasBuildSettings(pro)) {
- if (!pro->activeTarget()->activeRunConfiguration()->isEnabled()) {
- if (!showBuildConfigDialog())
+ if (!pro->activeTarget()->activeRunConfiguration()->isEnabled()) {
+ if (!showBuildConfigDialog())
+ return;
+ }
+
+ if (!saveModifiedFiles())
+ return;
+
+ bool delayRun = false;
+ // Deploy/build first?
+ if (d->m_projectExplorerSettings.deployBeforeRun) {
+ const QList<Project *> & projects = d->m_session->projectOrder(pro);
+ QList<BuildConfiguration *> configurations;
+ foreach(Project *project, projects)
+ if (project->activeTarget()->activeBuildConfiguration())
+ configurations << project->activeTarget()->activeBuildConfiguration();
+
+ if (d->m_projectExplorerSettings.buildBeforeDeploy && hasBuildSettings(pro)) {
+ if (!d->m_buildManager->buildProjects(configurations))
return;
+ delayRun = true;
}
- if (saveModifiedFiles()) {
- d->m_runMode = mode;
- d->m_delayedRunConfiguration = pro->activeTarget()->activeRunConfiguration();
-
- const QList<Project *> & projects = d->m_session->projectOrder(pro);
- QList<BuildConfiguration *> configurations;
- foreach(Project *project, projects)
- if (project->activeTarget()->activeBuildConfiguration())
- configurations << project->activeTarget()->activeBuildConfiguration();
- d->m_buildManager->buildProjects(configurations);
-
- updateRunActions();
+ if (hasDeploySettings(pro)) {
+ if (!d->m_buildManager->deployProjects(configurations))
+ return;
+ delayRun = true;
}
+ }
+
+ // Actually run (delayed)
+ if (delayRun) {
+ d->m_runMode = mode;
+ d->m_delayedRunConfiguration = pro->activeTarget()->activeRunConfiguration();
} else {
- // TODO this ignores RunConfiguration::isEnabled()
- if (saveModifiedFiles())
- executeRunConfiguration(pro->activeTarget()->activeRunConfiguration(), mode);
+ executeRunConfiguration(pro->activeTarget()->activeRunConfiguration(), mode);
}
+ updateRunActions();
}
void ProjectExplorerPlugin::debugProject()
@@ -1735,7 +1842,7 @@ void ProjectExplorerPlugin::startupProjectChanged()
this, SLOT(updateRunActions()));
disconnect(previousStartupProject, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
- this, SLOT(updateRunActions()));
+ this, SLOT(updateDeployActions()));
disconnect(previousStartupProject->activeTarget()->activeRunConfiguration(),
SIGNAL(isEnabledChanged(bool)), this, SLOT(updateRunActions()));
@@ -1748,7 +1855,7 @@ void ProjectExplorerPlugin::startupProjectChanged()
if (project) {
connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
- this, SLOT(updateRunActions()));
+ this, SLOT(updateDeployActions()));
connect(project->activeTarget(), SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)),
this, SLOT(updateRunActions()));
@@ -1776,6 +1883,35 @@ IRunControlFactory *ProjectExplorerPlugin::findRunControlFactory(RunConfiguratio
return 0;
}
+void ProjectExplorerPlugin::updateDeployActions()
+{
+ Project *project = startupProject();
+
+ bool enableDeployActions = project
+ && ! (d->m_buildManager->isBuilding(project))
+ && hasDeploySettings(project);
+ bool enableDeployActionsContextMenu = d->m_currentProject
+ && ! (d->m_buildManager->isBuilding(d->m_currentProject))
+ && hasDeploySettings(d->m_currentProject);
+
+ const QString projectName = project ? project->displayName() : QString();
+ const QString projectNameContextMenu = d->m_currentProject ? d->m_currentProject->displayName() : QString();
+ bool hasProjects = !d->m_session->projects().isEmpty();
+ bool building = d->m_buildManager->isBuilding();
+
+ d->m_deployAction->setParameter(projectName);
+ d->m_deployAction->setEnabled(enableDeployActions);
+
+ d->m_deployActionContextMenu->setParameter(projectNameContextMenu);
+ d->m_deployActionContextMenu->setEnabled(enableDeployActionsContextMenu);
+
+ d->m_deployProjectOnlyAction->setEnabled(enableDeployActions);
+
+ d->m_deploySessionAction->setEnabled(hasProjects && !building);
+
+ updateRunActions();
+}
+
void ProjectExplorerPlugin::updateRunActions()
{
const Project *project = startupProject();
@@ -1863,7 +1999,7 @@ void ProjectExplorerPlugin::updateRecentProjectMenu()
const QPair<QString, QString> &s = *it;
if (s.first.endsWith(QLatin1String(".qws")))
continue;
- QAction *action = menu->addAction(s.first);
+ QAction *action = menu->addAction(Utils::withTildeHomePath(s.first));
action->setData(s.first);
connect(action, SIGNAL(triggered()), this, SLOT(openRecentProject()));
}
diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h
index 13491c3364..973dc53535 100644
--- a/src/plugins/projectexplorer/projectexplorer.h
+++ b/src/plugins/projectexplorer/projectexplorer.h
@@ -100,7 +100,7 @@ public:
//PluginInterface
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
- void aboutToShutdown();
+ ShutdownFlag aboutToShutdown();
void setProjectExplorerSettings(const Internal::ProjectExplorerSettings &pes);
Internal::ProjectExplorerSettings projectExplorerSettings() const;
@@ -140,6 +140,10 @@ private slots:
void rebuildProject();
void rebuildProjectContextMenu();
void rebuildSession();
+ void deployProjectOnly();
+ void deployProject();
+ void deployProjectContextMenu();
+ void deploySession();
void cleanProjectOnly();
void cleanProject();
void cleanProjectContextMenu();
@@ -182,6 +186,7 @@ private slots:
void runControlFinished();
void startupProjectChanged(); // Calls updateRunAction
+ void updateDeployActions();
void updateRunActions();
void loadProject(const QString &project) { openProject(project); }
@@ -207,6 +212,8 @@ private:
void runProjectImpl(Project *pro, QString mode);
void executeRunConfiguration(RunConfiguration *, const QString &mode);
bool hasBuildSettings(Project *pro);
+ bool hasDeploySettings(Project *pro);
+
bool showBuildConfigDialog();
void setCurrent(Project *project, QString filePath, Node *node);
diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h
index 5a09c20c61..e25693ca97 100644
--- a/src/plugins/projectexplorer/projectexplorerconstants.h
+++ b/src/plugins/projectexplorer/projectexplorerconstants.h
@@ -53,6 +53,10 @@ const char * const REBUILDPROJECTONLY = "ProjectExplorer.RebuildProjectOnly";
const char * const REBUILD = "ProjectExplorer.Rebuild";
const char * const REBUILDCM = "ProjectExplorer.RebuildCM";
const char * const REBUILDSESSION = "ProjectExplorer.RebuildSession";
+const char * const DEPLOYPROJECTONLY = "ProjectExplorer.DeployProjectOnly";
+const char * const DEPLOY = "ProjectExplorer.Deploy";
+const char * const DEPLOYCM = "ProjectExplorer.DeployCM";
+const char * const DEPLOYSESSION = "ProjectExplorer.DeploySession";
const char * const CLEANPROJECTONLY = "ProjectExplorer.CleanProjectOnly";
const char * const CLEAN = "ProjectExplorer.Clean";
const char * const CLEANCM = "ProjectExplorer.CleanCM";
diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h
index 74b6f1c363..256f4ef263 100644
--- a/src/plugins/projectexplorer/projectexplorersettings.h
+++ b/src/plugins/projectexplorer/projectexplorersettings.h
@@ -37,11 +37,11 @@ namespace Internal {
struct ProjectExplorerSettings
{
- ProjectExplorerSettings() : buildBeforeRun(true), saveBeforeBuild(false),
- showCompilerOutput(false),
- cleanOldAppOutput(false), useJom(true) {}
+ ProjectExplorerSettings() : buildBeforeDeploy(true), deployBeforeRun(true), saveBeforeBuild(false),
+ showCompilerOutput(false), cleanOldAppOutput(false), useJom(true) {}
- bool buildBeforeRun;
+ bool buildBeforeDeploy;
+ bool deployBeforeRun;
bool saveBeforeBuild;
bool showCompilerOutput;
bool cleanOldAppOutput;
@@ -54,7 +54,8 @@ struct ProjectExplorerSettings
inline bool operator==(const ProjectExplorerSettings &p1, const ProjectExplorerSettings &p2)
{
- return p1.buildBeforeRun == p2.buildBeforeRun
+ return p1.buildBeforeDeploy == p2.buildBeforeDeploy
+ && p1.deployBeforeRun == p2.deployBeforeRun
&& p1.saveBeforeBuild == p2.saveBeforeBuild
&& p1.showCompilerOutput == p2.showCompilerOutput
&& p1.cleanOldAppOutput == p2.cleanOldAppOutput
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettingspage.cpp
index 806b9b4570..71f6ed60d9 100644
--- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp
+++ b/src/plugins/projectexplorer/projectexplorersettingspage.cpp
@@ -65,7 +65,8 @@ void ProjectExplorerSettingsWidget::setJomVisible(bool v)
ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const
{
ProjectExplorerSettings pes;
- pes.buildBeforeRun = m_ui.buildProjectBeforeRunCheckBox->isChecked();
+ pes.buildBeforeDeploy = m_ui.buildProjectBeforeDeployCheckBox->isChecked();
+ pes.deployBeforeRun = m_ui.deployProjectBeforeRunCheckBox->isChecked();
pes.saveBeforeBuild = m_ui.saveAllFilesCheckBox->isChecked();
pes.showCompilerOutput = m_ui.showCompileOutputCheckBox->isChecked();
pes.cleanOldAppOutput = m_ui.cleanOldAppOutputCheckBox->isChecked();
@@ -75,7 +76,8 @@ ProjectExplorerSettings ProjectExplorerSettingsWidget::settings() const
void ProjectExplorerSettingsWidget::setSettings(const ProjectExplorerSettings &pes) const
{
- m_ui.buildProjectBeforeRunCheckBox->setChecked(pes.buildBeforeRun);
+ m_ui.buildProjectBeforeDeployCheckBox->setChecked(pes.buildBeforeDeploy);
+ m_ui.deployProjectBeforeRunCheckBox->setChecked(pes.deployBeforeRun);
m_ui.saveAllFilesCheckBox->setChecked(pes.saveBeforeBuild);
m_ui.showCompileOutputCheckBox->setChecked(pes.showCompilerOutput);
m_ui.cleanOldAppOutputCheckBox->setChecked(pes.cleanOldAppOutput);
diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.ui b/src/plugins/projectexplorer/projectexplorersettingspage.ui
index a08005b5f3..dc0c30526d 100644
--- a/src/plugins/projectexplorer/projectexplorersettingspage.ui
+++ b/src/plugins/projectexplorer/projectexplorersettingspage.ui
@@ -63,9 +63,16 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="buildProjectBeforeRunCheckBox">
+ <widget class="QCheckBox" name="buildProjectBeforeDeployCheckBox">
<property name="text">
- <string>Always build project before running</string>
+ <string>Always build project before deploying it</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="deployProjectBeforeRunCheckBox">
+ <property name="text">
+ <string>Always deploy project before running it</string>
</property>
</widget>
</item>
diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp
index 04ad845036..f34e85ab25 100644
--- a/src/plugins/projectexplorer/projectnodes.cpp
+++ b/src/plugins/projectexplorer/projectnodes.cpp
@@ -154,8 +154,7 @@ bool FileNode::isGenerated() const
*/
FolderNode::FolderNode(const QString &folderPath) :
Node(FolderNodeType, folderPath),
- m_displayName(QDir::toNativeSeparators(folderPath)),
- m_icon(Core::FileIconProvider::instance()->icon(QFileIconProvider::Folder))
+ m_displayName(QDir::toNativeSeparators(folderPath))
{
}
@@ -182,6 +181,9 @@ QString FolderNode::displayName() const
*/
QIcon FolderNode::icon() const
{
+ // Instantiating the Icon provider is expensive.
+ if (m_icon.isNull())
+ m_icon = Core::FileIconProvider::instance()->icon(QFileIconProvider::Folder);
return m_icon;
}
diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h
index 3a4511e8b5..6679b6184a 100644
--- a/src/plugins/projectexplorer/projectnodes.h
+++ b/src/plugins/projectexplorer/projectnodes.h
@@ -147,7 +147,7 @@ private:
// managed by ProjectNode
friend class ProjectNode;
QString m_displayName;
- QIcon m_icon;
+ mutable QIcon m_icon;
};
class PROJECTEXPLORER_EXPORT ProjectNode : public FolderNode
diff --git a/src/plugins/projectexplorer/projectwelcomepagewidget.cpp b/src/plugins/projectexplorer/projectwelcomepagewidget.cpp
index e75df35e9b..9c30289eb4 100644
--- a/src/plugins/projectexplorer/projectwelcomepagewidget.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepagewidget.cpp
@@ -39,6 +39,8 @@
#include <coreplugin/mainwindow.h>
#include <coreplugin/filemanager.h>
+#include <utils/stringutils.h>
+
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QPair>
@@ -146,8 +148,9 @@ void ProjectWelcomePageWidget::updateWelcomePage(const WelcomePageData &welcomeP
break;
const QFileInfo fi(it.first);
QString label = "<b>" + it.second +
- "</b><br><font color=gray>" +
- fm.elidedText(it.first, Qt::ElideMiddle, 250);
+ "</b><br><font color=gray>" +
+ fm.elidedText(QDir::toNativeSeparators(Utils::withTildeHomePath(it.first)),
+ Qt::ElideMiddle, 250);
ui->projTreeWidget->addItem(label, it.first,
QDir::toNativeSeparators(fi.absolutePath()));
}
diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp
index d6b4e505cb..cd1a210ca7 100644
--- a/src/plugins/projectexplorer/projectwindow.cpp
+++ b/src/plugins/projectexplorer/projectwindow.cpp
@@ -59,6 +59,7 @@
#include <QtGui/QScrollArea>
#include <QtGui/QLabel>
#include <QtGui/QPainter>
+#include <QtGui/QStackedWidget>
#include <QtGui/QPaintEvent>
#include <QtGui/QMenu>
diff --git a/src/plugins/projectexplorer/projectwindow.h b/src/plugins/projectexplorer/projectwindow.h
index b8f9e0e01a..b4bf4fc948 100644
--- a/src/plugins/projectexplorer/projectwindow.h
+++ b/src/plugins/projectexplorer/projectwindow.h
@@ -30,22 +30,13 @@
#ifndef PROJECTWINDOW_H
#define PROJECTWINDOW_H
-#include "iprojectproperties.h"
-
-#include <QtCore/QPair>
-#include <QtCore/QMap>
-#include <QtGui/QApplication>
-#include <QtGui/QComboBox>
-#include <QtGui/QLabel>
-#include <QtGui/QPushButton>
#include <QtGui/QScrollArea>
-#include <QtGui/QStackedWidget>
-#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class QLabel;
class QGridLayout;
class QMenu;
+class QStackedWidget;
QT_END_NAMESPACE
namespace ProjectExplorer {
@@ -105,7 +96,6 @@ private:
QList<ProjectExplorer::Project *> m_tabIndexToProject;
};
-
} // namespace Internal
} // namespace ProjectExplorer
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index 33b59d6984..d089ef8ecf 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -179,6 +179,11 @@ Target *RunConfiguration::target() const
return m_target;
}
+ProjectExplorer::OutputFormatter *RunConfiguration::createOutputFormatter() const
+{
+ return new OutputFormatter();
+}
+
IRunConfigurationFactory::IRunConfigurationFactory(QObject *parent) :
QObject(parent)
{
@@ -218,13 +223,23 @@ IRunControlFactory::~IRunControlFactory()
RunControl::RunControl(RunConfiguration *runConfiguration, QString mode)
: m_runMode(mode), m_runConfiguration(runConfiguration)
{
- if (runConfiguration)
+ if (runConfiguration) {
m_displayName = runConfiguration->displayName();
+ m_outputFormatter = runConfiguration->createOutputFormatter();
+ }
+ // We need to ensure that there's always a OutputFormatter
+ if (!m_outputFormatter)
+ m_outputFormatter = new OutputFormatter();
}
RunControl::~RunControl()
{
+ delete m_outputFormatter;
+}
+OutputFormatter *RunControl::outputFormatter()
+{
+ return m_outputFormatter;
}
QString RunControl::runMode() const
@@ -242,11 +257,6 @@ bool RunControl::sameRunConfiguration(RunControl *other)
return other->m_runConfiguration.data() == m_runConfiguration.data();
}
-OutputFormatter *RunControl::createOutputFormatter(QObject *parent)
-{
- return new OutputFormatter(parent);
-}
-
void RunControl::bringApplicationToForeground(qint64 pid)
{
#ifdef Q_OS_MAC
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index f9e897e509..c1532590f6 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -85,6 +85,8 @@ public:
Target *target() const;
+ virtual ProjectExplorer::OutputFormatter *createOutputFormatter() const;
+
signals:
void isEnabledChanged(bool value);
@@ -169,7 +171,7 @@ public:
bool sameRunConfiguration(RunControl *other);
- virtual OutputFormatter *createOutputFormatter(QObject *parent = 0);
+ OutputFormatter *outputFormatter();
QString runMode() const;
signals:
@@ -189,6 +191,7 @@ private:
QString m_displayName;
QString m_runMode;
const QWeakPointer<RunConfiguration> m_runConfiguration;
+ OutputFormatter *m_outputFormatter;
#ifdef Q_OS_MAC
//these two are used to bring apps in the foreground on Mac
diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.h b/src/plugins/projectexplorer/runsettingspropertiespage.h
index 29eeb04b2e..5491a2afbd 100644
--- a/src/plugins/projectexplorer/runsettingspropertiespage.h
+++ b/src/plugins/projectexplorer/runsettingspropertiespage.h
@@ -33,7 +33,6 @@
#include "iprojectproperties.h"
#include <QtGui/QWidget>
-#include <QtCore/QAbstractListModel>
QT_BEGIN_NAMESPACE
class QMenu;
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index d92128a824..f68e311766 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -33,8 +33,6 @@
#include "projectconfiguration.h"
#include "projectexplorer_export.h"
-#include <QtCore/QObject>
-#include <QtGui/QFileSystemModel>
#include <QtGui/QIcon>
namespace ProjectExplorer {
diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp
index 69a77ae983..8ef5beb3d9 100644
--- a/src/plugins/projectexplorer/targetsettingspanel.cpp
+++ b/src/plugins/projectexplorer/targetsettingspanel.cpp
@@ -43,6 +43,7 @@
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>
#include <QtGui/QVBoxLayout>
+#include <QtGui/QStackedWidget>
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
diff --git a/src/plugins/projectexplorer/targetsettingspanel.h b/src/plugins/projectexplorer/targetsettingspanel.h
index 6d449ec1c7..874263b936 100644
--- a/src/plugins/projectexplorer/targetsettingspanel.h
+++ b/src/plugins/projectexplorer/targetsettingspanel.h
@@ -30,19 +30,18 @@
#ifndef TARGETSETTINGSPANEL_H
#define TARGETSETTINGSPANEL_H
-#include "iprojectproperties.h"
-
-#include <QtGui/QStackedWidget>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
+class QStackedWidget;
QT_END_NAMESPACE
namespace ProjectExplorer {
class Target;
+class Project;
namespace Internal {
diff --git a/src/plugins/projectexplorer/taskhub.h b/src/plugins/projectexplorer/taskhub.h
index ec27b0a3c9..1fd5a10f92 100644
--- a/src/plugins/projectexplorer/taskhub.h
+++ b/src/plugins/projectexplorer/taskhub.h
@@ -31,7 +31,7 @@
#define TASKHUB_H
#include "task.h"
-#include "projectexplorer_export.h"
+
#include <QtCore/QObject>
#include <QtGui/QIcon>
diff --git a/src/plugins/projectexplorer/taskwindow.h b/src/plugins/projectexplorer/taskwindow.h
index b6f1f4c8fb..d5a0f410c0 100644
--- a/src/plugins/projectexplorer/taskwindow.h
+++ b/src/plugins/projectexplorer/taskwindow.h
@@ -30,17 +30,17 @@
#ifndef TASKWINDOW_H
#define TASKWINDOW_H
-#include "task.h"
#include <coreplugin/ioutputpane.h>
-#include <QtGui/QIcon>
QT_BEGIN_NAMESPACE
class QAction;
class QModelIndex;
+class QPoint;
QT_END_NAMESPACE
namespace ProjectExplorer {
class TaskHub;
+class Task;
namespace Internal {
class TaskWindowPrivate;
diff --git a/src/plugins/projectexplorer/vcsannotatetaskhandler.h b/src/plugins/projectexplorer/vcsannotatetaskhandler.h
index 27fa40a804..18bfcaf6b8 100644
--- a/src/plugins/projectexplorer/vcsannotatetaskhandler.h
+++ b/src/plugins/projectexplorer/vcsannotatetaskhandler.h
@@ -34,9 +34,6 @@
#include "itaskhandler.h"
-#include <QtCore/QHash>
-#include <QtCore/QString>
-
namespace Core {
class IVersionControl;
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp
index f49c6077e9..eb8b87fde5 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.cpp
@@ -32,6 +32,7 @@
#include <utils/filterlineedit.h>
#include "itemlibrarywidgets.h"
#include "itemlibrarymodel.h"
+#include "itemlibraryimageprovider.h"
#include "customdraganddrop.h"
#include <QFileInfo>
@@ -164,6 +165,9 @@ ItemLibrary::ItemLibrary(QWidget *parent) :
m_d->m_resourcesView->setModel(m_d->m_resourcesDirModel);
m_d->m_resourcesView->setIconSize(m_d->m_resIconSize);
+ /* create image provider for loading item icons */
+ m_d->m_itemsView->engine()->addImageProvider(QLatin1String("qmldesigner_itemlibrary"), new Internal::ItemLibraryImageProvider);
+
/* other widgets */
QTabBar *tabBar = new QTabBar(this);
tabBar->addTab(tr("Items", "Title of library items view"));
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
index d4e3b0f71b..4cd2f619cd 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
@@ -6,8 +6,8 @@ VPATH += $$PWD
INCLUDEPATH += $$PWD
# Input
-HEADERS += itemlibrary.h customdraganddrop.h itemlibrarymodel.h itemlibrarywidgets.h
-SOURCES += itemlibrary.cpp customdraganddrop.cpp itemlibrarymodel.cpp itemlibrarywidgets.cpp
+HEADERS += itemlibrary.h customdraganddrop.h itemlibrarymodel.h itemlibrarywidgets.h itemlibraryimageprovider.h
+SOURCES += itemlibrary.cpp customdraganddrop.cpp itemlibrarymodel.cpp itemlibrarywidgets.cpp itemlibraryimageprovider.cpp
RESOURCES += itemlibrary.qrc
OTHER_FILES += \
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.cpp
new file mode 100644
index 0000000000..b3bb37b146
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.cpp
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+#include "itemlibraryimageprovider.h"
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+ItemLibraryImageProvider::ItemLibraryImageProvider() :
+ QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap)
+{
+}
+
+QPixmap ItemLibraryImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ QPixmap pixmap(id);
+ if (size) {
+ size->setWidth(pixmap.width());
+ size->setHeight(pixmap.height());
+ }
+ if (requestedSize.isValid())
+ return pixmap.scaled(requestedSize);
+ return pixmap;
+}
+
+}
+
+}
+
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.h
new file mode 100644
index 0000000000..861e4cfd47
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimageprovider.h
@@ -0,0 +1,51 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef ITEMLIBRARYIMAGEPROVIDER_H
+#define ITEMLIBRARYIMAGEPROVIDER_H
+
+#include <QDeclarativeImageProvider>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class ItemLibraryImageProvider : public QDeclarativeImageProvider
+{
+public:
+ ItemLibraryImageProvider();
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+};
+
+}
+
+}
+
+#endif // ITEMLIBRARYIMAGEPROVIDER_H
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index 28764e0ad9..42d8cc6889 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -176,7 +176,6 @@ ItemLibraryItemModel::ItemLibraryItemModel(QScriptEngine *scriptEngine, int item
m_scriptEngine(scriptEngine),
m_libId(itemLibId),
m_name(itemName),
- m_icon(),
m_iconSize(64, 64)
{
QScriptValue pixmapScriptValue(m_scriptEngine->newVariant(QPixmap()));
@@ -204,21 +203,19 @@ QString ItemLibraryItemModel::itemName() const
return m_name;
}
-
-void ItemLibraryItemModel::setItemIcon(const QIcon &itemIcon)
+void ItemLibraryItemModel::setItemIconPath(const QString &iconPath)
{
- m_icon = itemIcon;
+ m_iconPath = iconPath;
- QScriptValue pixmapScriptValue(m_scriptEngine->newVariant(m_icon.pixmap(m_iconSize)));
- setProperty(QLatin1String("itemPixmap"), pixmapScriptValue);
+ setProperty(QLatin1String("itemLibraryIconPath"),
+ QString(QLatin1String("image://qmldesigner_itemlibrary/") + iconPath));
}
-
void ItemLibraryItemModel::setItemIconSize(const QSize &itemIconSize)
{
m_iconSize = itemIconSize;
// qDebug() << "set icon size" << itemIconSize;
- setItemIcon(m_icon);
+ setItemIconPath(m_iconPath);
}
@@ -414,12 +411,12 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo)
itemModel = new ItemLibraryItemModel(m_scriptEngine.data(), itemId, entry.name());
// delayed creation of (default) icons
- if (entry.icon().isNull())
- entry.setIcon(QIcon(QLatin1String(":/ItemLibrary/images/item-default-icon.png")));
+ if (entry.iconPath().isEmpty())
+ entry.setIconPath(QLatin1String(":/ItemLibrary/images/item-default-icon.png"));
if (entry.dragIcon().isNull())
entry.setDragIcon(createDragPixmap(getWidth(entry), getHeight(entry)));
- itemModel->setItemIcon(entry.icon());
+ itemModel->setItemIconPath(entry.iconPath());
itemModel->setItemIconSize(m_itemIconSize);
sectionModel->addSectionEntry(itemModel);
m_sections.insert(itemId, sectionId);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
index 896710f411..5859ae61ba 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
@@ -84,7 +84,7 @@ public:
int itemLibId() const;
QString itemName() const;
- void setItemIcon(const QIcon &itemIcon);
+ void setItemIconPath(const QString &iconPath);
void setItemIconSize(const QSize &itemIconSize);
bool operator<(const ItemLibraryItemModel &other) const;
@@ -93,7 +93,7 @@ private:
QWeakPointer<QScriptEngine> m_scriptEngine;
int m_libId;
QString m_name;
- QIcon m_icon;
+ QString m_iconPath;
QSize m_iconSize;
};
diff --git a/src/plugins/qmldesigner/components/itemlibrary/qml/ItemView.qml b/src/plugins/qmldesigner/components/itemlibrary/qml/ItemView.qml
index ca3babe624..9380c90ad9 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/qml/ItemView.qml
+++ b/src/plugins/qmldesigner/components/itemlibrary/qml/ItemView.qml
@@ -96,7 +96,7 @@ Item {
width: itemLibraryIconWidth // to be set in Qml context
height: itemLibraryIconHeight // to be set in Qml context
- pixmap: itemPixmap // to be set by model
+ source: itemLibraryIconPath // to be set by model
}
Text {
diff --git a/src/plugins/qmldesigner/components/propertyeditor/contextpanetextwidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/contextpanetextwidget.cpp
index 7d1bf03308..09a893062e 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/contextpanetextwidget.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/contextpanetextwidget.cpp
@@ -272,14 +272,14 @@ void ContextPaneTextWidget::onHorizontalAlignmentChanged()
{
QString alignment;
if (ui->centerHAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignHCenter");
+ alignment = QLatin1String("AlignHCenter");
else if (ui->leftAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignLeft");
+ alignment = QLatin1String("AlignLeft");
else if (ui->rightAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignRight");
+ alignment = QLatin1String("AlignRight");
if (m_horizontalAlignment != alignment) {
m_horizontalAlignment = alignment;
- if (alignment == QLatin1String("Text.AlignLeft"))
+ if (alignment == QLatin1String("AlignLeft"))
emit removeProperty(QLatin1String("horizontalAlignment"));
else
emit propertyChanged(QLatin1String("horizontalAlignment"), alignment);
@@ -298,14 +298,14 @@ void ContextPaneTextWidget::onVerticalAlignmentChanged()
{
QString alignment;
if (ui->centerVAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignVCenter");
+ alignment = QLatin1String("AlignVCenter");
else if (ui->topAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignTop");
+ alignment = QLatin1String("AlignTop");
else if (ui->bottomAlignmentButton->isChecked())
- alignment = QLatin1String("Text.AlignBottom");
+ alignment = QLatin1String("AlignBottom");
if (m_verticalAlignment != alignment) {
m_verticalAlignment = alignment;
- if (alignment == QLatin1String("Text.AlignTop"))
+ if (alignment == QLatin1String("AlignTop"))
emit removeProperty(QLatin1String("verticalAlignment"));
else
emit propertyChanged(QLatin1String("verticalAlignment"), alignment);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.cpp b/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.cpp
index addd0dcb74..1b198005ae 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.cpp
@@ -65,7 +65,7 @@ ContextPaneWidget::~ContextPaneWidget()
m_bauhausColorDialog.clear();
}
-void ContextPaneWidget::activate(const QPoint &pos, const QPoint &alternative)
+void ContextPaneWidget::activate(const QPoint &pos, const QPoint &alternative, const QPoint &alternative2)
{
//uncheck all color buttons
foreach (ColorButton *colorButton, findChildren<ColorButton*>()) {
@@ -73,20 +73,19 @@ void ContextPaneWidget::activate(const QPoint &pos, const QPoint &alternative)
}
resize(sizeHint());
show();
- rePosition(pos, alternative);
+ rePosition(pos, alternative, alternative2);
raise();
}
-void ContextPaneWidget::rePosition(const QPoint &position, const QPoint &alternative)
+void ContextPaneWidget::rePosition(const QPoint &position, const QPoint &alternative, const QPoint &alternative2)
{
- if (position.y() > 0)
+ if ((position.x() + width()) < parentWidget()->width())
move(position);
else
move(alternative);
- m_originalPos = pos();
- if (m_xPos > 0)
- move(m_xPos, pos().y());
+ if (pos().y() < 0)
+ move(alternative2);
}
void ContextPaneWidget::deactivate()
@@ -178,13 +177,17 @@ void ContextPaneWidget::mouseReleaseEvent(QMouseEvent *event)
void ContextPaneWidget::mouseMoveEvent(QMouseEvent * event)
{
if (event->buttons() && Qt::LeftButton) {
-
+ if (pos().x() < 10 && event->pos().x() < -20)
+ return;
if (m_oldPos != QPoint(-1, -1)) {
QPoint diff = event->globalPos() - m_oldPos;
- move(pos() + diff);
- if (m_bauhausColorDialog)
- m_bauhausColorDialog->move(m_bauhausColorDialog->pos() + diff);
- m_xPos = pos().x();
+ if (m_bauhausColorDialog) {
+ QPoint newPos = pos() + diff;
+ if (newPos.x() > 0 && newPos.y() > 0 && (newPos.x() + width()) < parentWidget()->width() && (newPos.y() + height()) < parentWidget()->height()) {
+ m_bauhausColorDialog->move(m_bauhausColorDialog->pos() + diff);
+ move(newPos);
+ }
+ }
} else {
m_opacityEffect = new QGraphicsOpacityEffect;
setGraphicsEffect(m_opacityEffect);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.h b/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.h
index ce600a5399..7097d6a5e1 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/contextpanewidget.h
@@ -22,8 +22,8 @@ class ContextPaneWidget : public QFrame
public:
explicit ContextPaneWidget(QWidget *parent = 0);
~ContextPaneWidget();
- void activate(const QPoint &pos, const QPoint &alternative);
- void rePosition(const QPoint &pos, const QPoint &alternative);
+ void activate(const QPoint &pos, const QPoint &alternative, const QPoint &alternative2);
+ void rePosition(const QPoint &pos, const QPoint &alternative , const QPoint &alternative3);
void deactivate();
BauhausColorDialog *colorDialog();
void setProperties(QmlJS::PropertyReader *propertyReader);
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri b/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri
index bb98fc009e..b2e922c513 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditor.pri
@@ -2,10 +2,12 @@ VPATH += $$PWD
INCLUDEPATH += $$PWD
SOURCES += stateseditorwidget.cpp \
stateseditormodel.cpp \
- stateseditorview.cpp
+ stateseditorview.cpp \
+ stateseditorimageprovider.cpp
HEADERS += stateseditorwidget.h \
stateseditormodel.h \
- stateseditorview.h
+ stateseditorview.h \
+ stateseditorimageprovider.cpp
RESOURCES += $$PWD/stateseditor.qrc
OTHER_FILES +=$$PWD/stateslist.qml \
$$PWD/HorizontalScrollBar.qml
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp
new file mode 100644
index 0000000000..2d08b622b7
--- /dev/null
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.cpp
@@ -0,0 +1,65 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+#include "stateseditorimageprovider.h"
+#include "stateseditorview.h"
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+StatesEditorImageProvider::StatesEditorImageProvider(StatesEditorView *view) :
+ QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap),
+ m_view(view)
+{
+}
+
+QPixmap StatesEditorImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
+{
+ if (!m_view.isNull()) {
+ // discard the count number (see StatesEditorModel m_updateCounter)
+ QString s = id.mid(0, id.lastIndexOf(QLatin1Char('-')));
+
+ bool ok = false;
+ int state = s.toInt(&ok);
+ if (ok) {
+ QPixmap pm = m_view->renderState(state);
+ if (size)
+ *size = pm.size();
+ if (requestedSize.isValid())
+ return pm.scaled(requestedSize);
+ return pm;
+ }
+ }
+ return QPixmap();
+}
+
+}
+
+}
+
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h
new file mode 100644
index 0000000000..ae61446236
--- /dev/null
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorimageprovider.h
@@ -0,0 +1,59 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef STATESEDITORIMAGEPROVIDER_H
+#define STATESEDITORIMAGEPROVIDER_H
+
+#include <QDeclarativeImageProvider>
+
+#include <QWeakPointer>
+
+namespace QmlDesigner {
+
+namespace Internal {
+
+class StatesEditorView;
+
+class StatesEditorImageProvider : public QDeclarativeImageProvider
+{
+public:
+ StatesEditorImageProvider(StatesEditorView *view);
+
+ QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize);
+
+private:
+ QWeakPointer<StatesEditorView> m_view;
+};
+
+}
+
+}
+
+#endif // STATESEDITORIMAGEPROVIDER_H
+
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
index 8c5b5ac476..71e51b6d8a 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp
@@ -42,12 +42,12 @@ namespace QmlDesigner {
namespace Internal {
StatesEditorModel::StatesEditorModel(QObject *parent) :
- QAbstractListModel(parent)
+ QAbstractListModel(parent),
+ m_updateCounter(0)
{
-
QHash<int, QByteArray> roleNames;
roleNames.insert(StateNameRole, "stateName");
- roleNames.insert(StatesPixmapRole, "statePixmap");
+ roleNames.insert(StateImageSourceRole, "stateImageSource");
setRoleNames(roleNames);
}
@@ -78,11 +78,9 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
result = m_stateNames.at(index.row());
break;
}
- case StatesPixmapRole: {
- // TODO: Maybe cache this?
- if (!m_statesView.isNull()) {
- result = m_statesView->renderState(index.row());
- }
+ case StateImageSourceRole: {
+ if (!m_statesView.isNull())
+ return QString("image://qmldesigner_stateseditor/%1-%2").arg(index.row()).arg(m_updateCounter);
break;
}
}
@@ -120,7 +118,10 @@ void StatesEditorModel::renameState(int i, const QString &newName)
if (m_stateNames[i] != newName) {
if (m_stateNames.contains(newName) || newName.isEmpty()) {
- QMessageBox::warning(0, tr("Invalid state name"), newName.isEmpty()?tr("The empty string as a name is reserved for the base state."):tr("Name already used in another state"));
+ QMessageBox::warning(0, tr("Invalid state name"),
+ newName.isEmpty() ?
+ tr("The empty string as a name is reserved for the base state.") :
+ tr("Name already used in another state"));
} else {
m_stateNames.replace(i, newName);
m_statesView->renameState(i,newName);
@@ -134,6 +135,14 @@ void StatesEditorModel::updateState(int i)
{
Q_ASSERT(i >= 0 && i < m_stateNames.count());
+ // QML images with the same URL are always cached, so this changes the URL each
+ // time to ensure the image is loaded from the StatesImageProvider and not the
+ // cache.
+ // TODO: only increase imageId when the scene has changed so that we can load
+ // from the cache instead where possible.
+ if (++m_updateCounter == INT_MAX)
+ m_updateCounter = 0;
+
emit dataChanged(createIndex(i, 0), createIndex(i, 0));
}
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
index 89b748ed92..36f92105e8 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.h
@@ -47,7 +47,7 @@ class StatesEditorModel : public QAbstractListModel
enum {
StateNameRole = Qt::DisplayRole,
- StatesPixmapRole = Qt::UserRole
+ StateImageSourceRole = Qt::UserRole,
};
public:
@@ -71,6 +71,7 @@ signals:
private:
QList<QString> m_stateNames;
QWeakPointer<StatesEditorView> m_statesView;
+ int m_updateCounter;
};
} // namespace Itnernal
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
index 937af6e900..689f5e7a17 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp
@@ -30,6 +30,7 @@
#include "stateseditorwidget.h"
#include "stateseditormodel.h"
#include "stateseditorview.h"
+#include "stateseditorimageprovider.h"
#include <qmlitemnode.h>
#include <invalidargumentexception.h>
@@ -203,6 +204,10 @@ void StatesEditorWidget::setup(Model *model)
m_d->model = model;
if (m_d->statesEditorView.isNull())
m_d->statesEditorView = new Internal::StatesEditorView(m_d->statesEditorModel.data(), this);
+
+ m_d->listView->engine()->addImageProvider(
+ QLatin1String("qmldesigner_stateseditor"), new Internal::StatesEditorImageProvider(m_d->statesEditorView.data()));
+
m_d->statesEditorModel->setStatesEditorView(m_d->statesEditorView.data());
m_d->model->attachView(m_d->statesEditorView.data());
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateslist.qml b/src/plugins/qmldesigner/components/stateseditor/stateslist.qml
index dc2b649e35..2a552c32bb 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateslist.qml
+++ b/src/plugins/qmldesigner/components/stateseditor/stateslist.qml
@@ -145,7 +145,7 @@ Rectangle {
anchors.bottomMargin: 9
Image {
anchors.centerIn:parent
- pixmap: statePixmap
+ source: stateImageSource
Rectangle {
anchors.fill:parent
color:"transparent"
diff --git a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
index 35286f2f0b..5a0baeba59 100644
--- a/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
+++ b/src/plugins/qmldesigner/designercore/include/itemlibraryinfo.h
@@ -62,6 +62,7 @@ public:
QString name() const;
QString typeName() const;
QIcon icon() const;
+ QString iconPath() const;
int majorVersion() const;
int minorVersion() const;
QString category() const;
@@ -77,7 +78,7 @@ public:
void setType(const QString &typeName, int majorVersion, int minorVersion);
void setName(const QString &name);
- void setIcon(const QIcon &icon);
+ void setIconPath(const QString &iconPath);
void addProperty(const Property &p);
void addProperty(QString &name, QString &type, QString &value);
void setDragIcon(const QIcon &icon);
diff --git a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp
index c640c77b76..18757e8b9c 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/itemlibraryinfo.cpp
@@ -46,6 +46,7 @@ public:
QString category;
int majorVersion;
int minorVersion;
+ QString iconPath;
QIcon icon;
QIcon dragIcon;
QList<PropertyContainer> properties;
@@ -148,6 +149,11 @@ QIcon ItemLibraryEntry::icon() const
return m_data->icon;
}
+QString ItemLibraryEntry::iconPath() const
+{
+ return m_data->iconPath;
+}
+
void ItemLibraryEntry::setName(const QString &name)
{
m_data->name = name;
@@ -160,9 +166,9 @@ void ItemLibraryEntry::setType(const QString &typeName, int majorVersion, int mi
m_data->minorVersion = minorVersion;
}
-void ItemLibraryEntry::setIcon(const QIcon &icon)
+void ItemLibraryEntry::setIconPath(const QString &iconPath)
{
- m_data->icon = icon;
+ m_data->iconPath = iconPath;
}
void ItemLibraryEntry::setQml(const QString &qml)
@@ -184,6 +190,7 @@ QDataStream& operator<<(QDataStream& stream, const ItemLibraryEntry &itemLibrary
stream << itemLibraryEntry.majorVersion();
stream << itemLibraryEntry.minorVersion();
stream << itemLibraryEntry.icon();
+ stream << itemLibraryEntry.iconPath();
stream << itemLibraryEntry.category();
stream << itemLibraryEntry.dragIcon();
stream << itemLibraryEntry.m_data->properties;
@@ -198,6 +205,7 @@ QDataStream& operator>>(QDataStream& stream, ItemLibraryEntry &itemLibraryEntry)
stream >> itemLibraryEntry.m_data->majorVersion;
stream >> itemLibraryEntry.m_data->minorVersion;
stream >> itemLibraryEntry.m_data->icon;
+ stream >> itemLibraryEntry.m_data->iconPath;
stream >> itemLibraryEntry.m_data->category;
stream >> itemLibraryEntry.m_data->dragIcon;
stream >> itemLibraryEntry.m_data->properties;
diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainfoparser.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainfoparser.cpp
index 216ba28dd3..9edf26e538 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/metainfoparser.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/metainfoparser.cpp
@@ -205,8 +205,8 @@ void MetaInfoParser::handleNodeItemLibraryEntryElement(QXmlStreamReader &reader,
entry.setName(name);
QString iconPath = reader.attributes().value("icon").toString();
- if (!iconPath.isEmpty())
- entry.setIcon(QIcon(iconPath));
+ if (!iconPath.isEmpty())
+ entry.setIconPath(iconPath);
QString category = reader.attributes().value("category").toString();
if (!category.isEmpty())
diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp
index fece615974..c1f7de9342 100644
--- a/src/plugins/qmldesigner/designersettings.cpp
+++ b/src/plugins/qmldesigner/designersettings.cpp
@@ -52,7 +52,7 @@ void DesignerSettings::fromSettings(QSettings *settings)
snapMargin = settings->value(
QLatin1String(QmlDesigner::Constants::QML_SNAPMARGIN_KEY), QVariant(0)).toInt();
enableContextPane = settings->value(
- QLatin1String(QmlDesigner::Constants::QML_CONTEXTPANE_KEY), QVariant(0)).toBool();
+ QLatin1String(QmlDesigner::Constants::QML_CONTEXTPANE_KEY), QVariant(1)).toBool();
settings->endGroup();
settings->endGroup();
diff --git a/src/plugins/qmldesigner/qmlcontextpane.cpp b/src/plugins/qmldesigner/qmlcontextpane.cpp
index 88c65ab9a1..ca07a3adf9 100644
--- a/src/plugins/qmldesigner/qmlcontextpane.cpp
+++ b/src/plugins/qmldesigner/qmlcontextpane.cpp
@@ -8,6 +8,9 @@
#include <qmljs/qmljspropertyreader.h>
#include <qmljs/qmljsrewriter.h>
#include <qmljs/qmljsindenter.h>
+#include <qmljs/qmljslookupcontext.h>
+#include <qmljs/qmljsinterpreter.h>
+#include <qmljs/qmljsbind.h>
#include <texteditor/basetexteditor.h>
#include <texteditor/tabsettings.h>
#include <colorwidget.h>
@@ -62,7 +65,7 @@ QmlContextPane::~QmlContextPane()
m_widget.clear();
}
-void QmlContextPane::apply(TextEditor::BaseTextEditorEditable *editor, Document::Ptr doc, Node *node, bool update)
+void QmlContextPane::apply(TextEditor::BaseTextEditorEditable *editor, Document::Ptr doc, const QmlJS::Snapshot &snapshot, AST::Node *node, bool update)
{
if (!Internal::BauhausPlugin::pluginInstance()->settings().enableContextPane)
return;
@@ -73,6 +76,15 @@ void QmlContextPane::apply(TextEditor::BaseTextEditorEditable *editor, Document:
if (update && editor != m_editor)
return; //do not update for different editor
+ LookupContext::Ptr lookupContext = LookupContext::create(doc, snapshot, QList<Node*>());
+ const Interpreter::ObjectValue *scopeObject = doc->bind()->findQmlObject(node);
+
+ QStringList prototypes;
+ while (scopeObject) {
+ prototypes.append(scopeObject->className());
+ scopeObject = scopeObject->prototype(lookupContext->context());
+ }
+
setEnabled(doc->isParsedCorrectly());
m_editor = editor;
contextWidget()->setParent(editor->widget()->parentWidget());
@@ -97,6 +109,24 @@ void QmlContextPane::apply(TextEditor::BaseTextEditorEditable *editor, Document:
offset = objectBinding->firstSourceLocation().offset;
end = objectBinding->lastSourceLocation().end();
}
+
+ int line1;
+ int column1;
+ int line2;
+ int column2;
+ m_editor->convertPosition(offset, &line1, &column1); //get line
+ m_editor->convertPosition(end, &line2, &column2); //get line
+
+ QRegion reg;
+ if (line1 > -1 && line2 > -1)
+ reg = m_editor->editor()->translatedLineRegion(line1 - 1, line2);
+
+ QRect rect;
+ rect.setHeight(m_widget->height() + 10);
+ rect.setWidth(reg.boundingRect().width() - reg.boundingRect().left());
+ rect.moveTo(reg.boundingRect().topLeft());
+ reg = reg.intersect(rect);
+
if (name.contains("Text")) {
m_node = 0;
PropertyReader propertyReader(doc.data(), initializer);
@@ -105,11 +135,15 @@ void QmlContextPane::apply(TextEditor::BaseTextEditorEditable *editor, Document:
QPoint p1 = editor->editor()->mapToParent(editor->editor()->viewport()->mapToParent(editor->editor()->cursorRect(tc).topLeft()) - QPoint(0, contextWidget()->height() + 10));
tc.setPosition(end);
QPoint p2 = editor->editor()->mapToParent(editor->editor()->viewport()->mapToParent(editor->editor()->cursorRect(tc).bottomLeft()) + QPoint(0, 10));
+ QPoint offset = QPoint(10, 0);
+ if (reg.boundingRect().width() < 400)
+ offset = QPoint(400 - reg.boundingRect().width() + 10 ,0);
+ QPoint p3 = editor->editor()->mapToParent(editor->editor()->viewport()->mapToParent(reg.boundingRect().topRight()) + offset);
p2.setX(p1.x());
if (!update)
- contextWidget()->activate(p1 , p2);
+ contextWidget()->activate(p3 , p1, p2);
else
- contextWidget()->rePosition(p1 , p2);
+ contextWidget()->rePosition(p3 , p1, p2);
m_blockWriting = true;
contextWidget()->setType(name);
contextWidget()->setProperties(&propertyReader);
@@ -163,7 +197,6 @@ void QmlContextPane::setProperty(const QString &propertyName, const QVariant &va
QTextCursor tc(m_editor->editor()->document());
tc.beginEditBlock();
int cursorPostion = tc.position();
- tc.beginEditBlock();
changeSet.apply(&tc);
if (line > 0) {
@@ -171,17 +204,16 @@ void QmlContextPane::setProperty(const QString &propertyName, const QVariant &va
QmlJSIndenter indenter;
indenter.setTabSize(ts.m_tabSize);
indenter.setIndentSize(ts.m_indentSize);
- QTextBlock start = m_editor->editor()->document()->findBlockByLineNumber(line);
- QTextBlock end = m_editor->editor()->document()->findBlockByLineNumber(line);
+ QTextBlock start = m_editor->editor()->document()->findBlockByNumber(line);
+ QTextBlock end = m_editor->editor()->document()->findBlockByNumber(line);
- const int indent = indenter.indentForBottomLine(m_editor->editor()->document()->begin(), end.next(), QChar::Null);
- ts.indentLine(start, indent);
+ if (end.isValid()) {
+ const int indent = indenter.indentForBottomLine(m_editor->editor()->document()->begin(), end.next(), QChar::Null);
+ ts.indentLine(start, indent);
+ }
}
tc.endEditBlock();
tc.setPosition(cursorPostion);
-
- tc.endEditBlock();
- tc.setPosition(cursorPostion);
}
}
@@ -234,8 +266,14 @@ void QmlContextPane::onPropertyRemovedAndChange(const QString &remove, const QSt
if (!m_doc)
return;
- setProperty(change, value);
+ QTextCursor tc(m_editor->editor()->document());
+ tc.beginEditBlock();
+
removeProperty(remove);
+ setProperty(change, value);
+
+ tc.endEditBlock();
+
m_doc.clear(); //the document is outdated
}
diff --git a/src/plugins/qmldesigner/qmlcontextpane.h b/src/plugins/qmldesigner/qmlcontextpane.h
index d13d7125e0..a2b799042b 100644
--- a/src/plugins/qmldesigner/qmlcontextpane.h
+++ b/src/plugins/qmldesigner/qmlcontextpane.h
@@ -28,7 +28,7 @@ class QmlContextPane : public QmlJS::IContextPane
public:
QmlContextPane(QObject *parent = 0);
~QmlContextPane();
- void apply(TextEditor::BaseTextEditorEditable *editor, QmlJS::Document::Ptr doc, QmlJS::AST::Node *node, bool update);
+ void apply(TextEditor::BaseTextEditorEditable *editor, QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot, QmlJS::AST::Node *node, bool update);
void setProperty(const QString &propertyName, const QVariant &value);
void removeProperty(const QString &propertyName);
void setEnabled(bool);
diff --git a/src/plugins/qmldesigner/settingspage.ui b/src/plugins/qmldesigner/settingspage.ui
index 48b3f50b83..66f276ca89 100644
--- a/src/plugins/qmldesigner/settingspage.ui
+++ b/src/plugins/qmldesigner/settingspage.ui
@@ -101,13 +101,13 @@
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
- <string>Text Editor Helper</string>
+ <string>Quick Toolbars</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="textEditHelperCheckBox">
<property name="text">
- <string>enable</string>
+ <string>Text Quick Toolbar</string>
</property>
</widget>
</item>
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp
index 1bd3c77a52..5f28d8acac 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp
@@ -878,8 +878,19 @@ void CodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
}
}
-void CodeCompletion::complete(const TextEditor::CompletionItem &item)
+bool CodeCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
{
+ if (item.data.canConvert<QString>()) // snippet
+ return false;
+
+ return (item.text.endsWith(QLatin1String(": ")) && typedChar == QLatin1Char(':'))
+ || (item.text.endsWith(QLatin1Char('.')) && typedChar == QLatin1Char('.'));
+}
+
+void CodeCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+ Q_UNUSED(typedChar) // Currently always included in the completion item when used
+
QString toInsert = item.text;
if (QmlJSTextEditor *edit = qobject_cast<QmlJSTextEditor *>(m_editor->widget())) {
@@ -895,7 +906,7 @@ void CodeCompletion::complete(const TextEditor::CompletionItem &item)
QString replacableChars;
if (toInsert.endsWith(QLatin1String(": ")))
replacableChars = QLatin1String(": ");
- else if (toInsert.endsWith(QLatin1String(".")))
+ else if (toInsert.endsWith(QLatin1Char('.')))
replacableChars = QLatin1String(".");
int replacedLength = 0;
@@ -924,7 +935,7 @@ bool CodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &
const TextEditor::CompletionItem item = completionItems.first();
if (!item.data.canConvert<QString>()) {
- complete(item);
+ complete(item, QChar());
return true;
}
}
diff --git a/src/plugins/qmljseditor/qmljscodecompletion.h b/src/plugins/qmljseditor/qmljscodecompletion.h
index 0cae652ad2..f435badf24 100644
--- a/src/plugins/qmljseditor/qmljscodecompletion.h
+++ b/src/plugins/qmljseditor/qmljscodecompletion.h
@@ -68,7 +68,8 @@ public:
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
virtual int startCompletion(TextEditor::ITextEditable *editor);
virtual void completions(QList<TextEditor::CompletionItem> *completions);
- virtual void complete(const TextEditor::CompletionItem &item);
+ virtual bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
+ virtual void complete(const TextEditor::CompletionItem &item, QChar typedChar);
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
virtual QList<TextEditor::CompletionItem> getCompletions();
diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp
index 0f5ec78049..ea6c3741e5 100644
--- a/src/plugins/qmljseditor/qmljseditor.cpp
+++ b/src/plugins/qmljseditor/qmljseditor.cpp
@@ -32,6 +32,7 @@
#include "qmljshighlighter.h"
#include "qmljseditorplugin.h"
#include "qmljsmodelmanager.h"
+#include "qmloutlinemodel.h"
#include <qmljs/qmljsindenter.h>
#include <qmljs/qmljsinterpreter.h>
@@ -49,6 +50,7 @@
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/modemanager.h>
@@ -60,6 +62,7 @@
#include <texteditor/textblockiterator.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
+#include <texteditor/syntaxhighlighter.h>
#include <qmldesigner/qmldesignerconstants.h>
#include <utils/changeset.h>
#include <utils/uncommentselection.h>
@@ -69,13 +72,16 @@
#include <QtGui/QMenu>
#include <QtGui/QComboBox>
+#include <QtGui/QHeaderView>
#include <QtGui/QInputDialog>
#include <QtGui/QMainWindow>
#include <QtGui/QToolBar>
+#include <QtGui/QTreeView>
enum {
UPDATE_DOCUMENT_DEFAULT_INTERVAL = 50,
- UPDATE_USES_DEFAULT_INTERVAL = 150
+ UPDATE_USES_DEFAULT_INTERVAL = 150,
+ UPDATE_OUTLINE_INTERVAL = 150 // msecs after new semantic info has been arrived / cursor has moved
};
using namespace QmlJS;
@@ -610,7 +616,8 @@ QString QmlJSEditorEditable::preferredMode() const
QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
TextEditor::BaseTextEditor(parent),
- m_methodCombo(0),
+ m_outlineCombo(0),
+ m_outlineModel(new QmlOutlineModel(this)),
m_modelManager(0),
m_contextPane(0)
{
@@ -642,13 +649,23 @@ QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
connect(this, SIGNAL(textChanged()), this, SLOT(updateDocument()));
connect(this, SIGNAL(textChanged()), this, SLOT(updateUses()));
+ m_updateOutlineTimer = new QTimer(this);
+ m_updateOutlineTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
+ m_updateOutlineTimer->setSingleShot(true);
+ connect(m_updateOutlineTimer, SIGNAL(timeout()), this, SLOT(updateOutlineNow()));
+
+ m_updateOutlineIndexTimer = new QTimer(this);
+ m_updateOutlineIndexTimer->setInterval(UPDATE_OUTLINE_INTERVAL);
+ m_updateOutlineIndexTimer->setSingleShot(true);
+ connect(m_updateOutlineIndexTimer, SIGNAL(timeout()), this, SLOT(updateOutlineIndexNow()));
+
baseTextDocument()->setSyntaxHighlighter(new Highlighter(document()));
m_modelManager = ExtensionSystem::PluginManager::instance()->getObject<ModelManagerInterface>();
m_contextPane = ExtensionSystem::PluginManager::instance()->getObject<QmlJS::IContextPane>();
if (m_contextPane)
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(onCursorPositionChanged()));
- m_oldCurserPosition = -1;
+ m_oldCursorPosition = -1;
if (m_modelManager) {
m_semanticHighlighter->setModelManager(m_modelManager);
@@ -674,19 +691,33 @@ SemanticInfo QmlJSTextEditor::semanticInfo() const
return m_semanticInfo;
}
-int QmlJSTextEditor::documentRevision() const
+int QmlJSTextEditor::editorRevision() const
{
return document()->revision();
}
bool QmlJSTextEditor::isOutdated() const
{
- if (m_semanticInfo.revision() != documentRevision())
+ if (m_semanticInfo.revision() != editorRevision())
return true;
return false;
}
+QmlOutlineModel *QmlJSTextEditor::outlineModel() const
+{
+ return m_outlineModel;
+}
+
+QModelIndex QmlJSTextEditor::outlineModelIndex()
+{
+ if (!m_outlineModelIndex.isValid()) {
+ m_outlineModelIndex = indexForPosition(position());
+ emit outlineModelIndexChanged(m_outlineModelIndex);
+ }
+ return m_outlineModelIndex;
+}
+
Core::IEditor *QmlJSEditorEditable::duplicate(QWidget *parent)
{
QmlJSTextEditor *newEditor = new QmlJSTextEditor(parent);
@@ -779,6 +810,8 @@ void QmlJSTextEditor::onDocumentUpdated(QmlJS::Document::Ptr doc)
const SemanticHighlighter::Source source = currentSource(/*force = */ true);
m_semanticHighlighter->rehighlight(source);
+
+ m_updateOutlineTimer->start();
} else {
// show parsing errors
QList<QTextEdit::ExtraSelection> selections;
@@ -793,33 +826,70 @@ void QmlJSTextEditor::modificationChanged(bool changed)
m_modelManager->fileChangedOnDisk(file()->fileName());
}
-void QmlJSTextEditor::jumpToMethod(int index)
+void QmlJSTextEditor::jumpToOutlineElement(int /*index*/)
+{
+ QModelIndex index = m_outlineCombo->view()->currentIndex();
+ AST::SourceLocation location = index.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
+
+ Core::EditorManager *editorManager = Core::EditorManager::instance();
+ editorManager->cutForwardNavigationHistory();
+ editorManager->addCurrentPositionToNavigationHistory();
+
+ QTextCursor cursor = textCursor();
+ cursor.setPosition(location.offset);
+ setTextCursor(cursor);
+
+ setFocus();
+}
+
+void QmlJSTextEditor::updateOutlineNow()
{
- if (index > 0 && index <= m_semanticInfo.declarations.size()) { // indexes are 1-based
- Declaration d = m_semanticInfo.declarations.at(index - 1);
- gotoLine(d.startLine, d.startColumn - 1);
- setFocus();
+ const Snapshot snapshot = m_modelManager->snapshot();
+ Document::Ptr document = snapshot.document(file()->fileName());
+
+ if (!document)
+ return;
+
+ if (document->editorRevision() != editorRevision()) {
+ m_updateOutlineTimer->start();
+ return;
}
+
+ m_outlineModel->update(document, snapshot);
+
+ QTreeView *treeView = static_cast<QTreeView*>(m_outlineCombo->view());
+ treeView->expandAll();
+
+ updateOutlineIndexNow();
}
-void QmlJSTextEditor::updateMethodBoxIndex()
+void QmlJSTextEditor::updateOutlineIndexNow()
{
- int line = 0, column = 0;
- convertPosition(position(), &line, &column);
+ if (m_updateOutlineTimer->isActive())
+ return; // updateOutlineNow will call this function soon anyway
+
+ if (!m_outlineModel->document())
+ return;
- int currentSymbolIndex = 0;
+ if (m_outlineModel->document()->editorRevision() != editorRevision()) {
+ m_updateOutlineIndexTimer->start();
+ return;
+ }
- int index = 0;
- while (index < m_semanticInfo.declarations.size()) {
- const Declaration &d = m_semanticInfo.declarations.at(index++);
+ m_outlineModelIndex = QModelIndex(); // invalidate
+ QModelIndex comboIndex = outlineModelIndex();
- if (line < d.startLine)
- break;
- else
- currentSymbolIndex = index;
+ if (comboIndex.isValid()) {
+ bool blocked = m_outlineCombo->blockSignals(true);
+
+ // There is no direct way to select a non-root item
+ m_outlineCombo->setRootModelIndex(comboIndex.parent());
+ m_outlineCombo->setCurrentIndex(comboIndex.row());
+ m_outlineCombo->setRootModelIndex(QModelIndex());
+
+ m_outlineCombo->blockSignals(blocked);
}
- m_methodCombo->setCurrentIndex(currentSymbolIndex);
updateUses();
}
@@ -995,10 +1065,6 @@ void QmlJSTextEditor::setSelectedElement()
emit selectedElementsChanged(offsets, wordAtCursor);
}
-void QmlJSTextEditor::updateMethodBoxToolTip()
-{
-}
-
void QmlJSTextEditor::updateFileName()
{
}
@@ -1122,25 +1188,33 @@ TextEditor::BaseTextEditorEditable *QmlJSTextEditor::createEditableInterface()
void QmlJSTextEditor::createToolBar(QmlJSEditorEditable *editable)
{
- m_methodCombo = new QComboBox;
- m_methodCombo->setMinimumContentsLength(22);
- //m_methodCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ m_outlineCombo = new QComboBox;
+ m_outlineCombo->setMinimumContentsLength(22);
+ m_outlineCombo->setModel(m_outlineModel);
+
+ QTreeView *treeView = new QTreeView;
+ treeView->header()->hide();
+ treeView->setItemsExpandable(false);
+ treeView->setRootIsDecorated(false);
+ m_outlineCombo->setView(treeView);
+ treeView->expandAll();
+
+ //m_outlineCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
// Make the combo box prefer to expand
- QSizePolicy policy = m_methodCombo->sizePolicy();
+ QSizePolicy policy = m_outlineCombo->sizePolicy();
policy.setHorizontalPolicy(QSizePolicy::Expanding);
- m_methodCombo->setSizePolicy(policy);
+ m_outlineCombo->setSizePolicy(policy);
- connect(m_methodCombo, SIGNAL(activated(int)), this, SLOT(jumpToMethod(int)));
- connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateMethodBoxIndex()));
- connect(m_methodCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateMethodBoxToolTip()));
+ connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int)));
+ connect(this, SIGNAL(cursorPositionChanged()), m_updateOutlineIndexTimer, SLOT(start()));
connect(file(), SIGNAL(changed()), this, SLOT(updateFileName()));
QToolBar *toolBar = static_cast<QToolBar*>(editable->toolBar());
QList<QAction*> actions = toolBar->actions();
- toolBar->insertWidget(actions.first(), m_methodCombo);
+ toolBar->insertWidget(actions.first(), m_outlineCombo);
}
TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor &cursor, bool /*resolveTarget*/)
@@ -1226,7 +1300,7 @@ bool QmlJSTextEditor::event(QEvent *e)
switch (e->type()) {
case QEvent::ShortcutOverride:
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_contextPane) {
- m_contextPane->apply(editableInterface(), m_semanticInfo.document, 0, false);
+ m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, 0, false);
}
break;
default:
@@ -1241,7 +1315,7 @@ void QmlJSTextEditor::wheelEvent(QWheelEvent *event)
{
BaseTextEditor::wheelEvent(event);
if (m_contextPane)
- m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.declaringMember(position()), true);
+ m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, m_semanticInfo.declaringMember(position()), true);
}
void QmlJSTextEditor::unCommentSelection()
@@ -1473,20 +1547,11 @@ void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
FindDeclarations findDeclarations;
m_semanticInfo.declarations = findDeclarations(doc->ast());
- QStringList items;
- items.append(tr("<Select Symbol>"));
-
- foreach (Declaration decl, m_semanticInfo.declarations)
- items.append(decl.text);
-
- m_methodCombo->clear();
- m_methodCombo->addItems(items);
- updateMethodBoxIndex();
if (m_contextPane) {
Node *newNode = m_semanticInfo.declaringMember(position());
if (newNode) {
- m_contextPane->apply(editableInterface(), doc, newNode, true);
- m_oldCurserPosition = position();
+ m_contextPane->apply(editableInterface(), doc, m_semanticInfo.snapshot, newNode, true);
+ m_oldCursorPosition = position();
}
}
}
@@ -1496,19 +1561,43 @@ void QmlJSTextEditor::updateSemanticInfo(const SemanticInfo &semanticInfo)
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
appendExtraSelectionsForMessages(&selections, m_semanticInfo.semanticMessages, document());
setExtraSelections(CodeWarningsSelection, selections);
-
- emit semanticInfoUpdated(semanticInfo);
}
void QmlJSTextEditor::onCursorPositionChanged()
{
+
+
if (m_contextPane) {
Node *newNode = m_semanticInfo.declaringMember(position());
- Node *oldNode = m_semanticInfo.declaringMember(m_oldCurserPosition);
+ Node *oldNode = m_semanticInfo.declaringMember(m_oldCursorPosition);
if (oldNode != newNode)
- m_contextPane->apply(editableInterface(), m_semanticInfo.document, newNode, false);
- m_oldCurserPosition = position();
+ m_contextPane->apply(editableInterface(), m_semanticInfo.document, m_semanticInfo.snapshot, newNode, false);
+ m_oldCursorPosition = position();
+ }
+}
+
+QModelIndex QmlJSTextEditor::indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex) const
+{
+ QModelIndex lastIndex = rootIndex;
+
+
+ const int rowCount = m_outlineModel->rowCount(rootIndex);
+ for (int i = 0; i < rowCount; ++i) {
+ QModelIndex childIndex = m_outlineModel->index(i, 0, rootIndex);
+ AST::SourceLocation location = childIndex.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
+
+ if ((cursorPosition >= location.offset)
+ && (cursorPosition <= location.offset + location.length)) {
+ lastIndex = childIndex;
+ break;
+ }
}
+
+ if (lastIndex != rootIndex) {
+ // recurse
+ lastIndex = indexForPosition(cursorPosition, lastIndex);
+ }
+ return lastIndex;
}
SemanticHighlighter::Source QmlJSTextEditor::currentSource(bool force)
@@ -1633,3 +1722,4 @@ void SemanticHighlighter::setModelManager(QmlJS::ModelManagerInterface *modelMan
{
m_modelManager = modelManager;
}
+
diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h
index e2916f4733..17b7da2a74 100644
--- a/src/plugins/qmljseditor/qmljseditor.h
+++ b/src/plugins/qmljseditor/qmljseditor.h
@@ -37,6 +37,7 @@
#include <texteditor/basetexteditor.h>
#include <QtCore/QWaitCondition>
+#include <QtCore/QModelIndex>
#include <QtCore/QMutex>
#include <QtCore/QThread>
@@ -60,6 +61,7 @@ class Highlighter;
namespace Internal {
class QmlJSTextEditor;
+class QmlOutlineModel;
class QmlJSEditorEditable : public TextEditor::BaseTextEditorEditable
{
@@ -213,18 +215,19 @@ public:
virtual void unCommentSelection();
SemanticInfo semanticInfo() const;
- int documentRevision() const;
+ int editorRevision() const;
bool isOutdated() const;
-signals:
- void selectedElementsChanged(QList<int> offsets, const QString &wordAtCursor);
+ QmlOutlineModel *outlineModel() const;
+ QModelIndex outlineModelIndex();
public slots:
void followSymbolUnderCursor();
virtual void setFontSettings(const TextEditor::FontSettings &);
signals:
- void semanticInfoUpdated(const QmlJSEditor::Internal::SemanticInfo &semanticInfo);
+ void outlineModelIndexChanged(const QModelIndex &index);
+ void selectedElementsChanged(QList<int> offsets, const QString &wordAtCursor);
private slots:
void onDocumentUpdated(QmlJS::Document::Ptr doc);
@@ -232,9 +235,9 @@ private slots:
void updateDocument();
void updateDocumentNow();
- void jumpToMethod(int index);
- void updateMethodBoxIndex();
- void updateMethodBoxToolTip();
+ void jumpToOutlineElement(int index);
+ void updateOutlineNow();
+ void updateOutlineIndexNow();
void updateFileName();
void updateUses();
@@ -272,13 +275,18 @@ private:
QString wordUnderCursor() const;
SemanticHighlighter::Source currentSource(bool force = false);
+ QModelIndex indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex = QModelIndex()) const;
const Core::Context m_context;
QTimer *m_updateDocumentTimer;
QTimer *m_updateUsesTimer;
QTimer *m_semanticRehighlightTimer;
- QComboBox *m_methodCombo;
+ QTimer *m_updateOutlineTimer;
+ QTimer *m_updateOutlineIndexTimer;
+ QComboBox *m_outlineCombo;
+ QmlOutlineModel *m_outlineModel;
+ QModelIndex m_outlineModelIndex;
QmlJS::ModelManagerInterface *m_modelManager;
QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrencesUnusedFormat;
@@ -288,7 +296,7 @@ private:
SemanticInfo m_semanticInfo;
QmlJS::IContextPane *m_contextPane;
- int m_oldCurserPosition;
+ int m_oldCursorPosition;
};
} // namespace Internal
diff --git a/src/plugins/qmljseditor/qmljseditor.pro b/src/plugins/qmljseditor/qmljseditor.pro
index f92ba23c6c..e2de54e54c 100644
--- a/src/plugins/qmljseditor/qmljseditor.pro
+++ b/src/plugins/qmljseditor/qmljseditor.pro
@@ -24,7 +24,9 @@ HEADERS += \
qmljsquickfix.h \
qmljsrefactoringchanges.h \
qmljscomponentfromobjectdef.h \
- qmljsoutline.h
+ qmljsoutline.h \
+ qmloutlinemodel.h \
+ qmltaskmanager.h
SOURCES += \
qmljscodecompletion.cpp \
@@ -41,7 +43,9 @@ SOURCES += \
qmljsquickfix.cpp \
qmljsrefactoringchanges.cpp \
qmljscomponentfromobjectdef.cpp \
- qmljsoutline.cpp
+ qmljsoutline.cpp \
+ qmloutlinemodel.cpp \
+ qmltaskmanager.cpp
RESOURCES += qmljseditor.qrc
OTHER_FILES += QmlJSEditor.pluginspec QmlJSEditor.mimetypes.xml
diff --git a/src/plugins/qmljseditor/qmljseditorconstants.h b/src/plugins/qmljseditor/qmljseditorconstants.h
index 0db49ad417..f3df94ff8c 100644
--- a/src/plugins/qmljseditor/qmljseditorconstants.h
+++ b/src/plugins/qmljseditor/qmljseditorconstants.h
@@ -49,7 +49,7 @@ const char * const FOLLOW_SYMBOL_UNDER_CURSOR = "QmlJSEditor.FollowSymbolUnderCu
const char * const QML_MIMETYPE = "application/x-qml";
const char * const JS_MIMETYPE = "application/javascript";
-
+const char *const TASK_CATEGORY_QML = "Task.Category.Qml";
} // namespace Constants
} // namespace QmlJSEditor
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp
index c611c36aa4..f67dd204fe 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.cpp
+++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp
@@ -39,6 +39,8 @@
#include "qmljsoutline.h"
#include "qmljspreviewrunner.h"
#include "qmljsquickfix.h"
+#include "qmljs/qmljsicons.h"
+#include "qmltaskmanager.h"
#include <qmldesigner/qmldesignerconstants.h>
@@ -51,6 +53,7 @@
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <projectexplorer/taskhub.h>
#include <extensionsystem/pluginmanager.h>
#include <texteditor/fontsettings.h>
#include <texteditor/storagesettings.h>
@@ -179,11 +182,29 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
addAutoReleasedObject(new QmlJSOutlineWidgetFactory);
+ m_qmlTaskManager = new QmlTaskManager;
+ addAutoReleasedObject(m_qmlTaskManager);
+
+ connect(m_modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
+ m_qmlTaskManager, SLOT(documentChangedOnDisk(QmlJS::Document::Ptr)));
+ connect(m_modelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
+ m_qmlTaskManager, SLOT(documentsRemoved(QStringList)));
+
return true;
}
void QmlJSEditorPlugin::extensionsInitialized()
{
+ ProjectExplorer::TaskHub *taskHub =
+ ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
+ taskHub->addCategory(Constants::TASK_CATEGORY_QML, tr("QML"));
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
+{
+ delete QmlJS::Icons::instance(); // delete object held by singleton
+
+ return IPlugin::aboutToShutdown();
}
void QmlJSEditorPlugin::openPreview()
@@ -251,7 +272,7 @@ void QmlJSEditorPlugin::quickFixNow()
if (QmlJSTextEditor *editor = qobject_cast<QmlJSTextEditor*>(m_currentTextEditable->widget())) {
if (currentEditor == editor) {
if (editor->isOutdated()) {
- // qDebug() << "TODO: outdated document" << editor->documentRevision() << editor->semanticInfo().revision();
+ // qDebug() << "TODO: outdated document" << editor->editorRevision() << editor->semanticInfo().revision();
// ### FIXME: m_quickFixTimer->start(QUICKFIX_INTERVAL);
m_quickFixTimer->stop();
} else {
diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h
index af6e5f9280..fb19f78afd 100644
--- a/src/plugins/qmljseditor/qmljseditorplugin.h
+++ b/src/plugins/qmljseditor/qmljseditorplugin.h
@@ -65,6 +65,7 @@ class QmlJSEditorFactory;
class QmlJSTextEditor;
class QmlJSPreviewRunner;
class QmlJSQuickFixCollector;
+class QmlTaskManager;
class QmlJSEditorPlugin : public ExtensionSystem::IPlugin
{
@@ -77,6 +78,7 @@ public:
// IPlugin
bool initialize(const QStringList &arguments, QString *errorMessage = 0);
void extensionsInitialized();
+ ShutdownFlag aboutToShutdown();
static QmlJSEditorPlugin *instance()
{ return m_instance; }
@@ -111,6 +113,7 @@ private:
QTimer *m_quickFixTimer;
QPointer<TextEditor::ITextEditable> m_currentTextEditable;
+ QmlTaskManager *m_qmlTaskManager;
};
} // namespace Internal
diff --git a/src/plugins/qmljseditor/qmljshighlighter.cpp b/src/plugins/qmljseditor/qmljshighlighter.cpp
index e7965286d4..423598a4e6 100644
--- a/src/plugins/qmljseditor/qmljshighlighter.cpp
+++ b/src/plugins/qmljseditor/qmljshighlighter.cpp
@@ -39,7 +39,7 @@ using namespace QmlJSEditor;
using namespace QmlJS;
Highlighter::Highlighter(QTextDocument *parent)
- : QSyntaxHighlighter(parent),
+ : TextEditor::SyntaxHighlighter(parent),
m_qmlEnabled(true),
m_inMultilineComment(false)
{
diff --git a/src/plugins/qmljseditor/qmljshighlighter.h b/src/plugins/qmljseditor/qmljshighlighter.h
index 3c33ebe721..0be4f9bbf4 100644
--- a/src/plugins/qmljseditor/qmljshighlighter.h
+++ b/src/plugins/qmljseditor/qmljshighlighter.h
@@ -39,10 +39,11 @@
#include <QtGui/QSyntaxHighlighter>
#include <texteditor/basetextdocumentlayout.h>
+#include <texteditor/syntaxhighlighter.h>
namespace QmlJSEditor {
-class QMLJSEDITOR_EXPORT Highlighter : public QSyntaxHighlighter
+class QMLJSEDITOR_EXPORT Highlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp
index 88d1326a59..519eec2190 100644
--- a/src/plugins/qmljseditor/qmljshoverhandler.cpp
+++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp
@@ -126,7 +126,7 @@ void HoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int p
const SemanticInfo semanticInfo = edit->semanticInfo();
- if (semanticInfo.revision() != edit->documentRevision())
+ if (semanticInfo.revision() != edit->editorRevision())
return;
const Snapshot snapshot = semanticInfo.snapshot;
diff --git a/src/plugins/qmljseditor/qmljsoutline.cpp b/src/plugins/qmljseditor/qmljsoutline.cpp
index d808a2e2d1..4c018a5b6e 100644
--- a/src/plugins/qmljseditor/qmljsoutline.cpp
+++ b/src/plugins/qmljseditor/qmljsoutline.cpp
@@ -1,15 +1,11 @@
#include "qmljsoutline.h"
+#include "qmloutlinemodel.h"
-#include <coreplugin/icore.h>
-#include <extensionsystem/pluginmanager.h>
-#include <qmljs/parser/qmljsast_p.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
-
-#include <QtCore/QDebug>
+#include <coreplugin/ifile.h>
+#include <coreplugin/editormanager/editormanager.h>
#include <QtGui/QVBoxLayout>
-#include <typeinfo>
-
+#include <QDebug>
using namespace QmlJS;
enum {
@@ -19,11 +15,6 @@ enum {
namespace QmlJSEditor {
namespace Internal {
-QmlOutlineModel::QmlOutlineModel(QObject *parent) :
- QStandardItemModel(parent)
-{
-}
-
QmlJSOutlineTreeView::QmlJSOutlineTreeView(QWidget *parent) :
QTreeView(parent)
{
@@ -38,207 +29,9 @@ QmlJSOutlineTreeView::QmlJSOutlineTreeView(QWidget *parent) :
setExpandsOnDoubleClick(false);
}
-void QmlOutlineModel::startSync()
-{
- m_treePos.clear();
- m_treePos.append(0);
- m_currentItem = invisibleRootItem();
-}
-
-QModelIndex QmlOutlineModel::enterElement(const QString &type, const AST::SourceLocation &sourceLocation)
-{
- QStandardItem *item = enterNode(sourceLocation);
- item->setText(type);
- item->setIcon(m_icons.objectDefinitionIcon());
- return item->index();
-}
-
-void QmlOutlineModel::leaveElement()
-{
- leaveNode();
-}
-
-QModelIndex QmlOutlineModel::enterProperty(const QString &name, const AST::SourceLocation &sourceLocation)
-{
- QStandardItem *item = enterNode(sourceLocation);
- item->setText(name);
- item->setIcon(m_icons.scriptBindingIcon());
- return item->index();
-}
-
-void QmlOutlineModel::leaveProperty()
-{
- leaveNode();
-}
-
-QStandardItem *QmlOutlineModel::enterNode(const QmlJS::AST::SourceLocation &location)
-{
- int siblingIndex = m_treePos.last();
- if (siblingIndex == 0) {
- // first child
- if (!m_currentItem->hasChildren()) {
- QStandardItem *parentItem = m_currentItem;
- m_currentItem = new QStandardItem;
- m_currentItem->setEditable(false);
- parentItem->appendRow(m_currentItem);
- if (debug)
- qDebug() << "QmlOutlineModel - Adding" << "element to" << parentItem->text();
- } else {
- m_currentItem = m_currentItem->child(0);
- }
- } else {
- // sibling
- if (m_currentItem->rowCount() <= siblingIndex) {
- // attach
- QStandardItem *oldItem = m_currentItem;
- m_currentItem = new QStandardItem;
- m_currentItem->setEditable(false);
- oldItem->appendRow(m_currentItem);
- if (debug)
- qDebug() << "QmlOutlineModel - Adding" << "element to" << oldItem->text();
- } else {
- m_currentItem = m_currentItem->child(siblingIndex);
- }
- }
-
- m_treePos.append(0);
- m_currentItem->setData(QVariant::fromValue(location), SourceLocationRole);
-
- return m_currentItem;
-}
-
-void QmlOutlineModel::leaveNode()
-{
- int lastIndex = m_treePos.takeLast();
-
-
- if (lastIndex > 0) {
- // element has children
- if (lastIndex < m_currentItem->rowCount()) {
- if (debug)
- qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << lastIndex << m_currentItem->rowCount() - lastIndex;
- m_currentItem->removeRows(lastIndex, m_currentItem->rowCount() - lastIndex);
- }
- m_currentItem = parentItem();
- } else {
- if (m_currentItem->hasChildren()) {
- if (debug)
- qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << 0 << m_currentItem->rowCount();
- m_currentItem->removeRows(0, m_currentItem->rowCount());
- }
- m_currentItem = parentItem();
- }
-
-
- m_treePos.last()++;
-}
-
-QStandardItem *QmlOutlineModel::parentItem()
-{
- QStandardItem *parent = m_currentItem->parent();
- if (!parent)
- parent = invisibleRootItem();
- return parent;
-}
-
-class QmlOutlineModelSync : protected AST::Visitor
-{
-public:
- QmlOutlineModelSync(QmlOutlineModel *model) :
- m_model(model),
- indent(0)
- {
- }
-
- void operator()(Document::Ptr doc)
- {
- m_nodeToIndex.clear();
-
- if (debug)
- qDebug() << "QmlOutlineModel ------";
- m_model->startSync();
- if (doc && doc->ast())
- doc->ast()->accept(this);
- }
-
-private:
- bool preVisit(AST::Node *node)
- {
- if (!node)
- return false;
- if (debug)
- qDebug() << "QmlOutlineModel -" << QByteArray(indent++, '-').constData() << node << typeid(*node).name();
- return true;
- }
-
- void postVisit(AST::Node *)
- {
- indent--;
- }
-
- QString asString(AST::UiQualifiedId *id)
- {
- QString text;
- for (; id; id = id->next) {
- if (id->name)
- text += id->name->asString();
- else
- text += QLatin1Char('?');
-
- if (id->next)
- text += QLatin1Char('.');
- }
-
- return text;
- }
-
- bool visit(AST::UiObjectDefinition *objDef)
- {
- AST::SourceLocation location;
- location.offset = objDef->firstSourceLocation().offset;
- location.length = objDef->lastSourceLocation().offset
- - objDef->firstSourceLocation().offset
- + objDef->lastSourceLocation().length;
-
- QModelIndex index = m_model->enterElement(asString(objDef->qualifiedTypeNameId), location);
- m_nodeToIndex.insert(objDef, index);
- return true;
- }
-
- void endVisit(AST::UiObjectDefinition * /*objDefinition*/)
- {
- m_model->leaveElement();
- }
-
- bool visit(AST::UiScriptBinding *scriptBinding)
- {
- AST::SourceLocation location;
- location.offset = scriptBinding->firstSourceLocation().offset;
- location.length = scriptBinding->lastSourceLocation().offset
- - scriptBinding->firstSourceLocation().offset
- + scriptBinding->lastSourceLocation().length;
-
- QModelIndex index = m_model->enterProperty(asString(scriptBinding->qualifiedId), location);
- m_nodeToIndex.insert(scriptBinding, index);
-
- return true;
- }
-
- void endVisit(AST::UiScriptBinding * /*scriptBinding*/)
- {
- m_model->leaveProperty();
- }
-
- QmlOutlineModel *m_model;
- QHash<AST::Node*, QModelIndex> m_nodeToIndex;
- int indent;
-};
-
-
QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) :
TextEditor::IOutlineWidget(parent),
- m_treeView(new QmlJSOutlineTreeView()),
- m_model(new QmlOutlineModel),
+ m_treeView(new QmlJSOutlineTreeView(this)),
m_enableCursorSync(true),
m_blockCursorSync(false)
{
@@ -249,94 +42,44 @@ QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) :
layout->addWidget(m_treeView);
setLayout(layout);
-
- m_treeView->setModel(m_model);
-
- connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
- this, SLOT(updateSelectionInText(QItemSelection)));
}
void QmlJSOutlineWidget::setEditor(QmlJSTextEditor *editor)
{
m_editor = editor;
- connect(m_editor.data(), SIGNAL(semanticInfoUpdated(QmlJSEditor::Internal::SemanticInfo)),
- this, SLOT(updateOutline(QmlJSEditor::Internal::SemanticInfo)));
- connect(m_editor.data(), SIGNAL(cursorPositionChanged()),
- this, SLOT(updateSelectionInTree()));
+ m_treeView->setModel(m_editor.data()->outlineModel());
+ modelUpdated();
+
+ connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
+ this, SLOT(updateSelectionInText(QItemSelection)));
- updateOutline(m_editor.data()->semanticInfo());
+ connect(m_editor.data(), SIGNAL(outlineModelIndexChanged(QModelIndex)),
+ this, SLOT(updateSelectionInTree(QModelIndex)));
+ connect(m_editor.data()->outlineModel(), SIGNAL(updated()),
+ this, SLOT(modelUpdated()));
}
void QmlJSOutlineWidget::setCursorSynchronization(bool syncWithCursor)
{
m_enableCursorSync = syncWithCursor;
if (m_enableCursorSync)
- updateSelectionInTree();
+ updateSelectionInTree(m_editor.data()->outlineModelIndex());
}
-void QmlJSOutlineWidget::updateOutline(const QmlJSEditor::Internal::SemanticInfo &semanticInfo)
+void QmlJSOutlineWidget::modelUpdated()
{
- Document::Ptr doc = semanticInfo.document;
-
- if (!doc) {
- return;
- }
-
- if (!m_editor
- || m_editor.data()->file()->fileName() != doc->fileName()
- || m_editor.data()->documentRevision() != doc->editorRevision()) {
- return;
- }
-
- if (doc->ast()
- && m_model) {
-
- // got a correctly parsed (or recovered) file.
-
- if (QmlOutlineModel *qmlModel = qobject_cast<QmlOutlineModel*>(m_model)) {
- QmlOutlineModelSync syncModel(qmlModel);
- syncModel(doc);
- }
- } else {
- // TODO: Maybe disable view?
- }
-
m_treeView->expandAll();
}
-QModelIndex QmlJSOutlineWidget::indexForPosition(const QModelIndex &rootIndex, int cursorPosition)
-{
- if (!rootIndex.isValid())
- return QModelIndex();
-
- AST::SourceLocation location = rootIndex.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
-
- if (!offsetInsideLocation(cursorPosition, location)) {
- return QModelIndex();
- }
-
- const int rowCount = rootIndex.model()->rowCount(rootIndex);
- for (int i = 0; i < rowCount; ++i) {
- QModelIndex childIndex = rootIndex.child(i, 0);
- QModelIndex resultIndex = indexForPosition(childIndex, cursorPosition);
- if (resultIndex.isValid())
- return resultIndex;
- }
-
- return rootIndex;
-}
-
-void QmlJSOutlineWidget::updateSelectionInTree()
+void QmlJSOutlineWidget::updateSelectionInTree(const QModelIndex &index)
{
if (!syncCursor())
return;
- int absoluteCursorPos = m_editor.data()->textCursor().position();
- QModelIndex index = indexForPosition(m_model->index(0, 0), absoluteCursorPos);
-
m_blockCursorSync = true;
m_treeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
+ m_treeView->scrollTo(index);
m_blockCursorSync = false;
}
@@ -350,6 +93,10 @@ void QmlJSOutlineWidget::updateSelectionInText(const QItemSelection &selection)
QModelIndex index = selection.indexes().first();
AST::SourceLocation location = index.data(QmlOutlineModel::SourceLocationRole).value<AST::SourceLocation>();
+ Core::EditorManager *editorManager = Core::EditorManager::instance();
+ editorManager->cutForwardNavigationHistory();
+ editorManager->addCurrentPositionToNavigationHistory();
+
QTextCursor textCursor = m_editor.data()->textCursor();
m_blockCursorSync = true;
textCursor.setPosition(location.offset);
@@ -358,12 +105,6 @@ void QmlJSOutlineWidget::updateSelectionInText(const QItemSelection &selection)
}
}
-bool QmlJSOutlineWidget::offsetInsideLocation(quint32 offset, const QmlJS::AST::SourceLocation &location)
-{
- return ((offset >= location.offset)
- && (offset <= location.offset + location.length));
-}
-
bool QmlJSOutlineWidget::syncCursor()
{
return m_enableCursorSync && !m_blockCursorSync;
diff --git a/src/plugins/qmljseditor/qmljsoutline.h b/src/plugins/qmljseditor/qmljsoutline.h
index 1571069afb..c42685dd8d 100644
--- a/src/plugins/qmljseditor/qmljsoutline.h
+++ b/src/plugins/qmljseditor/qmljsoutline.h
@@ -3,16 +3,9 @@
#include "qmljseditor.h"
-#include <coreplugin/editormanager/editormanager.h>
-#include <coreplugin/inavigationwidgetfactory.h>
#include <texteditor/ioutlinewidget.h>
-#include <qmljs/parser/qmljsastvisitor_p.h>
-#include <qmljs/qmljsdocument.h>
-#include <qmljs/qmljsicons.h>
-#include <QtGui/QStandardItemModel>
#include <QtGui/QTreeView>
-#include <QtGui/QWidget>
namespace Core {
class IEditor;
@@ -25,35 +18,6 @@ class Editor;
namespace QmlJSEditor {
namespace Internal {
-class QmlOutlineModel : public QStandardItemModel
-{
- Q_OBJECT
-public:
- enum CustomRoles {
- SourceLocationRole = Qt::UserRole + 1
- };
-
- QmlOutlineModel(QObject *parent = 0);
-
- void startSync();
-
- QModelIndex enterElement(const QString &typeName, const QmlJS::AST::SourceLocation &location);
- void leaveElement();
-
- QModelIndex enterProperty(const QString &name, const QmlJS::AST::SourceLocation &location);
- void leaveProperty();
-
-private:
- QStandardItem *enterNode(const QmlJS::AST::SourceLocation &location);
- void leaveNode();
-
- QStandardItem *parentItem();
-
- QList<int> m_treePos;
- QStandardItem *m_currentItem;
- QmlJS::Icons m_icons;
-};
-
class QmlJSOutlineTreeView : public QTreeView
{
Q_OBJECT
@@ -61,7 +25,6 @@ public:
QmlJSOutlineTreeView(QWidget *parent = 0);
};
-
class QmlJSOutlineWidget : public TextEditor::IOutlineWidget
{
Q_OBJECT
@@ -74,18 +37,15 @@ public:
virtual void setCursorSynchronization(bool syncWithCursor);
private slots:
- void updateOutline(const QmlJSEditor::Internal::SemanticInfo &semanticInfo);
- void updateSelectionInTree();
+ void modelUpdated();
+ void updateSelectionInTree(const QModelIndex &index);
void updateSelectionInText(const QItemSelection &selection);
private:
- QModelIndex indexForPosition(const QModelIndex &rootIndex, int cursorPosition);
- bool offsetInsideLocation(quint32 offset, const QmlJS::AST::SourceLocation &location);
bool syncCursor();
private:
QmlJSOutlineTreeView *m_treeView;
- QAbstractItemModel *m_model;
QWeakPointer<QmlJSTextEditor> m_editor;
bool m_enableCursorSync;
@@ -103,6 +63,4 @@ public:
} // namespace Internal
} // namespace QmlJSEditor
-Q_DECLARE_METATYPE(QmlJS::AST::SourceLocation);
-
#endif // QMLJSOUTLINE_H
diff --git a/src/plugins/qmljseditor/qmloutlinemodel.cpp b/src/plugins/qmljseditor/qmloutlinemodel.cpp
new file mode 100644
index 0000000000..ef2c838013
--- /dev/null
+++ b/src/plugins/qmljseditor/qmloutlinemodel.cpp
@@ -0,0 +1,337 @@
+#include "qmloutlinemodel.h"
+#include <qmljs/parser/qmljsastvisitor_p.h>
+#include <qmljs/qmljsinterpreter.h>
+#include <qmljs/qmljslookupcontext.h>
+
+#include <coreplugin/icore.h>
+#include <QtCore/QDebug>
+#include <QtCore/QTime>
+#include <typeinfo>
+
+using namespace QmlJS;
+using namespace QmlJSEditor::Internal;
+
+enum {
+ debug = false
+};
+
+namespace QmlJSEditor {
+namespace Internal {
+
+class QmlOutlineModelSync : protected AST::Visitor
+{
+public:
+ QmlOutlineModelSync(QmlOutlineModel *model) :
+ m_model(model),
+ indent(0)
+ {
+ }
+
+ void operator()(Document::Ptr doc, const Snapshot &snapshot)
+ {
+ m_nodeToIndex.clear();
+
+ // Set up lookup context once to do the element type lookup
+ //
+ // We're simplifying here by using the root context everywhere
+ // (empty node list). However, creating the LookupContext is quite expensive (about 3ms),
+ // and there is AFAIK no way to introduce new type names in a sub-context.
+ m_context = LookupContext::create(doc, snapshot, QList<AST::Node*>());
+
+ if (debug)
+ qDebug() << "QmlOutlineModel ------";
+ if (doc && doc->ast())
+ doc->ast()->accept(this);
+
+ m_context.clear();
+ }
+
+private:
+ bool preVisit(AST::Node *node)
+ {
+ if (!node)
+ return false;
+ if (debug)
+ qDebug() << "QmlOutlineModel -" << QByteArray(indent++, '-').constData() << node << typeid(*node).name();
+ return true;
+ }
+
+ void postVisit(AST::Node *)
+ {
+ indent--;
+ }
+
+ QString asString(AST::UiQualifiedId *id)
+ {
+ QString text;
+ for (; id; id = id->next) {
+ if (id->name)
+ text += id->name->asString();
+ else
+ text += QLatin1Char('?');
+
+ if (id->next)
+ text += QLatin1Char('.');
+ }
+
+ return text;
+ }
+
+
+ typedef QPair<QString,QString> ElementType;
+ bool visit(AST::UiObjectDefinition *objDef)
+ {
+ if (!validElement(objDef)) {
+ return true;
+ }
+
+ AST::SourceLocation location = getLocation(objDef);
+
+ const QString typeName = asString(objDef->qualifiedTypeNameId);
+
+ if (!m_typeToIcon.contains(typeName)) {
+ m_typeToIcon.insert(typeName, getIcon(objDef));
+ }
+ const QIcon icon = m_typeToIcon.value(typeName);
+ QString id = getId(objDef);
+
+ QModelIndex index = m_model->enterElement(typeName, id, icon, location);
+ m_nodeToIndex.insert(objDef, index);
+ return true;
+ }
+
+ void endVisit(AST::UiObjectDefinition *objDef)
+ {
+ if (!validElement(objDef)) {
+ return;
+ }
+ m_model->leaveElement();
+ }
+
+ bool visit(AST::UiScriptBinding *scriptBinding)
+ {
+ AST::SourceLocation location = getLocation(scriptBinding);
+
+ QModelIndex index = m_model->enterProperty(asString(scriptBinding->qualifiedId), false, location);
+ m_nodeToIndex.insert(scriptBinding, index);
+
+ return true;
+ }
+
+ void endVisit(AST::UiScriptBinding * /*scriptBinding*/)
+ {
+ m_model->leaveProperty();
+ }
+
+ bool visit(AST::UiPublicMember *publicMember)
+ {
+ AST::SourceLocation location = getLocation(publicMember);
+ QModelIndex index = m_model->enterProperty(publicMember->name->asString(), true, location);
+ m_nodeToIndex.insert(publicMember, index);
+
+ return true;
+ }
+
+ void endVisit(AST::UiPublicMember * /*publicMember*/)
+ {
+ m_model->leaveProperty();
+ }
+
+ bool validElement(AST::UiObjectDefinition *objDef) {
+ // For 'Rectangle { id }', id is parsed as UiObjectDefinition ... Filter this out.
+ return objDef->qualifiedTypeNameId->name->asString().at(0).isUpper();
+ }
+
+ QIcon getIcon(AST::UiObjectDefinition *objDef) {
+ const QmlJS::Interpreter::Value *value = m_context->evaluate(objDef->qualifiedTypeNameId);
+
+ if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
+ do {
+ QString module;
+ QString typeName;
+ if (const Interpreter::QmlObjectValue *qmlObjectValue =
+ dynamic_cast<const Interpreter::QmlObjectValue*>(objectValue)) {
+ module = qmlObjectValue->packageName();
+ }
+ typeName = objectValue->className();
+
+ QIcon icon = m_model->m_icons->icon(module, typeName);
+ if (! icon.isNull())
+ return icon;
+
+ objectValue = objectValue->prototype(m_context->context());
+ } while (objectValue);
+ }
+ return QIcon();
+ }
+
+ QString getId(AST::UiObjectDefinition *objDef) {
+ QString id;
+ for (AST::UiObjectMemberList *it = objDef->initializer->members; it; it = it->next) {
+ if (AST::UiScriptBinding *binding = dynamic_cast<AST::UiScriptBinding*>(it->member)) {
+ if (binding->qualifiedId->name->asString() == "id") {
+ AST::ExpressionStatement *expr = dynamic_cast<AST::ExpressionStatement*>(binding->statement);
+ if (!expr)
+ continue;
+ AST::IdentifierExpression *idExpr = dynamic_cast<AST::IdentifierExpression*>(expr->expression);
+ if (!idExpr)
+ continue;
+ id = idExpr->name->asString();
+ break;
+ }
+ }
+ }
+ return id;
+ }
+
+ AST::SourceLocation getLocation(AST::UiObjectMember *objMember) {
+ AST::SourceLocation location;
+ location.offset = objMember->firstSourceLocation().offset;
+ location.length = objMember->lastSourceLocation().offset
+ - objMember->firstSourceLocation().offset
+ + objMember->lastSourceLocation().length;
+ return location;
+ }
+
+ QmlOutlineModel *m_model;
+ LookupContext::Ptr m_context;
+
+ QHash<AST::Node*, QModelIndex> m_nodeToIndex;
+ QHash<QString, QIcon> m_typeToIcon;
+ int indent;
+};
+
+QmlOutlineModel::QmlOutlineModel(QObject *parent) :
+ QStandardItemModel(parent)
+{
+ m_icons = Icons::instance();
+ const QString resourcePath = Core::ICore::instance()->resourcePath();
+ QmlJS::Icons::instance()->setIconFilesPath(resourcePath + "/qmlicons");
+}
+
+QmlJS::Document::Ptr QmlOutlineModel::document() const
+{
+ return m_document;
+}
+
+void QmlOutlineModel::update(QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot)
+{
+ m_document = doc;
+
+ m_treePos.clear();
+ m_treePos.append(0);
+ m_currentItem = invisibleRootItem();
+
+ QmlOutlineModelSync syncModel(this);
+ syncModel(doc, snapshot);
+
+ emit updated();
+}
+
+QModelIndex QmlOutlineModel::enterElement(const QString &type, const QString &id, const QIcon &icon, const AST::SourceLocation &sourceLocation)
+{
+ QStandardItem *item = enterNode(sourceLocation);
+ if (!id.isEmpty()) {
+ item->setText(id);
+ } else {
+ item->setText(type);
+ }
+ item->setIcon(icon);
+ item->setToolTip(type);
+ return item->index();
+}
+
+void QmlOutlineModel::leaveElement()
+{
+ leaveNode();
+}
+
+QModelIndex QmlOutlineModel::enterProperty(const QString &name, bool isCustomProperty, const AST::SourceLocation &sourceLocation)
+{
+ QStandardItem *item = enterNode(sourceLocation);
+ item->setText(name);
+ if (isCustomProperty) {
+ item->setIcon(m_icons->publicMemberIcon());
+ } else {
+ item->setIcon(m_icons->scriptBindingIcon());
+ }
+ return item->index();
+}
+
+void QmlOutlineModel::leaveProperty()
+{
+ leaveNode();
+}
+
+QStandardItem *QmlOutlineModel::enterNode(const QmlJS::AST::SourceLocation &location)
+{
+ int siblingIndex = m_treePos.last();
+ if (siblingIndex == 0) {
+ // first child
+ if (!m_currentItem->hasChildren()) {
+ QStandardItem *parentItem = m_currentItem;
+ m_currentItem = new QStandardItem;
+ m_currentItem->setEditable(false);
+ parentItem->appendRow(m_currentItem);
+ if (debug)
+ qDebug() << "QmlOutlineModel - Adding" << "element to" << parentItem->text();
+ } else {
+ m_currentItem = m_currentItem->child(0);
+ }
+ } else {
+ // sibling
+ if (m_currentItem->rowCount() <= siblingIndex) {
+ // attach
+ QStandardItem *oldItem = m_currentItem;
+ m_currentItem = new QStandardItem;
+ m_currentItem->setEditable(false);
+ oldItem->appendRow(m_currentItem);
+ if (debug)
+ qDebug() << "QmlOutlineModel - Adding" << "element to" << oldItem->text();
+ } else {
+ m_currentItem = m_currentItem->child(siblingIndex);
+ }
+ }
+
+ m_treePos.append(0);
+ m_currentItem->setData(QVariant::fromValue(location), SourceLocationRole);
+
+ return m_currentItem;
+}
+
+void QmlOutlineModel::leaveNode()
+{
+ int lastIndex = m_treePos.takeLast();
+
+
+ if (lastIndex > 0) {
+ // element has children
+ if (lastIndex < m_currentItem->rowCount()) {
+ if (debug)
+ qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << lastIndex << m_currentItem->rowCount() - lastIndex;
+ m_currentItem->removeRows(lastIndex, m_currentItem->rowCount() - lastIndex);
+ }
+ m_currentItem = parentItem();
+ } else {
+ if (m_currentItem->hasChildren()) {
+ if (debug)
+ qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << 0 << m_currentItem->rowCount();
+ m_currentItem->removeRows(0, m_currentItem->rowCount());
+ }
+ m_currentItem = parentItem();
+ }
+
+
+ m_treePos.last()++;
+}
+
+QStandardItem *QmlOutlineModel::parentItem()
+{
+ QStandardItem *parent = m_currentItem->parent();
+ if (!parent)
+ parent = invisibleRootItem();
+ return parent;
+}
+
+} // namespace Internal
+} // namespace QmlJSEditor
diff --git a/src/plugins/qmljseditor/qmloutlinemodel.h b/src/plugins/qmljseditor/qmloutlinemodel.h
new file mode 100644
index 0000000000..6b22538ed8
--- /dev/null
+++ b/src/plugins/qmljseditor/qmloutlinemodel.h
@@ -0,0 +1,55 @@
+#ifndef QMLOUTLINEMODEL_H
+#define QMLOUTLINEMODEL_H
+
+#include <qmljs/qmljsdocument.h>
+#include <qmljs/qmljsicons.h>
+
+#include <QStandardItemModel>
+
+namespace QmlJSEditor {
+namespace Internal {
+
+class QmlOutlineModel : public QStandardItemModel
+{
+ Q_OBJECT
+public:
+ enum CustomRoles {
+ SourceLocationRole = Qt::UserRole + 1
+ };
+
+ QmlOutlineModel(QObject *parent = 0);
+
+ QmlJS::Document::Ptr document() const;
+ void update(QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot);
+
+ QModelIndex enterElement(const QString &typeName, const QString &id, const QIcon &icon,
+ const QmlJS::AST::SourceLocation &location);
+ void leaveElement();
+
+ QModelIndex enterProperty(const QString &name, bool isCustomProperty,
+ const QmlJS::AST::SourceLocation &location);
+ void leaveProperty();
+
+signals:
+ void updated();
+
+private:
+ QStandardItem *enterNode(const QmlJS::AST::SourceLocation &location);
+ void leaveNode();
+
+ QStandardItem *parentItem();
+
+ QmlJS::Document::Ptr m_document;
+ QList<int> m_treePos;
+ QStandardItem *m_currentItem;
+ QmlJS::Icons *m_icons;
+
+ friend class QmlOutlineModelSync;
+};
+
+} // namespace Internal
+} // namespace QmlJSEditor
+
+Q_DECLARE_METATYPE(QmlJS::AST::SourceLocation);
+
+#endif // QMLOUTLINEMODEL_H
diff --git a/src/plugins/qmlprojectmanager/qmltaskmanager.cpp b/src/plugins/qmljseditor/qmltaskmanager.cpp
index fc8905f2b8..30cd0a9dbe 100644
--- a/src/plugins/qmlprojectmanager/qmltaskmanager.cpp
+++ b/src/plugins/qmljseditor/qmltaskmanager.cpp
@@ -28,14 +28,15 @@
**************************************************************************/
#include "qmltaskmanager.h"
-#include "qmlprojectconstants.h"
+#include "qmljseditorconstants.h"
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/taskhub.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
#include <QDebug>
-namespace QmlProjectManager {
+namespace QmlJSEditor {
namespace Internal {
QmlTaskManager::QmlTaskManager(QObject *parent) :
@@ -45,12 +46,6 @@ QmlTaskManager::QmlTaskManager(QObject *parent) :
m_taskHub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
}
-QmlTaskManager *QmlTaskManager::instance()
-{
- ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
- return pluginManager->getObject<QmlTaskManager>();
-}
-
void QmlTaskManager::documentChangedOnDisk(QmlJS::Document::Ptr doc)
{
const QString fileName = doc->fileName();
diff --git a/src/plugins/qmlprojectmanager/qmltaskmanager.h b/src/plugins/qmljseditor/qmltaskmanager.h
index 74e9d0a313..18ccc828c6 100644
--- a/src/plugins/qmlprojectmanager/qmltaskmanager.h
+++ b/src/plugins/qmljseditor/qmltaskmanager.h
@@ -42,7 +42,7 @@ namespace ProjectExplorer {
class TaskHub;
} // namespace ProjectExplorer
-namespace QmlProjectManager {
+namespace QmlJSEditor {
namespace Internal {
class QmlTaskManager : public QObject
@@ -51,7 +51,7 @@ class QmlTaskManager : public QObject
public:
QmlTaskManager(QObject *parent = 0);
- static QmlTaskManager *instance();
+ void extensionsInitialized();
public slots:
void documentChangedOnDisk(QmlJS::Document::Ptr doc);
@@ -67,6 +67,6 @@ private:
};
} // Internal
-} // QmlProjectManager
+} // QmlJSEditor
#endif // QMLTASKMANAGER_H
diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp
index 5a21144c28..87e17bb232 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp
+++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.cpp
@@ -114,8 +114,9 @@ Inspector *InspectorPlugin::inspector() const
return _inspector;
}
-void InspectorPlugin::aboutToShutdown()
+ExtensionSystem::IPlugin::ShutdownFlag InspectorPlugin::aboutToShutdown()
{
+ return SynchronousShutdown;
}
bool InspectorPlugin::initialize(const QStringList &arguments, QString *errorString)
diff --git a/src/plugins/qmljsinspector/qmljsinspectorplugin.h b/src/plugins/qmljsinspector/qmljsinspectorplugin.h
index d1696b0668..237b8e566c 100644
--- a/src/plugins/qmljsinspector/qmljsinspectorplugin.h
+++ b/src/plugins/qmljsinspector/qmljsinspectorplugin.h
@@ -70,7 +70,7 @@ public:
// ExtensionSystem::IPlugin interface
virtual bool initialize(const QStringList &arguments, QString *errorString);
virtual void extensionsInitialized();
- virtual void aboutToShutdown();
+ virtual ExtensionSystem::IPlugin::ShutdownFlag aboutToShutdown();
public slots:
void activateDebuggerForProject(ProjectExplorer::Project *project, const QString &runMode);
diff --git a/src/plugins/qmlprojectmanager/images/qml_wizard.png b/src/plugins/qmlprojectmanager/images/qml_wizard.png
index 5355c80bf8..23c0236871 100644
--- a/src/plugins/qmlprojectmanager/images/qml_wizard.png
+++ b/src/plugins/qmlprojectmanager/images/qml_wizard.png
Binary files differ
diff --git a/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp b/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
index eafbc0385b..f913cc7d82 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectapplicationwizard.cpp
@@ -61,14 +61,7 @@ QmlProjectApplicationWizard::~QmlProjectApplicationWizard()
Core::BaseFileWizardParameters QmlProjectApplicationWizard::parameters()
{
Core::BaseFileWizardParameters parameters(ProjectWizard);
- // TODO: provide icons in correct size
- {
- QPixmap icon(22, 22);
- icon.fill(Qt::transparent);
- QPainter p(&icon);
- p.drawPixmap(3, 3, 16, 16, QPixmap(QLatin1String(Constants::QML_WIZARD_ICON)));
- parameters.setIcon(icon);
- }
+ parameters.setIcon(QIcon(QLatin1String(Constants::QML_WIZARD_ICON)));
parameters.setDisplayName(tr("Qt QML Application"));
parameters.setId(QLatin1String("QA.QML Application"));
parameters.setDescription(tr("Creates a Qt QML application project with a single QML file containing the main view.\n\n"
diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
index 1fd04264a0..3cd4d9c7e0 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h
@@ -47,8 +47,6 @@ const char *const FILES_EDITOR_ID = "Qt4.QmlProjectEditor";
const char *const FILES_EDITOR_DISPLAY_NAME = QT_TRANSLATE_NOOP("OpenWith::Editors", ".qmlproject Editor");
const char *const FILES_MIMETYPE = QMLMIMETYPE;
-const char *const TASK_CATEGORY_QML = "Task.Category.Qml";
-
// Wizard category
const char * const QML_WIZARD_CATEGORY = "F.Projects"; // (after Qt)
const char * const QML_WIZARD_TR_SCOPE = "QmlProjectManager";
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp b/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp
index 683bc64f89..8a810319a6 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.cpp
@@ -30,7 +30,6 @@
#include "qmlprojectmanager.h"
#include "qmlprojectconstants.h"
#include "qmlproject.h"
-#include "qmltaskmanager.h"
#include <coreplugin/icore.h>
#include <coreplugin/ifile.h>
diff --git a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
index 72e0d2cadd..d97c600458 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
+++ b/src/plugins/qmlprojectmanager/qmlprojectmanager.pro
@@ -19,11 +19,9 @@ HEADERS += qmlproject.h \
qmlprojectrunconfiguration.h \
qmlprojectrunconfigurationfactory.h \
qmlprojectapplicationwizard.h \
- qmltaskmanager.h \
qmlprojectmanager_global.h \
qmlprojectmanagerconstants.h \
- qmlprojecttarget.h \
- qmloutputformatter.h
+ qmlprojecttarget.h
SOURCES += qmlproject.cpp \
qmlprojectplugin.cpp \
qmlprojectmanager.cpp \
@@ -34,9 +32,7 @@ SOURCES += qmlproject.cpp \
qmlprojectrunconfiguration.cpp \
qmlprojectrunconfigurationfactory.cpp \
qmlprojectapplicationwizard.cpp \
- qmltaskmanager.cpp \
- qmlprojecttarget.cpp \
- qmloutputformatter.cpp
+ qmlprojecttarget.cpp
RESOURCES += qmlproject.qrc
OTHER_FILES += QmlProjectManager.pluginspec \
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
index 44229263ce..622f594079 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp
@@ -35,7 +35,6 @@
#include "qmlproject.h"
#include "qmlprojectrunconfigurationfactory.h"
#include "qmlprojectruncontrol.h"
-#include "qmltaskmanager.h"
#include "fileformat/qmlprojectfileformat.h"
#include <extensionsystem/pluginmanager.h>
@@ -47,15 +46,13 @@
#include <texteditor/texteditoractionhandler.h>
#include <projectexplorer/taskhub.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
#include <QtCore/QtPlugin>
namespace QmlProjectManager {
namespace Internal {
-QmlProjectPlugin::QmlProjectPlugin() :
- m_qmlTaskManager(0)
+QmlProjectPlugin::QmlProjectPlugin()
{ }
QmlProjectPlugin::~QmlProjectPlugin()
@@ -76,9 +73,6 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage)
Manager *manager = new Manager;
- m_qmlTaskManager = new QmlTaskManager(this);
- addAutoReleasedObject(m_qmlTaskManager);
-
addAutoReleasedObject(manager);
addAutoReleasedObject(new Internal::QmlProjectRunConfigurationFactory);
addAutoReleasedObject(new Internal::QmlRunControlFactory);
@@ -94,16 +88,6 @@ bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage)
void QmlProjectPlugin::extensionsInitialized()
{
- ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
- ProjectExplorer::TaskHub *taskHub = pluginManager->getObject<ProjectExplorer::TaskHub>();
- taskHub->addCategory(Constants::TASK_CATEGORY_QML, tr("QML"));
-
- QmlJS::ModelManagerInterface *modelManager = pluginManager->getObject<QmlJS::ModelManagerInterface>();
- Q_ASSERT(modelManager);
- connect(modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
- m_qmlTaskManager, SLOT(documentChangedOnDisk(QmlJS::Document::Ptr)));
- connect(modelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
- m_qmlTaskManager, SLOT(documentsRemoved(QStringList)));
}
} // namespace Internal
diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.h b/src/plugins/qmlprojectmanager/qmlprojectplugin.h
index 140a16f2c5..7fb905d820 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectplugin.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.h
@@ -38,7 +38,6 @@ namespace QmlProjectManager {
namespace Internal {
class ProjectFilesFactory;
-class QmlTaskManager;
class QmlProjectPlugin: public ExtensionSystem::IPlugin
{
@@ -50,9 +49,6 @@ public:
virtual bool initialize(const QStringList &arguments, QString *errorString);
virtual void extensionsInitialized();
-
-private:
- QmlTaskManager *m_qmlTaskManager;
};
} // namespace Internal
diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
index fbd9076b10..a9eee44e6f 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp
@@ -27,9 +27,9 @@
**
**************************************************************************/
+#include "qmlprojectrunconfiguration.h"
#include "qmlproject.h"
#include "qmlprojectmanagerconstants.h"
-#include "qmlprojectrunconfiguration.h"
#include "qmlprojecttarget.h"
#include "projectexplorer/projectexplorer.h"
diff --git a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp
index 7461ef73f4..59663372e8 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp
+++ b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.cpp
@@ -27,7 +27,6 @@
**
**************************************************************************/
-#include "qmloutputformatter.h"
#include "qmlprojectruncontrol.h"
#include "qmlprojectrunconfiguration.h"
#include "qmlprojectconstants.h"
@@ -103,11 +102,6 @@ bool QmlRunControl::isRunning() const
return m_applicationLauncher.isRunning();
}
-ProjectExplorer::OutputFormatter *QmlRunControl::createOutputFormatter(QObject *parent)
-{
- return new QmlOutputFormatter(parent);
-}
-
void QmlRunControl::slotBringApplicationToForeground(qint64 pid)
{
bringApplicationToForeground(pid);
diff --git a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h
index 89d0d7dc54..7e85370a6e 100644
--- a/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h
+++ b/src/plugins/qmlprojectmanager/qmlprojectruncontrol.h
@@ -51,8 +51,6 @@ public:
virtual void stop();
virtual bool isRunning() const;
- virtual ProjectExplorer::OutputFormatter *createOutputFormatter(QObject *parent = 0);
-
private slots:
void processExited(int exitCode);
void slotBringApplicationToForeground(qint64 pid);
diff --git a/src/plugins/qt4projectmanager/gettingstartedwelcomepagewidget.h b/src/plugins/qt4projectmanager/gettingstartedwelcomepagewidget.h
index db2664dee9..e0903d3eed 100644
--- a/src/plugins/qt4projectmanager/gettingstartedwelcomepagewidget.h
+++ b/src/plugins/qt4projectmanager/gettingstartedwelcomepagewidget.h
@@ -30,7 +30,7 @@
#ifndef GETTINGSTARTEDWELCOMEPAGEWIDGET_H
#define GETTINGSTARTEDWELCOMEPAGEWIDGET_H
-#include <QWidget>
+#include <QtGui/QWidget>
namespace Qt4ProjectManager {
namespace Internal {
diff --git a/src/plugins/qt4projectmanager/profilehighlighter.cpp b/src/plugins/qt4projectmanager/profilehighlighter.cpp
index 69a7ca2bf4..cf9e72d2ca 100644
--- a/src/plugins/qt4projectmanager/profilehighlighter.cpp
+++ b/src/plugins/qt4projectmanager/profilehighlighter.cpp
@@ -157,7 +157,7 @@ static bool isFunction(const QString &word)
}
ProFileHighlighter::ProFileHighlighter(QTextDocument *document) :
- QSyntaxHighlighter(document)
+ TextEditor::SyntaxHighlighter(document)
{
}
diff --git a/src/plugins/qt4projectmanager/profilehighlighter.h b/src/plugins/qt4projectmanager/profilehighlighter.h
index 14f3401451..87dadce27e 100644
--- a/src/plugins/qt4projectmanager/profilehighlighter.h
+++ b/src/plugins/qt4projectmanager/profilehighlighter.h
@@ -30,6 +30,7 @@
#ifndef PROFILEHIGHLIGHTER_H
#define PROFILEHIGHLIGHTER_H
+#include <texteditor/syntaxhighlighter.h>
#include <QtCore/QtAlgorithms>
#include <QtGui/QSyntaxHighlighter>
#include <QtGui/QTextCharFormat>
@@ -37,7 +38,7 @@
namespace Qt4ProjectManager {
namespace Internal {
-class ProFileHighlighter : public QSyntaxHighlighter
+class ProFileHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
public:
diff --git a/src/plugins/qt4projectmanager/projectloadwizard.cpp b/src/plugins/qt4projectmanager/projectloadwizard.cpp
index 3a5e3986e8..277b3b8ae0 100644
--- a/src/plugins/qt4projectmanager/projectloadwizard.cpp
+++ b/src/plugins/qt4projectmanager/projectloadwizard.cpp
@@ -35,6 +35,7 @@
#include "makestep.h"
#include "qt4buildconfiguration.h"
#include "qt4projectmanagerconstants.h"
+#include "qtversionmanager.h"
#include "wizards/targetsetuppage.h"
diff --git a/src/plugins/qt4projectmanager/projectloadwizard.h b/src/plugins/qt4projectmanager/projectloadwizard.h
index c05345c981..b6e56d6ce1 100644
--- a/src/plugins/qt4projectmanager/projectloadwizard.h
+++ b/src/plugins/qt4projectmanager/projectloadwizard.h
@@ -30,17 +30,13 @@
#ifndef PROJECTLOADWIZARD_H
#define PROJECTLOADWIZARD_H
-#include "qtversionmanager.h"
-#include <wizards/targetsetuppage.h>
-
#include <QtGui/QWizard>
namespace Qt4ProjectManager {
class Qt4Project;
namespace Internal {
-
-class TargetsPage;
+class TargetSetupPage;
class ProjectLoadWizard : public QWizard
{
diff --git a/src/plugins/qt4projectmanager/qmakeparser.h b/src/plugins/qt4projectmanager/qmakeparser.h
index 9f6999b14e..0bbd17e30e 100644
--- a/src/plugins/qt4projectmanager/qmakeparser.h
+++ b/src/plugins/qt4projectmanager/qmakeparser.h
@@ -32,8 +32,6 @@
#include <projectexplorer/ioutputparser.h>
-#include <QtCore/QRegExp>
-
namespace Qt4ProjectManager {
namespace Internal {
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.cpp
index eddb077acd..575da23144 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.cpp
@@ -35,38 +35,14 @@
#include "maemoconfigtestdialog.h"
#include "ui_maemoconfigtestdialog.h"
-#include "maemosshthread.h"
+#include "maemodeviceconfigurations.h"
-#include <QtGui/QPushButton>
-
-namespace {
-
-/**
- * Class that waits until a thread is finished and then deletes it, and then
- * schedules itself to be deleted.
- */
-class SafeThreadDeleter : public QThread
-{
-public:
- SafeThreadDeleter(QThread *thread) : m_thread(thread) {}
- ~SafeThreadDeleter() { wait(); }
-
-protected:
- void run()
- {
- // Wait for m_thread to finish and then delete it
- m_thread->wait();
- delete m_thread;
-
- // Schedule this thread for deletion
- deleteLater();
- }
+#include <coreplugin/ssh/sshconnection.h>
+#include <coreplugin/ssh/sshremoteprocess.h>
-private:
- QThread *m_thread;
-};
+#include <QtGui/QPushButton>
-} // anonymous namespace
+using namespace Core;
namespace Qt4ProjectManager {
namespace Internal {
@@ -75,7 +51,6 @@ MaemoConfigTestDialog::MaemoConfigTestDialog(const MaemoDeviceConfig &config, QW
: QDialog(parent)
, m_ui(new Ui_MaemoConfigTestDialog)
, m_config(config)
- , m_deviceTester(0)
{
setAttribute(Qt::WA_DeleteOnClose);
@@ -94,65 +69,86 @@ MaemoConfigTestDialog::~MaemoConfigTestDialog()
void MaemoConfigTestDialog::startConfigTest()
{
- if (m_deviceTester)
+ if (m_testProcess)
return;
m_ui->testResultEdit->setPlainText(tr("Testing configuration..."));
m_closeButton->setText(tr("Stop Test"));
+ m_connection = SshConnection::create();
+ connect(m_connection.data(), SIGNAL(connected()), this,
+ SLOT(handleConnected()));
+ connect(m_connection.data(), SIGNAL(error(SshError)), this,
+ SLOT(handleConnectionError()));
+ m_connection->connectToHost(m_config.server);
+}
+void MaemoConfigTestDialog::handleConnected()
+{
+ if (!m_connection)
+ return;
QLatin1String sysInfoCmd("uname -rsm");
QLatin1String qtInfoCmd("dpkg -l |grep libqt "
"|sed 's/[[:space:]][[:space:]]*/ /g' "
"|cut -d ' ' -f 2,3 |sed 's/~.*//g'");
QString command(sysInfoCmd + " && " + qtInfoCmd);
- m_deviceTester = new MaemoSshRunner(m_config.server, command);
- connect(m_deviceTester, SIGNAL(remoteOutput(QString)),
- this, SLOT(processSshOutput(QString)));
- connect(m_deviceTester, SIGNAL(finished()),
- this, SLOT(handleTestThreadFinished()));
+ m_testProcess = m_connection->createRemoteProcess(command.toUtf8());
+ connect(m_testProcess.data(), SIGNAL(closed(int)), this,
+ SLOT(handleProcessFinished(int)));
+ connect(m_testProcess.data(), SIGNAL(outputAvailable(QByteArray)), this,
+ SLOT(processSshOutput(QByteArray)));
+ m_testProcess->start();
+}
- m_deviceTester->start();
+void MaemoConfigTestDialog::handleConnectionError()
+{
+ if (!m_connection)
+ return;
+ QString output = tr("Could not connect to host: %1")
+ .arg(m_connection->errorString());
+ if (m_config.type == MaemoDeviceConfig::Simulator)
+ output += tr("\nDid you start Qemu?");
+ m_ui->testResultEdit->setPlainText(output);
+ stopConfigTest();
}
-void MaemoConfigTestDialog::handleTestThreadFinished()
+void MaemoConfigTestDialog::handleProcessFinished(int exitStatus)
{
- if (!m_deviceTester)
+ Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
+ || exitStatus == SshRemoteProcess::KilledBySignal
+ || exitStatus == SshRemoteProcess::ExitedNormally);
+
+ if (!m_testProcess)
return;
- QString output;
- if (m_deviceTester->hasError()) {
- output = tr("Device configuration test failed:\n%1").arg(m_deviceTester->error());
- if (m_config.type == MaemoDeviceConfig::Simulator)
- output.append(tr("\nDid you start Qemu?"));
+ if (exitStatus != SshRemoteProcess::ExitedNormally
+ || m_testProcess->exitCode() != 0) {
+ m_ui->testResultEdit->setPlainText(tr("Remote process failed: %1")
+ .arg(m_testProcess->errorString()));
} else {
- output = parseTestOutput();
+ const QString &output = parseTestOutput();
if (!m_qtVersionOk) {
m_ui->errorLabel->setText(tr("Qt version mismatch! "
" Expected Qt on device: 4.6.2 or later."));
}
+ m_ui->testResultEdit->setPlainText(output);
}
- m_ui->testResultEdit->setPlainText(output);
stopConfigTest();
}
void MaemoConfigTestDialog::stopConfigTest()
{
- if (m_deviceTester) {
- m_deviceTester->disconnect(); // Disconnect signals
- m_deviceTester->stop();
+ if (m_testProcess)
+ disconnect(m_testProcess.data(), 0, this, 0);
+ if (m_connection)
+ disconnect(m_connection.data(), 0, this, 0);
- SafeThreadDeleter *deleter = new SafeThreadDeleter(m_deviceTester);
- deleter->start();
-
- m_deviceTester = 0;
- m_deviceTestOutput.clear();
- m_closeButton->setText(tr("Close"));
- }
+ m_deviceTestOutput.clear();
+ m_closeButton->setText(tr("Close"));
}
-void MaemoConfigTestDialog::processSshOutput(const QString &data)
+void MaemoConfigTestDialog::processSshOutput(const QByteArray &output)
{
- m_deviceTestOutput.append(data);
+ m_deviceTestOutput.append(QString::fromUtf8(output));
}
QString MaemoConfigTestDialog::parseTestOutput()
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.h b/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.h
index 8bc2d63930..a6ec321ca2 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoconfigtestdialog.h
@@ -35,18 +35,23 @@
#ifndef MAEMOCONFIGTESTDIALOG_H
#define MAEMOCONFIGTESTDIALOG_H
-#include <QDialog>
+#include <QtCore/QSharedPointer>
+#include <QtGui/QDialog>
QT_BEGIN_NAMESPACE
class QPushButton;
class Ui_MaemoConfigTestDialog;
QT_END_NAMESPACE
+namespace Core {
+ class SshConnection;
+ class SshRemoteProcess;
+} // namespace Core
+
namespace Qt4ProjectManager {
namespace Internal {
class MaemoDeviceConfig;
-class MaemoSshRunner;
/**
* A dialog that runs a test of a device configuration.
@@ -60,8 +65,10 @@ public:
private slots:
void stopConfigTest();
- void processSshOutput(const QString &data);
- void handleTestThreadFinished();
+ void processSshOutput(const QByteArray &output);
+ void handleConnected();
+ void handleConnectionError();
+ void handleProcessFinished(int exitStatus);
private:
void startConfigTest();
@@ -71,7 +78,8 @@ private:
QPushButton *m_closeButton;
const MaemoDeviceConfig &m_config;
- MaemoSshRunner *m_deviceTester;
+ QSharedPointer<Core::SshConnection> m_connection;
+ QSharedPointer<Core::SshRemoteProcess> m_testProcess;
QString m_deviceTestOutput;
bool m_qtVersionOk;
};
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.cpp
index 203b0333a0..1c4b8fbd5b 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.cpp
@@ -42,9 +42,9 @@
#include "maemodeployables.h"
#include "maemodeployablelistmodel.h"
-#include "maemopackagecreationstep.h"
#include "maemotoolchain.h"
+#include <projectexplorer/buildstep.h>
#include <qt4projectmanager/qt4buildconfiguration.h>
#include <qt4projectmanager/qt4nodes.h>
#include <qt4projectmanager/qt4project.h>
@@ -55,8 +55,8 @@
namespace Qt4ProjectManager {
namespace Internal {
-MaemoDeployables::MaemoDeployables(MaemoPackageCreationStep *packagingStep)
- : m_packagingStep(packagingStep), m_proFilesWatcher(0)
+MaemoDeployables::MaemoDeployables(const ProjectExplorer::BuildStep *buildStep)
+ : m_buildStep(buildStep), m_proFilesWatcher(0)
{
QTimer::singleShot(0, this, SLOT(createModels()));
}
@@ -64,8 +64,8 @@ MaemoDeployables::MaemoDeployables(MaemoPackageCreationStep *packagingStep)
void MaemoDeployables::createModels()
{
m_listModels.clear();
- Qt4ProFileNode *rootNode = m_packagingStep->qt4BuildConfiguration()
- ->qt4Target()->qt4Project()->rootProjectNode();
+ Qt4ProFileNode *rootNode
+ = qt4BuildConfiguration()->qt4Target()->qt4Project()->rootProjectNode();
createModels(rootNode);
if (!m_proFilesWatcher) {
m_proFilesWatcher = new Qt4NodesWatcher(this);
@@ -84,7 +84,10 @@ void MaemoDeployables::createModels(const Qt4ProFileNode *proFileNode)
case ApplicationTemplate:
case LibraryTemplate:
case ScriptTemplate: {
- const QString qConfigFile = m_packagingStep->maemoToolChain()->sysrootRoot()
+ const MaemoToolChain * const tc
+ = dynamic_cast<MaemoToolChain *>(qt4BuildConfiguration()->toolChain());
+ Q_ASSERT(tc);
+ const QString qConfigFile = tc->sysrootRoot()
+ QLatin1String("/usr/share/qt/mkspecs/qconfig.pri");
m_listModels << new MaemoDeployableListModel(proFileNode, qConfigFile, this);
break;
@@ -151,5 +154,13 @@ QString MaemoDeployables::remoteExecutableFilePath(const QString &localExecutabl
return QString();
}
+const Qt4BuildConfiguration *MaemoDeployables::qt4BuildConfiguration() const
+{
+ const Qt4BuildConfiguration * const bc
+ = qobject_cast<Qt4BuildConfiguration *>(m_buildStep->buildConfiguration());
+ Q_ASSERT(bc);
+ return bc;
+}
+
} // namespace Qt4ProjectManager
} // namespace Internal
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.h b/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.h
index d732882f69..1e46b91d82 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeployables.h
@@ -47,10 +47,13 @@
#include <QtCore/QList>
#include <QtCore/QObject>
+namespace ProjectExplorer { class BuildStep; }
+
namespace Qt4ProjectManager {
namespace Internal {
+
class MaemoDeployableListModel;
-class MaemoPackageCreationStep;
+class Qt4BuildConfiguration;
class Qt4NodesWatcher;
class Qt4ProFileNode;
@@ -58,7 +61,7 @@ class MaemoDeployables : public QObject
{
Q_OBJECT
public:
- MaemoDeployables(MaemoPackageCreationStep *packagingStep);
+ MaemoDeployables(const ProjectExplorer::BuildStep *buildStep);
void setUnmodified();
bool isModified() const;
int deployableCount() const;
@@ -73,9 +76,10 @@ signals:
private:
Q_SLOT void createModels();
void createModels(const Qt4ProFileNode *proFileNode);
+ const Qt4BuildConfiguration *qt4BuildConfiguration() const;
QList<MaemoDeployableListModel *> m_listModels;
- MaemoPackageCreationStep * const m_packagingStep;
+ const ProjectExplorer::BuildStep * const m_buildStep;
Qt4NodesWatcher *m_proFilesWatcher;
};
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.cpp
new file mode 100644
index 0000000000..200923f9ff
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.cpp
@@ -0,0 +1,473 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "maemodeploystep.h"
+
+#include "maemoconstants.h"
+#include "maemodeployables.h"
+#include "maemodeploystepwidget.h"
+#include "maemoglobal.h"
+#include "maemopackagecreationstep.h"
+#include "maemorunconfiguration.h"
+
+#include <coreplugin/ssh/sftpchannel.h>
+#include <coreplugin/ssh/sshconnection.h>
+#include <coreplugin/ssh/sshremoteprocess.h>
+
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/target.h>
+
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QEventLoop>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTimer>
+
+using namespace Core;
+using namespace ProjectExplorer;
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+const QLatin1String MaemoDeployStep::Id("Qt4ProjectManager.MaemoDeployStep");
+
+
+MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc)
+ : BuildStep(bc, Id), m_deployables(new MaemoDeployables(this))
+{
+ ctor();
+}
+
+MaemoDeployStep::MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc,
+ MaemoDeployStep *other)
+ : BuildStep(bc, other), m_deployables(new MaemoDeployables(this)),
+ m_lastDeployed(other->m_lastDeployed)
+{
+ ctor();
+}
+
+MaemoDeployStep::~MaemoDeployStep()
+{
+ delete m_deployables;
+}
+
+void MaemoDeployStep::ctor()
+{
+}
+
+bool MaemoDeployStep::init()
+{
+ return true;
+}
+
+void MaemoDeployStep::run(QFutureInterface<bool> &fi)
+{
+ // Move to GUI thread for connection sharing with run control.
+ QTimer::singleShot(0, this, SLOT(start()));
+
+ MaemoDeployEventHandler eventHandler(this, fi);
+ connect (&eventHandler, SIGNAL(destroyed()), this, SLOT(stop()));
+}
+
+BuildStepConfigWidget *MaemoDeployStep::createConfigWidget()
+{
+ return new MaemoDeployStepWidget(this);
+}
+
+QVariantMap MaemoDeployStep::toMap() const
+{
+ QVariantMap map(BuildStep::toMap());
+ addDeployTimesToMap(map);
+ return map;
+}
+
+void MaemoDeployStep::addDeployTimesToMap(QVariantMap &map) const
+{
+ QVariantList hostList;
+ QVariantList fileList;
+ QVariantList remotePathList;
+ QVariantList timeList;
+ typedef QHash<DeployablePerHost, QDateTime>::ConstIterator DepIt;
+ for (DepIt it = m_lastDeployed.begin(); it != m_lastDeployed.end(); ++it) {
+ fileList << it.key().first.localFilePath;
+ remotePathList << it.key().first.remoteDir;
+ hostList << it.key().second;
+ timeList << it.value();
+ }
+ map.insert(LastDeployedHostsKey, hostList);
+ map.insert(LastDeployedFilesKey, fileList);
+ map.insert(LastDeployedRemotePathsKey, remotePathList);
+ map.insert(LastDeployedTimesKey, timeList);
+}
+
+bool MaemoDeployStep::fromMap(const QVariantMap &map)
+{
+ if (!BuildStep::fromMap(map))
+ return false;
+ getDeployTimesFromMap(map);
+ return true;
+}
+
+void MaemoDeployStep::getDeployTimesFromMap(const QVariantMap &map)
+{
+ const QVariantList &hostList = map.value(LastDeployedHostsKey).toList();
+ const QVariantList &fileList = map.value(LastDeployedFilesKey).toList();
+ const QVariantList &remotePathList
+ = map.value(LastDeployedRemotePathsKey).toList();
+ const QVariantList &timeList = map.value(LastDeployedTimesKey).toList();
+ const int elemCount
+ = qMin(qMin(hostList.size(), fileList.size()),
+ qMin(remotePathList.size(), timeList.size()));
+ for (int i = 0; i < elemCount; ++i) {
+ const MaemoDeployable d(fileList.at(i).toString(),
+ remotePathList.at(i).toString());
+ m_lastDeployed.insert(DeployablePerHost(d, hostList.at(i).toString()),
+ timeList.at(i).toDateTime());
+ }
+}
+
+const MaemoPackageCreationStep *MaemoDeployStep::packagingStep() const
+{
+ const MaemoPackageCreationStep * const step
+ = MaemoGlobal::buildStep<MaemoPackageCreationStep>(buildConfiguration());
+ Q_ASSERT_X(step, Q_FUNC_INFO,
+ "Impossible: Maemo build configuration without packaging step.");
+ return step;
+}
+
+void MaemoDeployStep::raiseError(const QString &errorString)
+{
+ emit addTask(Task(Task::Error, errorString, QString(), -1,
+ Constants::TASK_CATEGORY_BUILDSYSTEM));
+ stop();
+ emit error();
+}
+
+void MaemoDeployStep::writeOutput(const QString &text,
+ const QTextCharFormat &format)
+{
+ emit addOutput(text, format);
+}
+
+void MaemoDeployStep::stop()
+{
+ if (m_installer && m_installer->isRunning()) {
+ disconnect(m_installer.data(), 0, this, 0);
+ } else if (!m_uploadsInProgress.isEmpty() || !m_linksInProgress.isEmpty()) {
+ m_uploadsInProgress.clear();
+ m_linksInProgress.clear();
+ disconnect(m_uploader.data(), 0, this, 0);
+ m_uploader->closeChannel();
+ }
+ if (m_connection)
+ disconnect(m_connection.data(), 0, this, 0);
+ m_stopped = true;
+}
+
+QString MaemoDeployStep::uploadDir() const
+{
+ return MaemoGlobal::homeDirOnDevice(m_connection->connectionParameters().uname);
+}
+
+bool MaemoDeployStep::currentlyNeedsDeployment(const QString &host,
+ const MaemoDeployable &deployable) const
+{
+ const QDateTime &lastDeployed
+ = m_lastDeployed.value(DeployablePerHost(deployable, host));
+ return !lastDeployed.isValid()
+ || QFileInfo(deployable.localFilePath).lastModified() > lastDeployed;
+}
+
+void MaemoDeployStep::setDeployed(const QString &host,
+ const MaemoDeployable &deployable)
+{
+ m_lastDeployed.insert(DeployablePerHost(deployable, host),
+ QDateTime::currentDateTime());
+}
+
+MaemoDeviceConfig MaemoDeployStep::deviceConfig() const
+{
+ // TODO: For lib template, get info from config widget
+ const RunConfiguration * const rc =
+ buildConfiguration()->target()->activeRunConfiguration();
+ return rc ? qobject_cast<const MaemoRunConfiguration *>(rc)->deviceConfig()
+ : MaemoDeviceConfig();
+}
+
+void MaemoDeployStep::start()
+{
+ m_stopped = false;
+ if (m_stopped)
+ return;
+
+ // TODO: Re-use if possible (disconnect + reconnect).
+ if (m_connection)
+ disconnect(m_connection.data(), 0, this, 0);
+ m_connection = SshConnection::create();
+
+ const MaemoDeviceConfig &devConfig = deviceConfig();
+ if (!devConfig.isValid()) {
+ raiseError(tr("Deployment failed: No valid device set."));
+ return;
+ }
+ connect(m_connection.data(), SIGNAL(connected()), this,
+ SLOT(handleConnected()));
+ connect(m_connection.data(), SIGNAL(error(SshError)), this,
+ SLOT(handleConnectionFailure()));
+ m_connection->connectToHost(devConfig.server);
+}
+
+void MaemoDeployStep::handleConnected()
+{
+ if (m_stopped)
+ return;
+
+ // TODO: If nothing to deploy, skip this step.
+ m_uploader = m_connection->createSftpChannel();
+ connect(m_uploader.data(), SIGNAL(initialized()), this,
+ SLOT(handleSftpChannelInitialized()));
+ connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
+ SLOT(handleSftpChannelInitializationFailed(QString)));
+ connect(m_uploader.data(), SIGNAL(finished(Core::SftpJobId, QString)),
+ this, SLOT(handleSftpJobFinished(Core::SftpJobId, QString)));
+ m_uploader->initialize();
+}
+
+void MaemoDeployStep::handleConnectionFailure()
+{
+ if (m_stopped)
+ return;
+
+ raiseError(tr("Could not connect to host: %1")
+ .arg(m_connection->errorString()));
+}
+
+void MaemoDeployStep::handleSftpChannelInitialized()
+{
+ if (m_stopped)
+ return;
+
+ m_uploadsInProgress.clear();
+ m_linksInProgress.clear();
+ m_needsInstall = false;
+ const MaemoPackageCreationStep * const pStep = packagingStep();
+ const QString hostName = m_connection->connectionParameters().host;
+ if (pStep->isPackagingEnabled()) {
+ const MaemoDeployable d(pStep->packageFilePath(), uploadDir());
+ if (currentlyNeedsDeployment(hostName, d)) {
+ if (!deploy(MaemoDeployable(d)))
+ return;
+ m_needsInstall = true;
+ } else {
+ m_needsInstall = false;
+ }
+ } else {
+ const int deployableCount = m_deployables->deployableCount();
+ for (int i = 0; i < deployableCount; ++i) {
+ const MaemoDeployable &d = m_deployables->deployableAt(i);
+ if (currentlyNeedsDeployment(hostName, d)
+ && !deploy(MaemoDeployable(d)))
+ return;
+ }
+ m_needsInstall = false;
+ }
+ if (m_uploadsInProgress.isEmpty())
+ emit done();
+}
+
+bool MaemoDeployStep::deploy(const MaemoDeployable &deployable)
+{
+ const QString fileName = QFileInfo(deployable.localFilePath).fileName();
+ const QString remoteFilePath = deployable.remoteDir + '/' + fileName;
+ const QString uploadFilePath = uploadDir() + '/' + fileName + '.'
+ + QCryptographicHash::hash(remoteFilePath.toUtf8(),
+ QCryptographicHash::Md5).toHex();
+ const SftpJobId job = m_uploader->uploadFile(deployable.localFilePath,
+ uploadFilePath, SftpOverwriteExisting);
+ if (job == SftpInvalidJob) {
+ raiseError(tr("Upload failed: Could not open file '%1'")
+ .arg(deployable.localFilePath));
+ return false;
+ }
+ writeOutput(tr("Started uploading file '%1'.").arg(deployable.localFilePath));
+ m_uploadsInProgress.insert(job, DeployInfo(deployable, uploadFilePath));
+ return true;
+}
+
+void MaemoDeployStep::handleSftpChannelInitializationFailed(const QString &error)
+{
+ if (m_stopped)
+ return;
+ raiseError(tr("Could not set up SFTP connection: %1").arg(error));
+}
+
+void MaemoDeployStep::handleSftpJobFinished(Core::SftpJobId job,
+ const QString &error)
+{
+ if (m_stopped)
+ return;
+
+ QMap<SftpJobId, DeployInfo>::Iterator it = m_uploadsInProgress.find(job);
+ if (it == m_uploadsInProgress.end()) {
+ qWarning("%s: Job %u not found in map.", Q_FUNC_INFO, job);
+ return;
+ }
+
+ const DeployInfo &deployInfo = it.value();
+ if (!error.isEmpty()) {
+ raiseError(tr("Failed to upload file %1: %2")
+ .arg(deployInfo.first.localFilePath, error));
+ return;
+ }
+
+ writeOutput(tr("Successfully uploaded file '%1'.")
+ .arg(deployInfo.first.localFilePath));
+ const QString remoteFilePath = deployInfo.first.remoteDir + '/'
+ + QFileInfo(deployInfo.first.localFilePath).fileName();
+ QByteArray linkCommand = MaemoGlobal::remoteSudo().toUtf8() + " ln -sf "
+ + deployInfo.second.toUtf8() + ' ' + remoteFilePath.toUtf8();
+ SshRemoteProcess::Ptr linkProcess
+ = m_connection->createRemoteProcess(linkCommand);
+ connect(linkProcess.data(), SIGNAL(closed(int)), this,
+ SLOT(handleLinkProcessFinished(int)));
+ m_linksInProgress.insert(linkProcess, deployInfo.first);
+ linkProcess->start();
+ m_uploadsInProgress.erase(it);
+}
+
+void MaemoDeployStep::handleLinkProcessFinished(int exitStatus)
+{
+ if (m_stopped)
+ return;
+
+ SshRemoteProcess * const proc = static_cast<SshRemoteProcess *>(sender());
+
+ // TODO: List instead of map? We can't use it for lookup anyway.
+ QMap<SshRemoteProcess::Ptr, MaemoDeployable>::Iterator it;
+ for (it = m_linksInProgress.begin(); it != m_linksInProgress.end(); ++it) {
+ if (it.key().data() == proc)
+ break;
+ }
+ if (it == m_linksInProgress.end()) {
+ qWarning("%s: Remote process %p not found in process list.",
+ Q_FUNC_INFO, proc);
+ return;
+ }
+
+ const MaemoDeployable &deployable = it.value();
+ if (exitStatus != SshRemoteProcess::ExitedNormally
+ || proc->exitCode() != 0) {
+ raiseError(tr("Deployment failed for file '%1': "
+ "Could not create link '%2' on remote system.")
+ .arg(deployable.localFilePath, deployable.remoteDir + '/'
+ + QFileInfo(deployable.localFilePath).fileName()));
+ return;
+ }
+
+ setDeployed(m_connection->connectionParameters().host, it.value());
+ m_linksInProgress.erase(it);
+ if (m_linksInProgress.isEmpty() && m_uploadsInProgress.isEmpty()) {
+ if (m_needsInstall) {
+ writeOutput(tr("Installing package ..."));
+ const QString packageFileName
+ = QFileInfo(packagingStep()->packageFilePath()).fileName();
+ const QByteArray cmd = MaemoGlobal::remoteSudo().toUtf8()
+ + " dpkg -i " + packageFileName.toUtf8();
+ m_installer = m_connection->createRemoteProcess(cmd);
+ connect(m_installer.data(), SIGNAL(closed(int)), this,
+ SLOT(handleInstallationFinished(int)));
+ connect(m_installer.data(), SIGNAL(outputAvailable(QByteArray)),
+ this, SLOT(handleInstallerOutput(QByteArray)));
+ connect(m_installer.data(),
+ SIGNAL(errorOutputAvailable(QByteArray)), this,
+ SLOT(handleInstallerErrorOutput(QByteArray)));
+ m_installer->start();
+ } else {
+ emit done();
+ }
+ }
+}
+
+void MaemoDeployStep::handleInstallationFinished(int exitStatus)
+{
+ if (m_stopped)
+ return;
+
+ if (exitStatus != SshRemoteProcess::ExitedNormally
+ || m_installer->exitCode() != 0) {
+ raiseError(tr("Installing package failed."));
+ } else {
+ writeOutput(tr("Package installation finished."));
+ emit done();
+ }
+}
+
+void MaemoDeployStep::handleInstallerOutput(const QByteArray &output)
+{
+ writeOutput(QString::fromUtf8(output));
+}
+
+void MaemoDeployStep::handleInstallerErrorOutput(const QByteArray &output)
+{
+ QTextCharFormat format;
+ format.setForeground(QBrush(QColor("red")));
+ writeOutput(output, format);
+}
+
+
+MaemoDeployEventHandler::MaemoDeployEventHandler(MaemoDeployStep *deployStep,
+ QFutureInterface<bool> &future)
+ : m_deployStep(deployStep), m_future(future), m_eventLoop(new QEventLoop)
+{
+ connect(m_deployStep, SIGNAL(done()), this, SLOT(handleDeployingDone()));
+ connect(m_deployStep, SIGNAL(error()), this, SLOT(handleDeployingFailed()));
+ QTimer cancelChecker;
+ connect(&cancelChecker, SIGNAL(timeout()), this, SLOT(checkForCanceled()));
+ cancelChecker.start(500);
+ future.reportResult(m_eventLoop->exec() == 0);
+}
+
+void MaemoDeployEventHandler::handleDeployingDone()
+{
+ m_eventLoop->exit(0);
+}
+
+void MaemoDeployEventHandler::handleDeployingFailed()
+{
+ m_eventLoop->exit(1);
+}
+
+void MaemoDeployEventHandler::checkForCanceled()
+{
+ if (m_future.isCanceled())
+ handleDeployingFailed();
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.h b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.h
new file mode 100644
index 0000000000..2363e1ea85
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystep.h
@@ -0,0 +1,146 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef MAEMODEPLOYSTEP_H
+#define MAEMODEPLOYSTEP_H
+
+#include "maemodeployable.h"
+#include "maemodeviceconfigurations.h"
+
+#include <coreplugin/ssh/sftpdefs.h>
+#include <projectexplorer/buildstep.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QList>
+#include <QtCore/QMap>
+#include <QtCore/QPair>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QEventLoop;
+QT_END_NAMESPACE
+
+namespace Core {
+class SftpChannel;
+class SshConnection;
+class SshRemoteProcess;
+}
+
+namespace Qt4ProjectManager {
+namespace Internal {
+class MaemoDeployables;
+class MaemoPackageCreationStep;
+
+class MaemoDeployStep : public ProjectExplorer::BuildStep
+{
+ Q_OBJECT
+ friend class MaemoDeployStepFactory;
+public:
+ MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc);
+ virtual ~MaemoDeployStep();
+ MaemoDeviceConfig deviceConfig() const;
+ bool currentlyNeedsDeployment(const QString &host,
+ const MaemoDeployable &deployable) const;
+ void setDeployed(const QString &host, const MaemoDeployable &deployable);
+ MaemoDeployables *deployables() const { return m_deployables; }
+
+signals:
+ void done();
+ void error();
+
+private slots:
+ void start();
+ void stop();
+ void handleConnected();
+ void handleConnectionFailure();
+ void handleSftpChannelInitialized();
+ void handleSftpChannelInitializationFailed(const QString &error);
+ void handleSftpJobFinished(Core::SftpJobId job, const QString &error);
+ void handleLinkProcessFinished(int exitStatus);
+ void handleInstallationFinished(int exitStatus);
+ void handleInstallerOutput(const QByteArray &output);
+ void handleInstallerErrorOutput(const QByteArray &output);
+
+private:
+ MaemoDeployStep(ProjectExplorer::BuildConfiguration *bc,
+ MaemoDeployStep *other);
+ virtual bool init();
+ virtual void run(QFutureInterface<bool> &fi);
+ virtual ProjectExplorer::BuildStepConfigWidget *createConfigWidget();
+ virtual bool immutable() const { return true; }
+ virtual QVariantMap toMap() const;
+ virtual bool fromMap(const QVariantMap &map);
+
+ void ctor();
+ void raiseError(const QString &error);
+ void writeOutput(const QString &text,
+ const QTextCharFormat &format = QTextCharFormat());
+ void addDeployTimesToMap(QVariantMap &map) const;
+ void getDeployTimesFromMap(const QVariantMap &map);
+ const MaemoPackageCreationStep *packagingStep() const;
+ bool deploy(const MaemoDeployable &deployable);
+ QString uploadDir() const;
+
+ static const QLatin1String Id;
+
+ MaemoDeployables * const m_deployables;
+ QSharedPointer<Core::SshConnection> m_connection;
+ QSharedPointer<Core::SftpChannel> m_uploader;
+ QSharedPointer<Core::SshRemoteProcess> m_installer;
+ typedef QPair<MaemoDeployable, QString> DeployInfo;
+ QMap<Core::SftpJobId, DeployInfo> m_uploadsInProgress;
+ QMap<QSharedPointer<Core::SshRemoteProcess>, MaemoDeployable> m_linksInProgress;
+ bool m_needsInstall;
+ bool m_stopped;
+ typedef QPair<MaemoDeployable, QString> DeployablePerHost;
+ QHash<DeployablePerHost, QDateTime> m_lastDeployed;
+};
+
+class MaemoDeployEventHandler : public QObject
+{
+ Q_OBJECT
+public:
+ MaemoDeployEventHandler(MaemoDeployStep *deployStep,
+ QFutureInterface<bool> &future);
+
+private slots:
+ void handleDeployingDone();
+ void handleDeployingFailed();
+ void checkForCanceled();
+
+private:
+ const MaemoDeployStep * const m_deployStep;
+ const QFutureInterface<bool> m_future;
+ QEventLoop * const m_eventLoop;
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // MAEMODEPLOYSTEP_H
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.cpp
new file mode 100644
index 0000000000..2d1e712e1a
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.cpp
@@ -0,0 +1,82 @@
+#include "maemodeploystepfactory.h"
+
+#include "maemodeploystep.h"
+
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
+#include <qt4projectmanager/qt4projectmanagerconstants.h>
+
+using namespace ProjectExplorer;
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+MaemoDeployStepFactory::MaemoDeployStepFactory(QObject *parent)
+ : IBuildStepFactory(parent)
+{
+}
+
+QStringList MaemoDeployStepFactory::availableCreationIds(BuildConfiguration *,
+ BuildStep::Type) const
+{
+ return QStringList();
+}
+
+QString MaemoDeployStepFactory::displayNameForId(const QString &) const
+{
+ return QString();
+}
+
+bool MaemoDeployStepFactory::canCreate(BuildConfiguration *,
+ BuildStep::Type, const QString &) const
+{
+ return false;
+}
+
+BuildStep *MaemoDeployStepFactory::create(BuildConfiguration *,
+ BuildStep::Type, const QString &)
+{
+ Q_ASSERT(false);
+ return 0;
+}
+
+bool MaemoDeployStepFactory::canRestore(BuildConfiguration *parent,
+ BuildStep::Type type, const QVariantMap &map) const
+{
+ return canCreateInternally(parent, type, idFromMap(map));
+}
+
+BuildStep *MaemoDeployStepFactory::restore(BuildConfiguration *parent,
+ BuildStep::Type type, const QVariantMap &map)
+{
+ Q_ASSERT(canRestore(parent, type, map));
+ MaemoDeployStep * const step = new MaemoDeployStep(parent);
+ if (!step->fromMap(map)) {
+ delete step;
+ return 0;
+ }
+ return step;
+}
+
+bool MaemoDeployStepFactory::canClone(BuildConfiguration *parent,
+ BuildStep::Type type, BuildStep *product) const
+{
+ return canCreateInternally(parent, type, product->id());
+}
+
+BuildStep *MaemoDeployStepFactory::clone(BuildConfiguration *parent,
+ BuildStep::Type type, BuildStep *product)
+{
+ Q_ASSERT(canClone(parent, type, product));
+ return new MaemoDeployStep(parent, static_cast<MaemoDeployStep*>(product));
+}
+
+bool MaemoDeployStepFactory::canCreateInternally(BuildConfiguration *parent,
+ BuildStep::Type type, const QString &id) const
+{
+ return type == BuildStep::Deploy && id == MaemoDeployStep::Id
+ && parent->target()->id() == Constants::MAEMO_DEVICE_TARGET_ID;
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.h b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.h
new file mode 100644
index 0000000000..0516578673
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepfactory.h
@@ -0,0 +1,49 @@
+#ifndef MAEMODEPLOYSTEPFACTORY_H
+#define MAEMODEPLOYSTEPFACTORY_H
+
+#include <projectexplorer/buildstep.h>
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+class MaemoDeployStepFactory : public ProjectExplorer::IBuildStepFactory
+{
+public:
+ MaemoDeployStepFactory(QObject *parent);
+
+ virtual QStringList availableCreationIds(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type) const;
+ virtual QString displayNameForId(const QString &id) const;
+
+ virtual bool canCreate(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type,
+ const QString &id) const;
+ virtual ProjectExplorer::BuildStep *
+ create(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type, const QString &id);
+
+ virtual bool canRestore(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type,
+ const QVariantMap &map) const;
+ virtual ProjectExplorer::BuildStep *
+ restore(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type, const QVariantMap &map);
+
+ virtual bool canClone(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type,
+ ProjectExplorer::BuildStep *product) const;
+ virtual ProjectExplorer::BuildStep *
+ clone(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type,
+ ProjectExplorer::BuildStep *product);
+
+private:
+ bool canCreateInternally(ProjectExplorer::BuildConfiguration *parent,
+ ProjectExplorer::BuildStep::Type type,
+ const QString &id) const;
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // MAEMODEPLOYSTEPFACTORY_H
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.cpp
new file mode 100644
index 0000000000..33c61dc9de
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.cpp
@@ -0,0 +1,72 @@
+#include "maemodeploystepwidget.h"
+#include "ui_maemodeploystepwidget.h"
+
+#include "maemodeploystep.h"
+#include "maemodeployablelistmodel.h"
+#include "maemodeployablelistwidget.h"
+#include "maemodeployables.h"
+#include "maemorunconfiguration.h"
+
+#include <projectexplorer/buildconfiguration.h>
+#include <projectexplorer/target.h>
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+MaemoDeployStepWidget::MaemoDeployStepWidget(MaemoDeployStep *step) :
+ ProjectExplorer::BuildStepConfigWidget(),
+ ui(new Ui::MaemoDeployStepWidget),
+ m_step(step)
+{
+ ui->setupUi(this);
+
+ connect(m_step->deployables(), SIGNAL(modelsCreated()), this,
+ SLOT(handleModelsCreated()));
+ handleModelsCreated();
+}
+
+MaemoDeployStepWidget::~MaemoDeployStepWidget()
+{
+ delete ui;
+}
+
+void MaemoDeployStepWidget::init()
+{
+ const ProjectExplorer::RunConfiguration * const rc =
+ m_step->buildConfiguration()->target()->activeRunConfiguration();
+ if (rc) {
+ connect(qobject_cast<const MaemoRunConfiguration *>(rc),
+ SIGNAL(deviceConfigurationChanged(ProjectExplorer::Target *)),
+ this, SLOT(handleDeviceUpdate()));
+ }
+}
+
+void MaemoDeployStepWidget::handleDeviceUpdate()
+{
+ emit updateSummary();
+}
+
+QString MaemoDeployStepWidget::summaryText() const
+{
+ return tr("<b>Deploy to device</b>: ") + m_step->deviceConfig().name;
+}
+
+QString MaemoDeployStepWidget::displayName() const
+{
+ return QString();
+}
+
+
+void MaemoDeployStepWidget::handleModelsCreated()
+{
+ ui->tabWidget->clear();
+ for (int i = 0; i < m_step->deployables()->modelCount(); ++i) {
+ MaemoDeployableListModel * const model
+ = m_step->deployables()->modelAt(i);
+ ui->tabWidget->addTab(new MaemoDeployableListWidget(this, model),
+ model->projectName());
+ }
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.h b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.h
new file mode 100644
index 0000000000..bfa631fda5
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.h
@@ -0,0 +1,39 @@
+#ifndef MAEMODEPLOYSTEPWIDGET_H
+#define MAEMODEPLOYSTEPWIDGET_H
+
+#include <projectexplorer/buildstep.h>
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+ class MaemoDeployStepWidget;
+}
+QT_END_NAMESPACE
+
+namespace Qt4ProjectManager {
+namespace Internal {
+class MaemoDeployStep;
+
+class MaemoDeployStepWidget : public ProjectExplorer::BuildStepConfigWidget
+{
+ Q_OBJECT
+
+public:
+ MaemoDeployStepWidget(MaemoDeployStep *step);
+ ~MaemoDeployStepWidget();
+
+private:
+ Q_SLOT void handleModelsCreated();
+ virtual void init();
+ virtual QString summaryText() const;
+ virtual QString displayName() const;
+
+ Q_SLOT void handleDeviceUpdate();
+
+ Ui::MaemoDeployStepWidget *ui;
+ MaemoDeployStep * const m_step;
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // MAEMODEPLOYSTEPWIDGET_H
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.ui b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.ui
new file mode 100644
index 0000000000..b0cc2d6408
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeploystepwidget.ui
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MaemoDeployStepWidget</class>
+ <widget class="QWidget" name="MaemoDeployStepWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="toolTip">
+ <string>These show the INSTALLS settings from the project file(s).</string>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Files to install:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.cpp
index 7ff2747ddf..0749e00e1c 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.cpp
@@ -36,24 +36,18 @@
#include <coreplugin/icore.h>
+#include <QtCore/QCoreApplication>
#include <QtCore/QSettings>
#include <QtCore/QStringBuilder>
#include <QtGui/QDesktopServices>
#include <algorithm>
-typedef Core::SshServerInfo::AuthType AuthType;
+typedef Core::SshConnectionParameters::AuthType AuthType;
namespace Qt4ProjectManager {
namespace Internal {
-QString homeDirOnDevice(const QString &uname)
-{
- return uname == QLatin1String("root")
- ? QString::fromLatin1("/root")
- : QLatin1String("/home/") + uname;
-}
-
namespace {
const QLatin1String SettingsGroup("MaemoDeviceConfigs");
const QLatin1String IdCounterKey("IdCounter");
@@ -80,7 +74,7 @@ namespace {
const QString DefaultHostNameHW(QLatin1String("192.168.2.15"));
const QString DefaultHostNameSim(QLatin1String("localhost"));
const QString DefaultUserName(QLatin1String("developer"));
- const AuthType DefaultAuthType(Core::SshServerInfo::AuthByKey);
+ const AuthType DefaultAuthType(Core::SshConnectionParameters::AuthByKey);
const int DefaultTimeout(30);
const MaemoDeviceConfig::DeviceType DefaultDeviceType(MaemoDeviceConfig::Physical);
};
@@ -132,7 +126,8 @@ MaemoDeviceConfig::MaemoDeviceConfig(const QSettings &settings,
}
MaemoDeviceConfig::MaemoDeviceConfig()
- : internalId(InvalidId)
+ : name(QCoreApplication::translate("MaemoDeviceConfig", "(Invalid device)")),
+ internalId(InvalidId)
{
}
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.h b/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.h
index 7ffa28251e..463911f817 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemodeviceconfigurations.h
@@ -48,8 +48,6 @@ QT_END_NAMESPACE
namespace Qt4ProjectManager {
namespace Internal {
-QString homeDirOnDevice(const QString &uname);
-
class MaemoDeviceConfig
{
public:
@@ -60,7 +58,7 @@ public:
void save(QSettings &settings) const;
bool isValid() const;
- Core::SshServerInfo server;
+ Core::SshConnectionParameters server;
QString name;
DeviceType type;
int gdbServerPort;
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.cpp
new file mode 100644
index 0000000000..9a797fd7ad
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.cpp
@@ -0,0 +1,50 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#include "maemoglobal.h"
+
+#include <QtCore/QString>
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+QString MaemoGlobal::homeDirOnDevice(const QString &uname)
+{
+ return uname == QLatin1String("root")
+ ? QString::fromLatin1("/root")
+ : QLatin1String("/home/") + uname;
+}
+
+QString MaemoGlobal::remoteSudo()
+{
+ return QLatin1String("/usr/lib/mad-developer/devrootsh");
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.h b/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.h
new file mode 100644
index 0000000000..7a8b2ff8df
--- /dev/null
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoglobal.h
@@ -0,0 +1,65 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** Commercial Usage
+**
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://qt.nokia.com/contact.
+**
+**************************************************************************/
+
+#ifndef MAEMOGLOBAL_H
+#define MAEMOGLOBAL_H
+
+#include <projectexplorer/buildconfiguration.h>
+
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace Qt4ProjectManager {
+namespace Internal {
+
+class MaemoGlobal
+{
+public:
+ static QString homeDirOnDevice(const QString &uname);
+ static QString remoteSudo();
+
+ template<class T> static T *buildStep(const ProjectExplorer::BuildConfiguration *bc)
+ {
+ const QList<ProjectExplorer::BuildStep *> &buildSteps
+ = bc->steps(ProjectExplorer::BuildStep::Deploy);
+ for (int i = buildSteps.count() - 1; i >= 0; --i) {
+ if (T * const step = qobject_cast<T *>(buildSteps.at(i)))
+ return step;
+ }
+ return 0;
+ }
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // MAEMOGLOBAL_H
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemomanager.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemomanager.cpp
index 60f6a80fb5..eb36e1f79f 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemomanager.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemomanager.cpp
@@ -30,6 +30,7 @@
#include "maemomanager.h"
#include "maemoconstants.h"
+#include "maemodeploystepfactory.h"
#include "maemodeviceconfigurations.h"
#include "maemopackagecreationfactory.h"
#include "maemorunfactories.h"
@@ -57,6 +58,7 @@ MaemoManager::MaemoManager()
, m_runControlFactory(new MaemoRunControlFactory(this))
, m_runConfigurationFactory(new MaemoRunConfigurationFactory(this))
, m_packageCreationFactory(new MaemoPackageCreationFactory(this))
+ , m_deployStepFactory(new MaemoDeployStepFactory(this))
, m_settingsPage(new MaemoSettingsPage(this))
{
Q_ASSERT(!m_instance);
@@ -69,6 +71,7 @@ MaemoManager::MaemoManager()
pluginManager->addObject(m_runControlFactory);
pluginManager->addObject(m_runConfigurationFactory);
pluginManager->addObject(m_packageCreationFactory);
+ pluginManager->addObject(m_deployStepFactory);
pluginManager->addObject(m_settingsPage);
}
@@ -77,6 +80,7 @@ MaemoManager::~MaemoManager()
PluginManager *pluginManager = PluginManager::instance();
pluginManager->removeObject(m_runControlFactory);
pluginManager->removeObject(m_runConfigurationFactory);
+ pluginManager->removeObject(m_deployStepFactory);
pluginManager->removeObject(m_packageCreationFactory);
pluginManager->removeObject(m_settingsPage);
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemomanager.h b/src/plugins/qt4projectmanager/qt-maemo/maemomanager.h
index 689fbfa27f..865098d0f3 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemomanager.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemomanager.h
@@ -41,6 +41,7 @@ namespace Qt4ProjectManager {
class QtVersion;
namespace Internal {
+class MaemoDeployStepFactory;
class MaemoPackageCreationFactory;
class MaemoRunControlFactory;
class MaemoRunConfigurationFactory;
@@ -67,6 +68,7 @@ private:
MaemoRunControlFactory *m_runControlFactory;
MaemoRunConfigurationFactory *m_runConfigurationFactory;
MaemoPackageCreationFactory *m_packageCreationFactory;
+ MaemoDeployStepFactory *m_deployStepFactory;
MaemoSettingsPage *m_settingsPage;
QemuRuntimeManager *m_qemuRuntimeManager;
};
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp
index 76ac7327a3..3d8b061557 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.cpp
@@ -42,8 +42,10 @@
#include "maemopackagecreationstep.h"
#include "maemoconstants.h"
+#include "maemoglobal.h"
#include "maemopackagecreationwidget.h"
#include "maemodeployables.h"
+#include "maemodeploystep.h"
#include "maemotoolchain.h"
#include "profilewrapper.h"
@@ -73,7 +75,6 @@ namespace Internal {
MaemoPackageCreationStep::MaemoPackageCreationStep(BuildConfiguration *buildConfig)
: ProjectExplorer::BuildStep(buildConfig, CreatePackageId),
- m_deployables(new MaemoDeployables(this)),
m_packagingEnabled(true),
m_versionString(DefaultVersionNumber)
{
@@ -82,7 +83,6 @@ MaemoPackageCreationStep::MaemoPackageCreationStep(BuildConfiguration *buildConf
MaemoPackageCreationStep::MaemoPackageCreationStep(BuildConfiguration *buildConfig,
MaemoPackageCreationStep *other)
: BuildStep(buildConfig, other),
- m_deployables(new MaemoDeployables(this)),
m_packagingEnabled(other->m_packagingEnabled),
m_versionString(other->m_versionString)
{
@@ -90,7 +90,6 @@ MaemoPackageCreationStep::MaemoPackageCreationStep(BuildConfiguration *buildConf
MaemoPackageCreationStep::~MaemoPackageCreationStep()
{
- delete m_deployables;
}
bool MaemoPackageCreationStep::init()
@@ -245,7 +244,7 @@ bool MaemoPackageCreationStep::createPackage()
}
emit addOutput(tr("Package created."), textCharFormat);
- m_deployables->setUnmodified();
+ deployStep()->deployables()->setUnmodified();
return true;
}
@@ -313,6 +312,15 @@ const MaemoToolChain *MaemoPackageCreationStep::maemoToolChain() const
return static_cast<MaemoToolChain *>(qt4BuildConfiguration()->toolChain());
}
+MaemoDeployStep *MaemoPackageCreationStep::deployStep() const
+{
+ MaemoDeployStep * const deployStep
+ = MaemoGlobal::buildStep<MaemoDeployStep>(buildConfiguration());
+ Q_ASSERT(deployStep &&
+ "Fatal error: Maemo build configuration without deploy step.");
+ return deployStep;
+}
+
QString MaemoPackageCreationStep::maddeRoot() const
{
return maemoToolChain()->maddeRoot();
@@ -325,14 +333,15 @@ QString MaemoPackageCreationStep::targetRoot() const
bool MaemoPackageCreationStep::packagingNeeded() const
{
+ const MaemoDeployables * const deployables = deployStep()->deployables();
QFileInfo packageInfo(packageFilePath());
- if (!packageInfo.exists() || m_deployables->isModified())
+ if (!packageInfo.exists() || deployables->isModified())
return true;
- const int deployableCount = m_deployables->deployableCount();
+ const int deployableCount = deployables->deployableCount();
for (int i = 0; i < deployableCount; ++i) {
if (packageInfo.lastModified()
- <= QFileInfo(m_deployables->deployableAt(i).localFilePath)
+ <= QFileInfo(deployables->deployableAt(i).localFilePath)
.lastModified())
return true;
}
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h
index 6b6cbae768..9128b45947 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationstep.h
@@ -45,7 +45,6 @@
#include <projectexplorer/buildstep.h>
#include <QtCore/QScopedPointer>
-#include <QtCore/QSharedPointer>
QT_BEGIN_NAMESPACE
class QFile;
@@ -55,7 +54,7 @@ QT_END_NAMESPACE
namespace Qt4ProjectManager {
namespace Internal {
-class MaemoDeployables;
+class MaemoDeployStep;
class MaemoToolChain;
class ProFileWrapper;
class Qt4BuildConfiguration;
@@ -69,9 +68,6 @@ public:
~MaemoPackageCreationStep();
QString packageFilePath() const;
- MaemoDeployables *deployables() const { return m_deployables; }
- const Qt4BuildConfiguration *qt4BuildConfiguration() const;
- const MaemoToolChain *maemoToolChain() const;
bool isPackagingEnabled() const { return m_packagingEnabled; }
void setPackagingEnabled(bool enabled) { m_packagingEnabled = enabled; }
@@ -103,10 +99,12 @@ private:
const QString &detailedMsg = QString());
QString buildDirectory() const;
QString projectName() const;
+ const Qt4BuildConfiguration *qt4BuildConfiguration() const;
+ const MaemoToolChain *maemoToolChain() const;
+ MaemoDeployStep * deployStep() const;
static const QLatin1String CreatePackageId;
- MaemoDeployables * const m_deployables;
bool m_packagingEnabled;
QString m_versionString;
QScopedPointer<QProcess> m_buildProc;
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp
index 6c0c5cf13f..fae213969f 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.cpp
@@ -42,11 +42,7 @@
#include "maemopackagecreationwidget.h"
#include "ui_maemopackagecreationwidget.h"
-#include "maemodeployablelistmodel.h"
-#include "maemodeployablelistwidget.h"
-#include "maemodeployables.h"
#include "maemopackagecreationstep.h"
-#include "maemotoolchain.h"
#include <utils/qtcassert.h>
#include <projectexplorer/project.h>
@@ -70,10 +66,6 @@ MaemoPackageCreationWidget::MaemoPackageCreationWidget(MaemoPackageCreationStep
m_ui->minor->setValue(list.value(1, QLatin1String("0")).toInt());
m_ui->patch->setValue(list.value(2, QLatin1String("0")).toInt());
versionInfoChanged();
-
- connect(m_step->deployables(), SIGNAL(modelsCreated()), this,
- SLOT(handleModelsCreated()));
- handleModelsCreated();
}
void MaemoPackageCreationWidget::init()
@@ -92,6 +84,9 @@ QString MaemoPackageCreationWidget::displayName() const
void MaemoPackageCreationWidget::handleSkipButtonToggled(bool checked)
{
+ m_ui->major->setEnabled(!checked);
+ m_ui->minor->setEnabled(!checked);
+ m_ui->patch->setEnabled(!checked);
m_step->setPackagingEnabled(!checked);
}
@@ -102,16 +97,5 @@ void MaemoPackageCreationWidget::versionInfoChanged()
emit updateSummary();
}
-void MaemoPackageCreationWidget::handleModelsCreated()
-{
- m_ui->tabWidget->clear();
- for (int i = 0; i < m_step->deployables()->modelCount(); ++i) {
- MaemoDeployableListModel * const model
- = m_step->deployables()->modelAt(i);
- m_ui->tabWidget->addTab(new MaemoDeployableListWidget(this, model),
- model->projectName());
- }
-}
-
} // namespace Internal
} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.h b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.h
index 560a9ac1ee..6f01bdeaa7 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.h
@@ -66,7 +66,6 @@ public:
private slots:
void handleSkipButtonToggled(bool checked);
void versionInfoChanged();
- void handleModelsCreated();
private:
MaemoPackageCreationStep * const m_step;
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.ui b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.ui
index 8e3b923136..f415839e03 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.ui
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemopackagecreationwidget.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>478</width>
- <height>335</height>
+ <width>453</width>
+ <height>116</height>
</rect>
</property>
<property name="sizePolicy">
@@ -178,24 +178,17 @@
</layout>
</item>
<item>
- <widget class="QLabel" name="contentsLabel">
- <property name="font">
- <font>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Files to deploy:</string>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
</property>
- </widget>
- </item>
- <item>
- <widget class="QTabWidget" name="tabWidget">
- <property name="currentIndex">
- <number>-1</number>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
</property>
- </widget>
+ </spacer>
</item>
</layout>
</widget>
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.cpp
index 1aaf817a1f..6b09682b13 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.cpp
@@ -29,10 +29,13 @@
#include "maemorunconfiguration.h"
+#include "maemodeploystep.h"
+#include "maemoglobal.h"
#include "maemopackagecreationstep.h"
#include "maemorunconfigurationwidget.h"
#include "maemotoolchain.h"
#include "qemuruntimemanager.h"
+#include "qtoutputformatter.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -48,7 +51,7 @@
#include <QtCore/QStringBuilder>
namespace Qt4ProjectManager {
- namespace Internal {
+namespace Internal {
using namespace ProjectExplorer;
@@ -67,7 +70,6 @@ MaemoRunConfiguration::MaemoRunConfiguration(Qt4Target *parent,
, m_gdbPath(source->m_gdbPath)
, m_devConfig(source->m_devConfig)
, m_arguments(source->m_arguments)
- , m_lastDeployed(source->m_lastDeployed)
{
init();
}
@@ -112,6 +114,11 @@ QWidget *MaemoRunConfiguration::createConfigurationWidget()
return new MaemoRunConfigurationWidget(this);
}
+ProjectExplorer::OutputFormatter *MaemoRunConfiguration::createConfigurationWidget() const
+{
+ return new QtOutputFormatter(qt4Target()->qt4Project());
+}
+
void MaemoRunConfiguration::proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro)
{
if (m_proFilePath == pro->path())
@@ -123,32 +130,11 @@ QVariantMap MaemoRunConfiguration::toMap() const
QVariantMap map(RunConfiguration::toMap());
map.insert(DeviceIdKey, m_devConfig.internalId);
map.insert(ArgumentsKey, m_arguments);
- addDeployTimesToMap(map);
const QDir dir = QDir(target()->project()->projectDirectory());
map.insert(ProFileKey, dir.relativeFilePath(m_proFilePath));
-
return map;
}
-void MaemoRunConfiguration::addDeployTimesToMap(QVariantMap &map) const
-{
- QVariantList hostList;
- QVariantList fileList;
- QVariantList remotePathList;
- QVariantList timeList;
- typedef QHash<DeployablePerHost, QDateTime>::ConstIterator DepIt;
- for (DepIt it = m_lastDeployed.begin(); it != m_lastDeployed.end(); ++it) {
- hostList << it.key().first.localFilePath;
- remotePathList << it.key().first.remoteDir;
- fileList << it.key().second;
- timeList << it.value();
- }
- map.insert(LastDeployedHostsKey, hostList);
- map.insert(LastDeployedFilesKey, fileList);
- map.insert(LastDeployedRemotePathsKey, remotePathList);
- map.insert(LastDeployedTimesKey, timeList);
-}
-
bool MaemoRunConfiguration::fromMap(const QVariantMap &map)
{
if (!RunConfiguration::fromMap(map))
@@ -157,47 +143,12 @@ bool MaemoRunConfiguration::fromMap(const QVariantMap &map)
setDeviceConfig(MaemoDeviceConfigurations::instance().
find(map.value(DeviceIdKey, 0).toInt()));
m_arguments = map.value(ArgumentsKey).toStringList();
- getDeployTimesFromMap(map);
const QDir dir = QDir(target()->project()->projectDirectory());
m_proFilePath = dir.filePath(map.value(ProFileKey).toString());
return true;
}
-void MaemoRunConfiguration::getDeployTimesFromMap(const QVariantMap &map)
-{
- const QVariantList &hostList = map.value(LastDeployedHostsKey).toList();
- const QVariantList &fileList = map.value(LastDeployedFilesKey).toList();
- const QVariantList &remotePathList
- = map.value(LastDeployedRemotePathsKey).toList();
- const QVariantList &timeList = map.value(LastDeployedTimesKey).toList();
- const int elemCount
- = qMin(qMin(hostList.size(), fileList.size()),
- qMin(remotePathList.size(), timeList.size()));
- for (int i = 0; i < elemCount; ++i) {
- const MaemoDeployable d(fileList.at(i).toString(),
- remotePathList.at(i).toString());
- m_lastDeployed.insert(DeployablePerHost(d, hostList.at(i).toString()),
- timeList.at(i).toDateTime());
- }
-}
-
-bool MaemoRunConfiguration::currentlyNeedsDeployment(const QString &host,
- const MaemoDeployable &deployable) const
-{
- const QDateTime &lastDeployed
- = m_lastDeployed.value(DeployablePerHost(deployable, host));
- return !lastDeployed.isValid()
- || QFileInfo(deployable.localFilePath).lastModified() > lastDeployed;
-}
-
-void MaemoRunConfiguration::setDeployed(const QString &host,
- const MaemoDeployable &deployable)
-{
- m_lastDeployed.insert(DeployablePerHost(deployable, host),
- QDateTime::currentDateTime());
-}
-
void MaemoRunConfiguration::setDeviceConfig(const MaemoDeviceConfig &devConf)
{
m_devConfig = devConf;
@@ -225,18 +176,13 @@ const QString MaemoRunConfiguration::gdbCmd() const
return QString();
}
-const MaemoPackageCreationStep *MaemoRunConfiguration::packageStep() const
+MaemoDeployStep *MaemoRunConfiguration::deployStep() const
{
- const QList<ProjectExplorer::BuildStep *> &buildSteps
- = activeQt4BuildConfiguration()->steps(ProjectExplorer::BuildStep::Deploy);
- for (int i = buildSteps.count() - 1; i >= 0; --i) {
- const MaemoPackageCreationStep * const pStep
- = qobject_cast<MaemoPackageCreationStep *>(buildSteps.at(i));
- if (pStep)
- return pStep;
- }
- Q_ASSERT(!"Impossible: Maemo run configuration without packaging step.");
- return 0;
+ MaemoDeployStep * const step
+ = MaemoGlobal::buildStep<MaemoDeployStep>(activeQt4BuildConfiguration());
+ Q_ASSERT_X(step, Q_FUNC_INFO,
+ "Impossible: Maemo build configuration without deploy step.");
+ return step;
}
QString MaemoRunConfiguration::maddeRoot() const
@@ -278,8 +224,7 @@ QString MaemoRunConfiguration::executable() const
if (!ti.valid)
return QString();
- return QDir::toNativeSeparators(QDir::cleanPath(ti.workingDir
- + QLatin1Char('/') + ti.target));
+ return QDir::cleanPath(ti.workingDir + QLatin1Char('/') + ti.target);
}
QString MaemoRunConfiguration::runtimeGdbServerPort() const
@@ -312,5 +257,5 @@ void MaemoRunConfiguration::updateDeviceConfigurations()
emit deviceConfigurationsUpdated(target());
}
- } // namespace Internal
+} // namespace Internal
} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.h b/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.h
index 04b9f1cc3c..81a2897d53 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemorunconfiguration.h
@@ -53,6 +53,7 @@ class Qt4BuildConfiguration;
class Qt4ProFileNode;
class Qt4Target;
+class MaemoDeployStep;
class MaemoPackageCreationStep;
class MaemoRunConfigurationFactory;
@@ -67,14 +68,11 @@ public:
bool isEnabled(ProjectExplorer::BuildConfiguration *config) const;
QWidget *createConfigurationWidget();
+ ProjectExplorer::OutputFormatter *createConfigurationWidget() const;
Qt4Target *qt4Target() const;
Qt4BuildConfiguration *activeQt4BuildConfiguration() const;
- bool currentlyNeedsDeployment(const QString &host,
- const MaemoDeployable &deployable) const;
- void setDeployed(const QString &host, const MaemoDeployable &deployable);
-
- const MaemoPackageCreationStep *packageStep() const;
+ MaemoDeployStep *deployStep() const;
QString maddeRoot() const;
QString executable() const;
@@ -107,17 +105,12 @@ private slots:
private:
void init();
const MaemoToolChain *toolchain() const;
- void addDeployTimesToMap(QVariantMap &map) const;
- void getDeployTimesFromMap(const QVariantMap &map);
QString m_proFilePath;
mutable QString m_gdbPath;
MaemoDeviceConfig m_devConfig;
QStringList m_arguments;
-
- typedef QPair<MaemoDeployable, QString> DeployablePerHost;
- QHash<DeployablePerHost, QDateTime> m_lastDeployed;
};
} // namespace Internal
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp
index c173ddb607..007018c513 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.cpp
@@ -35,28 +35,30 @@
#include "maemoruncontrol.h"
#include "maemodeployables.h"
-#include "maemopackagecreationstep.h"
+#include "maemodeploystep.h"
+#include "maemoglobal.h"
#include "maemorunconfiguration.h"
#include <coreplugin/icore.h>
-#include <coreplugin/progressmanager/progressmanager.h>
+#include <coreplugin/ssh/sftpchannel.h>
+#include <coreplugin/ssh/sshconnection.h>
+#include <coreplugin/ssh/sshremoteprocess.h>
#include <debugger/debuggerengine.h>
#include <debugger/debuggerplugin.h>
#include <debugger/debuggerrunner.h>
#include <extensionsystem/pluginmanager.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchain.h>
+#include <qt4projectmanager/qt4buildconfiguration.h>
#include <utils/qtcassert.h>
-#include <projectexplorer/projectexplorerconstants.h>
-#include <QtCore/QCryptographicHash>
-#include <QtCore/QDir>
#include <QtCore/QFileInfo>
-#include <QtCore/QFuture>
-#include <QtCore/QProcess>
#include <QtCore/QStringBuilder>
#include <QtGui/QMessageBox>
+using namespace Core;
+
namespace Qt4ProjectManager {
namespace Internal {
@@ -78,215 +80,146 @@ AbstractMaemoRunControl::~AbstractMaemoRunControl()
{
}
+ // TODO: We can cache the connection. We'd have to check if the connection
+ // is active and its parameters are the same as m_devConfig. If yes,
+ // skip the connection step and jump right to handleConnected()
void AbstractMaemoRunControl::start()
{
- m_stoppedByUser = false;
if (!m_devConfig.isValid()) {
handleError(tr("No device configuration set for run configuration."));
} else {
+ m_stopped = false;
emit started();
- startInitialCleanup();
+ m_connection = SshConnection::create();
+ connect(m_connection.data(), SIGNAL(connected()), this,
+ SLOT(handleConnected()));
+ connect(m_connection.data(), SIGNAL(error(SshError)), this,
+ SLOT(handleConnectionFailure()));
+ m_connection->connectToHost(m_devConfig.server);
}
}
-void AbstractMaemoRunControl::startInitialCleanup()
-{
+void AbstractMaemoRunControl::handleConnected()
+{
+ if (m_stopped)
+ return;
+
emit appendMessage(this, tr("Cleaning up remote leftovers first ..."), false);
- const QStringList appsToKill
- = QStringList() << executableFileName() << QLatin1String("gdbserver");
+ const QStringList appsToKill = QStringList() << executableFileName()
+#ifdef USE_GDBSERVER
+ << QLatin1String("gdbserver");
+#else
+ << QLatin1String("gdb");
+#endif
killRemoteProcesses(appsToKill, true);
}
-void AbstractMaemoRunControl::stop()
+void AbstractMaemoRunControl::handleConnectionFailure()
{
- m_stoppedByUser = true;
- if (isCleaning())
- m_initialCleaner->stop();
- else if (isDeploying())
- m_sshDeployer->stop();
- else
- stopInternal();
+ if (m_stopped)
+ return;
+
+ handleError(tr("Could not connect to host: %1")
+ .arg(m_connection->errorString()));
+ emit finished();
}
-void AbstractMaemoRunControl::handleInitialCleanupFinished()
+void AbstractMaemoRunControl::stop()
{
- if (m_stoppedByUser) {
- emit appendMessage(this, tr("Initial cleanup canceled by user."), false);
+ m_stopped = true;
+ if (m_connection && m_connection->state() == SshConnection::Connecting) {
+ disconnect(m_connection.data(), 0, this, 0);
+ m_connection->disconnectFromHost();
emit finished();
- } else if (m_initialCleaner->hasError()) {
- handleError(tr("Error running initial cleanup: %1")
- .arg(m_initialCleaner->error()));
+ } else if (m_initialCleaner && m_initialCleaner->isRunning()) {
+ disconnect(m_initialCleaner.data(), 0, this, 0);
emit finished();
} else {
- emit appendMessage(this, tr("Initial cleanup done."), false);
- startInternal();
+ stopInternal();
}
}
-void AbstractMaemoRunControl::startDeployment(bool forDebugging)
+QString AbstractMaemoRunControl::executableFilePathOnTarget() const
{
- QTC_ASSERT(m_runConfig, return);
-
- if (m_stoppedByUser) {
- emit finished();
- } else {
- m_needsInstall = false;
- m_deployables.clear();
- m_remoteLinks.clear();
- const MaemoPackageCreationStep * const packageStep
- = m_runConfig->packageStep();
- if (packageStep->isPackagingEnabled()) {
- const MaemoDeployable d(packageFilePath(), uploadDir());
- m_needsInstall = addDeployableIfNeeded(d);
- } else {
- const MaemoDeployables * const deployables
- = packageStep->deployables();
- const int deployableCount = deployables->deployableCount();
- for (int i = 0; i < deployableCount; ++i) {
- const MaemoDeployable &d = deployables->deployableAt(i);
- if (addDeployableIfNeeded(d))
- m_needsInstall = true;
- }
- }
-
- if (forDebugging) {
- QFileInfo dumperInfo(m_runConfig->dumperLib());
- if (dumperInfo.exists()) {
- const MaemoDeployable d(m_runConfig->dumperLib(), uploadDir());
- m_needsInstall = addDeployableIfNeeded(d);
- }
- }
- deploy();
- }
-}
-
-bool AbstractMaemoRunControl::addDeployableIfNeeded(const MaemoDeployable &deployable)
-{
- if (m_runConfig->currentlyNeedsDeployment(m_devConfig.server.host,
- deployable)) {
- const QString fileName
- = QFileInfo(deployable.localFilePath).fileName();
- const QString remoteFilePath = deployable.remoteDir + '/' + fileName;
- const QString uploadFilePath =uploadDir() + '/' + fileName + '.'
- + QCryptographicHash::hash(remoteFilePath.toUtf8(),
- QCryptographicHash::Md5).toHex();
- m_deployables.append(Core::SftpTransferInfo(deployable.localFilePath,
- uploadFilePath.toUtf8(), Core::SftpTransferInfo::Upload));
- m_remoteLinks.insert(uploadFilePath, remoteFilePath);
- return true;
- } else {
- return false;
- }
+ const MaemoDeployables * const deployables
+ = m_runConfig->deployStep()->deployables();
+ return deployables->remoteExecutableFilePath(m_runConfig->executable());
}
-void AbstractMaemoRunControl::deploy()
+void AbstractMaemoRunControl::startExecutionIfPossible()
{
- Core::ICore::instance()->progressManager()
- ->addTask(m_progress.future(), tr("Deploying"),
- QLatin1String("Maemo.Deploy"));
- if (!m_deployables.isEmpty()) {
- QList<Core::SftpTransferInfo> deploySpecs;
- QStringList files;
- foreach (const Core::SftpTransferInfo &deployable, m_deployables)
- files << deployable.localFilePath;
- emit appendMessage(this,
- tr("Files to deploy: %1.").arg(files.join(" ")), false);
- m_sshDeployer.reset(new MaemoSshDeployer(m_devConfig.server, m_deployables));
- connect(m_sshDeployer.data(), SIGNAL(finished()),
- this, SLOT(handleDeployThreadFinished()));
- connect(m_sshDeployer.data(), SIGNAL(fileCopied(QString)),
- this, SLOT(handleFileCopied()));
- m_progress.setProgressRange(0, m_deployables.count());
- m_progress.setProgressValue(0);
- m_progress.reportStarted();
- m_sshDeployer->start();
+ if (executableFilePathOnTarget().isEmpty()) {
+ handleError(tr("Cannot run: No remote executable set."));
+ emit finished();
} else {
- m_progress.reportFinished();
- startExecutionIfPossible();
+ startExecution();
}
}
-void AbstractMaemoRunControl::handleFileCopied()
-{
- const Core::SftpTransferInfo &deployable = m_deployables.takeFirst();
- const QString remoteDir
- = QFileInfo(m_remoteLinks.value(QString::fromUtf8(deployable.remoteFilePath)))
- .dir().path();
-
- // TODO: This should be done after the linking step, in case the
- // operation is cancelled directly after the upload.
- m_runConfig->setDeployed(m_devConfig.server.host,
- MaemoDeployable(deployable.localFilePath, remoteDir));
-
- m_progress.setProgressValue(m_progress.progressValue() + 1);
-}
-
-bool AbstractMaemoRunControl::isDeploying() const
-{
- return m_sshDeployer && m_sshDeployer->isRunning();
-}
-
-QString AbstractMaemoRunControl::packageFileName() const
-{
- return QFileInfo(packageFilePath()).fileName();
-}
-
-QString AbstractMaemoRunControl::packageFilePath() const
+void AbstractMaemoRunControl::startExecution()
{
- return m_runConfig->packageStep()->packageFilePath();
+ m_runner = m_connection->createRemoteProcess(remoteCall().toUtf8());
+ connect(m_runner.data(), SIGNAL(started()), this,
+ SLOT(handleRemoteProcessStarted()));
+ connect(m_runner.data(), SIGNAL(closed(int)), this,
+ SLOT(handleRemoteProcessFinished(int)));
+ connect(m_runner.data(), SIGNAL(outputAvailable(QByteArray)), this,
+ SLOT(handleRemoteOutput(QByteArray)));
+ connect(m_runner.data(), SIGNAL(errorOutputAvailable(QByteArray)), this,
+ SLOT(handleRemoteErrorOutput(QByteArray)));
+ emit appendMessage(this, tr("Starting remote application."), false);
+ m_runner->start();
}
-QString AbstractMaemoRunControl::executableFilePathOnTarget() const
+void AbstractMaemoRunControl::handleRemoteProcessFinished(int exitStatus)
{
- const MaemoDeployables * const deployables
- = m_runConfig->packageStep()->deployables();
- return deployables->remoteExecutableFilePath(m_runConfig->executable());
-}
+ Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
+ || exitStatus == SshRemoteProcess::KilledBySignal
+ || exitStatus == SshRemoteProcess::ExitedNormally);
-bool AbstractMaemoRunControl::isCleaning() const
-{
- return m_initialCleaner && m_initialCleaner->isRunning();
-}
+ if (m_stopped)
+ return;
-void AbstractMaemoRunControl::startExecutionIfPossible()
-{
- if (executableFilePathOnTarget().isEmpty()) {
- handleError(tr("Cannot run: No remote executable set."));
+ if (exitStatus == SshRemoteProcess::ExitedNormally) {
+ emit appendMessage(this,
+ tr("Finished running remote process. Exit code was %1.")
+ .arg(m_runner->exitCode()), false);
emit finished();
} else {
- startExecution();
+ handleError(tr("Error running remote process: %1")
+ .arg(m_runner->errorString()));
}
}
-void AbstractMaemoRunControl::startExecution()
+void AbstractMaemoRunControl::handleRemoteOutput(const QByteArray &output)
{
- m_sshRunner.reset(new MaemoSshRunner(m_devConfig.server, remoteCall()));
- connect(m_sshRunner.data(), SIGNAL(finished()),
- this, SLOT(handleRunThreadFinished()));
- connect(m_sshRunner.data(), SIGNAL(remoteOutput(QString)),
- this, SLOT(handleRemoteOutput(QString)));
- emit appendMessage(this, tr("Starting remote application."), false);
- m_sshRunner->start();
+ emit addToOutputWindowInline(this, QString::fromUtf8(output), false);
+}
+
+void AbstractMaemoRunControl::handleRemoteErrorOutput(const QByteArray &output)
+{
+ emit addToOutputWindowInline(this, QString::fromUtf8(output), true);
}
bool AbstractMaemoRunControl::isRunning() const
{
- return isDeploying() || (m_sshRunner && m_sshRunner->isRunning());
+ return m_runner && m_runner->isRunning();
}
void AbstractMaemoRunControl::stopRunning(bool forDebugging)
{
- if (m_sshRunner && m_sshRunner->isRunning()) {
- m_sshRunner->stop();
+ if (m_runner && m_runner->isRunning()) {
+ disconnect(m_runner.data(), 0, this, 0);
QStringList apps(executableFileName());
if (forDebugging)
apps << QLatin1String("gdbserver");
killRemoteProcesses(apps, false);
+ emit finished();
}
}
void AbstractMaemoRunControl::killRemoteProcesses(const QStringList &apps,
- bool initialCleanup)
+ bool initialCleanup)
{
QString niceKill;
QString brutalKill;
@@ -296,54 +229,34 @@ void AbstractMaemoRunControl::killRemoteProcesses(const QStringList &apps,
}
QString remoteCall = niceKill + QLatin1String("sleep 1; ") + brutalKill;
remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon.
- QScopedPointer<MaemoSshRunner> &runner
- = initialCleanup ? m_initialCleaner : m_sshStopper;
- runner.reset(new MaemoSshRunner(m_devConfig.server, remoteCall));
- if (initialCleanup)
- connect(runner.data(), SIGNAL(finished()),
- this, SLOT(handleInitialCleanupFinished()));
- runner->start();
-}
-
-void AbstractMaemoRunControl::handleDeployThreadFinished()
-{
- bool cancel;
- if (m_stoppedByUser) {
- emit appendMessage(this, tr("Deployment canceled by user."), false);
- cancel = true;
- } else if (m_sshDeployer->hasError()) {
- handleError(tr("Deployment failed: %1").arg(m_sshDeployer->error()));
- cancel = true;
- } else {
- emit appendMessage(this, tr("Deployment finished."), false);
- cancel = false;
- }
-
- if (cancel) {
- m_progress.reportCanceled();
- m_progress.reportFinished();
- emit finished();
+ SshRemoteProcess::Ptr proc
+ = m_connection->createRemoteProcess(remoteCall.toUtf8());
+ if (initialCleanup) {
+ m_initialCleaner = proc;
+ connect(m_initialCleaner.data(), SIGNAL(closed(int)), this,
+ SLOT(handleInitialCleanupFinished(int)));
} else {
- m_progress.reportFinished();
- startExecutionIfPossible();
+ m_stopper = proc;
}
+ proc->start();
}
-void AbstractMaemoRunControl::handleRunThreadFinished()
+void AbstractMaemoRunControl::handleInitialCleanupFinished(int exitStatus)
{
- if (m_stoppedByUser) {
- emit appendMessage(this,
- tr("Remote execution canceled due to user request."),
- false);
- } else if (m_sshRunner && m_sshRunner->hasError()) {
- emit appendMessage(this, tr("Error running remote process: %1")
- .arg(m_sshRunner->error()),
- true);
+ Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
+ || exitStatus == SshRemoteProcess::KilledBySignal
+ || exitStatus == SshRemoteProcess::ExitedNormally);
+
+ if (m_stopped)
+ return;
+
+ if (exitStatus != SshRemoteProcess::ExitedNormally) {
+ handleError(tr("Initial cleanup failed: %1")
+ .arg(m_initialCleaner->errorString()));
} else {
- emit appendMessage(this, tr("Finished running remote process."),
- false);
+ emit appendMessage(this, tr("Initial cleanup done."), false);
+ startExecutionIfPossible();
}
- emit finished();
}
const QString AbstractMaemoRunControl::executableOnHost() const
@@ -358,40 +271,13 @@ const QString AbstractMaemoRunControl::executableFileName() const
const QString AbstractMaemoRunControl::uploadDir() const
{
- return homeDirOnDevice(m_devConfig.server.uname);
-}
-
-QString AbstractMaemoRunControl::remoteSudo() const
-{
- return QLatin1String("/usr/lib/mad-developer/devrootsh");
-}
-
-QString AbstractMaemoRunControl::remoteInstallCommand() const
-{
- Q_ASSERT(m_needsInstall);
- QString cmd;
- for (QMap<QString, QString>::ConstIterator it = m_remoteLinks.begin();
- it != m_remoteLinks.end(); ++it) {
- cmd += QString::fromLocal8Bit("%1 ln -sf %2 %3 && ")
- .arg(remoteSudo(), it.key(), it.value());
- }
- if (m_runConfig->packageStep()->isPackagingEnabled()) {
- cmd += QString::fromLocal8Bit("%1 dpkg -i %2").arg(remoteSudo())
- .arg(packageFileName());
- } else if (!m_remoteLinks.isEmpty()) {
- return cmd.remove(cmd.length() - 4, 4); // Trailing " && "
- }
-
- return cmd;
+ return MaemoGlobal::homeDirOnDevice(m_devConfig.server.uname);
}
const QString AbstractMaemoRunControl::targetCmdLinePrefix() const
{
- const QString &installPrefix = m_needsInstall
- ? remoteInstallCommand() + QLatin1String(" && ")
- : QString();
- return QString::fromLocal8Bit("%1%2 chmod a+x %3 && source /etc/profile && DISPLAY=:0.0 ")
- .arg(installPrefix).arg(remoteSudo()).arg(executableFilePathOnTarget());
+ return QString::fromLocal8Bit("%1 chmod a+x %2 && source /etc/profile && DISPLAY=:0.0 ")
+ .arg(MaemoGlobal::remoteSudo()).arg(executableFilePathOnTarget());
}
QString AbstractMaemoRunControl::targetCmdLineSuffix() const
@@ -403,6 +289,15 @@ void AbstractMaemoRunControl::handleError(const QString &errString)
{
QMessageBox::critical(0, tr("Remote Execution Failure"), errString);
emit appendMessage(this, errString, true);
+ stop();
+}
+
+template<typename SshChannel> void AbstractMaemoRunControl::closeSshChannel(SshChannel &channel)
+{
+ if (channel) {
+ disconnect(channel.data(), 0, this, 0);
+ // channel->closeChannel();
+ }
}
@@ -416,11 +311,6 @@ MaemoRunControl::~MaemoRunControl()
stop();
}
-void MaemoRunControl::startInternal()
-{
- startDeployment(false);
-}
-
QString MaemoRunControl::remoteCall() const
{
return QString::fromLocal8Bit("%1 %2 %3").arg(targetCmdLinePrefix())
@@ -432,16 +322,12 @@ void MaemoRunControl::stopInternal()
AbstractMaemoRunControl::stopRunning(false);
}
-void MaemoRunControl::handleRemoteOutput(const QString &output)
-{
- emit addToOutputWindowInline(this, output, false);
-}
-
MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
: AbstractMaemoRunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE)
, m_debuggerRunControl(0)
, m_startParams(new DebuggerStartParameters)
+ , m_uploadJob(SftpInvalidJob)
{
#ifdef USE_GDBSERVER
m_startParams->startMode = AttachToRemote;
@@ -453,8 +339,9 @@ MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
#else
m_startParams->startMode = StartRemoteGdb;
m_startParams->executable = executableFilePathOnTarget();
- m_startParams->debuggerCommand = QLatin1String("/usr/bin/gdb");
- m_startParams->sshserver = m_devConfig.server;
+ m_startParams->debuggerCommand = targetCmdLinePrefix()
+ + QLatin1String(" /usr/bin/gdb");
+ m_startParams->connParams = m_devConfig.server;
#endif
m_startParams->processArgs = m_runConfig->arguments();
m_startParams->sysRoot = m_runConfig->sysRoot();
@@ -475,12 +362,6 @@ MaemoDebugRunControl::~MaemoDebugRunControl()
stop();
}
-void MaemoDebugRunControl::startInternal()
-{
- m_debuggingStarted = false;
- startDeployment(true);
-}
-
QString MaemoDebugRunControl::remoteCall() const
{
return QString::fromLocal8Bit("%1 gdbserver :%2 %3 %4")
@@ -490,37 +371,101 @@ QString MaemoDebugRunControl::remoteCall() const
void MaemoDebugRunControl::startExecution()
{
-#ifdef USE_GDBSERVER
- AbstractMaemoRunControl::startExecution();
-#else
- startDebugging();
-#endif
+ const QString &dumperLib = m_runConfig->dumperLib();
+ if (!dumperLib.isEmpty()
+ && m_runConfig->deployStep()->currentlyNeedsDeployment(m_devConfig.server.host,
+ MaemoDeployable(dumperLib, uploadDir()))) {
+ m_uploader = m_connection->createSftpChannel();
+ connect(m_uploader.data(), SIGNAL(initialized()), this,
+ SLOT(handleSftpChannelInitialized()));
+ connect(m_uploader.data(), SIGNAL(initializationFailed(QString)), this,
+ SLOT(handleSftpChannelInitializationFailed(QString)));
+ connect(m_uploader.data(), SIGNAL(finished(Core::SftpJobId, QString)),
+ this, SLOT(handleSftpJobFinished(Core::SftpJobId, QString)));
+ m_uploader->initialize();
+ } else {
+ startDebugging();
+ }
}
-void MaemoDebugRunControl::handleRemoteOutput(const QString &output)
+void MaemoDebugRunControl::handleSftpChannelInitialized()
{
-#ifdef USE_GDBSERVER
- if (!m_debuggingStarted) {
- m_debuggingStarted = true;
+ if (m_stopped)
+ return;
+
+ const QString dumperLib = m_runConfig->dumperLib();
+ const QString fileName = QFileInfo(dumperLib).fileName();
+ const QString remoteFilePath = uploadDir() + '/' + fileName;
+ m_uploadJob = m_uploader->uploadFile(dumperLib, remoteFilePath,
+ SftpOverwriteExisting);
+ if (m_uploadJob == SftpInvalidJob) {
+ handleError(tr("Upload failed: Could not open file '%1'")
+ .arg(dumperLib));
+ } else {
+ emit appendMessage(this,
+ tr("Started uploading debugging helpers ('%1').").arg(dumperLib),
+ false);
+ }
+}
+
+void MaemoDebugRunControl::handleSftpChannelInitializationFailed(const QString &error)
+{
+ handleError(error);
+}
+
+void MaemoDebugRunControl::handleSftpJobFinished(Core::SftpJobId job,
+ const QString &error)
+{
+ if (m_stopped)
+ return;
+
+ if (job != m_uploadJob) {
+ qWarning("Warning: Unknown debugging helpers upload job %d finished.", job);
+ return;
+ }
+
+ if (!error.isEmpty()) {
+ handleError(tr("Error: Could not upload debugging helpers."));
+ } else {
+ m_runConfig->deployStep()->setDeployed(m_devConfig.server.host,
+ MaemoDeployable(m_runConfig->dumperLib(), uploadDir()));
+ emit appendMessage(this,
+ tr("Finished uploading debugging helpers."), false);
startDebugging();
}
-#endif
- emit addToOutputWindowInline(m_debuggerRunControl, output, false);
}
void MaemoDebugRunControl::startDebugging()
{
+#ifdef USE_GDBSERVER
+ AbstractMaemoRunControl::startExecution();
+#else
DebuggerPlugin::startDebugger(m_debuggerRunControl);
+#endif
+}
+
+bool MaemoDebugRunControl::isDeploying() const
+{
+ return m_uploader && m_uploadJob != SftpInvalidJob;
}
void MaemoDebugRunControl::stopInternal()
{
- m_debuggerRunControl->engine()->quitDebugger();
+ if (isDeploying()) {
+ disconnect(m_uploader.data(), 0, this, 0);
+ m_uploader->closeChannel();
+ m_uploadJob = SftpInvalidJob;
+ emit finished();
+ } else if (m_debuggerRunControl && m_debuggerRunControl->engine()) {
+ m_debuggerRunControl->engine()->quitDebugger();
+ } else {
+ emit finished();
+ }
}
bool MaemoDebugRunControl::isRunning() const
{
- return AbstractMaemoRunControl::isRunning()
+ return isDeploying() || AbstractMaemoRunControl::isRunning()
|| m_debuggerRunControl->state() != DebuggerNotReady;
}
@@ -529,10 +474,20 @@ void MaemoDebugRunControl::debuggingFinished()
#ifdef USE_GDBSERVER
AbstractMaemoRunControl::stopRunning(true);
#else
- AbstractMaemoRunControl::handleRunThreadFinished();
+ emit finished();
#endif
}
+void MaemoDebugRunControl::handleRemoteProcessStarted()
+{
+ DebuggerPlugin::startDebugger(m_debuggerRunControl);
+}
+
+void MaemoDebugRunControl::debuggerOutput(const QString &output)
+{
+ emit appendMessage(this, QLatin1String("[gdb says:] ") + output, true);
+}
+
QString MaemoDebugRunControl::gdbServerPort() const
{
return m_devConfig.type == MaemoDeviceConfig::Physical
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h
index ec6849b325..a9af1e6071 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemoruncontrol.h
@@ -36,19 +36,22 @@
#define MAEMORUNCONTROL_H
#include "maemodeviceconfigurations.h"
-#include "maemodeployable.h"
-#include "maemosshthread.h"
+#include <coreplugin/ssh/sftpdefs.h>
#include <projectexplorer/runconfiguration.h>
-#include <QtCore/QFutureInterface>
-#include <QtCore/QScopedPointer>
#include <QtCore/QString>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
+namespace Core {
+ class SftpChannel;
+ class SshConnection;
+ class SshRemoteProcess;
+}
+
namespace Debugger {
class DebuggerRunControl;
class DebuggerStartParameters;
@@ -56,8 +59,6 @@ namespace Debugger {
namespace Qt4ProjectManager {
namespace Internal {
-class MaemoSshDeployer;
-class MaemoSshRunner;
class MaemoRunConfiguration;
class AbstractMaemoRunControl : public ProjectExplorer::RunControl
@@ -73,8 +74,6 @@ protected:
virtual void start();
virtual void stop();
- void startDeployment(bool forDebugging);
- void deploy();
void stopRunning(bool forDebugging);
virtual void startExecution();
void handleError(const QString &errString);
@@ -83,49 +82,35 @@ protected:
const QString targetCmdLinePrefix() const;
QString targetCmdLineSuffix() const;
const QString uploadDir() const;
- QString packageFileName() const;
- QString packageFilePath() const;
QString executableFilePathOnTarget() const;
-protected slots:
- void handleRunThreadFinished();
-
private slots:
- virtual void handleRemoteOutput(const QString &output)=0;
- void handleInitialCleanupFinished();
- void handleDeployThreadFinished();
- void handleFileCopied();
+ void handleConnected();
+ void handleConnectionFailure();
+ void handleInitialCleanupFinished(int exitStatus);
+ virtual void handleRemoteProcessStarted() {}
+ void handleRemoteProcessFinished(int exitStatus);
+ void handleRemoteOutput(const QByteArray &output);
+ void handleRemoteErrorOutput(const QByteArray &output);
protected:
MaemoRunConfiguration *m_runConfig; // TODO this pointer can be invalid
const MaemoDeviceConfig m_devConfig;
+ QSharedPointer<Core::SshConnection> m_connection;
+ bool m_stopped;
private:
- bool addDeployableIfNeeded(const MaemoDeployable &deployable);
-
- virtual void startInternal()=0;
virtual void stopInternal()=0;
virtual QString remoteCall() const=0;
- void startInitialCleanup();
void killRemoteProcesses(const QStringList &apps, bool initialCleanup);
+ void cancelActions();
+ template<class SshChannel> void closeSshChannel(SshChannel &channel);
void startExecutionIfPossible();
- bool isCleaning() const;
- bool isDeploying() const;
- QString remoteSudo() const;
- QString remoteInstallCommand() const;
- QString uploadFilePath(const MaemoDeployable &deployable) const;
-
- QFutureInterface<void> m_progress;
- QScopedPointer<MaemoSshDeployer> m_sshDeployer;
- QScopedPointer<MaemoSshRunner> m_sshRunner;
- QScopedPointer<MaemoSshRunner> m_sshStopper;
- QScopedPointer<MaemoSshRunner> m_initialCleaner;
- bool m_stoppedByUser;
-
- QList<Core::SftpTransferInfo> m_deployables;
- QMap<QString, QString> m_remoteLinks;
- bool m_needsInstall;
+
+ QSharedPointer<Core::SshRemoteProcess> m_runner;
+ QSharedPointer<Core::SshRemoteProcess> m_stopper;
+ QSharedPointer<Core::SshRemoteProcess> m_initialCleaner;
};
class MaemoRunControl : public AbstractMaemoRunControl
@@ -135,11 +120,7 @@ public:
explicit MaemoRunControl(ProjectExplorer::RunConfiguration *runConfiguration);
~MaemoRunControl();
-private slots:
- virtual void handleRemoteOutput(const QString &output);
-
private:
- virtual void startInternal();
virtual void stopInternal();
virtual QString remoteCall() const;
};
@@ -153,22 +134,26 @@ public:
bool isRunning() const;
private slots:
- virtual void handleRemoteOutput(const QString &output);
+ virtual void handleRemoteProcessStarted();
+ void debuggerOutput(const QString &output);
void debuggingFinished();
+ void handleSftpChannelInitialized();
+ void handleSftpChannelInitializationFailed(const QString &error);
+ void handleSftpJobFinished(Core::SftpJobId job, const QString &error);
private:
- virtual void startInternal();
virtual void stopInternal();
virtual void startExecution();
virtual QString remoteCall() const;
QString gdbServerPort() const;
void startDebugging();
+ bool isDeploying() const;
Debugger::DebuggerRunControl *m_debuggerRunControl;
QSharedPointer<Debugger::DebuggerStartParameters> m_startParams;
-
- bool m_debuggingStarted;
+ QSharedPointer<Core::SftpChannel> m_uploader;
+ Core::SftpJobId m_uploadJob;
};
} // namespace Internal
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemorunfactories.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemorunfactories.cpp
index 420b31c3f3..b2ad7b1ad0 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemorunfactories.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemorunfactories.cpp
@@ -81,7 +81,10 @@ bool MaemoRunConfigurationFactory::canCreate(Target *parent,
bool MaemoRunConfigurationFactory::canRestore(Target *parent,
const QVariantMap &map) const
{
- return canCreate(parent, ProjectExplorer::idFromMap(map));
+ if (!qobject_cast<Qt4Target *>(parent))
+ return false;
+ return ProjectExplorer::idFromMap(map)
+ .startsWith(QLatin1String(MAEMO_RC_ID));
}
bool MaemoRunConfigurationFactory::canClone(Target *parent,
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.cpp
index 6ef22cf3d4..0d2e3a7fbe 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.cpp
@@ -39,7 +39,9 @@
#include "maemoconfigtestdialog.h"
#include "maemodeviceconfigurations.h"
#include "maemosshconfigdialog.h"
-#include "maemosshthread.h"
+
+#include <coreplugin/ssh/sshconnection.h>
+#include <coreplugin/ssh/sshremoteprocess.h>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
@@ -52,6 +54,8 @@
#include <algorithm>
+using namespace Core;
+
namespace Qt4ProjectManager {
namespace Internal {
@@ -97,8 +101,7 @@ MaemoSettingsWidget::MaemoSettingsWidget(QWidget *parent)
: QWidget(parent),
m_ui(new Ui_MaemoSettingsWidget),
m_devConfs(MaemoDeviceConfigurations::instance().devConfigs()),
- m_nameValidator(new NameValidator(m_devConfs)),
- m_keyDeployer(0)
+ m_nameValidator(new NameValidator(m_devConfs))
{
initGui();
}
@@ -195,7 +198,7 @@ void MaemoSettingsWidget::display(const MaemoDeviceConfig &devConfig)
otherConfig->server.pwd = devConfig.server.pwd;
otherConfig->server.privateKeyFile = devConfig.server.privateKeyFile;
- if (devConfig.server.authType == Core::SshServerInfo::AuthByPwd)
+ if (devConfig.server.authType == Core::SshConnectionParameters::AuthByPwd)
m_ui->passwordButton->setChecked(true);
else
m_ui->keyButton->setChecked(true);
@@ -272,7 +275,7 @@ void MaemoSettingsWidget::authenticationTypeChanged()
{
const bool usePassword = m_ui->passwordButton->isChecked();
currentConfig().server.authType
- = usePassword ? Core::SshServerInfo::AuthByPwd : Core::SshServerInfo::AuthByKey;
+ = usePassword ? Core::SshConnectionParameters::AuthByPwd : Core::SshConnectionParameters::AuthByKey;
m_ui->pwdLineEdit->setEnabled(usePassword);
m_ui->passwordLabel->setEnabled(usePassword);
m_ui->keyFileLineEdit->setEnabled(!usePassword);
@@ -337,13 +340,32 @@ void MaemoSettingsWidget::deployKey()
if (m_keyDeployer)
return;
+ disconnect(m_ui->deployKeyButton, 0, this, 0);
+ m_ui->deployKeyButton->setText(tr("Stop Deploying"));
+ connect(m_ui->deployKeyButton, SIGNAL(clicked()), this,
+ SLOT(stopDeploying()));
+ m_connection = SshConnection::create();
+ connect(m_connection.data(), SIGNAL(connected()), this,
+ SLOT(handleConnected()));
+ connect(m_connection.data(), SIGNAL(error(SshError)), this,
+ SLOT(handleConnectionFailure()));
+ m_connection->connectToHost(currentConfig().server);
+}
+
+void MaemoSettingsWidget::handleConnected()
+{
+ if (!m_connection)
+ return;
+
const QString &dir
= QFileInfo(currentConfig().server.privateKeyFile).path();
QString publicKeyFileName = QFileDialog::getOpenFileName(this,
tr("Choose Public Key File"), dir,
tr("Public Key Files(*.pub);;All Files (*)"));
- if (publicKeyFileName.isEmpty())
+ if (publicKeyFileName.isEmpty()) {
+ stopDeploying();
return;
+ }
QFile keyFile(publicKeyFileName);
QByteArray key;
@@ -353,32 +375,45 @@ void MaemoSettingsWidget::deployKey()
if (!keyFileAccessible || keyFile.error() != QFile::NoError) {
QMessageBox::critical(this, tr("Deployment Failed"),
tr("Could not read public key file '%1'.").arg(publicKeyFileName));
+ stopDeploying();
return;
}
- m_ui->deployKeyButton->disconnect();
- const QString command = QLatin1String("test -d .ssh "
- "|| mkdir .ssh && chmod 0700 .ssh && echo '")
- + key + QLatin1String("' >> .ssh/authorized_keys");
- m_keyDeployer = new MaemoSshRunner(currentConfig().server, command);
- connect(m_keyDeployer, SIGNAL(finished()),
- this, SLOT(handleDeployThreadFinished()));
- m_ui->deployKeyButton->setText(tr("Stop Deploying"));
- connect(m_ui->deployKeyButton, SIGNAL(clicked()), this, SLOT(stopDeploying()));
+ const QByteArray command = "test -d .ssh "
+ "|| mkdir .ssh && chmod 0700 .ssh && echo '"
+ + key + "' >> .ssh/authorized_keys";
+ m_keyDeployer = m_connection->createRemoteProcess(command);
+ connect(m_keyDeployer.data(), SIGNAL(closed(int)), this,
+ SLOT(handleKeyUploadFinished(int)));
m_keyDeployer->start();
}
-void MaemoSettingsWidget::handleDeployThreadFinished()
+void MaemoSettingsWidget::handleConnectionFailure()
{
- if (!m_keyDeployer)
+ if (!m_connection)
return;
- if (m_keyDeployer->hasError()) {
- QMessageBox::critical(this, tr("Deployment Failed"),
- tr("Key deployment failed: %1").arg(m_keyDeployer->error()));
- } else {
+ QMessageBox::critical(this, tr("Deployment Failed"),
+ tr("Could not connect to host: %1").arg(m_connection->errorString()));
+ stopDeploying();
+}
+
+void MaemoSettingsWidget::handleKeyUploadFinished(int exitStatus)
+{
+ Q_ASSERT(exitStatus == SshRemoteProcess::FailedToStart
+ || exitStatus == SshRemoteProcess::KilledBySignal
+ || exitStatus == SshRemoteProcess::ExitedNormally);
+
+ if (!m_connection)
+ return;
+
+ if (exitStatus == SshRemoteProcess::ExitedNormally
+ && m_keyDeployer->exitCode() == 0) {
QMessageBox::information(this, tr("Deployment Succeeded"),
tr("Key was successfully deployed."));
+ } else {
+ QMessageBox::critical(this, tr("Deployment Failed"),
+ tr("Key deployment failed: %1.").arg(m_keyDeployer->errorString()));
}
stopDeploying();
}
@@ -386,14 +421,14 @@ void MaemoSettingsWidget::handleDeployThreadFinished()
void MaemoSettingsWidget::stopDeploying()
{
if (m_keyDeployer) {
- m_ui->deployKeyButton->disconnect();
- m_keyDeployer->disconnect();
- m_keyDeployer->stop();
- delete m_keyDeployer;
- m_keyDeployer = 0;
- m_ui->deployKeyButton->setText(tr("Deploy Public Key ..."));
- connect(m_ui->deployKeyButton, SIGNAL(clicked()), this, SLOT(deployKey()));
+ disconnect(m_keyDeployer.data(), 0, this, 0);
+ m_keyDeployer = SshRemoteProcess::Ptr();
}
+ if (m_connection)
+ disconnect(m_connection.data(), 0, this, 0);
+ m_ui->deployKeyButton->disconnect();
+ m_ui->deployKeyButton->setText(tr("Deploy Public Key ..."));
+ connect(m_ui->deployKeyButton, SIGNAL(clicked()), this, SLOT(deployKey()));
}
void MaemoSettingsWidget::currentConfigChanged(int index)
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.h b/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.h
index e8c676696a..cc17e2262c 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.h
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemosettingswidget.h
@@ -38,6 +38,7 @@
#include "maemodeviceconfigurations.h"
#include <QtCore/QList>
+#include <QtCore/QSharedPointer>
#include <QtCore/QString>
#include <QtGui/QWidget>
@@ -47,6 +48,11 @@ class QLineEdit;
class Ui_MaemoSettingsWidget;
QT_END_NAMESPACE
+namespace Core {
+class SshConnection;
+class SshRemoteProcess;
+}
+
namespace Qt4ProjectManager {
namespace Internal {
@@ -86,8 +92,10 @@ private slots:
// For key deploying.
void deployKey();
- void handleDeployThreadFinished();
void stopDeploying();
+ void handleConnected();
+ void handleConnectionFailure();
+ void handleKeyUploadFinished(int exitStatus);
private:
void initGui();
@@ -102,7 +110,8 @@ private:
MaemoDeviceConfig m_lastConfigHW;
MaemoDeviceConfig m_lastConfigSim;
NameValidator * const m_nameValidator;
- MaemoSshRunner *m_keyDeployer;
+ QSharedPointer<Core::SshConnection> m_connection;
+ QSharedPointer<Core::SshRemoteProcess> m_keyDeployer;
};
} // namespace Internal
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemosshconfigdialog.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemosshconfigdialog.cpp
index 6052b702a2..48d48fb0f9 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/maemosshconfigdialog.cpp
+++ b/src/plugins/qt4projectmanager/qt-maemo/maemosshconfigdialog.cpp
@@ -45,13 +45,13 @@
#include <QtGui/QMessageBox>
#include <QtNetwork/QHostInfo>
-
+using namespace Core;
using namespace Qt4ProjectManager::Internal;
MaemoSshConfigDialog::MaemoSshConfigDialog(QWidget *parent)
: QDialog(parent)
, home(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))
- , m_keyGenerator(new Core::SshKeyGenerator)
+ , m_keyGenerator(new SshKeyGenerator)
{
m_ui.setupUi(this);
@@ -75,16 +75,16 @@ void MaemoSshConfigDialog::slotToggled()
void MaemoSshConfigDialog::generateSshKey()
{
- const Core::SshKeyGenerator::KeyType keyType = m_ui.rsa->isChecked()
- ? Core::SshKeyGenerator::Rsa
- : Core::SshKeyGenerator::Dsa;
+ const SshKeyGenerator::KeyType keyType = m_ui.rsa->isChecked()
+ ? SshKeyGenerator::Rsa
+ : SshKeyGenerator::Dsa;
QByteArray userId = QString(home.mid(home.lastIndexOf(QLatin1Char('/')) + 1)
+ QLatin1Char('@') + QHostInfo::localHostName()).toUtf8();
QApplication::setOverrideCursor(Qt::BusyCursor);
- if (m_keyGenerator->generateKeys(keyType, userId,
+ if (m_keyGenerator->generateKeys(keyType, SshKeyGenerator::OpenSsl,
m_ui.comboBox->currentText().toUShort())) {
m_ui.plainTextEdit->setPlainText(m_keyGenerator->publicKey());
m_ui.savePublicKey->setEnabled(true);
@@ -117,7 +117,7 @@ void MaemoSshConfigDialog::saveKey(bool publicKey)
{
checkSshDir();
const QString suggestedTypeSuffix =
- m_keyGenerator->type() == Core::SshKeyGenerator::Rsa ? "rsa" : "dsa";
+ m_keyGenerator->type() == SshKeyGenerator::Rsa ? "rsa" : "dsa";
const QString suggestedName = home + QString::fromLatin1("/.ssh/id_%1%2")
.arg(suggestedTypeSuffix).arg(publicKey ? ".pub" : "");
const QString dlgTitle
@@ -131,8 +131,8 @@ void MaemoSshConfigDialog::saveKey(bool publicKey)
const bool canOpen = file.open(QIODevice::WriteOnly);
if (canOpen)
file.write(publicKey
- ? m_keyGenerator->publicKey().toUtf8()
- : m_keyGenerator->privateKey().toUtf8());
+ ? m_keyGenerator->publicKey()
+ : m_keyGenerator->privateKey());
if (!canOpen || file.error() != QFile::NoError) {
QMessageBox::critical(this, tr("Error writing file"),
tr("Could not write file '%1':\n %2")
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.cpp b/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.cpp
deleted file mode 100644
index a31c06dec4..0000000000
--- a/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the Qt Creator.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "maemosshthread.h"
-
-namespace Qt4ProjectManager {
-namespace Internal {
-
-template <class SshConnection> MaemoSshThread<SshConnection>::MaemoSshThread(const Core::SshServerInfo &server)
- : m_server(server), m_stopRequested(false)
-{
-}
-
-template <class SshConnection> MaemoSshThread<SshConnection>::~MaemoSshThread()
-{
- stop();
- wait();
-}
-
-template <class SshConnection> void MaemoSshThread<SshConnection>::run()
-{
- if (m_stopRequested)
- return;
-
- if (!runInternal())
- m_error = m_connection->error();
-}
-
-template<class SshConnection> void MaemoSshThread<SshConnection>::stop()
-{
- m_mutex.lock();
- m_stopRequested = true;
- m_waitCond.wakeAll();
- const bool hasConnection = !m_connection.isNull();
- if (hasConnection)
- m_connection->quit();
- m_mutex.unlock();
-}
-
-template<class SshConnection> void MaemoSshThread<SshConnection>::waitForStop()
-{
- m_mutex.lock();
- while (!stopRequested())
- m_waitCond.wait(&m_mutex);
- m_mutex.unlock();
-}
-
-template<class SshConnection> void MaemoSshThread<SshConnection>::createConnection()
-{
- typename SshConnection::Ptr connection = SshConnection::create(m_server);
- m_mutex.lock();
- m_connection = connection;
- m_mutex.unlock();
-}
-
-MaemoSshRunner::MaemoSshRunner(const Core::SshServerInfo &server,
- const QString &command)
- : MaemoSshThread<Core::InteractiveSshConnection>(server),
- m_command(command)
-{
- m_prompt = server.uname == QLatin1String("root") ? "#" : "$";
-}
-
-bool MaemoSshRunner::runInternal()
-{
- createConnection();
- connect(m_connection.data(), SIGNAL(remoteOutputAvailable()),
- this, SLOT(handleRemoteOutput()));
- initState();
- if (!m_connection->start())
- return false;
- if (stopRequested())
- return true;
-
- waitForStop();
- return !m_connection->hasError();
-}
-
-void MaemoSshRunner::initState()
-{
- m_endMarkerCount = 0;
- m_promptEncountered = false;
- m_potentialEndMarkerPrefix.clear();
-}
-
-void MaemoSshRunner::handleRemoteOutput()
-{
- const QByteArray output
- = m_potentialEndMarkerPrefix + m_connection->waitForRemoteOutput(0);
-
- // Wait for a prompt before sending the command.
- if (!m_promptEncountered) {
- if (output.indexOf(m_prompt) != -1) {
- m_promptEncountered = true;
-
- /*
- * We don't have access to the remote process management, so we
- * try to track the lifetime of the process by adding a second command
- * that prints a rare character. When it occurs for the second time (the
- * first one is the echo from the remote terminal), we assume the
- * process has finished. If anyone actually prints this special character
- * in their application, they are out of luck.
- */
- const QString finalCommand = m_command + QLatin1String(";echo ")
- + QString::fromUtf8(EndMarker) + QLatin1Char('\n');
- if (!m_connection->sendInput(finalCommand.toUtf8()))
- stop();
- }
- return;
- }
-
- /*
- * The output the user should see is everything after the first
- * and before the last occurrence of our marker string.
- */
- int firstCharToEmit;
- int charsToEmitCount;
- const int endMarkerPos = output.indexOf(EndMarker);
- if (endMarkerPos != -1) {
- if (m_endMarkerCount++ == 0) {
- firstCharToEmit = endMarkerPos + EndMarker.count() + 1;
- int endMarkerPos2
- = output.indexOf(EndMarker, firstCharToEmit);
- if (endMarkerPos2 != -1) {
- ++ m_endMarkerCount;
- charsToEmitCount = endMarkerPos2 - firstCharToEmit;
- } else {
- charsToEmitCount = -1;
- }
- } else {
- firstCharToEmit = m_potentialEndMarkerPrefix.count();
- charsToEmitCount = endMarkerPos - firstCharToEmit;
- }
- } else {
- if (m_endMarkerCount == 0) {
- charsToEmitCount = 0;
- } else {
- firstCharToEmit = m_potentialEndMarkerPrefix.count();
- charsToEmitCount = -1;
- }
- }
-
- if (charsToEmitCount != 0) {
- emit remoteOutput(QString::fromUtf8(output.data() + firstCharToEmit,
- charsToEmitCount));
- }
- if (m_endMarkerCount == 2)
- stop();
-
- m_potentialEndMarkerPrefix = output.right(EndMarker.count() - 1);
-}
-
-const QByteArray MaemoSshRunner::EndMarker(QString(QChar(0x13a0)).toUtf8());
-
-
-MaemoSshDeployer::MaemoSshDeployer(const Core::SshServerInfo &server,
- const QList<Core::SftpTransferInfo> &deploySpecs)
- : MaemoSshThread<Core::SftpConnection>(server),
- m_deploySpecs(deploySpecs)
-{
-}
-
-bool MaemoSshDeployer::runInternal()
-{
- createConnection();
- if (!m_connection->start())
- return false;
- if (stopRequested())
- return true;
-
- connect(m_connection.data(), SIGNAL(fileCopied(QString)),
- this, SIGNAL(fileCopied(QString)));
- return m_connection->transferFiles(m_deploySpecs);
-}
-
-} // namespace Internal
-} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.h b/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.h
deleted file mode 100644
index 79107d0f60..0000000000
--- a/src/plugins/qt4projectmanager/qt-maemo/maemosshthread.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
-** All rights reserved.
-** Contact: Nokia Corporation (qt-info@nokia.com)
-**
-** This file is part of the Qt Creator.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** No Commercial Usage
-** This file contains pre-release code and may not be distributed.
-** You may use this file in accordance with the terms and conditions
-** contained in the Technology Preview License Agreement accompanying
-** this package.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** If you have questions regarding the use of this file, please contact
-** Nokia at qt-info@nokia.com.
-**
-**
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MAEMOSSHTHREAD_H
-#define MAEMOSSHTHREAD_H
-
-#include "maemodeviceconfigurations.h"
-
-#include <coreplugin/ssh/sshconnection.h>
-
-#include <QtCore/QByteArray>
-#include <QtCore/QList>
-#include <QtCore/QMutex>
-#include <QtCore/QThread>
-#include <QtCore/QWaitCondition>
-
-namespace Qt4ProjectManager {
-namespace Internal {
-
-template<class SshConnection> class MaemoSshThread : public QThread
-{
- Q_DISABLE_COPY(MaemoSshThread)
-public:
- QString error() const { return m_error; }
- bool hasError() const { return !m_error.isEmpty(); }
- void stop();
- virtual void run();
- ~MaemoSshThread();
-
-protected:
- MaemoSshThread(const Core::SshServerInfo &server);
- void createConnection();
- bool stopRequested() const { return m_stopRequested; }
- void waitForStop();
-
- typename SshConnection::Ptr m_connection;
-
-private:
- virtual bool runInternal() = 0;
-
- const Core::SshServerInfo m_server;
- bool m_stopRequested;
- QString m_error;
- QMutex m_mutex;
- QWaitCondition m_waitCond;
-};
-
-
-class MaemoSshRunner : public MaemoSshThread<Core::InteractiveSshConnection>
-{
- Q_OBJECT
- Q_DISABLE_COPY(MaemoSshRunner)
-public:
- MaemoSshRunner(const Core::SshServerInfo &server, const QString &command);
-
-signals:
- void remoteOutput(const QString &output);
-
-private:
- virtual bool runInternal();
- Q_SLOT void handleRemoteOutput();
- void initState();
-
- static const QByteArray EndMarker;
-
- const QString m_command;
- const char *m_prompt;
- int m_endMarkerCount;
- bool m_promptEncountered;
- QByteArray m_potentialEndMarkerPrefix;
-};
-
-
-class MaemoSshDeployer : public MaemoSshThread<Core::SftpConnection>
-{
- Q_OBJECT
- Q_DISABLE_COPY(MaemoSshDeployer)
-public:
- MaemoSshDeployer(const Core::SshServerInfo &server,
- const QList<Core::SftpTransferInfo> &deploySpecs);
-
-signals:
- void fileCopied(const QString &filePath);
-
-private:
- virtual bool runInternal();
-
- const QList<Core::SftpTransferInfo> m_deploySpecs;
-};
-
-} // namespace Internal
-} // namespace Qt4ProjectManager
-
-#endif // MAEMOSSHTHREAD_H
diff --git a/src/plugins/qt4projectmanager/qt-maemo/qt-maemo.pri b/src/plugins/qt4projectmanager/qt-maemo/qt-maemo.pri
index 346c1b8a06..2ea996a8c0 100644
--- a/src/plugins/qt4projectmanager/qt-maemo/qt-maemo.pri
+++ b/src/plugins/qt4projectmanager/qt-maemo/qt-maemo.pri
@@ -10,7 +10,6 @@ HEADERS += \
$$PWD/maemosettingspage.h \
$$PWD/maemosettingswidget.h \
$$PWD/maemosshconfigdialog.h \
- $$PWD/maemosshthread.h \
$$PWD/maemotoolchain.h \
$$PWD/maemopackagecreationstep.h \
$$PWD/maemopackagecreationfactory.h \
@@ -20,7 +19,11 @@ HEADERS += \
$$PWD/profilewrapper.h \
$$PWD/maemodeployables.h \
$$PWD/maemodeployable.h \
- $$PWD/maemodeployablelistwidget.h
+ $$PWD/maemodeployablelistwidget.h \
+ $$PWD/maemodeploystep.h \
+ $$PWD/maemodeploystepwidget.h \
+ $$PWD/maemodeploystepfactory.h \
+ $$PWD/maemoglobal.h
SOURCES += \
$$PWD/maemoconfigtestdialog.cpp \
@@ -33,7 +36,6 @@ SOURCES += \
$$PWD/maemosettingspage.cpp \
$$PWD/maemosettingswidget.cpp \
$$PWD/maemosshconfigdialog.cpp \
- $$PWD/maemosshthread.cpp \
$$PWD/maemotoolchain.cpp \
$$PWD/maemopackagecreationstep.cpp \
$$PWD/maemopackagecreationfactory.cpp \
@@ -42,13 +44,18 @@ SOURCES += \
$$PWD/qemuruntimemanager.cpp \
$$PWD/profilewrapper.cpp \
$$PWD/maemodeployables.cpp \
- $$PWD/maemodeployablelistwidget.cpp
+ $$PWD/maemodeployablelistwidget.cpp \
+ $$PWD/maemodeploystep.cpp \
+ $$PWD/maemodeploystepwidget.cpp \
+ $$PWD/maemodeploystepfactory.cpp \
+ $$PWD/maemoglobal.cpp
FORMS += \
$$PWD/maemoconfigtestdialog.ui \
$$PWD/maemosettingswidget.ui \
$$PWD/maemosshconfigdialog.ui \
$$PWD/maemopackagecreationwidget.ui \
- $$PWD/maemodeployablelistwidget.ui
+ $$PWD/maemodeployablelistwidget.ui \
+ $$PWD/maemodeploystepwidget.ui
RESOURCES += $$PWD/qt-maemo.qrc
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
index 3683accd37..54a88c18aa 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp
@@ -37,6 +37,8 @@
#include <projectexplorer/target.h>
#include <projectexplorer/gnumakeparser.h>
+#include <QtCore/QDir>
+
using namespace Qt4ProjectManager::Internal;
namespace {
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
index c92eab72e1..3070b8a212 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
@@ -41,6 +41,7 @@
#include "qt4buildconfiguration.h"
#include "qt4projectmanagerconstants.h"
#include "s60createpackagestep.h"
+#include "qtoutputformatter.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -207,6 +208,11 @@ QWidget *S60DeviceRunConfiguration::createConfigurationWidget()
return new S60DeviceRunConfigurationWidget(this);
}
+ProjectExplorer::OutputFormatter *S60DeviceRunConfiguration::createOutputFormatter() const
+{
+ return new QtOutputFormatter(qt4Target()->qt4Project());
+}
+
QVariantMap S60DeviceRunConfiguration::toMap() const
{
QVariantMap map(ProjectExplorer::RunConfiguration::toMap());
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
index a1cb5d4590..7776692d25 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.h
@@ -74,6 +74,8 @@ public:
bool isEnabled(ProjectExplorer::BuildConfiguration *configuration) const;
QWidget *createConfigurationWidget();
+ ProjectExplorer::OutputFormatter *createOutputFormatter() const;
+
QString serialPortName() const;
void setSerialPortName(const QString &name);
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
index 9ae33ff517..20dc6b8c2c 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.cpp
@@ -37,6 +37,7 @@
#include "s60devices.h"
#include "qt4buildconfiguration.h"
#include "qt4projectmanagerconstants.h"
+#include "qtoutputformatter.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -131,6 +132,11 @@ QWidget *S60EmulatorRunConfiguration::createConfigurationWidget()
return new S60EmulatorRunConfigurationWidget(this);
}
+ProjectExplorer::OutputFormatter *S60EmulatorRunConfiguration::createOutputFormatter() const
+{
+ return new QtOutputFormatter(qt4Target()->qt4Project());
+}
+
QVariantMap S60EmulatorRunConfiguration::toMap() const
{
QVariantMap map(ProjectExplorer::RunConfiguration::toMap());
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
index 917eac35b3..62f0435e5f 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60emulatorrunconfiguration.h
@@ -67,6 +67,8 @@ public:
bool isEnabled(ProjectExplorer::BuildConfiguration *configuration) const;
QWidget *createConfigurationWidget();
+ ProjectExplorer::OutputFormatter *createOutputFormatter() const;
+
QString executable() const;
QVariantMap toMap() const;
diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp
index 9d91bf4eff..bd2b15dc51 100644
--- a/src/plugins/qt4projectmanager/qt4nodes.cpp
+++ b/src/plugins/qt4projectmanager/qt4nodes.cpp
@@ -674,7 +674,7 @@ bool Qt4PriFileNode::priFileWritable(const QString &path)
switch (Core::EditorManager::promptReadOnlyFile(path, versionControl, core->mainWindow(), false)) {
case Core::EditorManager::RO_OpenVCS:
if (!versionControl->vcsOpen(path)) {
- QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with SCC."));
+ QMessageBox::warning(core->mainWindow(), tr("Failed!"), tr("Could not open the file for edit with VCS."));
return false;
}
break;
diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.cpp b/src/plugins/qt4projectmanager/qt4projectmanager.cpp
index 46039b131a..24536e1f60 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanager.cpp
+++ b/src/plugins/qt4projectmanager/qt4projectmanager.cpp
@@ -243,6 +243,8 @@ void Qt4Manager::runQMakeContextMenu()
void Qt4Manager::runQMake(ProjectExplorer::Project *p, ProjectExplorer::Node *node)
{
+ if (!ProjectExplorer::ProjectExplorerPlugin::instance()->saveModifiedFiles())
+ return;
Qt4Project *qt4pro = qobject_cast<Qt4Project *>(p);
QTC_ASSERT(qt4pro, return);
diff --git a/src/plugins/qt4projectmanager/qt4projectmanager.pro b/src/plugins/qt4projectmanager/qt4projectmanager.pro
index f4c492ac33..678bd5a9bc 100644
--- a/src/plugins/qt4projectmanager/qt4projectmanager.pro
+++ b/src/plugins/qt4projectmanager/qt4projectmanager.pro
@@ -44,7 +44,8 @@ HEADERS += qt4projectmanagerplugin.h \
gettingstartedwelcomepage.h \
qt4buildconfiguration.h \
qt4target.h \
- qmakeparser.h
+ qmakeparser.h \
+ qtoutputformatter.h
SOURCES += qt4projectmanagerplugin.cpp \
qt4projectmanager.cpp \
qt4project.cpp \
@@ -85,7 +86,8 @@ SOURCES += qt4projectmanagerplugin.cpp \
gettingstartedwelcomepage.cpp \
qt4buildconfiguration.cpp \
qt4target.cpp \
- qmakeparser.cpp
+ qmakeparser.cpp \
+ qtoutputformatter.cpp
FORMS += makestep.ui \
qmakestep.ui \
qt4projectconfigwidget.ui \
diff --git a/src/plugins/qt4projectmanager/qt4runconfiguration.cpp b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp
index a50d08eca1..d866299aa5 100644
--- a/src/plugins/qt4projectmanager/qt4runconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt4runconfiguration.cpp
@@ -36,6 +36,7 @@
#include "qt4target.h"
#include "qt4buildconfiguration.h"
#include "qt4projectmanagerconstants.h"
+#include "qtoutputformatter.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -669,6 +670,11 @@ ProjectExplorer::ToolChain::ToolChainType Qt4RunConfiguration::toolChainType() c
return qt4bc->toolChainType();
}
+ProjectExplorer::OutputFormatter *Qt4RunConfiguration::createOutputFormatter() const
+{
+ return new QtOutputFormatter(qt4Target()->qt4Project());
+}
+
///
/// Qt4RunConfigurationFactory
/// This class is used to restore run settings (saved in .user files)
diff --git a/src/plugins/qt4projectmanager/qt4runconfiguration.h b/src/plugins/qt4projectmanager/qt4runconfiguration.h
index 10ac7960f6..882131c5c5 100644
--- a/src/plugins/qt4projectmanager/qt4runconfiguration.h
+++ b/src/plugins/qt4projectmanager/qt4runconfiguration.h
@@ -93,6 +93,8 @@ public:
// TODO detectQtShadowBuild() ? how did this work ?
QVariantMap toMap() const;
+ ProjectExplorer::OutputFormatter *createOutputFormatter() const;
+
signals:
void commandLineArgumentsChanged(const QString&);
void workingDirectoryChanged(const QString&);
diff --git a/src/plugins/qt4projectmanager/qt4target.cpp b/src/plugins/qt4projectmanager/qt4target.cpp
index 198e2e7eff..4f262dbea0 100644
--- a/src/plugins/qt4projectmanager/qt4target.cpp
+++ b/src/plugins/qt4projectmanager/qt4target.cpp
@@ -35,6 +35,7 @@
#include "qt4project.h"
#include "qt4runconfiguration.h"
#include "qt4projectmanagerconstants.h"
+#include "qt-maemo/maemodeploystep.h"
#include "qt-maemo/maemopackagecreationstep.h"
#include "qt-maemo/maemorunconfiguration.h"
#include "qt-s60/s60devicerunconfiguration.h"
@@ -285,7 +286,10 @@ Qt4BuildConfiguration *Qt4Target::addQt4BuildConfiguration(QString displayName,
S60CreatePackageStep *packageStep = new S60CreatePackageStep(bc);
bc->insertStep(ProjectExplorer::BuildStep::Deploy, 2, packageStep);
} else if (id() == Constants::MAEMO_DEVICE_TARGET_ID) {
- bc->insertStep(ProjectExplorer::BuildStep::Deploy, 2, new MaemoPackageCreationStep(bc));
+ bc->insertStep(ProjectExplorer::BuildStep::Deploy, 2,
+ new MaemoPackageCreationStep(bc));
+ bc->insertStep(ProjectExplorer::BuildStep::Deploy, 3,
+ new MaemoDeployStep(bc));
}
MakeStep* cleanStep = new MakeStep(bc);
diff --git a/src/plugins/qt4projectmanager/qtoptionspage.h b/src/plugins/qt4projectmanager/qtoptionspage.h
index 5f066880c0..65a3956a3a 100644
--- a/src/plugins/qt4projectmanager/qtoptionspage.h
+++ b/src/plugins/qt4projectmanager/qtoptionspage.h
@@ -35,8 +35,6 @@
#include <QtCore/QFutureInterface>
#include <QtGui/QWidget>
-#include <QtGui/QPixmap>
-#include <QtGui/QIcon>
QT_BEGIN_NAMESPACE
class QTreeWidgetItem;
diff --git a/src/plugins/qmlprojectmanager/qmloutputformatter.cpp b/src/plugins/qt4projectmanager/qtoutputformatter.cpp
index bd371334db..4fe349be51 100644
--- a/src/plugins/qmlprojectmanager/qmloutputformatter.cpp
+++ b/src/plugins/qt4projectmanager/qtoutputformatter.cpp
@@ -27,24 +27,28 @@
**
**************************************************************************/
-#include "qmloutputformatter.h"
+#include "qtoutputformatter.h"
#include <texteditor/basetexteditor.h>
+#include <qt4projectmanager/qt4project.h>
+#include <QtCore/QFileInfo>
#include <QtGui/QPlainTextEdit>
using namespace ProjectExplorer;
-using namespace QmlProjectManager::Internal;
+using namespace Qt4ProjectManager;
-QmlOutputFormatter::QmlOutputFormatter(QObject *parent)
- : OutputFormatter(parent)
+QtOutputFormatter::QtOutputFormatter(Qt4Project *project)
+ : OutputFormatter()
, m_qmlError(QLatin1String("(file:///[^:]+:\\d+:\\d+):"))
- , m_linksActive(true)
- , m_mousePressed(false)
+ , m_qtError(QLatin1String("Object::.*in (.*:\\d+)"))
+ , m_project(project)
+
{
+
}
-void QmlOutputFormatter::appendApplicationOutput(const QString &text, bool onStdErr)
+void QtOutputFormatter::appendApplicationOutput(const QString &text, bool onStdErr)
{
QTextCharFormat linkFormat;
linkFormat.setForeground(plainTextEdit()->palette().link().color());
@@ -52,37 +56,33 @@ void QmlOutputFormatter::appendApplicationOutput(const QString &text, bool onStd
linkFormat.setAnchor(true);
// Create links from QML errors (anything of the form "file:///...:[line]:[column]:")
- int index = 0;
- while (m_qmlError.indexIn(text, index) != -1) {
+ if (m_qmlError.indexIn(text) != -1) {
const int matchPos = m_qmlError.pos(1);
- const QString leader = text.mid(index, matchPos - index);
+ const QString leader = text.left(matchPos);
append(leader, onStdErr ? StdErrFormat : StdOutFormat);
const QString matched = m_qmlError.cap(1);
linkFormat.setAnchorHref(matched);
append(matched, linkFormat);
- index = matchPos + m_qmlError.matchedLength() - 1;
- }
- append(text.mid(index), onStdErr ? StdErrFormat : StdOutFormat);
-}
-
-void QmlOutputFormatter::mousePressEvent(QMouseEvent * /*e*/)
-{
- m_mousePressed = true;
-}
+ int index = matchPos + m_qmlError.matchedLength() - 1;
+ append(text.mid(index), onStdErr ? StdErrFormat : StdOutFormat);
+ } else if (m_qtError.indexIn(text) != -1) {
+ const int matchPos = m_qtError.pos(1);
+ const QString leader = text.left(matchPos);
+ append(leader, onStdErr ? StdErrFormat : StdOutFormat);
-void QmlOutputFormatter::mouseReleaseEvent(QMouseEvent *e)
-{
- m_mousePressed = false;
+ const QString matched = m_qtError.cap(1);
+ linkFormat.setAnchorHref(m_qtError.cap(1));
+ append(matched, linkFormat);
- if (!m_linksActive) {
- // Mouse was released, activate links again
- m_linksActive = true;
- return;
+ int index = matchPos + m_qtError.matchedLength() - 1;
+ append(text.mid(index), onStdErr ? StdErrFormat : StdOutFormat);
}
+}
- const QString href = plainTextEdit()->anchorAt(e->pos());
+void QtOutputFormatter::handleLink(const QString &href)
+{
if (!href.isEmpty()) {
QRegExp qmlErrorLink(QLatin1String("^file://(/[^:]+):(\\d+):(\\d+)"));
@@ -91,18 +91,30 @@ void QmlOutputFormatter::mouseReleaseEvent(QMouseEvent *e)
const int line = qmlErrorLink.cap(2).toInt();
const int column = qmlErrorLink.cap(3).toInt();
TextEditor::BaseTextEditor::openEditorAt(fileName, line, column - 1);
+ return;
}
- }
-}
-void QmlOutputFormatter::mouseMoveEvent(QMouseEvent *e)
-{
- // Cursor was dragged to make a selection, deactivate links
- if (m_mousePressed && plainTextEdit()->textCursor().hasSelection())
- m_linksActive = false;
-
- if (!m_linksActive || plainTextEdit()->anchorAt(e->pos()).isEmpty())
- plainTextEdit()->viewport()->setCursor(Qt::IBeamCursor);
- else
- plainTextEdit()->viewport()->setCursor(Qt::PointingHandCursor);
+ QRegExp qtErrorLink(QLatin1String("^(.*):(\\d+)$"));
+ if (qtErrorLink.indexIn(href) != 1) {
+ QString fileName = qtErrorLink.cap(1);
+ const int line = qtErrorLink.cap(2).toInt();
+ QFileInfo fi(fileName);
+ if (fi.isRelative()) {
+ // Yeah fileName is relative, no suprise
+ Qt4Project *pro = m_project.data();
+ if (pro) {
+ QString baseName = fi.fileName();
+ foreach (const QString &file, pro->files(Project::AllFiles)) {
+ if (file.endsWith(baseName)) {
+ // pick the first one...
+ fileName = file;
+ break;
+ }
+ }
+ }
+ }
+ TextEditor::BaseTextEditor::openEditorAt(fileName, line, 0);
+ return;
+ }
+ }
}
diff --git a/src/plugins/qmlprojectmanager/qmloutputformatter.h b/src/plugins/qt4projectmanager/qtoutputformatter.h
index 8d0e64d6d5..9a039581db 100644
--- a/src/plugins/qmlprojectmanager/qmloutputformatter.h
+++ b/src/plugins/qt4projectmanager/qtoutputformatter.h
@@ -31,30 +31,29 @@
#define QMLOUTPUTFORMATTER_H
#include <projectexplorer/outputformatter.h>
-
#include <QtCore/QRegExp>
+#include <QSharedPointer>
-namespace QmlProjectManager {
-namespace Internal {
+namespace Qt4ProjectManager
+{
+class Qt4Project;
-class QmlOutputFormatter: public ProjectExplorer::OutputFormatter
+class QtOutputFormatter: public ProjectExplorer::OutputFormatter
{
public:
- QmlOutputFormatter(QObject *parent = 0);
+ QtOutputFormatter(Qt4Project *project);
virtual void appendApplicationOutput(const QString &text, bool onStdErr);
- virtual void mousePressEvent(QMouseEvent *e);
- virtual void mouseReleaseEvent(QMouseEvent *e);
- virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void handleLink(const QString &href);
private:
QRegExp m_qmlError;
- bool m_linksActive;
- bool m_mousePressed;
+ QRegExp m_qtError;
+ QWeakPointer<Qt4Project> m_project;
};
-} // namespace Internal
+
} // namespace QmlProjectManager
#endif // QMLOUTPUTFORMATTER_H
diff --git a/src/plugins/qt4projectmanager/qtversionmanager.h b/src/plugins/qt4projectmanager/qtversionmanager.h
index 385c22730f..60f77252f3 100644
--- a/src/plugins/qt4projectmanager/qtversionmanager.h
+++ b/src/plugins/qt4projectmanager/qtversionmanager.h
@@ -30,8 +30,8 @@
#ifndef QTVERSIONMANAGER_H
#define QTVERSIONMANAGER_H
-#include <projectexplorer/taskwindow.h>
#include <projectexplorer/toolchain.h>
+#include <projectexplorer/task.h>
#include <QtCore/QHash>
#include <QtCore/QSet>
diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp
index be2214d7fc..1e13af7e10 100644
--- a/src/plugins/texteditor/basetextdocument.cpp
+++ b/src/plugins/texteditor/basetextdocument.cpp
@@ -32,6 +32,7 @@
#include "basetextdocumentlayout.h"
#include "basetexteditor.h"
#include "storagesettings.h"
+#include "syntaxhighlighter.h"
#include <QtCore/QFile>
#include <QtCore/QDir>
@@ -162,7 +163,16 @@ bool BaseTextDocument::save(const QString &fileName)
{
QTextCursor cursor(m_document);
+ // When saving the current editor, make sure to maintain the cursor position for undo
+ Core::IEditor *currentEditor = Core::EditorManager::instance()->currentEditor();
+ if (BaseTextEditorEditable *editable = qobject_cast<BaseTextEditorEditable*>(currentEditor)) {
+ if (editable->file() == this)
+ cursor = editable->editor()->textCursor();
+ }
+
cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::Start);
+
if (m_storageSettings.m_cleanWhitespace)
cleanWhitespace(cursor, m_storageSettings.m_cleanIndentation, m_storageSettings.m_inEntireDocument);
if (m_storageSettings.m_addFinalNewLine)
@@ -357,7 +367,7 @@ void BaseTextDocument::reload(ReloadFlag flag, ChangeType type)
}
}
-void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter)
+void BaseTextDocument::setSyntaxHighlighter(SyntaxHighlighter *highlighter)
{
if (m_highlighter)
delete m_highlighter;
diff --git a/src/plugins/texteditor/basetextdocument.h b/src/plugins/texteditor/basetextdocument.h
index 7447d8167d..cd503276b3 100644
--- a/src/plugins/texteditor/basetextdocument.h
+++ b/src/plugins/texteditor/basetextdocument.h
@@ -40,11 +40,12 @@
QT_BEGIN_NAMESPACE
class QTextCursor;
class QTextDocument;
-class QSyntaxHighlighter;
QT_END_NAMESPACE
namespace TextEditor {
+class SyntaxHighlighter;
+
class DocumentMarker : public ITextMarkable
{
Q_OBJECT
@@ -101,8 +102,8 @@ public:
virtual void reload();
QTextDocument *document() const { return m_document; }
- void setSyntaxHighlighter(QSyntaxHighlighter *highlighter);
- QSyntaxHighlighter *syntaxHighlighter() const { return m_highlighter; }
+ void setSyntaxHighlighter(SyntaxHighlighter *highlighter);
+ SyntaxHighlighter *syntaxHighlighter() const { return m_highlighter; }
inline bool isBinaryData() const { return m_isBinaryData; }
@@ -127,7 +128,7 @@ private:
TabSettings m_tabSettings;
QTextDocument *m_document;
DocumentMarker *m_documentMarker;
- QSyntaxHighlighter *m_highlighter;
+ SyntaxHighlighter *m_highlighter;
enum LineTerminatorMode {
LFLineTerminator,
diff --git a/src/plugins/texteditor/basetextdocumentlayout.cpp b/src/plugins/texteditor/basetextdocumentlayout.cpp
index 087eb09ccf..c69808f8de 100644
--- a/src/plugins/texteditor/basetextdocumentlayout.cpp
+++ b/src/plugins/texteditor/basetextdocumentlayout.cpp
@@ -366,28 +366,6 @@ TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *
return NoMatch;
}
-int TextBlockUserData::lexerState(const QTextBlock &block)
-{
- if (!block.isValid())
- return -1;
-
- int data = block.userState();
- if (data == -1)
- return -1;
- return data & 0xFF;
-}
-
-void TextBlockUserData::setLexerState(QTextBlock block, int state)
-{
- if (!block.isValid())
- return;
-
- int data = block.userState();
- if (data == -1)
- data = 0;
- block.setUserState((data & ~0xFF) | (state & 0xFF));
-}
-
void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
{
if (m_codeFormatterData)
@@ -400,6 +378,7 @@ BaseTextDocumentLayout::BaseTextDocumentLayout(QTextDocument *doc)
:QPlainTextDocumentLayout(doc) {
lastSaveRevision = 0;
hasMarks = 0;
+ m_requiredWidth = 0;
}
BaseTextDocumentLayout::~BaseTextDocumentLayout()
@@ -479,6 +458,23 @@ void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
setBraceDepth(block, braceDepth(block) + delta);
}
+void BaseTextDocumentLayout::setLexerState(const QTextBlock &block, int state)
+{
+ if (state == 0) {
+ if (TextBlockUserData *userData = testUserData(block))
+ userData->setLexerState(0);
+ } else {
+ userData(block)->setLexerState(qMax(0,state));
+ }
+}
+
+int BaseTextDocumentLayout::lexerState(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = testUserData(block))
+ return userData->lexerState();
+ return 0;
+}
+
void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
{
if (indent == 0) {
diff --git a/src/plugins/texteditor/basetextdocumentlayout.h b/src/plugins/texteditor/basetextdocumentlayout.h
index d6fa481ec7..e70802d009 100644
--- a/src/plugins/texteditor/basetextdocumentlayout.h
+++ b/src/plugins/texteditor/basetextdocumentlayout.h
@@ -68,6 +68,7 @@ public:
: m_folded(false),
m_ifdefedOut(false),
m_foldingIndent(0),
+ m_lexerState(0),
m_foldingStartIncluded(false),
m_foldingEndIncluded(false),
m_codeFormatterData(0)
@@ -106,15 +107,15 @@ public:
static bool findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition = false);
static bool findNextBlockClosingParenthesis(QTextCursor *cursor);
- int foldingIndent() const { return m_foldingIndent; }
- void setFoldingIndent(int indent) { m_foldingIndent = indent; }
- void setFoldingStartIncluded(bool included) { m_foldingStartIncluded = included; }
- bool foldingStartIncluded() const { return m_foldingStartIncluded; }
- void setFoldingEndIncluded(bool included) { m_foldingEndIncluded = included; }
- bool foldingEndIncluded() const { return m_foldingEndIncluded; }
+ inline int foldingIndent() const { return m_foldingIndent; }
+ inline void setFoldingIndent(int indent) { m_foldingIndent = indent; }
+ inline void setFoldingStartIncluded(bool included) { m_foldingStartIncluded = included; }
+ inline bool foldingStartIncluded() const { return m_foldingStartIncluded; }
+ inline void setFoldingEndIncluded(bool included) { m_foldingEndIncluded = included; }
+ inline bool foldingEndIncluded() const { return m_foldingEndIncluded; }
+ inline int lexerState() const { return m_lexerState; }
+ inline void setLexerState(int state) {m_lexerState = state; }
- static int lexerState(const QTextBlock &block);
- static void setLexerState(QTextBlock block, int state);
CodeFormatterData *codeFormatterData() const { return m_codeFormatterData; }
void setCodeFormatterData(CodeFormatterData *data);
@@ -124,6 +125,7 @@ private:
uint m_folded : 1;
uint m_ifdefedOut : 1;
uint m_foldingIndent : 16;
+ uint m_lexerState : 4;
uint m_foldingStartIncluded : 1;
uint m_foldingEndIncluded : 1;
Parentheses m_parentheses;
@@ -152,6 +154,8 @@ public:
static void changeBraceDepth(QTextBlock &block, int delta);
static void setFoldingIndent(const QTextBlock &block, int indent);
static int foldingIndent(const QTextBlock &block);
+ static void setLexerState(const QTextBlock &block, int state);
+ static int lexerState(const QTextBlock &block);
static void changeFoldingIndent(QTextBlock &block, int delta);
static bool canFold(const QTextBlock &block);
static void doFoldOrUnfold(const QTextBlock& block, bool unfold);
@@ -177,6 +181,7 @@ public:
void setRequiredWidth(int width);
QSizeF documentSize() const;
+
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index a21aba0b5a..6a6fd7451c 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -39,6 +39,7 @@
#include "tabsettings.h"
#include "texteditorconstants.h"
#include "texteditorplugin.h"
+#include "syntaxhighlighter.h"
#include <aggregation/aggregate.h>
#include <coreplugin/actionmanager/actionmanager.h>
@@ -572,8 +573,19 @@ void BaseTextEditor::triggerQuickFix()
emit requestQuickFix(editableInterface());
}
+QString BaseTextEditor::msgTextTooLarge(quint64 size)
+{
+ return tr("The text is too large to be displayed (%1 MB).").
+ arg(size >> 20);
+}
+
bool BaseTextEditor::createNew(const QString &contents)
{
+ if (contents.size() > Core::EditorManager::maxTextFileSize()) {
+ setPlainText(msgTextTooLarge(contents.size()));
+ document()->setModified(false);
+ return false;
+ }
setPlainText(contents);
document()->setModified(false);
return true;
@@ -4027,11 +4039,7 @@ int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
return 0;
// verify that we indeed do have an extra opening brace in the document
- int braceDepth = document()->lastBlock().userState();
- if (braceDepth >= 0)
- braceDepth >>= 8;
- else
- braceDepth= 0;
+ int braceDepth = BaseTextDocumentLayout::braceDepth(document()->lastBlock());
if (braceDepth <= 0)
return 0; // braces are all balanced or worse, no need to do anything
@@ -4048,9 +4056,16 @@ int BaseTextEditor::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor)
const TabSettings &ts = tabSettings();
QTextBlock block = cursor.block();
int indentation = ts.indentationColumn(block.text());
- if (block.next().isValid()
- && ts.indentationColumn(block.next().text()) > indentation)
- return 0;
+
+ if (block.next().isValid()) { // not the last block
+ block = block.next();
+ //skip all empty blocks
+ while (block.isValid() && ts.onlySpace(block.text()))
+ block = block.next();
+ if (block.isValid()
+ && ts.indentationColumn(block.text()) > indentation)
+ return 0;
+ }
int pos = cursor.position();
@@ -4269,6 +4284,25 @@ int BaseTextEditor::verticalBlockSelection() const
return qAbs(b.positionInBlock() - e.positionInBlock()) + d->m_blockSelectionExtraX;
}
+QRegion BaseTextEditor::translatedLineRegion(int lineStart, int lineEnd) const
+{
+ QRegion region;
+ for (int i = lineStart ; i <= lineEnd; i++) {
+ QTextBlock block = document()->findBlockByNumber(i);
+ QPoint topLeft = blockBoundingGeometry(block).translated(contentOffset()).topLeft().toPoint();
+
+ if (block.isValid()) {
+ QTextLayout *layout = block.layout();
+
+ for (int i = 0; i < layout->lineCount();i++) {
+ QTextLine line = layout->lineAt(i);
+ region += line.naturalTextRect().translated(topLeft).toRect();
+ }
+ }
+ }
+ return region;
+}
+
void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection)
{
if (start != d->m_findScopeStart || end != d->m_findScopeEnd) {
@@ -4631,7 +4665,6 @@ void BaseTextEditor::maybeClearSomeExtraSelections(const QTextCursor &cursor)
if (cursor.selectionEnd() - cursor.selectionStart() < smallSelectionSize)
return;
- d->m_extraSelections[TypeSelection].clear();
d->m_extraSelections[UndefinedSymbolSelection].clear();
d->m_extraSelections[ObjCSelection].clear();
d->m_extraSelections[CodeWarningsSelection].clear();
@@ -4940,7 +4973,7 @@ void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds)
setCenterOnScroll(ds.m_centerCursorOnScroll);
if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
- if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
+ if (SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter())
highlighter->rehighlight();
QTextOption option = document()->defaultTextOption();
if (ds.m_visualizeWhitespace)
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index 087d67e642..450c335073 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -216,6 +216,8 @@ public:
int verticalBlockSelection() const;
+ QRegion translatedLineRegion(int lineStart, int lineEnd) const;
+
public slots:
void setDisplayName(const QString &title);
@@ -292,6 +294,8 @@ protected:
bool canInsertFromMimeData(const QMimeData *source) const;
void insertFromMimeData(const QMimeData *source);
+ static QString msgTextTooLarge(quint64 size);
+
private:
void maybeSelectLine();
@@ -345,7 +349,6 @@ public:
FakeVimSelection,
OtherSelection,
SnippetPlaceholderSelection,
- TypeSelection,
ObjCSelection,
NExtraSelectionKinds
};
diff --git a/src/plugins/texteditor/completionsupport.cpp b/src/plugins/texteditor/completionsupport.cpp
index 3b06580a74..bd6cc6c12e 100644
--- a/src/plugins/texteditor/completionsupport.cpp
+++ b/src/plugins/texteditor/completionsupport.cpp
@@ -67,7 +67,7 @@ CompletionSupport::CompletionSupport()
void CompletionSupport::performCompletion(const CompletionItem &item)
{
- item.collector->complete(item);
+ item.collector->complete(item, m_completionList->typedChar());
m_checkCompletionTrigger = true;
}
diff --git a/src/plugins/texteditor/completionwidget.cpp b/src/plugins/texteditor/completionwidget.cpp
index c8a6eae08a..5b005c17b0 100644
--- a/src/plugins/texteditor/completionwidget.cpp
+++ b/src/plugins/texteditor/completionwidget.cpp
@@ -199,6 +199,11 @@ void CompletionWidget::showCompletions(int startPos)
setFocus();
}
+QChar CompletionWidget::typedChar() const
+{
+ return m_completionListView->m_typedChar;
+}
+
void CompletionWidget::updatePositionAndSize(int startPos)
{
// Determine size by calculating the space of the visible items
@@ -415,6 +420,16 @@ bool CompletionListView::event(QEvent *e)
}
if (forwardKeys && ! m_quickFix) {
+ if (ke->text().length() == 1 && currentIndex().isValid() && qApp->focusWidget() == this) {
+ QChar typedChar = ke->text().at(0);
+ const CompletionItem &item = m_model->itemAt(currentIndex());
+ if (item.collector->typedCharCompletes(item, typedChar)) {
+ m_typedChar = typedChar;
+ m_completionWidget->closeList(currentIndex());
+ return true;
+ }
+ }
+
m_blockFocusOut = true;
QApplication::sendEvent(m_editorWidget, e);
m_blockFocusOut = false;
diff --git a/src/plugins/texteditor/completionwidget.h b/src/plugins/texteditor/completionwidget.h
index ece02904b1..813fd89f30 100644
--- a/src/plugins/texteditor/completionwidget.h
+++ b/src/plugins/texteditor/completionwidget.h
@@ -61,6 +61,8 @@ public:
void setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems);
void showCompletions(int startPos);
+ QChar typedChar() const;
+
signals:
void itemSelected(const TextEditor::CompletionItem &item);
void completionListClosed();
@@ -115,6 +117,7 @@ private:
CompletionSupport *m_support;
QPointer<CompletionInfoFrame> m_infoFrame;
QTimer m_infoTimer;
+ QChar m_typedChar;
};
} // namespace Internal
diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp
index 5c51b7cb09..575d2fd228 100644
--- a/src/plugins/texteditor/displaysettings.cpp
+++ b/src/plugins/texteditor/displaysettings.cpp
@@ -44,6 +44,7 @@ static const char * const animateMatchingParenthesesKey= "AnimateMatchingParenth
static const char * const markTextChangesKey = "MarkTextChanges";
static const char * const autoFoldFirstCommentKey = "AutoFoldFirstComment";
static const char * const centerCursorOnScrollKey = "CenterCursorOnScroll";
+static const char * const integrateDocsIntoTooltips = "IntegrateDocsIntoTooltips";
static const char * const groupPostfix = "DisplaySettings";
namespace TextEditor {
@@ -60,7 +61,8 @@ DisplaySettings::DisplaySettings() :
m_animateMatchingParentheses(true),
m_markTextChanges(true),
m_autoFoldFirstComment(true),
- m_centerCursorOnScroll(false)
+ m_centerCursorOnScroll(false),
+ m_integrateDocsIntoTooltips(true)
{
}
@@ -82,6 +84,7 @@ void DisplaySettings::toSettings(const QString &category, QSettings *s) const
s->setValue(QLatin1String(markTextChangesKey), m_markTextChanges);
s->setValue(QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment);
s->setValue(QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll);
+ s->setValue(QLatin1String(integrateDocsIntoTooltips), m_integrateDocsIntoTooltips);
s->endGroup();
}
@@ -106,6 +109,7 @@ void DisplaySettings::fromSettings(const QString &category, const QSettings *s)
m_markTextChanges = s->value(group + QLatin1String(markTextChangesKey), m_markTextChanges).toBool();
m_autoFoldFirstComment = s->value(group + QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment).toBool();
m_centerCursorOnScroll = s->value(group + QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll).toBool();
+ m_integrateDocsIntoTooltips = s->value(group + QLatin1String(integrateDocsIntoTooltips), m_integrateDocsIntoTooltips).toBool();
}
bool DisplaySettings::equals(const DisplaySettings &ds) const
@@ -122,6 +126,7 @@ bool DisplaySettings::equals(const DisplaySettings &ds) const
&& m_markTextChanges == ds.m_markTextChanges
&& m_autoFoldFirstComment== ds.m_autoFoldFirstComment
&& m_centerCursorOnScroll == ds.m_centerCursorOnScroll
+ && m_integrateDocsIntoTooltips == ds.m_integrateDocsIntoTooltips
;
}
diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h
index 374be16969..a421ea7ee3 100644
--- a/src/plugins/texteditor/displaysettings.h
+++ b/src/plugins/texteditor/displaysettings.h
@@ -57,6 +57,7 @@ struct TEXTEDITOR_EXPORT DisplaySettings
bool m_markTextChanges;
bool m_autoFoldFirstComment;
bool m_centerCursorOnScroll;
+ bool m_integrateDocsIntoTooltips;
bool equals(const DisplaySettings &ds) const;
};
diff --git a/src/plugins/texteditor/displaysettingspage.cpp b/src/plugins/texteditor/displaysettingspage.cpp
index 6a30c609c5..27d00766c5 100644
--- a/src/plugins/texteditor/displaysettingspage.cpp
+++ b/src/plugins/texteditor/displaysettingspage.cpp
@@ -93,7 +93,8 @@ QWidget *DisplaySettingsPage::createPage(QWidget *parent)
<< ' ' << m_d->m_page.animateMatchingParentheses->text()
<< ' ' << m_d->m_page.enableTextWrapping->text()
<< ' ' << m_d->m_page.autoFoldFirstComment->text()
- << ' ' << m_d->m_page.centerOnScroll->text();
+ << ' ' << m_d->m_page.centerOnScroll->text()
+ << ' ' << m_d->m_page.integrateDocsIntoTooltips->text();
m_d->m_searchKeywords.remove(QLatin1Char('&'));
}
return w;
@@ -121,6 +122,7 @@ void DisplaySettingsPage::settingsFromUI(DisplaySettings &displaySettings) const
displaySettings.m_markTextChanges = m_d->m_page.markTextChanges->isChecked();
displaySettings.m_autoFoldFirstComment = m_d->m_page.autoFoldFirstComment->isChecked();
displaySettings.m_centerCursorOnScroll = m_d->m_page.centerOnScroll->isChecked();
+ displaySettings.m_integrateDocsIntoTooltips = m_d->m_page.integrateDocsIntoTooltips->isChecked();
}
void DisplaySettingsPage::settingsToUI()
@@ -138,6 +140,7 @@ void DisplaySettingsPage::settingsToUI()
m_d->m_page.markTextChanges->setChecked(displaySettings.m_markTextChanges);
m_d->m_page.autoFoldFirstComment->setChecked(displaySettings.m_autoFoldFirstComment);
m_d->m_page.centerOnScroll->setChecked(displaySettings.m_centerCursorOnScroll);
+ m_d->m_page.integrateDocsIntoTooltips->setChecked(displaySettings.m_integrateDocsIntoTooltips);
}
const DisplaySettings &DisplaySettingsPage::displaySettings() const
diff --git a/src/plugins/texteditor/displaysettingspage.ui b/src/plugins/texteditor/displaysettingspage.ui
index c402d98a64..28e6b71397 100644
--- a/src/plugins/texteditor/displaysettingspage.ui
+++ b/src/plugins/texteditor/displaysettingspage.ui
@@ -96,6 +96,13 @@
</property>
</widget>
</item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="integrateDocsIntoTooltips">
+ <property name="text">
+ <string>Integrate Qt docs into tooltips</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp
index 736bc78ae4..51f9eb3fe8 100644
--- a/src/plugins/texteditor/findincurrentfile.cpp
+++ b/src/plugins/texteditor/findincurrentfile.cpp
@@ -57,7 +57,7 @@ QString FindInCurrentFile::id() const
return "Current File";
}
-QString FindInCurrentFile::name() const
+QString FindInCurrentFile::displayName() const
{
return tr("Current File");
}
diff --git a/src/plugins/texteditor/findincurrentfile.h b/src/plugins/texteditor/findincurrentfile.h
index 7c6f28c93a..b532e4cfa7 100644
--- a/src/plugins/texteditor/findincurrentfile.h
+++ b/src/plugins/texteditor/findincurrentfile.h
@@ -54,7 +54,7 @@ public:
explicit FindInCurrentFile(Find::SearchResultWindow *resultWindow);
QString id() const;
- QString name() const;
+ QString displayName() const;
QKeySequence defaultShortcut() const;
bool isEnabled() const;
QWidget *createConfigWidget();
diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp
index 963cb0dea1..a2d45a06b5 100644
--- a/src/plugins/texteditor/findinfiles.cpp
+++ b/src/plugins/texteditor/findinfiles.cpp
@@ -52,7 +52,7 @@ QString FindInFiles::id() const
return "Files on Disk";
}
-QString FindInFiles::name() const
+QString FindInFiles::displayName() const
{
return tr("Files on File System");
}
diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h
index baf899439d..45323c1c1d 100644
--- a/src/plugins/texteditor/findinfiles.h
+++ b/src/plugins/texteditor/findinfiles.h
@@ -52,7 +52,7 @@ public:
explicit FindInFiles(Find::SearchResultWindow *resultWindow);
QString id() const;
- QString name() const;
+ QString displayName() const;
QKeySequence defaultShortcut() const;
void findAll(const QString &txt, QTextDocument::FindFlags findFlags);
QWidget *createConfigWidget();
diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp
index 00f51ade2b..98de86d7fa 100644
--- a/src/plugins/texteditor/generichighlighter/highlighter.cpp
+++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp
@@ -53,7 +53,7 @@ namespace {
const Highlighter::KateFormatMap Highlighter::m_kateFormats;
Highlighter::Highlighter(QTextDocument *parent) :
- QSyntaxHighlighter(parent),
+ TextEditor::SyntaxHighlighter(parent),
m_regionDepth(0),
m_indentationBasedFolding(false),
m_tabSettings(0),
diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h
index ee3b94019c..a7135982f2 100644
--- a/src/plugins/texteditor/generichighlighter/highlighter.h
+++ b/src/plugins/texteditor/generichighlighter/highlighter.h
@@ -31,6 +31,7 @@
#define HIGHLIGHTER_H
#include "basetextdocumentlayout.h"
+#include <texteditor/syntaxhighlighter.h>
#include <QtCore/QString>
#include <QtCore/QVector>
@@ -52,8 +53,10 @@ class Context;
class HighlightDefinition;
class ProgressData;
-class Highlighter : public QSyntaxHighlighter
+class Highlighter : public TextEditor::SyntaxHighlighter
{
+ Q_OBJECT
+
public:
Highlighter(QTextDocument *parent = 0);
virtual ~Highlighter();
diff --git a/src/plugins/texteditor/icompletioncollector.h b/src/plugins/texteditor/icompletioncollector.h
index 354e45c75d..dc8549635b 100644
--- a/src/plugins/texteditor/icompletioncollector.h
+++ b/src/plugins/texteditor/icompletioncollector.h
@@ -111,9 +111,19 @@ public:
*/
virtual void completions(QList<CompletionItem> *completions) = 0;
- /* This method should complete the given completion item.
+ /**
+ * This method should return true when the given typed character should cause
+ * the selected completion item to be completed.
*/
- virtual void complete(const CompletionItem &item) = 0;
+ virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar) = 0;
+
+ /**
+ * This method should complete the given completion item.
+ *
+ * \param typedChar Non-null when completion was triggered by typing a
+ * character. Possible values depend on typedCharCompletes()
+ */
+ virtual void complete(const CompletionItem &item, QChar typedChar) = 0;
/* This method gives the completion collector a chance to partially complete
* based on a set of items. The general use case is to complete the common
@@ -153,6 +163,17 @@ public:
IQuickFixCollector(QObject *parent = 0) : ICompletionCollector(parent) {}
virtual ~IQuickFixCollector() {}
+ virtual bool typedCharCompletes(const CompletionItem &, QChar)
+ { return false; }
+
+ virtual void fix(const TextEditor::CompletionItem &item) = 0;
+
+ virtual void complete(const CompletionItem &item, QChar typedChar)
+ {
+ Q_UNUSED(typedChar)
+ fix(item);
+ }
+
virtual bool triggersCompletion(TextEditor::ITextEditable *)
{ return false; }
diff --git a/src/plugins/texteditor/outlinefactory.cpp b/src/plugins/texteditor/outlinefactory.cpp
index 421c31f440..f280458ce6 100644
--- a/src/plugins/texteditor/outlinefactory.cpp
+++ b/src/plugins/texteditor/outlinefactory.cpp
@@ -17,6 +17,11 @@ OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
{
QLabel *label = new QLabel(tr("No outline available"), this);
label->setAlignment(Qt::AlignCenter);
+
+ // set background to be white
+ label->setAutoFillBackground(true);
+ label->setBackgroundRole(QPalette::Base);
+
addWidget(label);
m_toggleSync = new QToolButton;
diff --git a/src/plugins/texteditor/quickfix.cpp b/src/plugins/texteditor/quickfix.cpp
index 9918ad4f5b..8e5e024750 100644
--- a/src/plugins/texteditor/quickfix.cpp
+++ b/src/plugins/texteditor/quickfix.cpp
@@ -171,7 +171,7 @@ void QuickFixCollector::completions(QList<TextEditor::CompletionItem> *quickFixI
}
}
-void QuickFixCollector::complete(const TextEditor::CompletionItem &item)
+void QuickFixCollector::fix(const TextEditor::CompletionItem &item)
{
const int index = item.data.toInt();
diff --git a/src/plugins/texteditor/quickfix.h b/src/plugins/texteditor/quickfix.h
index fd731c6c5a..2ad179e0ac 100644
--- a/src/plugins/texteditor/quickfix.h
+++ b/src/plugins/texteditor/quickfix.h
@@ -111,7 +111,7 @@ public:
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
virtual int startCompletion(TextEditor::ITextEditable *editor);
virtual void completions(QList<TextEditor::CompletionItem> *completions);
- virtual void complete(const TextEditor::CompletionItem &item);
+ virtual void fix(const TextEditor::CompletionItem &item);
virtual void cleanup();
virtual TextEditor::QuickFixState *initializeCompletion(TextEditor::ITextEditable *editable) = 0;
diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp
index 2bee7d79b0..94e7a9d1e3 100644
--- a/src/plugins/texteditor/refactoringchanges.cpp
+++ b/src/plugins/texteditor/refactoringchanges.cpp
@@ -39,6 +39,11 @@
using namespace TextEditor;
+RefactoringChanges::RefactoringChanges()
+ : m_lineToShow(0)
+ , m_columnToShow(0)
+{}
+
RefactoringChanges::~RefactoringChanges()
{}
@@ -149,12 +154,20 @@ QStringList RefactoringChanges::apply()
// ###
}
+ if (!m_fileNameToShow.isEmpty()) {
+ Core::EditorManager *editorManager = Core::EditorManager::instance();
+ BaseTextEditor *editor = editorForFile(m_fileNameToShow);
+ editorManager->activateEditor(editor->editableInterface());
+ if (m_lineToShow != -1)
+ editor->gotoLine(m_lineToShow + 1, m_columnToShow + 1);
+ }
+
return changed.toList();
}
int RefactoringChanges::positionInFile(const QString &fileName, int line, int column) const
{
- if (BaseTextEditor *editor = editorForFile(fileName)) {
+ if (BaseTextEditor *editor = editorForFile(fileName, true)) {
return editor->document()->findBlockByNumber(line).position() + column;
} else {
return -1;
@@ -191,3 +204,10 @@ BaseTextEditor *RefactoringChanges::editorForNewFile(const QString &fileName)
f.close();
return editorForFile(fileName, true);
}
+
+void RefactoringChanges::openEditor(const QString &fileName, int line, int column)
+{
+ m_fileNameToShow = fileName;
+ m_lineToShow = line;
+ m_columnToShow = column;
+}
diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h
index 93b576496c..f111123acd 100644
--- a/src/plugins/texteditor/refactoringchanges.h
+++ b/src/plugins/texteditor/refactoringchanges.h
@@ -46,6 +46,7 @@ public:
typedef Utils::ChangeSet::Range Range;
public:
+ RefactoringChanges();
virtual ~RefactoringChanges();
void createFile(const QString &fileName, const QString &contents);
@@ -63,10 +64,16 @@ public:
bool openIfClosed = false);
static BaseTextEditor *editorForNewFile(const QString &fileName);
+ /** line and column are zero-based */
+ void openEditor(const QString &fileName, int line, int column);
+
private:
QMap<QString, QString> m_contentsByCreatedFile;
QMap<QString, Utils::ChangeSet> m_changesByFile;
QMap<QString, QList<Range> > m_indentRangesByFile;
+ QString m_fileNameToShow;
+ int m_lineToShow;
+ int m_columnToShow;
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp
new file mode 100644
index 0000000000..a2245f7923
--- /dev/null
+++ b/src/plugins/texteditor/syntaxhighlighter.cpp
@@ -0,0 +1,743 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "syntaxhighlighter.h"
+
+#include <qtextdocument.h>
+#include <qtextlayout.h>
+#include <qpointer.h>
+#include <qtextobject.h>
+#include <qtextcursor.h>
+#include <qdebug.h>
+#include <qtextedit.h>
+#include <qtimer.h>
+
+using namespace TextEditor;
+
+class TextEditor::SyntaxHighlighterPrivate
+{
+ SyntaxHighlighter *q_ptr;
+ Q_DECLARE_PUBLIC(SyntaxHighlighter)
+public:
+ inline SyntaxHighlighterPrivate()
+ : q_ptr(0), rehighlightPending(false), inReformatBlocks(false)
+ {}
+
+ QPointer<QTextDocument> doc;
+
+ void _q_reformatBlocks(int from, int charsRemoved, int charsAdded);
+ void reformatBlocks(int from, int charsRemoved, int charsAdded);
+ void reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded);
+
+ inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) {
+ inReformatBlocks = true;
+ cursor.beginEditBlock();
+ int from = cursor.position();
+ cursor.movePosition(operation);
+ reformatBlocks(from, 0, cursor.position() - from);
+ cursor.endEditBlock();
+ inReformatBlocks = false;
+ }
+
+ inline void _q_delayedRehighlight() {
+ if (!rehighlightPending)
+ return;
+ rehighlightPending = false;
+ q_func()->rehighlight();
+ }
+
+ void applyFormatChanges(int from, int charsRemoved, int charsAdded);
+ QVector<QTextCharFormat> formatChanges;
+ QTextBlock currentBlock;
+ bool rehighlightPending;
+ bool inReformatBlocks;
+};
+
+static bool adjustRange(QTextLayout::FormatRange &range, int from, int charsRemoved, int charsAdded) {
+
+ if (range.start >= from) {
+ range.start += charsAdded - charsRemoved;
+ return true;
+ } else if (range.start + range.length > from) {
+ range.length += charsAdded - charsRemoved;
+ return true;
+ }
+ return false;
+}
+
+void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded)
+{
+ bool formatsChanged = false;
+
+ QTextLayout *layout = currentBlock.layout();
+
+ QList<QTextLayout::FormatRange> ranges = layout->additionalFormats();
+
+ const int preeditAreaStart = layout->preeditAreaPosition();
+ const int preeditAreaLength = layout->preeditAreaText().length();
+ bool doAdjustRange = currentBlock.contains(from);
+
+ if (preeditAreaLength != 0) {
+ QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
+ while (it != ranges.end()) {
+ if (it->format.property(QTextFormat::UserProperty).toBool()) {
+ if (doAdjustRange)
+ formatsChanged = adjustRange(*it, from - currentBlock.position(), charsRemoved, charsAdded)
+ || formatsChanged;
+ ++it;
+ } else if (it->start >= preeditAreaStart
+ && it->start + it->length <= preeditAreaStart + preeditAreaLength) {
+ ++it;
+ } else {
+ it = ranges.erase(it);
+ formatsChanged = true;
+ }
+ }
+ } else if (!ranges.isEmpty()) {
+ QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
+ while (it != ranges.end()) {
+ if (it->format.property(QTextFormat::UserProperty).toBool()) {
+ if (doAdjustRange)
+ formatsChanged = adjustRange(*it, from - currentBlock.position(), charsRemoved, charsAdded)
+ || formatsChanged;
+ ++it;
+ } else {
+ it = ranges.erase(it);
+ formatsChanged = true;
+ }
+ }
+ }
+
+ QTextCharFormat emptyFormat;
+
+ QTextLayout::FormatRange r;
+ r.start = -1;
+
+ int i = 0;
+ while (i < formatChanges.count()) {
+
+ while (i < formatChanges.count() && formatChanges.at(i) == emptyFormat)
+ ++i;
+
+ if (i >= formatChanges.count())
+ break;
+
+ r.start = i;
+ r.format = formatChanges.at(i);
+
+ while (i < formatChanges.count() && formatChanges.at(i) == r.format)
+ ++i;
+
+ if (i >= formatChanges.count())
+ break;
+
+ r.length = i - r.start;
+
+ if (preeditAreaLength != 0) {
+ if (r.start >= preeditAreaStart)
+ r.start += preeditAreaLength;
+ else if (r.start + r.length >= preeditAreaStart)
+ r.length += preeditAreaLength;
+ }
+
+ ranges << r;
+ formatsChanged = true;
+ r.start = -1;
+ }
+
+ if (r.start != -1) {
+ r.length = formatChanges.count() - r.start;
+
+ if (preeditAreaLength != 0) {
+ if (r.start >= preeditAreaStart)
+ r.start += preeditAreaLength;
+ else if (r.start + r.length >= preeditAreaStart)
+ r.length += preeditAreaLength;
+ }
+
+ ranges << r;
+ formatsChanged = true;
+ }
+
+ if (formatsChanged) {
+ layout->setAdditionalFormats(ranges);
+ doc->markContentsDirty(currentBlock.position(), currentBlock.length());
+ }
+}
+
+void SyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded)
+{
+ if (!inReformatBlocks)
+ reformatBlocks(from, charsRemoved, charsAdded);
+}
+
+void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded)
+{
+ rehighlightPending = false;
+
+ QTextBlock block = doc->findBlock(from);
+ if (!block.isValid())
+ return;
+
+ int endPosition;
+ QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0));
+ if (lastBlock.isValid())
+ endPosition = lastBlock.position() + lastBlock.length();
+ else
+ endPosition = doc->lastBlock().position() + doc->lastBlock().length(); //doc->docHandle()->length();
+
+ bool forceHighlightOfNextBlock = false;
+
+ while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
+ const int stateBeforeHighlight = block.userState();
+
+ reformatBlock(block, from, charsRemoved, charsAdded);
+
+ forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
+
+ block = block.next();
+ }
+
+ formatChanges.clear();
+}
+
+void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block, int from, int charsRemoved, int charsAdded)
+{
+ Q_Q(SyntaxHighlighter);
+
+ Q_ASSERT_X(!currentBlock.isValid(), "SyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively");
+
+ currentBlock = block;
+
+ formatChanges.fill(QTextCharFormat(), block.length() - 1);
+ q->highlightBlock(block.text());
+ applyFormatChanges(from, charsRemoved, charsAdded);
+
+ currentBlock = QTextBlock();
+}
+
+/*!
+ \class SyntaxHighlighter
+ \reentrant
+
+ \brief The SyntaxHighlighter class allows you to define syntax
+ highlighting rules, and in addition you can use the class to query
+ a document's current formatting or user data.
+
+ \since 4.1
+
+ \ingroup richtext-processing
+
+ The SyntaxHighlighter class is a base class for implementing
+ QTextEdit syntax highlighters. A syntax highligher automatically
+ highlights parts of the text in a QTextEdit, or more generally in
+ a QTextDocument. Syntax highlighters are often used when the user
+ is entering text in a specific format (for example source code)
+ and help the user to read the text and identify syntax errors.
+
+ To provide your own syntax highlighting, you must subclass
+ SyntaxHighlighter and reimplement highlightBlock().
+
+ When you create an instance of your SyntaxHighlighter subclass,
+ pass it the QTextEdit or QTextDocument that you want the syntax
+ highlighting to be applied to. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 0
+
+ After this your highlightBlock() function will be called
+ automatically whenever necessary. Use your highlightBlock()
+ function to apply formatting (e.g. setting the font and color) to
+ the text that is passed to it. SyntaxHighlighter provides the
+ setFormat() function which applies a given QTextCharFormat on
+ the current text block. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 1
+
+ Some syntaxes can have constructs that span several text
+ blocks. For example, a C++ syntax highlighter should be able to
+ cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
+ these cases it is necessary to know the end state of the previous
+ text block (e.g. "in comment").
+
+ Inside your highlightBlock() implementation you can query the end
+ state of the previous text block using the previousBlockState()
+ function. After parsing the block you can save the last state
+ using setCurrentBlockState().
+
+ The currentBlockState() and previousBlockState() functions return
+ an int value. If no state is set, the returned value is -1. You
+ can designate any other value to identify any given state using
+ the setCurrentBlockState() function. Once the state is set the
+ QTextBlock keeps that value until it is set set again or until the
+ corresponding paragraph of text is deleted.
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment":
+
+ \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 2
+
+ In the example above, we first set the current block state to
+ 0. Then, if the previous block ended within a comment, we higlight
+ from the beginning of the current block (\c {startIndex =
+ 0}). Otherwise, we search for the given start expression. If the
+ specified end expression cannot be found in the text block, we
+ change the current block state by calling setCurrentBlockState(),
+ and make sure that the rest of the block is higlighted.
+
+ In addition you can query the current formatting and user data
+ using the format() and currentBlockUserData() functions
+ respectively. You can also attach user data to the current text
+ block using the setCurrentBlockUserData() function.
+ QTextBlockUserData can be used to store custom settings. In the
+ case of syntax highlighting, it is in particular interesting as
+ cache storage for information that you may figure out while
+ parsing the paragraph's text. For an example, see the
+ setCurrentBlockUserData() documentation.
+
+ \sa QTextEdit, {Syntax Highlighter Example}
+*/
+
+/*!
+ Constructs a SyntaxHighlighter with the given \a parent.
+*/
+SyntaxHighlighter::SyntaxHighlighter(QObject *parent)
+ : QObject(parent), d_ptr(new SyntaxHighlighterPrivate)
+{
+ d_ptr->q_ptr = this;
+}
+
+/*!
+ Constructs a SyntaxHighlighter and installs it on \a parent.
+ The specified QTextDocument also becomes the owner of the
+ SyntaxHighlighter.
+*/
+SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent)
+ : QObject(parent), d_ptr(new SyntaxHighlighterPrivate)
+{
+ d_ptr->q_ptr = this;
+ setDocument(parent);
+}
+
+/*!
+ Constructs a SyntaxHighlighter and installs it on \a parent 's
+ QTextDocument. The specified QTextEdit also becomes the owner of
+ the SyntaxHighlighter.
+*/
+SyntaxHighlighter::SyntaxHighlighter(QTextEdit *parent)
+ : QObject(parent), d_ptr(new SyntaxHighlighterPrivate)
+{
+ d_ptr->q_ptr = this;
+ setDocument(parent->document());
+}
+
+/*!
+ Destructor. Uninstalls this syntax highlighter from the text document.
+*/
+SyntaxHighlighter::~SyntaxHighlighter()
+{
+ setDocument(0);
+}
+
+/*!
+ Installs the syntax highlighter on the given QTextDocument \a doc.
+ A SyntaxHighlighter can only be used with one document at a time.
+*/
+void SyntaxHighlighter::setDocument(QTextDocument *doc)
+{
+ Q_D(SyntaxHighlighter);
+ if (d->doc) {
+ disconnect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+
+ QTextCursor cursor(d->doc);
+ cursor.beginEditBlock();
+ for (QTextBlock blk = d->doc->begin(); blk.isValid(); blk = blk.next())
+ blk.layout()->clearAdditionalFormats();
+ cursor.endEditBlock();
+ }
+ d->doc = doc;
+ if (d->doc) {
+ connect(d->doc, SIGNAL(contentsChange(int,int,int)),
+ this, SLOT(_q_reformatBlocks(int,int,int)));
+ d->rehighlightPending = true;
+ QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight()));
+ }
+}
+
+/*!
+ Returns the QTextDocument on which this syntax highlighter is
+ installed.
+*/
+QTextDocument *SyntaxHighlighter::document() const
+{
+ Q_D(const SyntaxHighlighter);
+ return d->doc;
+}
+
+/*!
+ \since 4.2
+
+ Reapplies the highlighting to the whole document.
+
+ \sa rehighlightBlock()
+*/
+void SyntaxHighlighter::rehighlight()
+{
+ Q_D(SyntaxHighlighter);
+ if (!d->doc)
+ return;
+
+ QTextCursor cursor(d->doc);
+ d->rehighlight(cursor, QTextCursor::End);
+}
+
+/*!
+ \since 4.6
+
+ Reapplies the highlighting to the given QTextBlock \a block.
+
+ \sa rehighlight()
+*/
+void SyntaxHighlighter::rehighlightBlock(const QTextBlock &block)
+{
+ Q_D(SyntaxHighlighter);
+ if (!d->doc || !block.isValid() || block.document() != d->doc)
+ return;
+
+ const bool rehighlightPending = d->rehighlightPending;
+
+ QTextCursor cursor(block);
+ d->rehighlight(cursor, QTextCursor::EndOfBlock);
+
+ if (rehighlightPending)
+ d->rehighlightPending = rehighlightPending;
+}
+
+/*!
+ \fn void SyntaxHighlighter::highlightBlock(const QString &text)
+
+ Highlights the given text block. This function is called when
+ necessary by the rich text engine, i.e. on text blocks which have
+ changed.
+
+ To provide your own syntax highlighting, you must subclass
+ SyntaxHighlighter and reimplement highlightBlock(). In your
+ reimplementation you should parse the block's \a text and call
+ setFormat() as often as necessary to apply any font and color
+ changes that you require. For example:
+
+ \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 3
+
+ Some syntaxes can have constructs that span several text
+ blocks. For example, a C++ syntax highlighter should be able to
+ cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
+ these cases it is necessary to know the end state of the previous
+ text block (e.g. "in comment").
+
+ Inside your highlightBlock() implementation you can query the end
+ state of the previous text block using the previousBlockState()
+ function. After parsing the block you can save the last state
+ using setCurrentBlockState().
+
+ The currentBlockState() and previousBlockState() functions return
+ an int value. If no state is set, the returned value is -1. You
+ can designate any other value to identify any given state using
+ the setCurrentBlockState() function. Once the state is set the
+ QTextBlock keeps that value until it is set set again or until the
+ corresponding paragraph of text gets deleted.
+
+ For example, if you're writing a simple C++ syntax highlighter,
+ you might designate 1 to signify "in comment". For a text block
+ that ended in the middle of a comment you'd set 1 using
+ setCurrentBlockState, and for other paragraphs you'd set 0.
+ In your parsing code if the return value of previousBlockState()
+ is 1, you would highlight the text as a C++ comment until you
+ reached the closing \c{*}\c{/}.
+
+ \sa previousBlockState(), setFormat(), setCurrentBlockState()
+*/
+
+/*!
+ This function is applied to the syntax highlighter's current text
+ block (i.e. the text that is passed to the highlightBlock()
+ function).
+
+ The specified \a format is applied to the text from the \a start
+ position for a length of \a count characters (if \a count is 0,
+ nothing is done). The formatting properties set in \a format are
+ merged at display time with the formatting information stored
+ directly in the document, for example as previously set with
+ QTextCursor's functions. Note that the document itself remains
+ unmodified by the format set through this function.
+
+ \sa format(), highlightBlock()
+*/
+void SyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format)
+{
+ Q_D(SyntaxHighlighter);
+ if (start < 0 || start >= d->formatChanges.count())
+ return;
+
+ const int end = qMin(start + count, d->formatChanges.count());
+ for (int i = start; i < end; ++i)
+ d->formatChanges[i] = format;
+}
+
+/*!
+ \overload
+
+ The specified \a color is applied to the current text block from
+ the \a start position for a length of \a count characters.
+
+ The other attributes of the current text block, e.g. the font and
+ background color, are reset to default values.
+
+ \sa format(), highlightBlock()
+*/
+void SyntaxHighlighter::setFormat(int start, int count, const QColor &color)
+{
+ QTextCharFormat format;
+ format.setForeground(color);
+ setFormat(start, count, format);
+}
+
+/*!
+ \overload
+
+ The specified \a font is applied to the current text block from
+ the \a start position for a length of \a count characters.
+
+ The other attributes of the current text block, e.g. the font and
+ background color, are reset to default values.
+
+ \sa format(), highlightBlock()
+*/
+void SyntaxHighlighter::setFormat(int start, int count, const QFont &font)
+{
+ QTextCharFormat format;
+ format.setFont(font);
+ setFormat(start, count, format);
+}
+
+/*!
+ \fn QTextCharFormat SyntaxHighlighter::format(int position) const
+
+ Returns the format at \a position inside the syntax highlighter's
+ current text block.
+*/
+QTextCharFormat SyntaxHighlighter::format(int pos) const
+{
+ Q_D(const SyntaxHighlighter);
+ if (pos < 0 || pos >= d->formatChanges.count())
+ return QTextCharFormat();
+ return d->formatChanges.at(pos);
+}
+
+/*!
+ Returns the end state of the text block previous to the
+ syntax highlighter's current block. If no value was
+ previously set, the returned value is -1.
+
+ \sa highlightBlock(), setCurrentBlockState()
+*/
+int SyntaxHighlighter::previousBlockState() const
+{
+ Q_D(const SyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return -1;
+
+ const QTextBlock previous = d->currentBlock.previous();
+ if (!previous.isValid())
+ return -1;
+
+ return previous.userState();
+}
+
+/*!
+ Returns the state of the current text block. If no value is set,
+ the returned value is -1.
+*/
+int SyntaxHighlighter::currentBlockState() const
+{
+ Q_D(const SyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return -1;
+
+ return d->currentBlock.userState();
+}
+
+/*!
+ Sets the state of the current text block to \a newState.
+
+ \sa highlightBlock()
+*/
+void SyntaxHighlighter::setCurrentBlockState(int newState)
+{
+ Q_D(SyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return;
+
+ d->currentBlock.setUserState(newState);
+}
+
+/*!
+ Attaches the given \a data to the current text block. The
+ ownership is passed to the underlying text document, i.e. the
+ provided QTextBlockUserData object will be deleted if the
+ corresponding text block gets deleted.
+
+ QTextBlockUserData can be used to store custom settings. In the
+ case of syntax highlighting, it is in particular interesting as
+ cache storage for information that you may figure out while
+ parsing the paragraph's text.
+
+ For example while parsing the text, you can keep track of
+ parenthesis characters that you encounter ('{[(' and the like),
+ and store their relative position and the actual QChar in a simple
+ class derived from QTextBlockUserData:
+
+ \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 4
+
+ During cursor navigation in the associated editor, you can ask the
+ current QTextBlock (retrieved using the QTextCursor::block()
+ function) if it has a user data object set and cast it to your \c
+ BlockData object. Then you can check if the current cursor
+ position matches with a previously recorded parenthesis position,
+ and, depending on the type of parenthesis (opening or closing),
+ find the next opening or closing parenthesis on the same level.
+
+ In this way you can do a visual parenthesis matching and highlight
+ from the current cursor position to the matching parenthesis. That
+ makes it easier to spot a missing parenthesis in your code and to
+ find where a corresponding opening/closing parenthesis is when
+ editing parenthesis intensive code.
+
+ \sa QTextBlock::setUserData()
+*/
+void SyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data)
+{
+ Q_D(SyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return;
+
+ d->currentBlock.setUserData(data);
+}
+
+/*!
+ Returns the QTextBlockUserData object previously attached to the
+ current text block.
+
+ \sa QTextBlock::userData(), setCurrentBlockUserData()
+*/
+QTextBlockUserData *SyntaxHighlighter::currentBlockUserData() const
+{
+ Q_D(const SyntaxHighlighter);
+ if (!d->currentBlock.isValid())
+ return 0;
+
+ return d->currentBlock.userData();
+}
+
+/*!
+ \since 4.4
+
+ Returns the current text block.
+*/
+QTextBlock SyntaxHighlighter::currentBlock() const
+{
+ Q_D(const SyntaxHighlighter);
+ return d->currentBlock;
+}
+
+void SyntaxHighlighter::setExtraAdditionalFormats(const QTextBlock& block,
+ const QList<QTextLayout::FormatRange> &formats)
+{
+
+// qDebug() << "setAdditionalFormats() on block" << block.blockNumber();
+// for (int i = 0; i < overrides.count(); ++i)
+// qDebug() << " from " << overrides.at(i).start << "length"
+// << overrides.at(i).length
+// << "color:" << overrides.at(i).format.foreground().color();
+ Q_D(SyntaxHighlighter);
+
+ if (block.layout() == 0)
+ return;
+
+ QList<QTextLayout::FormatRange> all = block.layout()->additionalFormats();
+
+ bool modified = false;
+
+ int skip = 0;
+
+ QList<QTextLayout::FormatRange>::Iterator it = all.begin();
+ while (it != all.end()) {
+ if (it->format.property(QTextFormat::UserProperty).toBool()) {
+ if (skip < formats.size()
+ && it->start == formats.at(skip).start
+ && it->length == formats.at(skip).length) {
+ ++skip;
+ ++it;
+ } else {
+ it = all.erase(it);
+ modified = true;
+ }
+ } else {
+ ++it;
+ }
+ }
+
+ if (!modified && skip == formats.length())
+ return; // skip'em all
+
+ for (int i = skip; i < formats.length(); ++i) {
+ QTextLayout::FormatRange range = formats.at(i);
+ range.format.setProperty(QTextFormat::UserProperty, true);
+ all.append(range);
+ }
+
+ bool wasInReformatBlocks = d->inReformatBlocks;
+ d->inReformatBlocks = true;
+ block.layout()->setAdditionalFormats(all);
+ document()->markContentsDirty(block.position(), block.length()-1);
+ d->inReformatBlocks = wasInReformatBlocks;
+}
+
+#include "moc_syntaxhighlighter.cpp"
diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h
new file mode 100644
index 0000000000..5b6722b5a3
--- /dev/null
+++ b/src/plugins/texteditor/syntaxhighlighter.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEXTEDITOR_SYNTAXHIGHLIGHTER_H
+#define TEXTEDITOR_SYNTAXHIGHLIGHTER_H
+
+#include "texteditor_global.h"
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtGui/qtextobject.h>
+#include <QtGui/QTextLayout>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+class QSyntaxHighlighterPrivate;
+class QTextCharFormat;
+class QFont;
+class QColor;
+class QTextBlockUserData;
+class QTextEdit;
+QT_END_NAMESPACE
+
+namespace TextEditor {
+
+class SyntaxHighlighterPrivate;
+
+class TEXTEDITOR_EXPORT SyntaxHighlighter : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(SyntaxHighlighter)
+public:
+ SyntaxHighlighter(QObject *parent);
+ SyntaxHighlighter(QTextDocument *parent);
+ SyntaxHighlighter(QTextEdit *parent);
+ virtual ~SyntaxHighlighter();
+
+ void setDocument(QTextDocument *doc);
+ QTextDocument *document() const;
+
+ void setExtraAdditionalFormats(const QTextBlock& block, const QList<QTextLayout::FormatRange> &formats);
+
+public Q_SLOTS:
+ void rehighlight();
+ void rehighlightBlock(const QTextBlock &block);
+
+protected:
+ virtual void highlightBlock(const QString &text) = 0;
+
+ void setFormat(int start, int count, const QTextCharFormat &format);
+ void setFormat(int start, int count, const QColor &color);
+ void setFormat(int start, int count, const QFont &font);
+ QTextCharFormat format(int pos) const;
+
+ int previousBlockState() const;
+ int currentBlockState() const;
+ void setCurrentBlockState(int newState);
+
+ void setCurrentBlockUserData(QTextBlockUserData *data);
+ QTextBlockUserData *currentBlockUserData() const;
+
+ QTextBlock currentBlock() const;
+
+private:
+ Q_DISABLE_COPY(SyntaxHighlighter)
+ Q_PRIVATE_SLOT(d_ptr, void _q_reformatBlocks(int from, int charsRemoved, int charsAdded))
+ Q_PRIVATE_SLOT(d_ptr, void _q_delayedRehighlight())
+
+ QScopedPointer<SyntaxHighlighterPrivate> d_ptr;
+};
+
+} // end of namespace TextEditor
+
+#endif // TEXTEDITOR_SYNTAXHIGHLIGHTER_H
diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h
index 3a15787b09..ae63e116a8 100644
--- a/src/plugins/texteditor/tabsettings.h
+++ b/src/plugins/texteditor/tabsettings.h
@@ -59,6 +59,7 @@ struct TEXTEDITOR_EXPORT TabSettings
int lineIndentPosition(const QString &text) const;
int firstNonSpace(const QString &text) const;
+ inline bool onlySpace(const QString &text) const { return firstNonSpace(text) == text.length(); }
int columnAt(const QString &text, int position) const;
int spacesLeftFromPosition(const QString &text, int position) const;
int indentedColumn(int column, bool doIndent = true) const;
diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro
index 931f561490..55cb8c92c6 100644
--- a/src/plugins/texteditor/texteditor.pro
+++ b/src/plugins/texteditor/texteditor.pro
@@ -42,6 +42,7 @@ SOURCES += texteditorplugin.cpp \
normalindenter.cpp \
indenter.cpp \
quickfix.cpp \
+ syntaxhighlighter.cpp \
generichighlighter/itemdata.cpp \
generichighlighter/specificrules.cpp \
generichighlighter/rule.cpp \
@@ -103,6 +104,7 @@ HEADERS += texteditorplugin.h \
normalindenter.h \
indenter.h \
quickfix.h \
+ syntaxhighlighter.h \
generichighlighter/reuse.h \
generichighlighter/itemdata.h \
generichighlighter/specificrules.h \
diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp
index e314ae3de3..3c68486cac 100644
--- a/src/plugins/texteditor/texteditorplugin.cpp
+++ b/src/plugins/texteditor/texteditorplugin.cpp
@@ -157,7 +157,7 @@ void TextEditorPlugin::extensionsInitialized()
ExtensionSystem::PluginManager *pluginManager = ExtensionSystem::PluginManager::instance();
- m_searchResultWindow = pluginManager->getObject<Find::SearchResultWindow>();
+ m_searchResultWindow = Find::SearchResultWindow::instance();
m_outlineFactory->setWidgetFactories(pluginManager->getObjects<TextEditor::IOutlineWidgetFactory>());
diff --git a/src/plugins/vcsbase/baseannotationhighlighter.cpp b/src/plugins/vcsbase/baseannotationhighlighter.cpp
index 7ce6058453..b272ea03ae 100644
--- a/src/plugins/vcsbase/baseannotationhighlighter.cpp
+++ b/src/plugins/vcsbase/baseannotationhighlighter.cpp
@@ -47,7 +47,7 @@ struct BaseAnnotationHighlighterPrivate {
BaseAnnotationHighlighter::BaseAnnotationHighlighter(const ChangeNumbers &changeNumbers,
QTextDocument *document) :
- QSyntaxHighlighter(document),
+ TextEditor::SyntaxHighlighter(document),
m_d(new BaseAnnotationHighlighterPrivate)
{
setChangeNumbers(changeNumbers);
diff --git a/src/plugins/vcsbase/baseannotationhighlighter.h b/src/plugins/vcsbase/baseannotationhighlighter.h
index 4119adb109..2859745aed 100644
--- a/src/plugins/vcsbase/baseannotationhighlighter.h
+++ b/src/plugins/vcsbase/baseannotationhighlighter.h
@@ -31,7 +31,7 @@
#define BASEANNOTATIONHIGHLIGHTER_H
#include "vcsbase_global.h"
-
+#include <texteditor/syntaxhighlighter.h>
#include <QtCore/QMap>
#include <QtCore/QSet>
#include <QtGui/QSyntaxHighlighter>
@@ -47,7 +47,7 @@ struct BaseAnnotationHighlighterPrivate;
// 112: text1 <color 1>
// 113: text2 <color 2>
// 112: text3 <color 1>
-class VCSBASE_EXPORT BaseAnnotationHighlighter : public QSyntaxHighlighter
+class VCSBASE_EXPORT BaseAnnotationHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
public:
diff --git a/src/plugins/vcsbase/diffhighlighter.cpp b/src/plugins/vcsbase/diffhighlighter.cpp
index 7628763d62..6b1ca5dee1 100644
--- a/src/plugins/vcsbase/diffhighlighter.cpp
+++ b/src/plugins/vcsbase/diffhighlighter.cpp
@@ -88,7 +88,7 @@ DiffFormats DiffHighlighterPrivate::analyzeLine(const QString &text) const
// --- DiffHighlighter
DiffHighlighter::DiffHighlighter(const QRegExp &filePattern,
QTextDocument *document) :
- QSyntaxHighlighter(document),
+ TextEditor::SyntaxHighlighter(document),
m_d(new DiffHighlighterPrivate(filePattern))
{
}
diff --git a/src/plugins/vcsbase/diffhighlighter.h b/src/plugins/vcsbase/diffhighlighter.h
index a1a9c3d082..346b87ea3d 100644
--- a/src/plugins/vcsbase/diffhighlighter.h
+++ b/src/plugins/vcsbase/diffhighlighter.h
@@ -31,8 +31,7 @@
#define DIFFHIGHLIGHTER_H
#include "vcsbase_global.h"
-
-#include <QtGui/QSyntaxHighlighter>
+#include <texteditor/syntaxhighlighter.h>
#include <QtGui/QTextCharFormat>
#include <QtCore/QVector>
@@ -66,7 +65,7 @@ struct DiffHighlighterPrivate;
* \endcode
* */
-class VCSBASE_EXPORT DiffHighlighter : public QSyntaxHighlighter
+class VCSBASE_EXPORT DiffHighlighter : public TextEditor::SyntaxHighlighter
{
Q_OBJECT
public:
diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp
index 5186570852..af18459009 100644
--- a/src/plugins/vcsbase/vcsbaseeditor.cpp
+++ b/src/plugins/vcsbase/vcsbaseeditor.cpp
@@ -627,7 +627,11 @@ void VCSBaseEditor::jumpToChangeFromDiff(QTextCursor cursor)
void VCSBaseEditor::setPlainTextData(const QByteArray &data)
{
- setPlainText(codec()->toUnicode(data));
+ if (data.size() > Core::EditorManager::maxTextFileSize()) {
+ setPlainText(msgTextTooLarge(data.size()));
+ } else {
+ setPlainText(codec()->toUnicode(data));
+ }
}
void VCSBaseEditor::setFontSettings(const TextEditor::FontSettings &fs)