OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / gettingstartedwelcomepagewidget.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "gettingstartedwelcomepagewidget.h"
35 #include "ui_gettingstartedwelcomepagewidget.h"
36
37 #include <coreplugin/icore.h>
38 #include <coreplugin/helpmanager.h>
39 #include <coreplugin/coreconstants.h>
40 #include <coreplugin/editormanager/editormanager.h>
41 #include <coreplugin/rssfetcher.h>
42 #include <coreplugin/dialogs/iwizard.h>
43 #include <projectexplorer/projectexplorer.h>
44
45 #include <utils/pathchooser.h>
46 #include <utils/qtcassert.h>
47
48 #include <extensionsystem/pluginmanager.h>
49
50 #include <QtCore/QDateTime>
51 #include <QtCore/QDir>
52 #include <QtCore/QFileInfo>
53 #include <QtCore/QDebug>
54 #include <QtCore/QStringBuilder>
55 #include <QtCore/QUrl>
56 #include <QtCore/QTimer>
57 #include <QtCore/QSettings>
58 #include <QtCore/QXmlStreamReader>
59 #include <QtCore/QScopedPointer>
60 #include <QtGui/QDialogButtonBox>
61 #include <QtGui/QFont>
62 #include <QtGui/QMessageBox>
63 #include <QtGui/QPushButton>
64 #include <QtGui/QMenu>
65 #include <QtGui/QDesktopServices>
66
67 namespace Qt4ProjectManager {
68 namespace Internal {
69
70 const char ExamplePathPropertyName[] = "__qt_ExamplePath";
71 const char HelpPathPropertyName[] = "__qt_HelpPath";
72 const char QmlMainFileName[] = "__qt_QmlMainFileName";
73
74 void PixmapDownloader::populatePixmap(QNetworkReply *reply) {
75     QImage image;
76     image.loadFromData(reply->readAll());
77     m_label->setScaledContents(false);
78     m_label->setPixmap(QPixmap::fromImage(image));
79     deleteLater();
80 }
81
82 GettingStartedWelcomePageWidget::GettingStartedWelcomePageWidget(QWidget *parent) :
83     QWidget(parent), ui(new Ui::GettingStartedWelcomePageWidget),
84     m_currentFeature(0), m_rssFetcher(0)
85 {
86     ui->setupUi(this);
87
88     ui->didYouKnowTextBrowser->viewport()->setAutoFillBackground(false);
89     ui->detailsLabel->hide();
90
91     connect(ui->tutorialTreeWidget, SIGNAL(activated(QString)), SLOT(slotOpenHelpPage(const QString&)));
92
93     QFontMetrics fm = fontMetrics();
94     const int margins = 30;
95     int width = ui->tutorialTreeWidget->minimumWidth() - margins;
96
97     QString itemText = tr("The Qt Creator User Interface");
98     QString url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-quick-tour.html");
99     ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText);
100
101     itemText = tr("Building and Running an Example");
102     url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-build-example-application.html?view=split");
103     ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText);
104
105     itemText = tr("Creating a Qt C++ Application");
106     url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-writing-program.html?view=split");
107     ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText);
108
109     itemText = tr("Creating a Mobile Application");
110     url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-mobile-example.html?view=split");
111     ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText);
112
113     itemText = tr("Creating a Qt Quick Application");
114     url = QLatin1String("qthelp://com.nokia.qtcreator/doc/creator-qml-application.html?view=split");
115     ui->tutorialTreeWidget->addItem(fm.elidedText(itemText, Qt::ElideRight, width), url, itemText);
116
117     srand(QDateTime::currentDateTime().toTime_t());
118     QStringList tips = tipsOfTheDay();
119     m_currentTip = rand()%tips.count();
120
121     QTextDocument *doc = ui->didYouKnowTextBrowser->document();
122     doc->setDefaultStyleSheet("* {color:black;};");
123     ui->didYouKnowTextBrowser->setDocument(doc);
124     ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip));
125
126     connect(ui->nextTipBtn, SIGNAL(clicked()), this, SLOT(slotNextTip()));
127     connect(ui->prevTipBtn, SIGNAL(clicked()), this, SLOT(slotPrevTip()));
128     connect(ui->openProjectButton, SIGNAL(clicked()),
129             ProjectExplorer::ProjectExplorerPlugin::instance(),
130             SLOT(openOpenProjectDialog()));
131     connect(ui->createNewProjectButton, SIGNAL(clicked()), this, SLOT(slotCreateNewProject()));
132
133     ui->createNewProjectButton->setIcon(
134             QIcon::fromTheme(QLatin1String("document-new"), ui->createNewProjectButton->icon()));
135     ui->openProjectButton->setIcon(
136             QIcon::fromTheme(QLatin1String("document-open"), ui->openProjectButton->icon()));
137
138     m_rssFetcher = new Core::RssFetcher;
139     connect (m_rssFetcher, SIGNAL(rssItemReady(Core::RssItem)), SLOT(addToFeatures(Core::RssItem)));
140     connect (m_rssFetcher, SIGNAL(finished(bool)), SLOT(showFeature()), Qt::QueuedConnection);
141     connect(this, SIGNAL(startRssFetching(QUrl)), m_rssFetcher, SLOT(fetch(QUrl)), Qt::QueuedConnection);
142     m_rssFetcher->start(QThread::LowestPriority);
143     const QString featureRssFile = Core::ICore::instance()->resourcePath()+QLatin1String("/rss/featured.rss");
144     emit startRssFetching(QUrl::fromLocalFile(featureRssFile));
145
146     ui->nextFeatureBtn->hide();
147     ui->prevFeatureBtn->hide();
148     connect(ui->nextFeatureBtn, SIGNAL(clicked()), this, SLOT(slotNextFeature()));
149     connect(ui->prevFeatureBtn, SIGNAL(clicked()), this, SLOT(slotPrevFeature()));
150 }
151
152 GettingStartedWelcomePageWidget::~GettingStartedWelcomePageWidget()
153 {
154     m_rssFetcher->exit();
155     m_rssFetcher->wait();
156     delete m_rssFetcher;
157     delete ui;
158 }
159
160 void GettingStartedWelcomePageWidget::parseXmlFile(QFile *file, QMenuHash &cppSubMenuHash, QMenuHash &qmlSubMenuHash,
161                                                    const QString &examplePath, const QString &sourcePath)
162 {
163     QMenu *cppSubMenu = 0;
164     QMenu *qmlSubMenu = 0;
165     bool inExamples = false;
166     QString dirName;
167
168     QXmlStreamReader reader(file);
169
170     while (!reader.atEnd()) {
171         switch (reader.readNext()) {
172         case QXmlStreamReader::StartElement:
173             if (reader.name() == QLatin1String("category")) {
174                 QString name = reader.attributes().value(QLatin1String("name")).toString();
175                 if (name.contains(QLatin1String("Tutorial")))
176                     break;
177                 dirName = reader.attributes().value(QLatin1String("dirname")).toString();
178                 if (!cppSubMenuHash.contains(dirName)) {
179                     cppSubMenu = new QMenu(name, this);
180                     cppSubMenu->setObjectName(dirName);
181                     cppSubMenuHash.insert(dirName, cppSubMenu);
182                 } else {
183                     cppSubMenu = cppSubMenuHash.value(dirName);
184                 }
185                 if (!qmlSubMenuHash.contains(dirName)) {
186                     qmlSubMenu = new QMenu(name, this);
187                     qmlSubMenu->setObjectName(dirName);
188                     qmlSubMenuHash.insert(dirName, qmlSubMenu);
189                 } else {
190                     qmlSubMenu = qmlSubMenuHash.value(dirName);
191                 }
192                 inExamples = true;
193             }
194             if (inExamples && reader.name() == QLatin1String("example")) {
195                 const QChar slash = QLatin1Char('/');
196                 const QString name = reader.attributes().value(QLatin1String("name")).toString();
197                 const bool isQml = reader.attributes().value(QLatin1String("qml")).toString() == "true";
198                 const QString localDir = reader.attributes().value(QLatin1String("filename")).toString();
199                 const QString fileName = localDir.section('/', -1);
200                 QString replacedFileName = fileName;
201                 replacedFileName.replace(QLatin1Char('-'), QString());
202                 QString relativeProPath = slash + dirName + slash + localDir + slash + replacedFileName;
203
204                 QString finalFileName = examplePath + relativeProPath + QLatin1String(".pro");
205
206                 if (!QFile::exists(finalFileName))
207                     finalFileName = sourcePath + QLatin1String("/examples") + relativeProPath  + QLatin1String(".pro");
208
209                 if (isQml && !QFile::exists(finalFileName)) {
210                     // maybe it's an old-style QML project?
211                     relativeProPath = slash + dirName + slash + localDir + slash + fileName;
212                     finalFileName = examplePath + relativeProPath + QLatin1String(".qmlproject");
213
214                     if (!QFile::exists(finalFileName))
215                         finalFileName = sourcePath + QLatin1String("/examples") + relativeProPath  + QLatin1String(".qmlproject");;
216                 }
217
218                 if (!QFile::exists(finalFileName))
219                     break;
220
221                 QString dirNameforHelp = dirName;
222                 dirNameforHelp.replace(slash, QLatin1Char('-'));
223                 QString helpPath = QLatin1String("qthelp://com.trolltech.qt/qdoc/") +
224                         dirNameforHelp +
225                         QLatin1Char('-') + fileName + QLatin1String(".html");
226
227                 QAction *exampleAction = 0;
228                 QAction *beforeAction = 0;
229                 bool duplicate = false;
230                 QMenu *subMenu;
231                 subMenu = isQml ? qmlSubMenu : cppSubMenu;
232
233                 foreach (beforeAction, subMenu->actions()) {
234                     int res = beforeAction->text().compare(name, Qt::CaseInsensitive);
235                     if (res==0) {
236                         duplicate = true;
237                         break;
238                     } else if (res<0)
239                         beforeAction = 0;
240                     else if (res>0) {
241                         break;
242                     }
243                 }
244
245                 if (!duplicate) {
246                     exampleAction = new QAction(name, subMenu);
247                     subMenu->insertAction(beforeAction, exampleAction);
248                     connect(exampleAction, SIGNAL(triggered()), SLOT(slotOpenExample()));
249                     exampleAction->setProperty(ExamplePathPropertyName, finalFileName);
250                     exampleAction->setProperty(HelpPathPropertyName, helpPath);
251                     if (isQml)
252                         exampleAction->setProperty(QmlMainFileName, fileName);
253                 }
254             }
255             break;
256         case QXmlStreamReader::EndElement:
257             if (inExamples && reader.name() == QLatin1String("category")) {
258                 if (cppSubMenu->actions().isEmpty())
259                     delete cppSubMenuHash.take(dirName);
260
261                 if (qmlSubMenu->actions().isEmpty())
262                     delete qmlSubMenuHash.take(dirName);
263
264                 inExamples = false;
265             }
266             break;
267         default:
268             break;
269         }
270     }
271 }
272
273 bool menuEntryCompare(QMenu* first, QMenu* second)
274 {
275     return (QString::localeAwareCompare(first->title(), second->title()) < 0);
276 }
277
278 void GettingStartedWelcomePageWidget::updateExamples(const QString &examplePath,
279                                                      const QString &demosPath,
280                                                      const QString &sourcePath)
281 {
282
283     QString demoXml = demosPath + "/qtdemo/xml/examples.xml";
284     if (!QFile::exists(demoXml)) {
285         demoXml = sourcePath + "/demos/qtdemo/xml/examples.xml";
286         if (!QFile::exists(demoXml))
287             return;
288     }
289
290     QMenuHash cppSubMenuHash;
291     QMenuHash qmlSubMenuHash;
292
293     const QString dropDownLabel = tr("Choose an Example...");
294     QMenu *cppMenu = new QMenu(ui->cppExamplesButton);
295     ui->cppExamplesButton->setMenu(cppMenu);
296     QMenu *qmlMenu = new QMenu(ui->qmlExamplesButton);
297
298
299     // let Creator's files take precedence
300     QString localQmlExamplesXml =
301             Core::ICore::instance()->resourcePath()+QLatin1String("/examplebrowser/qmlexamples.xml");
302
303     QFile localDescriptions(localQmlExamplesXml);
304     if (localDescriptions.open(QFile::ReadOnly)) {
305         parseXmlFile(&localDescriptions, cppSubMenuHash, qmlSubMenuHash, examplePath, sourcePath);
306     }
307
308     QFile descriptions(demoXml);
309     if (!descriptions.open(QFile::ReadOnly))
310         return;
311
312     ui->cppExamplesButton->setEnabled(true);
313     ui->cppExamplesButton->setText(dropDownLabel);
314
315     parseXmlFile(&descriptions, cppSubMenuHash, qmlSubMenuHash, examplePath, sourcePath);
316
317     QList<QMenu*> cppSubMenus = cppSubMenuHash.values();
318     qSort(cppSubMenus.begin(), cppSubMenus.end(), menuEntryCompare);
319     QList<QMenu*> qmlSubMenus = qmlSubMenuHash.values();
320     qSort(qmlSubMenus.begin(), qmlSubMenus.end(), menuEntryCompare);
321
322     foreach (QMenu *menu, cppSubMenus)
323         cppMenu->addMenu(menu);
324     foreach (QMenu *menu, qmlSubMenus)
325         qmlMenu->addMenu(menu);
326
327     if (!qmlMenu->isEmpty()) {
328         ui->qmlExamplesButton->setMenu(qmlMenu);
329         ui->qmlExamplesButton->setEnabled(true);
330         ui->qmlExamplesButton->setText(dropDownLabel);
331     }
332 }
333
334 namespace {
335 void copyRecursive(const QDir& from, const QDir& to, const QString& dir)
336 {
337     QDir dest(to);
338     dest.mkdir(dir);
339     dest.cd(dir);
340     QDir src(from);
341     src.cd(dir);
342     foreach(const QFileInfo& roFile, src.entryInfoList(QDir::Files)) {
343         QFile::copy(roFile.absoluteFilePath(), dest.absolutePath() + '/' + roFile.fileName());
344     }
345     foreach(const QString& roDir, src.entryList(QDir::NoDotAndDotDot|QDir::Dirs)) {
346         copyRecursive(src, dest, QDir(roDir).dirName());
347     }
348 }
349 } // namespace
350
351 void GettingStartedWelcomePageWidget::slotOpenExample()
352 {
353     QAction *action = qobject_cast<QAction*>(sender());
354     if (!action)
355         return;
356
357     QString helpFile = action->property(HelpPathPropertyName).toString();
358     QString proFile = action->property(ExamplePathPropertyName).toString();
359     QString qmlMainFileName;
360     bool isQmlProject = false;
361     if (action->dynamicPropertyNames().contains(QmlMainFileName)) {
362         qmlMainFileName = action->property(QmlMainFileName).toString();
363         isQmlProject = true;
364     }
365     QStringList files;
366
367     QFileInfo proFileInfo(proFile);
368     // If the Qt is a distro Qt on Linux, it will not be writable, hence compilation will fail
369     if (!proFileInfo.isWritable())
370     {
371         QDialog d;
372         QGridLayout *lay = new QGridLayout(&d);
373         QLabel *descrLbl = new QLabel;
374         d.setWindowTitle(tr("Copy Project to writable Location?"));
375         descrLbl->setTextFormat(Qt::RichText);
376         descrLbl->setWordWrap(true);
377         descrLbl->setText(tr("<p>The project you are about to open is located in the "
378                              "write-protected location:</p><blockquote>%1</blockquote>"
379                              "<p>Please select a writable location below and click \"Copy Project and Open\" "
380                              "to open a modifiable copy of the project or click \"Keep Project and Open\" "
381                              "to open the project in location.</p><p><b>Note:</b> You will not "
382                              "be able to alter or compile your project in the current location.</p>")
383                           .arg(QDir::toNativeSeparators(proFileInfo.dir().absolutePath())));
384         lay->addWidget(descrLbl, 0, 0, 1, 2);
385         QLabel *txt = new QLabel(tr("&Location:"));
386         Utils::PathChooser *chooser = new Utils::PathChooser;
387         txt->setBuddy(chooser);
388         chooser->setExpectedKind(Utils::PathChooser::Directory);
389         QSettings *settings = Core::ICore::instance()->settings();
390         chooser->setPath(settings->value(
391                 QString::fromLatin1("General/ProjectsFallbackRoot"), QDir::homePath()).toString());
392         lay->addWidget(txt, 1, 0);
393         lay->addWidget(chooser, 1, 1);
394         QDialogButtonBox *bb = new QDialogButtonBox;
395         connect(bb, SIGNAL(accepted()), &d, SLOT(accept()));
396         connect(bb, SIGNAL(rejected()), &d, SLOT(reject()));
397         QPushButton *copyBtn = bb->addButton(tr("&Copy Project and Open"), QDialogButtonBox::AcceptRole);
398         copyBtn->setDefault(true);
399         bb->addButton(tr("&Keep Project and Open"), QDialogButtonBox::RejectRole);
400         lay->addWidget(bb, 2, 0, 1, 2);
401         connect(chooser, SIGNAL(validChanged(bool)), copyBtn, SLOT(setEnabled(bool)));
402         if (d.exec() == QDialog::Accepted) {
403             QString exampleDirName = proFileInfo.dir().dirName();
404             QString toDir = chooser->path();
405             settings->setValue(QString::fromLatin1("General/ProjectsFallbackRoot"), toDir);
406             QDir toDirWithExamplesDir(toDir);
407             if (toDirWithExamplesDir.cd(exampleDirName)) {
408                 toDirWithExamplesDir.cdUp(); // step out, just to not be in the way
409                 QMessageBox::warning(topLevelWidget(), tr("Warning"),
410                                      tr("The specified location already exists. "
411                                         "Please specify a valid location."),
412                                      QMessageBox::Ok, QMessageBox::NoButton);
413                 return;
414             } else {
415                 QDir from = proFileInfo.dir();
416                 from.cdUp();
417                 copyRecursive(from, toDir, exampleDirName);
418                 // set vars to new location
419                 proFileInfo = QFileInfo(toDir + '/'+ exampleDirName + '/' + proFileInfo.fileName());
420                 proFile = proFileInfo.absoluteFilePath();
421             }
422         }
423     }
424
425     QString tryFile;
426     files << proFile;
427     if (isQmlProject) {
428         tryFile = proFileInfo.path() + '/' + "/main.qml";
429         if(!QFile::exists(tryFile))
430             tryFile = proFileInfo.path() + "/qml/" + qmlMainFileName + ".qml";
431         // legacy qmlproject case
432         if(!QFile::exists(tryFile))
433             tryFile = proFileInfo.path() + '/' + qmlMainFileName + ".qml";
434         if(QFile::exists(tryFile))
435             files << tryFile;
436     } else {
437         tryFile = proFileInfo.path() + "/main.cpp";
438         if(!QFile::exists(tryFile))
439             tryFile = proFileInfo.path() + '/' + proFileInfo.baseName() + ".cpp";
440     }
441     Core::ICore::instance()->openFiles(files, static_cast<Core::ICore::OpenFilesFlags>(Core::ICore::SwitchMode | Core::ICore::StopOnLoadFail));
442     if (!tryFile.isEmpty() && Core::EditorManager::instance()->hasEditor(tryFile) && !helpFile.isEmpty())
443         slotOpenContextHelpPage(helpFile);
444 }
445
446 void GettingStartedWelcomePageWidget::slotOpenHelpPage(const QString& url)
447 {
448     Core::HelpManager *helpManager = Core::HelpManager::instance();
449     Q_ASSERT(helpManager);
450     helpManager->handleHelpRequest(url);
451 }
452 void GettingStartedWelcomePageWidget::slotOpenContextHelpPage(const QString& url)
453 {
454     Core::HelpManager *helpManager = Core::HelpManager::instance();
455     Q_ASSERT(helpManager);
456     helpManager->handleHelpRequest(url % QLatin1String("?view=split"));
457 }
458
459 void GettingStartedWelcomePageWidget::slotCreateNewProject()
460 {
461     Core::ICore::instance()->showNewItemDialog(tr("New Project"),
462                                                Core::IWizard::wizardsOfKind(Core::IWizard::ProjectWizard));
463 }
464
465 void GettingStartedWelcomePageWidget::slotNextTip()
466 {
467     QStringList tips = tipsOfTheDay();
468     m_currentTip = ((m_currentTip+1)%tips.count());
469     ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip));
470 }
471
472 void GettingStartedWelcomePageWidget::slotPrevTip()
473 {
474     QStringList tips = tipsOfTheDay();
475     m_currentTip = ((m_currentTip-1)+tips.count())%tips.count();
476     ui->didYouKnowTextBrowser->setText(tips.at(m_currentTip));
477 }
478
479 QStringList GettingStartedWelcomePageWidget::tipsOfTheDay()
480 {
481     static QStringList tips;
482     if (tips.isEmpty()) {
483         QString altShortcut =
484 #ifdef Q_WS_MAC
485             tr("Cmd", "Shortcut key");
486 #else
487             tr("Alt", "Shortcut key");
488 #endif
489
490         QString ctrlShortcut =
491 #ifdef Q_WS_MAC
492             tr("Cmd", "Shortcut key");
493 #else
494             tr("Ctrl", "Shortcut key");
495 #endif
496
497         //:%1 gets replaced by Alt (Win/Unix) or Cmd (Mac)
498         tips.append(tr("You can show and hide the side bar using <tt>%1+0<tt>.").arg(altShortcut));
499         tips.append(tr("You can fine tune the <tt>Find</tt> function by selecting &quot;Whole Words&quot; "
500                        "or &quot;Case Sensitive&quot;. Simply click on the icons on the right end of the line edit."));
501         tips.append(tr("If you add external libraries to your project, Qt Creator will automatically offer syntax highlighting "
502                         "and code completion."));
503         tips.append(tr("The code completion is CamelCase-aware. For example, to complete <tt>namespaceUri</tt> "
504                        "you can just type <tt>nU</tt> and hit <tt>Ctrl+Space</tt>."));
505         tips.append(tr("You can force code completion at any time using <tt>Ctrl+Space</tt>."));
506         tips.append(tr("You can start Qt Creator with a session by calling <tt>qtcreator &lt;sessionname&gt;</tt>."));
507         tips.append(tr("You can return to edit mode from any other mode at any time by hitting <tt>Escape</tt>."));
508         //:%1 gets replaced by Alt (Win/Unix) or Cmd (Mac)
509         tips.append(tr("You can switch between the output pane by hitting <tt>%1+n</tt> where n is the number denoted "
510                        "on the buttons at the window bottom: <br /><br />"
511                        "1: Build Issues, 2: Search Results, 3: Application Output, "
512                        "4: Compile Output").arg(altShortcut));
513         tips.append(tr("You can quickly search methods, classes, help and more using the "
514                        "<a href=\"qthelp://com.nokia.qtcreator/doc/creator-editor-locator.html\">Locator bar</a> (<tt>%1+K</tt>).").arg(ctrlShortcut));
515         tips.append(tr("You can add custom build steps in the "
516                        "<a href=\"qthelp://com.nokia.qtcreator/doc/creator-build-settings.html\">build settings</a>."));
517         tips.append(tr("Within a session, you can add "
518                        "<a href=\"qthelp://com.nokia.qtcreator/doc/creator-build-dependencies.html\">dependencies</a> between projects."));
519         tips.append(tr("You can set the preferred editor encoding for every project in <tt>Projects -> Editor Settings -> Default Encoding</tt>."));
520         tips.append(tr("You can use Qt Creator with a number of <a href=\"qthelp://com.nokia.qtcreator/doc/creator-version-control.html\">"
521                        "revision control systems</a> such as Subversion, Perforce, CVS and Git."));
522         tips.append(tr("In the editor, <tt>F2</tt> follows symbol definition, <tt>Shift+F2</tt> toggles declaration and definition "
523                        "while <tt>F4</tt> toggles header file and source file."));
524     }
525     return tips;
526 }
527
528 void GettingStartedWelcomePageWidget::addToFeatures(const Core::RssItem &feature)
529 {
530     m_featuredItems.append(feature);
531     if (m_featuredItems.count() > 1) {
532         ui->nextFeatureBtn->show();
533         ui->prevFeatureBtn->show();
534     }
535 }
536
537 void GettingStartedWelcomePageWidget::showFeature(int feature)
538 {
539     if (m_featuredItems.isEmpty())
540         return;
541
542     if (feature == -1) {
543         srand(QDateTime::currentDateTime().toTime_t());
544         m_currentFeature = rand()%m_featuredItems.count();
545     }
546
547     const Core::RssItem &item = m_featuredItems.at(m_currentFeature);
548     ui->featuredTextLabel->setTextFormat(Qt::RichText);
549     QString text = QString::fromLatin1("<b style='color: rgb(85, 85, 85);'>%1</b><br><b>%2</b><br/><br/>%3").arg(item.category).arg(item.title).arg(item.description);
550     ui->featuredTextLabel->setText(text);
551     QString imagePath = item.imagePath;
552     if (!imagePath.startsWith("http")) {
553         imagePath = Core::ICore::instance()->resourcePath() + "/rss/" + item.imagePath;
554         ui->featuredImage->setPixmap(QPixmap(imagePath));
555     } else {
556         new PixmapDownloader(QUrl(imagePath), ui->featuredImage);
557     }
558
559     if (item.category == QLatin1String("Event")) {
560         ui->detailsLabel->setText(tr("<a href='%1'>Details...</a>").arg(item.url));
561         ui->detailsLabel->show();
562         ui->detailsLabel->setOpenExternalLinks(true);
563     }
564     else if (item.category == QLatin1String("Tutorial")) {
565         ui->detailsLabel->setText(tr("<a href='%1'>Take Tutorial</a>").arg(item.url+"?view=split"));
566         ui->detailsLabel->show();
567         ui->detailsLabel->setOpenExternalLinks(true);
568     }
569 }
570
571 void GettingStartedWelcomePageWidget::slotNextFeature()
572 {
573     QTC_ASSERT(!m_featuredItems.isEmpty(), return);
574     m_currentFeature = (m_currentFeature+1) % m_featuredItems.count();
575     showFeature(m_currentFeature);
576 }
577
578 void GettingStartedWelcomePageWidget::slotPrevFeature()
579 {
580     QTC_ASSERT(!m_featuredItems.isEmpty(), return);
581     m_currentFeature = ((m_currentFeature-1)+m_featuredItems.count()) % m_featuredItems.count();
582     showFeature(m_currentFeature);
583 }
584
585 } // namespace Internal
586 } // namespace Qt4ProjectManager