/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** No Commercial Usage ** ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** **************************************************************************/ #include "gettingstartedwelcomepagewidget.h" #include "ui_gettingstartedwelcomepagewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Qt4ProjectManager { namespace Internal { const char ExamplePathPropertyName[] = "__qt_ExamplePath"; const char HelpPathPropertyName[] = "__qt_HelpPath"; const char QmlMainFileName[] = "__qt_QmlMainFileName"; void PixmapDownloader::populatePixmap(QNetworkReply *reply) { QImage image; image.loadFromData(reply->readAll()); m_label->setScaledContents(false); m_label->setPixmap(QPixmap::fromImage(image)); deleteLater(); } GettingStartedWelcomePageWidget::GettingStartedWelcomePageWidget(QWidget *parent) : QWidget(parent), ui(new Ui::GettingStartedWelcomePageWidget), m_currentFeature(0), m_rssFetcher(0) { ui->setupUi(this); ui->didYouKnowTextBrowser->viewport()->setAutoFillBackground(false); ui->detailsLabel->hide(); connect(ui->tutorialTreeWidget, SIGNAL(activated(QString)), SLOT(slotOpenHelpPage(const QString&))); QFontMetrics fm = fontMetrics(); const int margins = 30; int width = ui->tutorialTreeWidget->minimumWidth() - margins; QString itemText = tr("The Qt Creator User Interface"); QString url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-quick-tour.html"); ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText); itemText = tr("Building and Running an Example"); url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-build-example-application.html?view=split"); ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText); itemText = tr("Creating a Qt C++ Application"); url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-writing-program.html?view=split"); ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText); itemText = tr("Creating a Mobile Application"); url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-mobile-example.html?view=split"); ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText); itemText = tr("Creating a Qt Quick Application"); url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-qml-application.html?view=split"); ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText); srand(QDateTime::currentDateTime().toTime_t()); QStringList tips = tipsOfTheDay(); m_currentTip = rand()%tips.count(); QTextDocument *doc = ui->didYouKnowTextBrowser->document(); doc->setDefaultStyleSheet("* {color:black;};"); ui->didYouKnowTextBrowser->setDocument(doc); ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip)); connect(ui->nextTipBtn, SIGNAL(clicked()), this, SLOT(slotNextTip())); connect(ui->prevTipBtn, SIGNAL(clicked()), this, SLOT(slotPrevTip())); connect(ui->openProjectButton, SIGNAL(clicked()), ProjectExplorer::ProjectExplorerPlugin::instance(), SLOT(openOpenProjectDialog())); connect(ui->createNewProjectButton, SIGNAL(clicked()), this, SLOT(slotCreateNewProject())); ui->createNewProjectButton->setIcon( QIcon::fromTheme(QLatin1String("document-new"), ui->createNewProjectButton->icon())); ui->openProjectButton->setIcon( QIcon::fromTheme(QLatin1String("document-open"), ui->openProjectButton->icon())); m_rssFetcher = new Core::RssFetcher; connect (m_rssFetcher, SIGNAL(rssItemReady(Core::RssItem)), SLOT(addToFeatures(Core::RssItem))); connect (m_rssFetcher, SIGNAL(finished(bool)), SLOT(showFeature()), Qt::QueuedConnection); connect(this, SIGNAL(startRssFetching(QUrl)), m_rssFetcher, SLOT(fetch(QUrl)), Qt::QueuedConnection); m_rssFetcher->start(QThread::LowestPriority); const QString featureRssFile = Core::ICore::instance()->resourcePath()+QLatin1String("/rss/featured.rss"); emit startRssFetching(QUrl::fromLocalFile(featureRssFile)); ui->nextFeatureBtn->hide(); ui->prevFeatureBtn->hide(); connect(ui->nextFeatureBtn, SIGNAL(clicked()), this, SLOT(slotNextFeature())); connect(ui->prevFeatureBtn, SIGNAL(clicked()), this, SLOT(slotPrevFeature())); } GettingStartedWelcomePageWidget::~GettingStartedWelcomePageWidget() { m_rssFetcher->exit(); m_rssFetcher->wait(); delete m_rssFetcher; delete ui; } void GettingStartedWelcomePageWidget::parseXmlFile(QFile *file, QMenuHash &cppSubMenuHash, QMenuHash &qmlSubMenuHash, const QString &examplePath, const QString &sourcePath) { QMenu *cppSubMenu = 0; QMenu *qmlSubMenu = 0; bool inExamples = false; QString dirName; QXmlStreamReader reader(file); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: if (reader.name() == QLatin1String("category")) { QString name = reader.attributes().value(QLatin1String("name")).toString(); if (name.contains(QLatin1String("Tutorial"))) break; dirName = reader.attributes().value(QLatin1String("dirname")).toString(); if (!cppSubMenuHash.contains(dirName)) { cppSubMenu = new QMenu(name, this); cppSubMenu->setObjectName(dirName); cppSubMenuHash.insert(dirName, cppSubMenu); } else { cppSubMenu = cppSubMenuHash.value(dirName); } if (!qmlSubMenuHash.contains(dirName)) { qmlSubMenu = new QMenu(name, this); qmlSubMenu->setObjectName(dirName); qmlSubMenuHash.insert(dirName, qmlSubMenu); } else { qmlSubMenu = qmlSubMenuHash.value(dirName); } inExamples = true; } if (inExamples && reader.name() == QLatin1String("example")) { const QChar slash = QLatin1Char('/'); const QString name = reader.attributes().value(QLatin1String("name")).toString(); const bool isQml = reader.attributes().value(QLatin1String("qml")).toString() == "true"; const QString localDir = reader.attributes().value(QLatin1String("filename")).toString(); const QString fileName = localDir.section('/', -1); QString replacedFileName = fileName; replacedFileName.replace(QLatin1Char('-'), QString()); QString relativeProPath = slash + dirName + slash + localDir + slash + replacedFileName; QString finalFileName = examplePath + relativeProPath + QLatin1String(".pro"); if (!QFile::exists(finalFileName)) finalFileName = sourcePath + QLatin1String("/examples") + relativeProPath + QLatin1String(".pro"); if (isQml && !QFile::exists(finalFileName)) { // maybe it's an old-style QML project? relativeProPath = slash + dirName + slash + localDir + slash + fileName; finalFileName = examplePath + relativeProPath + QLatin1String(".qmlproject"); if (!QFile::exists(finalFileName)) finalFileName = sourcePath + QLatin1String("/examples") + relativeProPath + QLatin1String(".qmlproject");; } if (!QFile::exists(finalFileName)) break; QString dirNameforHelp = dirName; dirNameforHelp.replace(slash, QLatin1Char('-')); QString helpPath = QLatin1String("qthelp://com.trolltech.qt/qdoc/") + dirNameforHelp + QLatin1Char('-') + fileName + QLatin1String(".html"); QAction *exampleAction = 0; QAction *beforeAction = 0; bool duplicate = false; QMenu *subMenu; subMenu = isQml ? qmlSubMenu : cppSubMenu; foreach (beforeAction, subMenu->actions()) { int res = beforeAction->text().compare(name, Qt::CaseInsensitive); if (res==0) { duplicate = true; break; } else if (res<0) beforeAction = 0; else if (res>0) { break; } } if (!duplicate) { exampleAction = new QAction(name, subMenu); subMenu->insertAction(beforeAction, exampleAction); connect(exampleAction, SIGNAL(triggered()), SLOT(slotOpenExample())); exampleAction->setProperty(ExamplePathPropertyName, finalFileName); exampleAction->setProperty(HelpPathPropertyName, helpPath); if (isQml) exampleAction->setProperty(QmlMainFileName, fileName); } } break; case QXmlStreamReader::EndElement: if (inExamples && reader.name() == QLatin1String("category")) { if (cppSubMenu->actions().isEmpty()) delete cppSubMenuHash.take(dirName); if (qmlSubMenu->actions().isEmpty()) delete qmlSubMenuHash.take(dirName); inExamples = false; } break; default: break; } } } bool menuEntryCompare(QMenu* first, QMenu* second) { return (QString::localeAwareCompare(first->title(), second->title()) < 0); } void GettingStartedWelcomePageWidget::updateExamples(const QString &examplePath, const QString &demosPath, const QString &sourcePath) { QString demoXml = demosPath + "/qtdemo/xml/examples.xml"; if (!QFile::exists(demoXml)) { demoXml = sourcePath + "/demos/qtdemo/xml/examples.xml"; if (!QFile::exists(demoXml)) return; } QMenuHash cppSubMenuHash; QMenuHash qmlSubMenuHash; const QString dropDownLabel = tr("Choose an Example..."); QMenu *cppMenu = new QMenu(ui->cppExamplesButton); ui->cppExamplesButton->setMenu(cppMenu); QMenu *qmlMenu = new QMenu(ui->qmlExamplesButton); // let Creator's files take precedence QString localQmlExamplesXml = Core::ICore::instance()->resourcePath()+QLatin1String("/examplebrowser/qmlexamples.xml"); QFile localDescriptions(localQmlExamplesXml); if (localDescriptions.open(QFile::ReadOnly)) { parseXmlFile(&localDescriptions, cppSubMenuHash, qmlSubMenuHash, examplePath, sourcePath); } QFile descriptions(demoXml); if (!descriptions.open(QFile::ReadOnly)) return; ui->cppExamplesButton->setEnabled(true); ui->cppExamplesButton->setText(dropDownLabel); parseXmlFile(&descriptions, cppSubMenuHash, qmlSubMenuHash, examplePath, sourcePath); QList cppSubMenus = cppSubMenuHash.values(); qSort(cppSubMenus.begin(), cppSubMenus.end(), menuEntryCompare); QList qmlSubMenus = qmlSubMenuHash.values(); qSort(qmlSubMenus.begin(), qmlSubMenus.end(), menuEntryCompare); foreach (QMenu *menu, cppSubMenus) cppMenu->addMenu(menu); foreach (QMenu *menu, qmlSubMenus) qmlMenu->addMenu(menu); if (!qmlMenu->isEmpty()) { ui->qmlExamplesButton->setMenu(qmlMenu); ui->qmlExamplesButton->setEnabled(true); ui->qmlExamplesButton->setText(dropDownLabel); } } namespace { void copyRecursive(const QDir& from, const QDir& to, const QString& dir) { QDir dest(to); dest.mkdir(dir); dest.cd(dir); QDir src(from); src.cd(dir); foreach(const QFileInfo& roFile, src.entryInfoList(QDir::Files)) { QFile::copy(roFile.absoluteFilePath(), dest.absolutePath() + '/' + roFile.fileName()); } foreach(const QString& roDir, src.entryList(QDir::NoDotAndDotDot|QDir::Dirs)) { copyRecursive(src, dest, QDir(roDir).dirName()); } } } // namespace void GettingStartedWelcomePageWidget::slotOpenExample() { QAction *action = qobject_cast(sender()); if (!action) return; QString helpFile = action->property(HelpPathPropertyName).toString(); QString proFile = action->property(ExamplePathPropertyName).toString(); QString qmlMainFileName; bool isQmlProject = false; if (action->dynamicPropertyNames().contains(QmlMainFileName)) { qmlMainFileName = action->property(QmlMainFileName).toString(); isQmlProject = true; } QStringList files; QFileInfo proFileInfo(proFile); // If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail if (!proFileInfo.isWritable()) { QDialog d; QGridLayout *lay = new QGridLayout(&d); QLabel *descrLbl = new QLabel; d.setWindowTitle(tr("Copy Project to writable Location?")); descrLbl->setTextFormat(Qt::RichText); descrLbl->setWordWrap(true); descrLbl->setText(tr("

