1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "icore.h"
27 
28 #include "windowsupport.h"
29 #include "dialogs/settingsdialog.h"
30 
31 #include <app/app_version.h>
32 #include <extensionsystem/pluginmanager.h>
33 
34 #include <utils/qtcassert.h>
35 
36 #include <QApplication>
37 #include <QDebug>
38 #include <QStandardPaths>
39 #include <QSysInfo>
40 
41 /*!
42     \namespace Core
43     \inmodule QtCreator
44     \brief The Core namespace contains all classes that make up the Core plugin
45     which constitute the basic functionality of \QC.
46 */
47 
48 /*!
49     \enum Core::FindFlag
50     This enum holds the find flags.
51 
52     \value FindBackward
53            Searches backwards.
54     \value FindCaseSensitively
55            Considers case when searching.
56     \value FindWholeWords
57            Finds only whole words.
58     \value FindRegularExpression
59            Uses a regular epression as a search term.
60     \value FindPreserveCase
61            Preserves the case when replacing search terms.
62 */
63 
64 /*!
65     \enum Core::ICore::ContextPriority
66 
67     This enum defines the priority of additional contexts.
68 
69     \value High
70            Additional contexts that have higher priority than contexts from
71            Core::IContext instances.
72     \value Low
73            Additional contexts that have lower priority than contexts from
74            Core::IContext instances.
75 
76     \sa Core::ICore::updateAdditionalContexts()
77 */
78 
79 /*!
80     \enum Core::SaveSettingsReason
81     \internal
82 */
83 
84 /*!
85     \namespace Core::Internal
86     \internal
87 */
88 
89 /*!
90     \class Core::ICore
91     \inheaderfile coreplugin/icore.h
92     \inmodule QtCreator
93     \ingroup mainclasses
94 
95     \brief The ICore class allows access to the different parts that make up
96     the basic functionality of \QC.
97 
98     You should never create a subclass of this interface. The one and only
99     instance is created by the Core plugin. You can access this instance
100     from your plugin through instance().
101 */
102 
103 /*!
104     \fn void Core::ICore::coreAboutToOpen()
105 
106     Indicates that all plugins have been loaded and the main window is about to
107     be shown.
108 */
109 
110 /*!
111     \fn void Core::ICore::coreOpened()
112     Indicates that all plugins have been loaded and the main window is shown.
113 */
114 
115 /*!
116     \fn void Core::ICore::saveSettingsRequested(Core::ICore::SaveSettingsReason reason)
117     Signals that the user has requested that the global settings
118     should be saved to disk for a \a reason.
119 
120     At the moment that happens when the application is closed, and on \uicontrol{Save All}.
121 */
122 
123 /*!
124     \fn void Core::ICore::coreAboutToClose()
125     Enables plugins to perform some pre-end-of-life actions.
126 
127     The application is guaranteed to shut down after this signal is emitted.
128     It is there as an addition to the usual plugin lifecycle functions, namely
129     \c IPlugin::aboutToShutdown(), just for convenience.
130 */
131 
132 /*!
133     \fn void Core::ICore::contextAboutToChange(const QList<Core::IContext *> &context)
134     Indicates that a new \a context will shortly become the current context
135     (meaning that its widget got focus).
136 */
137 
138 /*!
139     \fn void Core::ICore::contextChanged(const Core::Context &context)
140     Indicates that a new \a context just became the current context. This includes the context
141     from the focus object as well as the additional context.
142 */
143 
144 #include "dialogs/newdialog.h"
145 #include "iwizardfactory.h"
146 #include "mainwindow.h"
147 #include "documentmanager.h"
148 
149 #include <utils/hostosinfo.h>
150 
151 #include <QCoreApplication>
152 #include <QDebug>
153 #include <QDir>
154 #include <QMessageBox>
155 #include <QPushButton>
156 #include <QStatusBar>
157 
158 using namespace Core::Internal;
159 using namespace ExtensionSystem;
160 using namespace Utils;
161 
162 namespace Core {
163 
164 // The Core Singleton
165 static ICore *m_instance = nullptr;
166 static MainWindow *m_mainwindow = nullptr;
167 
168 /*!
169     Returns the pointer to the instance. Only use for connecting to signals.
170 */
instance()171 ICore *ICore::instance()
172 {
173     return m_instance;
174 }
175 
176 /*!
177     Returns whether the new item dialog is currently open.
178 */
isNewItemDialogRunning()179 bool ICore::isNewItemDialogRunning()
180 {
181     return NewDialog::currentDialog() || IWizardFactory::isWizardRunning();
182 }
183 
184 /*!
185     Returns the currently open new item dialog widget, or \c nullptr if there is none.
186 
187     \sa isNewItemDialogRunning()
188     \sa showNewItemDialog()
189 */
newItemDialog()190 QWidget *ICore::newItemDialog()
191 {
192     if (NewDialog::currentDialog())
193         return NewDialog::currentDialog();
194     return IWizardFactory::currentWizard();
195 }
196 
197 /*!
198     \internal
199 */
ICore(MainWindow * mainwindow)200 ICore::ICore(MainWindow *mainwindow)
201 {
202     m_instance = this;
203     m_mainwindow = mainwindow;
204     // Save settings once after all plugins are initialized:
205     connect(PluginManager::instance(), &PluginManager::initializationDone,
206             this, [] { ICore::saveSettings(ICore::InitializationDone); });
207     connect(PluginManager::instance(), &PluginManager::testsFinished, [this] (int failedTests) {
208         emit coreAboutToClose();
209         if (failedTests != 0)
210             qWarning("Test run was not successful: %d test(s) failed.", failedTests);
211         QCoreApplication::exit(failedTests);
212     });
213     connect(PluginManager::instance(), &PluginManager::scenarioFinished, [this] (int exitCode) {
214         emit coreAboutToClose();
215         QCoreApplication::exit(exitCode);
216     });
217 }
218 
219 /*!
220     \internal
221 */
~ICore()222 ICore::~ICore()
223 {
224     m_instance = nullptr;
225     m_mainwindow = nullptr;
226 }
227 
228 /*!
229     Opens a dialog where the user can choose from a set of \a factories that
230     create new files or projects.
231 
232     The \a title argument is shown as the dialog title. The path where the
233     files will be created (if the user does not change it) is set
234     in \a defaultLocation. Defaults to DocumentManager::projectsDirectory()
235     or DocumentManager::fileDialogLastVisitedDirectory(), depending on wizard
236     kind.
237 
238     Additional variables for the wizards are set in \a extraVariables.
239 
240     \sa Core::DocumentManager
241     \sa isNewItemDialogRunning()
242     \sa newItemDialog()
243 */
showNewItemDialog(const QString & title,const QList<IWizardFactory * > & factories,const QString & defaultLocation,const QVariantMap & extraVariables)244 void ICore::showNewItemDialog(const QString &title,
245                               const QList<IWizardFactory *> &factories,
246                               const QString &defaultLocation,
247                               const QVariantMap &extraVariables)
248 {
249     QTC_ASSERT(!isNewItemDialogRunning(), return);
250     auto newDialog = new NewDialog(dialogParent());
251     connect(newDialog, &QObject::destroyed, m_instance, &ICore::updateNewItemDialogState);
252     newDialog->setWizardFactories(factories, defaultLocation, extraVariables);
253     newDialog->setWindowTitle(title);
254     newDialog->showDialog();
255 
256     updateNewItemDialogState();
257 }
258 
259 /*!
260     Opens the options dialog on the specified \a page. The dialog's \a parent
261     defaults to dialogParent(). If the dialog is already shown when this method
262     is called, it is just switched to the specified \a page.
263 
264     Returns whether the user accepted the dialog.
265 
266     \sa msgShowOptionsDialog()
267     \sa msgShowOptionsDialogToolTip()
268 */
showOptionsDialog(const Id page,QWidget * parent)269 bool ICore::showOptionsDialog(const Id page, QWidget *parent)
270 {
271     return executeSettingsDialog(parent ? parent : dialogParent(), page);
272 }
273 
274 /*!
275     Returns the text to use on buttons that open the options dialog.
276 
277     \sa showOptionsDialog()
278     \sa msgShowOptionsDialogToolTip()
279 */
msgShowOptionsDialog()280 QString ICore::msgShowOptionsDialog()
281 {
282     return QCoreApplication::translate("Core", "Configure...", "msgShowOptionsDialog");
283 }
284 
285 /*!
286     Returns the tool tip to use on buttons that open the options dialog.
287 
288     \sa showOptionsDialog()
289     \sa msgShowOptionsDialog()
290 */
msgShowOptionsDialogToolTip()291 QString ICore::msgShowOptionsDialogToolTip()
292 {
293     if (Utils::HostOsInfo::isMacHost())
294         return QCoreApplication::translate("Core", "Open Preferences dialog.",
295                                            "msgShowOptionsDialogToolTip (mac version)");
296     else
297         return QCoreApplication::translate("Core", "Open Options dialog.",
298                                            "msgShowOptionsDialogToolTip (non-mac version)");
299 }
300 
301 /*!
302     Creates a message box with \a parent that contains a \uicontrol Configure
303     button for opening the settings page specified by \a settingsId.
304 
305     The dialog has \a title and displays the message \a text and detailed
306     information specified by \a details.
307 
308     Use this function to display configuration errors and to point users to the
309     setting they should fix.
310 
311     Returns \c true if the user accepted the settings dialog.
312 
313     \sa showOptionsDialog()
314 */
showWarningWithOptions(const QString & title,const QString & text,const QString & details,Id settingsId,QWidget * parent)315 bool ICore::showWarningWithOptions(const QString &title, const QString &text,
316                                    const QString &details, Id settingsId, QWidget *parent)
317 {
318     if (!parent)
319         parent = m_mainwindow;
320     QMessageBox msgBox(QMessageBox::Warning, title, text,
321                        QMessageBox::Ok, parent);
322     if (!details.isEmpty())
323         msgBox.setDetailedText(details);
324     QAbstractButton *settingsButton = nullptr;
325     if (settingsId.isValid())
326         settingsButton = msgBox.addButton(msgShowOptionsDialog(), QMessageBox::AcceptRole);
327     msgBox.exec();
328     if (settingsButton && msgBox.clickedButton() == settingsButton)
329         return showOptionsDialog(settingsId);
330     return false;
331 }
332 
333 /*!
334     Returns the application's main settings object.
335 
336     You can use it to retrieve or set application-wide settings
337     (in contrast to session or project specific settings).
338 
339     If \a scope is \c QSettings::UserScope (the default), the
340     settings will be read from the user's settings, with
341     a fallback to global settings provided with \QC.
342 
343     If \a scope is \c QSettings::SystemScope, only the installation settings
344     shipped with the current version of \QC will be read. This
345     functionality exists for internal purposes only.
346 
347     \sa settingsDatabase()
348 */
settings(QSettings::Scope scope)349 QtcSettings *ICore::settings(QSettings::Scope scope)
350 {
351     if (scope == QSettings::UserScope)
352         return PluginManager::settings();
353     else
354         return PluginManager::globalSettings();
355 }
356 
357 /*!
358     Returns the application's settings database.
359 
360     The settings database is meant as an alternative to the regular settings
361     object. It is more suitable for storing large amounts of data. The settings
362     are application wide.
363 
364     \sa SettingsDatabase
365     \sa settings()
366 */
settingsDatabase()367 SettingsDatabase *ICore::settingsDatabase()
368 {
369     return m_mainwindow->settingsDatabase();
370 }
371 
372 /*!
373     Returns the application's printer object.
374 
375     Always use this printer object for printing, so the different parts of the
376     application re-use its settings.
377 */
printer()378 QPrinter *ICore::printer()
379 {
380     return m_mainwindow->printer();
381 }
382 
383 /*!
384     Returns the locale string for the user interface language that is currently
385     configured in \QC. Use this to install your plugin's translation file with
386     QTranslator.
387 */
userInterfaceLanguage()388 QString ICore::userInterfaceLanguage()
389 {
390     return qApp->property("qtc_locale").toString();
391 }
392 
pathHelper(const QString & rel)393 static QString pathHelper(const QString &rel)
394 {
395     if (rel.isEmpty())
396         return rel;
397     if (rel.startsWith('/'))
398         return rel;
399     return '/' + rel;
400 }
401 /*!
402     Returns the absolute path that is used for resources like
403     project templates and the debugger macros.
404 
405     This abstraction is needed to avoid platform-specific code all over
406     the place, since on \macos, for example, the resources are part of the
407     application bundle.
408 
409     \sa userResourcePath()
410 */
resourcePath(const QString & rel)411 FilePath ICore::resourcePath(const QString &rel)
412 {
413     return FilePath::fromString(
414                QDir::cleanPath(QCoreApplication::applicationDirPath() + '/' + RELATIVE_DATA_PATH))
415            / rel;
416 }
417 
418 /*!
419     Returns the absolute path in the users directory that is used for
420     resources like project templates.
421 
422     Use this function for finding the place for resources that the user may
423     write to, for example, to allow for custom palettes or templates.
424 
425     \sa resourcePath()
426 */
427 
userResourcePath(const QString & rel)428 FilePath ICore::userResourcePath(const QString &rel)
429 {
430     // Create qtcreator dir if it doesn't yet exist
431     const QString configDir = QFileInfo(settings(QSettings::UserScope)->fileName()).path();
432     const QString urp = configDir + '/' + QLatin1String(Constants::IDE_ID);
433 
434     if (!QFileInfo::exists(urp + QLatin1Char('/'))) {
435         QDir dir;
436         if (!dir.mkpath(urp))
437             qWarning() << "could not create" << urp;
438     }
439 
440     return FilePath::fromString(urp + pathHelper(rel));
441 }
442 
443 /*!
444     Returns a writable path that can be used for persistent cache files.
445 */
cacheResourcePath(const QString & rel)446 FilePath ICore::cacheResourcePath(const QString &rel)
447 {
448     return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
449                                 + pathHelper(rel));
450 }
451 
452 /*!
453     Returns the path to resources written by the installer, for example
454     pre-defined kits and toolchains.
455 */
installerResourcePath(const QString & rel)456 FilePath ICore::installerResourcePath(const QString &rel)
457 {
458     return FilePath::fromString(settings(QSettings::SystemScope)->fileName()).parentDir()
459                                 / Constants::IDE_ID / rel;
460 }
461 
462 /*!
463     Returns the path to the plugins that are included in the \QC installation.
464 
465     \internal
466 */
pluginPath()467 QString ICore::pluginPath()
468 {
469     return QDir::cleanPath(QCoreApplication::applicationDirPath() + '/' + RELATIVE_PLUGIN_PATH);
470 }
471 
472 /*!
473     Returns the path where user-specific plugins should be written.
474 
475     \internal
476 */
userPluginPath()477 QString ICore::userPluginPath()
478 {
479     QString pluginPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
480     if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost())
481         pluginPath += "/data";
482     pluginPath += '/' + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR) + '/';
483     pluginPath += QLatin1String(Utils::HostOsInfo::isMacHost() ? Core::Constants::IDE_DISPLAY_NAME
484                                                                : Core::Constants::IDE_ID);
485     pluginPath += "/plugins/";
486     pluginPath += QString::number(IDE_VERSION_MAJOR) + '.' + QString::number(IDE_VERSION_MINOR)
487                   + '.' + QString::number(IDE_VERSION_RELEASE);
488     return pluginPath;
489 }
490 
491 /*!
492     Returns the path to the command line tools that are included in the \QC
493     installation.
494  */
libexecPath(const QString & rel)495 FilePath ICore::libexecPath(const QString &rel)
496 {
497     return FilePath::fromString(QDir::cleanPath(QApplication::applicationDirPath()
498                                                 + pathHelper(RELATIVE_LIBEXEC_PATH)))
499            / rel;
500 }
501 
crashReportsPath()502 FilePath ICore::crashReportsPath()
503 {
504     if (Utils::HostOsInfo::isMacHost())
505         return libexecPath("crashpad_reports/completed");
506     else
507         return libexecPath("crashpad_reports/reports");
508 }
509 
ideDisplayName()510 QString ICore::ideDisplayName()
511 {
512     return Constants::IDE_DISPLAY_NAME;
513 }
514 
clangIncludePath(const QString & clangVersion)515 static QString clangIncludePath(const QString &clangVersion)
516 {
517     return "/lib/clang/" + clangVersion + "/include";
518 }
519 
520 /*!
521     \internal
522 */
clangIncludeDirectory(const QString & clangVersion,const QString & clangFallbackIncludeDir)523 QString ICore::clangIncludeDirectory(const QString &clangVersion,
524                                      const QString &clangFallbackIncludeDir)
525 {
526     FilePath dir = libexecPath("clang" + clangIncludePath(clangVersion));
527     if (!dir.exists() || !dir.pathAppended("stdint.h").exists())
528         dir = FilePath::fromString(clangFallbackIncludeDir);
529     return dir.canonicalPath().toUserOutput();
530 }
531 
532 /*!
533     \internal
534 */
clangBinary(const QString & binaryBaseName,const QString & clangBinDirectory)535 static QString clangBinary(const QString &binaryBaseName, const QString &clangBinDirectory)
536 {
537     const QString hostExeSuffix(QTC_HOST_EXE_SUFFIX);
538     FilePath executable = ICore::libexecPath("clang/bin") / binaryBaseName + hostExeSuffix;
539     if (!executable.exists())
540         executable = FilePath::fromString(clangBinDirectory) / binaryBaseName + hostExeSuffix;
541     return executable.canonicalPath().toUserOutput();
542 }
543 
544 /*!
545     \internal
546 */
clangExecutable(const QString & clangBinDirectory)547 QString ICore::clangExecutable(const QString &clangBinDirectory)
548 {
549     return clangBinary("clang", clangBinDirectory);
550 }
551 
552 /*!
553     \internal
554 */
clangdExecutable(const QString & clangBinDirectory)555 QString ICore::clangdExecutable(const QString &clangBinDirectory)
556 {
557     return clangBinary("clangd", clangBinDirectory);
558 }
559 
560 /*!
561     \internal
562 */
clangTidyExecutable(const QString & clangBinDirectory)563 QString ICore::clangTidyExecutable(const QString &clangBinDirectory)
564 {
565     return clangBinary("clang-tidy", clangBinDirectory);
566 }
567 
568 /*!
569     \internal
570 */
clazyStandaloneExecutable(const QString & clangBinDirectory)571 QString ICore::clazyStandaloneExecutable(const QString &clangBinDirectory)
572 {
573     return clangBinary("clazy-standalone", clangBinDirectory);
574 }
575 
compilerString()576 static QString compilerString()
577 {
578 #if defined(Q_CC_CLANG) // must be before GNU, because clang claims to be GNU too
579     QString platformSpecific;
580 #if defined(__apple_build_version__) // Apple clang has other version numbers
581     platformSpecific = QLatin1String(" (Apple)");
582 #elif defined(Q_CC_MSVC)
583     platformSpecific = QLatin1String(" (clang-cl)");
584 #endif
585     return QLatin1String("Clang " ) + QString::number(__clang_major__) + QLatin1Char('.')
586             + QString::number(__clang_minor__) + platformSpecific;
587 #elif defined(Q_CC_GNU)
588     return QLatin1String("GCC " ) + QLatin1String(__VERSION__);
589 #elif defined(Q_CC_MSVC)
590     if (_MSC_VER > 1999)
591         return QLatin1String("MSVC <unknown>");
592     if (_MSC_VER >= 1920)
593         return QLatin1String("MSVC 2019");
594     if (_MSC_VER >= 1910)
595         return QLatin1String("MSVC 2017");
596     if (_MSC_VER >= 1900)
597         return QLatin1String("MSVC 2015");
598 #endif
599     return QLatin1String("<unknown compiler>");
600 }
601 
602 /*!
603     Returns a string with the IDE's name and version, in the form "\QC X.Y.Z".
604     Use this for "Generated by" strings and similar tasks.
605 */
versionString()606 QString ICore::versionString()
607 {
608     QString ideVersionDescription;
609     if (QLatin1String(Constants::IDE_VERSION_LONG) != QLatin1String(Constants::IDE_VERSION_DISPLAY))
610         ideVersionDescription = tr(" (%1)").arg(QLatin1String(Constants::IDE_VERSION_LONG));
611     return tr("%1 %2%3").arg(QLatin1String(Constants::IDE_DISPLAY_NAME),
612                              QLatin1String(Constants::IDE_VERSION_DISPLAY),
613                              ideVersionDescription);
614 }
615 
616 /*!
617     \internal
618 */
buildCompatibilityString()619 QString ICore::buildCompatibilityString()
620 {
621     return tr("Based on Qt %1 (%2, %3 bit)").arg(QLatin1String(qVersion()),
622                                                  compilerString(),
623                                                  QString::number(QSysInfo::WordSize));
624 }
625 
626 /*!
627     Returns the top level IContext of the current context, or \c nullptr if
628     there is none.
629 
630     \sa updateAdditionalContexts()
631     \sa addContextObject()
632     \sa {The Action Manager and Commands}
633 */
currentContextObject()634 IContext *ICore::currentContextObject()
635 {
636     return m_mainwindow->currentContextObject();
637 }
638 
639 /*!
640     Returns the widget of the top level IContext of the current context, or \c
641     nullptr if there is none.
642 
643     \sa currentContextObject()
644 */
currentContextWidget()645 QWidget *ICore::currentContextWidget()
646 {
647     IContext *context = currentContextObject();
648     return context ? context->widget() : nullptr;
649 }
650 
651 /*!
652     Returns the registered IContext instance for the specified \a widget,
653     if any.
654 */
contextObject(QWidget * widget)655 IContext *ICore::contextObject(QWidget *widget)
656 {
657     return m_mainwindow->contextObject(widget);
658 }
659 
660 /*!
661     Returns the main window of the application.
662 
663     For dialog parents use dialogParent().
664 
665     \sa dialogParent()
666 */
mainWindow()667 QMainWindow *ICore::mainWindow()
668 {
669     return m_mainwindow;
670 }
671 
672 /*!
673     Returns a widget pointer suitable to use as parent for QDialogs.
674 */
dialogParent()675 QWidget *ICore::dialogParent()
676 {
677     QWidget *active = QApplication::activeModalWidget();
678     if (!active)
679         active = QApplication::activeWindow();
680     if (!active || (active && active->windowFlags().testFlag(Qt::SplashScreen)))
681         active = m_mainwindow;
682     return active;
683 }
684 
685 /*!
686     \internal
687 */
statusBar()688 QStatusBar *ICore::statusBar()
689 {
690     return m_mainwindow->statusBar();
691 }
692 
693 /*!
694     Returns a central InfoBar that is shown in \QC's main window.
695     Use for notifying the user of something without interrupting with
696     dialog. Use sparingly.
697 */
infoBar()698 Utils::InfoBar *ICore::infoBar()
699 {
700     return m_mainwindow->infoBar();
701 }
702 
703 /*!
704     Raises and activates the window for \a widget. This contains workarounds
705     for X11.
706 */
raiseWindow(QWidget * widget)707 void ICore::raiseWindow(QWidget *widget)
708 {
709     if (!widget)
710         return;
711     QWidget *window = widget->window();
712     if (window && window == m_mainwindow) {
713         m_mainwindow->raiseWindow();
714     } else {
715         window->raise();
716         window->activateWindow();
717     }
718 }
719 
720 /*!
721     Removes the contexts specified by \a remove from the list of active
722     additional contexts, and adds the contexts specified by \a add with \a
723     priority.
724 
725     The additional contexts are not associated with an IContext instance.
726 
727     High priority additional contexts have higher priority than the contexts
728     added by IContext instances, low priority additional contexts have lower
729     priority than the contexts added by IContext instances.
730 
731     \sa addContextObject()
732     \sa {The Action Manager and Commands}
733 */
updateAdditionalContexts(const Context & remove,const Context & add,ContextPriority priority)734 void ICore::updateAdditionalContexts(const Context &remove, const Context &add,
735                                      ContextPriority priority)
736 {
737     m_mainwindow->updateAdditionalContexts(remove, add, priority);
738 }
739 
740 /*!
741     Adds \a context with \a priority to the list of active additional contexts.
742 
743     \sa updateAdditionalContexts()
744 */
addAdditionalContext(const Context & context,ContextPriority priority)745 void ICore::addAdditionalContext(const Context &context, ContextPriority priority)
746 {
747     m_mainwindow->updateAdditionalContexts(Context(), context, priority);
748 }
749 
750 /*!
751     Removes \a context from the list of active additional contexts.
752 
753     \sa updateAdditionalContexts()
754 */
removeAdditionalContext(const Context & context)755 void ICore::removeAdditionalContext(const Context &context)
756 {
757     m_mainwindow->updateAdditionalContexts(context, Context(), ContextPriority::Low);
758 }
759 
760 /*!
761     Adds \a context to the list of registered IContext instances.
762     Whenever the IContext's \l{IContext::widget()}{widget} is in the application
763     focus widget's parent hierarchy, its \l{IContext::context()}{context} is
764     added to the list of active contexts.
765 
766     \sa removeContextObject()
767     \sa updateAdditionalContexts()
768     \sa currentContextObject()
769     \sa {The Action Manager and Commands}
770 */
addContextObject(IContext * context)771 void ICore::addContextObject(IContext *context)
772 {
773     m_mainwindow->addContextObject(context);
774 }
775 
776 /*!
777     Unregisters a \a context object from the list of registered IContext
778     instances. IContext instances are automatically removed when they are
779     deleted.
780 
781     \sa addContextObject()
782     \sa updateAdditionalContexts()
783     \sa currentContextObject()
784 */
removeContextObject(IContext * context)785 void ICore::removeContextObject(IContext *context)
786 {
787     m_mainwindow->removeContextObject(context);
788 }
789 
790 /*!
791     Registers a \a window with the specified \a context. Registered windows are
792     shown in the \uicontrol Window menu and get registered for the various
793     window related actions, like the minimize, zoom, fullscreen and close
794     actions.
795 
796     Whenever the application focus is in \a window, its \a context is made
797     active.
798 */
registerWindow(QWidget * window,const Context & context)799 void ICore::registerWindow(QWidget *window, const Context &context)
800 {
801     new WindowSupport(window, context); // deletes itself when widget is destroyed
802 }
803 
804 /*!
805     Opens files using \a arguments and \a flags like it would be
806     done if they were given to \QC on the command line, or
807     they were opened via \uicontrol File > \uicontrol Open.
808 */
809 
openFiles(const QStringList & arguments,ICore::OpenFilesFlags flags)810 void ICore::openFiles(const QStringList &arguments, ICore::OpenFilesFlags flags)
811 {
812     MainWindow::openFiles(arguments, flags);
813 }
814 
815 /*!
816     Provides a hook for plugins to veto on closing the application.
817 
818     When the application window requests a close, all listeners are called. If
819     one of the \a listener calls returns \c false, the process is aborted and
820     the event is ignored. If all calls return \c true, coreAboutToClose()
821     is emitted and the event is accepted or performed.
822 */
addPreCloseListener(const std::function<bool ()> & listener)823 void ICore::addPreCloseListener(const std::function<bool ()> &listener)
824 {
825     m_mainwindow->addPreCloseListener(listener);
826 }
827 
828 /*!
829     \internal
830 */
systemInformation()831 QString ICore::systemInformation()
832 {
833     QString result = PluginManager::systemInformation() + '\n';
834     result += versionString() + '\n';
835     result += buildCompatibilityString() + '\n';
836 #ifdef IDE_REVISION
837     result += QString("From revision %1\n").arg(QString::fromLatin1(Constants::IDE_REVISION_STR).left(10));
838 #endif
839 #ifdef QTC_SHOW_BUILD_DATE
840      result += QString("Built on %1 %2\n").arg(QLatin1String(__DATE__), QLatin1String(__TIME__));
841 #endif
842      return result;
843 }
844 
screenShotsPath()845 static const QByteArray &screenShotsPath()
846 {
847     static const QByteArray path = qgetenv("QTC_SCREENSHOTS_PATH");
848     return path;
849 }
850 
851 class ScreenShooter : public QObject
852 {
853 public:
ScreenShooter(QWidget * widget,const QString & name,const QRect & rc)854     ScreenShooter(QWidget *widget, const QString &name, const QRect &rc)
855         : m_widget(widget), m_name(name), m_rc(rc)
856     {
857         m_widget->installEventFilter(this);
858     }
859 
eventFilter(QObject * watched,QEvent * event)860     bool eventFilter(QObject *watched, QEvent *event) override
861     {
862         QTC_ASSERT(watched == m_widget, return false);
863         if (event->type() == QEvent::Show)
864             QMetaObject::invokeMethod(this, &ScreenShooter::helper, Qt::QueuedConnection);
865         return false;
866     }
867 
helper()868     void helper()
869     {
870         if (m_widget) {
871             QRect rc = m_rc.isValid() ? m_rc : m_widget->rect();
872             QPixmap pm = m_widget->grab(rc);
873             for (int i = 0; ; ++i) {
874                 QString fileName = screenShotsPath() + '/' + m_name + QString("-%1.png").arg(i);
875                 if (!QFileInfo::exists(fileName)) {
876                     pm.save(fileName);
877                     break;
878                 }
879             }
880         }
881         deleteLater();
882     }
883 
884     QPointer<QWidget> m_widget;
885     QString m_name;
886     QRect m_rc;
887 };
888 
889 /*!
890     \internal
891 */
setupScreenShooter(const QString & name,QWidget * w,const QRect & rc)892 void ICore::setupScreenShooter(const QString &name, QWidget *w, const QRect &rc)
893 {
894     if (!screenShotsPath().isEmpty())
895         new ScreenShooter(w, name, rc);
896 }
897 
898 /*!
899     Restarts \QC and restores the last session.
900 */
restart()901 void ICore::restart()
902 {
903     m_mainwindow->restart();
904 }
905 
906 /*!
907     \internal
908 */
saveSettings(SaveSettingsReason reason)909 void ICore::saveSettings(SaveSettingsReason reason)
910 {
911     emit m_instance->saveSettingsRequested(reason);
912     m_mainwindow->saveSettings();
913 
914     ICore::settings(QSettings::SystemScope)->sync();
915     ICore::settings(QSettings::UserScope)->sync();
916 }
917 
918 /*!
919     \internal
920 */
additionalAboutInformation()921 QStringList ICore::additionalAboutInformation()
922 {
923     return m_mainwindow->additionalAboutInformation();
924 }
925 
926 /*!
927     \internal
928 */
appendAboutInformation(const QString & line)929 void ICore::appendAboutInformation(const QString &line)
930 {
931     m_mainwindow->appendAboutInformation(line);
932 }
933 
updateNewItemDialogState()934 void ICore::updateNewItemDialogState()
935 {
936     static bool wasRunning = false;
937     static QWidget *previousDialog = nullptr;
938     if (wasRunning == isNewItemDialogRunning() && previousDialog == newItemDialog())
939         return;
940     wasRunning = isNewItemDialogRunning();
941     previousDialog = newItemDialog();
942     emit instance()->newItemDialogStateChanged();
943 }
944 
945 } // namespace Core
946