The project you are about to open is located in the " "write-protected location:

%1
" "

Please select a writable location below and click \"Copy Project and Open\" " "to open a modifiable copy of the project or click \"Keep Project and Open\" " "to open the project in location.

Note: You will not " "be able to alter or compile your project in the current location.

") .arg(QDir::toNativeSeparators(proFileInfo.dir().absolutePath()))); lay->addWidget(descrLbl, 0, 0, 1, 2); QLabel *txt = new QLabel(tr("&Location:")); Utils::PathChooser *chooser = new Utils::PathChooser; txt->setBuddy(chooser); chooser->setExpectedKind(Utils::PathChooser::Directory); QSettings *settings = Core::ICore::instance()->settings(); chooser->setPath(settings->value( QString::fromLatin1("General/ProjectsFallbackRoot"), QDir::homePath()).toString()); lay->addWidget(txt, 1, 0); lay->addWidget(chooser, 1, 1); QDialogButtonBox *bb = new QDialogButtonBox; connect(bb, SIGNAL(accepted()), &d, SLOT(accept())); connect(bb, SIGNAL(rejected()), &d, SLOT(reject())); QPushButton *copyBtn = bb->addButton(tr("&Copy Project and Open"), QDialogButtonBox::AcceptRole); copyBtn->setDefault(true); bb->addButton(tr("&Keep Project and Open"), QDialogButtonBox::RejectRole); lay->addWidget(bb, 2, 0, 1, 2); connect(chooser, SIGNAL(validChanged(bool)), copyBtn, SLOT(setEnabled(bool))); if (d.exec() == QDialog::Accepted) { QString exampleDirName = proFileInfo.dir().dirName(); QString toDir = chooser->path(); settings->setValue(QString::fromLatin1("General/ProjectsFallbackRoot"), toDir); QDir toDirWithExamplesDir(toDir); if (toDirWithExamplesDir.cd(exampleDirName)) { toDirWithExamplesDir.cdUp(); // step out, just to not be in the way QMessageBox::warning(topLevelWidget(), tr("Warning"), tr("The specified location already exists. " "Please specify a valid location."), QMessageBox::Ok, QMessageBox::NoButton); return; } else { QDir from = proFileInfo.dir(); from.cdUp(); copyRecursive(from, toDir, exampleDirName); // set vars to new location proFileInfo = QFileInfo(toDir + '/'+ exampleDirName + '/' + proFileInfo.fileName()); proFile = proFileInfo.absoluteFilePath(); } } } QString tryFile; files << proFile; if (isQmlProject) { tryFile = proFileInfo.path() + '/' + "/main.qml"; if(!QFile::exists(tryFile)) tryFile = proFileInfo.path() + "/qml/" + qmlMainFileName + ".qml"; // legacy qmlproject case if(!QFile::exists(tryFile)) tryFile = proFileInfo.path() + '/' + qmlMainFileName + ".qml"; if(QFile::exists(tryFile)) files << tryFile; } else { tryFile = proFileInfo.path() + "/main.cpp"; if(!QFile::exists(tryFile)) tryFile = proFileInfo.path() + '/' + proFileInfo.baseName() + ".cpp"; } Core::ICore::instance()->openFiles(files, static_cast(Core::ICore::SwitchMode | Core::ICore::StopOnLoadFail)); if (!tryFile.isEmpty() && Core::EditorManager::instance()->hasEditor(tryFile) && !helpFile.isEmpty()) slotOpenContextHelpPage(helpFile); } void GettingStartedWelcomePageWidget::slotOpenHelpPage(const QString& url) { Core::HelpManager *helpManager = Core::HelpManager::instance(); Q_ASSERT(helpManager); helpManager->handleHelpRequest(url); } void GettingStartedWelcomePageWidget::slotOpenContextHelpPage(const QString& url) { Core::HelpManager *helpManager = Core::HelpManager::instance(); Q_ASSERT(helpManager); helpManager->handleHelpRequest(url % QLatin1String("?view=split")); } void GettingStartedWelcomePageWidget::slotCreateNewProject() { Core::ICore::instance()->showNewItemDialog(tr("New Project"), Core::IWizard::wizardsOfKind(Core::IWizard::ProjectWizard)); } void GettingStartedWelcomePageWidget::slotNextTip() { QStringList tips = tipsOfTheDay(); m_currentTip = ((m_currentTip+1)%tips.count()); ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip)); } void GettingStartedWelcomePageWidget::slotPrevTip() { QStringList tips = tipsOfTheDay(); m_currentTip = ((m_currentTip-1)+tips.count())%tips.count(); ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip)); } QStringList GettingStartedWelcomePageWidget::tipsOfTheDay() { static QStringList tips; if (tips.isEmpty()) { QString altShortcut = #ifdef Q_WS_MAC tr("Cmd", "Shortcut key"); #else tr("Alt", "Shortcut key"); #endif QString ctrlShortcut = #ifdef Q_WS_MAC tr("Cmd", "Shortcut key"); #else tr("Ctrl", "Shortcut key"); #endif //:%1 gets replaced by Alt (Win/Unix) or Cmd (Mac) tips.append(tr("You can show and hide the side bar using %1+0.").arg(altShortcut)); tips.append(tr("You can fine tune the Find function by selecting "Whole Words" " "or "Case Sensitive". Simply click on the icons on the right end of the line edit.")); tips.append(tr("If you add external libraries to your project, Qt Creator will automatically offer syntax highlighting " "and code completion.")); tips.append(tr("The code completion is CamelCase-aware. For example, to complete namespaceUri " "you can just type nU and hit Ctrl+Space.")); tips.append(tr("You can force code completion at any time using Ctrl+Space.")); tips.append(tr("You can start Qt Creator with a session by calling qtcreator <sessionname>.")); tips.append(tr("You can return to edit mode from any other mode at any time by hitting Escape.")); //:%1 gets replaced by Alt (Win/Unix) or Cmd (Mac) tips.append(tr("You can switch between the output pane by hitting %1+n where n is the number denoted " "on the buttons at the window bottom:

" "1: Build Issues, 2: Search Results, 3: Application Output, " "4: Compile Output").arg(altShortcut)); tips.append(tr("You can quickly search methods, classes, help and more using the " "Locator bar (%1+K).").arg(ctrlShortcut)); tips.append(tr("You can add custom build steps in the " "build settings.")); tips.append(tr("Within a session, you can add " "dependencies between projects.")); tips.append(tr("You can set the preferred editor encoding for every project in Projects -> Editor Settings -> Default Encoding.")); tips.append(tr("You can use Qt Creator with a number of " "revision control systems such as Subversion, Perforce, CVS and Git.")); tips.append(tr("In the editor, F2 follows symbol definition, Shift+F2 toggles declaration and definition " "while F4 toggles header file and source file.")); } return tips; } void GettingStartedWelcomePageWidget::addToFeatures(const Core::RssItem &feature) { m_featuredItems.append(feature); if (m_featuredItems.count() > 1) { ui->nextFeatureBtn->show(); ui->prevFeatureBtn->show(); } } void GettingStartedWelcomePageWidget::showFeature(int feature) { if (m_featuredItems.isEmpty()) return; if (feature == -1) { srand(QDateTime::currentDateTime().toTime_t()); m_currentFeature = rand()%m_featuredItems.count(); } const Core::RssItem &item = m_featuredItems.at(m_currentFeature); ui->featuredTextLabel->setTextFormat(Qt::RichText); QString text = QString::fromLatin1("%1
%2

%3").arg(item.category).arg(item.title).arg(item.description); ui->featuredTextLabel->setText(text); QString imagePath = item.imagePath; if (!imagePath.startsWith("http")) { imagePath = Core::ICore::instance()->resourcePath() + "/rss/" + item.imagePath; ui->featuredImage->setPixmap(QPixmap(imagePath)); } else { new PixmapDownloader(QUrl(imagePath), ui->featuredImage); } if (item.category == QLatin1String("Event")) { ui->detailsLabel->setText(tr("Details...").arg(item.url)); ui->detailsLabel->show(); ui->detailsLabel->setOpenExternalLinks(true); } else if (item.category == QLatin1String("Tutorial")) { ui->detailsLabel->setText(tr("Take Tutorial").arg(item.url+"?view=split")); ui->detailsLabel->show(); ui->detailsLabel->setOpenExternalLinks(true); } } void GettingStartedWelcomePageWidget::slotNextFeature() { QTC_ASSERT(!m_featuredItems.isEmpty(), return); m_currentFeature = (m_currentFeature+1) % m_featuredItems.count(); showFeature(m_currentFeature); } void GettingStartedWelcomePageWidget::slotPrevFeature() { QTC_ASSERT(!m_featuredItems.isEmpty(), return); m_currentFeature = ((m_currentFeature-1)+m_featuredItems.count()) % m_featuredItems.count(); showFeature(m_currentFeature); } } // namespace Internal } // namespace Qt4ProjectManager