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 "outlinefactory.h"
27 #include <coreplugin/coreconstants.h>
28 #include <coreplugin/icore.h>
29 #include <coreplugin/editormanager/editormanager.h>
30 #include <coreplugin/editormanager/ieditor.h>
31
32 #include <utils/utilsicons.h>
33 #include <utils/qtcassert.h>
34
35 #include <QToolButton>
36 #include <QLabel>
37 #include <QStackedWidget>
38
39 #include <QDebug>
40
41 namespace TextEditor {
42
43 static QList<IOutlineWidgetFactory *> g_outlineWidgetFactories;
44 static QPointer<Internal::OutlineFactory> g_outlineFactory;
45
IOutlineWidgetFactory()46 IOutlineWidgetFactory::IOutlineWidgetFactory()
47 {
48 g_outlineWidgetFactories.append(this);
49 }
50
~IOutlineWidgetFactory()51 IOutlineWidgetFactory::~IOutlineWidgetFactory()
52 {
53 g_outlineWidgetFactories.removeOne(this);
54 }
55
updateOutline()56 void IOutlineWidgetFactory::updateOutline()
57 {
58 if (QTC_GUARD(!g_outlineFactory.isNull()))
59 emit g_outlineFactory->updateOutline();
60 }
61
62 namespace Internal {
63
OutlineWidgetStack(OutlineFactory * factory)64 OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) :
65 QStackedWidget(),
66 m_factory(factory),
67 m_syncWithEditor(true),
68 m_sorted(false)
69 {
70 QLabel *label = new QLabel(tr("No outline available"), this);
71 label->setAlignment(Qt::AlignCenter);
72
73 // set background to be white
74 label->setAutoFillBackground(true);
75 label->setBackgroundRole(QPalette::Base);
76
77 addWidget(label);
78
79 m_toggleSync = new QToolButton(this);
80 m_toggleSync->setIcon(Utils::Icons::LINK_TOOLBAR.icon());
81 m_toggleSync->setCheckable(true);
82 m_toggleSync->setChecked(true);
83 m_toggleSync->setToolTip(tr("Synchronize with Editor"));
84 connect(m_toggleSync, &QAbstractButton::clicked,
85 this, &OutlineWidgetStack::toggleCursorSynchronization);
86
87 m_filterButton = new QToolButton(this);
88 // The ToolButton needs a parent because updateFilterMenu() sets
89 // it visible. That would open a top-level window if the button
90 // did not have a parent in that moment.
91
92 m_filterButton->setIcon(Utils::Icons::FILTER.icon());
93 m_filterButton->setToolTip(tr("Filter tree"));
94 m_filterButton->setPopupMode(QToolButton::InstantPopup);
95 m_filterButton->setProperty("noArrow", true);
96 m_filterMenu = new QMenu(m_filterButton);
97 m_filterButton->setMenu(m_filterMenu);
98
99 m_toggleSort = new QToolButton(this);
100 m_toggleSort->setIcon(Utils::Icons::SORT_ALPHABETICALLY_TOOLBAR.icon());
101 m_toggleSort->setCheckable(true);
102 m_toggleSort->setChecked(false);
103 m_toggleSort->setToolTip(tr("Sort Alphabetically"));
104 connect(m_toggleSort, &QAbstractButton::clicked, this, &OutlineWidgetStack::toggleSort);
105
106 connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged,
107 this, &OutlineWidgetStack::updateEditor);
108 connect(factory, &OutlineFactory::updateOutline,
109 this, &OutlineWidgetStack::updateCurrentEditor);
110 updateCurrentEditor();
111 }
112
toolButtons()113 QList<QToolButton *> OutlineWidgetStack::toolButtons()
114 {
115 return {m_filterButton, m_toggleSort, m_toggleSync};
116 }
117
118 OutlineWidgetStack::~OutlineWidgetStack() = default;
119
saveSettings(QSettings * settings,int position)120 void OutlineWidgetStack::saveSettings(QSettings *settings, int position)
121 {
122 const QString baseKey = QStringLiteral("Outline.%1.").arg(position);
123 settings->setValue(baseKey + QLatin1String("SyncWithEditor"), m_toggleSync->isChecked());
124 for (auto iter = m_widgetSettings.constBegin(); iter != m_widgetSettings.constEnd(); ++iter)
125 settings->setValue(baseKey + iter.key(), iter.value());
126 }
127
restoreSettings(QSettings * settings,int position)128 void OutlineWidgetStack::restoreSettings(QSettings *settings, int position)
129 {
130 const QString baseKey = QStringLiteral("Outline.%1.").arg(position);
131
132 bool syncWithEditor = true;
133 m_widgetSettings.clear();
134 foreach (const QString &longKey, settings->allKeys()) {
135 if (!longKey.startsWith(baseKey))
136 continue;
137
138 const QString key = longKey.mid(baseKey.length());
139
140 if (key == QLatin1String("SyncWithEditor")) {
141 syncWithEditor = settings->value(longKey).toBool();
142 continue;
143 }
144 m_widgetSettings.insert(key, settings->value(longKey));
145 }
146
147 m_toggleSync->setChecked(syncWithEditor);
148 if (auto outlineWidget = qobject_cast<IOutlineWidget*>(currentWidget()))
149 outlineWidget->restoreSettings(m_widgetSettings);
150 }
151
isCursorSynchronized() const152 bool OutlineWidgetStack::isCursorSynchronized() const
153 {
154 return m_syncWithEditor;
155 }
156
toggleCursorSynchronization()157 void OutlineWidgetStack::toggleCursorSynchronization()
158 {
159 m_syncWithEditor = !m_syncWithEditor;
160 if (auto outlineWidget = qobject_cast<IOutlineWidget*>(currentWidget()))
161 outlineWidget->setCursorSynchronization(m_syncWithEditor);
162 }
163
toggleSort()164 void OutlineWidgetStack::toggleSort()
165 {
166 m_sorted = !m_sorted;
167 if (auto outlineWidget = qobject_cast<IOutlineWidget*>(currentWidget()))
168 outlineWidget->setSorted(m_sorted);
169 }
170
updateFilterMenu()171 void OutlineWidgetStack::updateFilterMenu()
172 {
173 m_filterMenu->clear();
174 if (auto outlineWidget = qobject_cast<IOutlineWidget*>(currentWidget())) {
175 foreach (QAction *filterAction, outlineWidget->filterMenuActions()) {
176 m_filterMenu->addAction(filterAction);
177 }
178 }
179 m_filterButton->setVisible(!m_filterMenu->actions().isEmpty());
180 }
181
updateCurrentEditor()182 void OutlineWidgetStack::updateCurrentEditor()
183 {
184 updateEditor(Core::EditorManager::currentEditor());
185 }
186
updateEditor(Core::IEditor * editor)187 void OutlineWidgetStack::updateEditor(Core::IEditor *editor)
188 {
189 IOutlineWidget *newWidget = nullptr;
190
191 if (editor) {
192 for (IOutlineWidgetFactory *widgetFactory : qAsConst(g_outlineWidgetFactories)) {
193 if (widgetFactory->supportsEditor(editor)) {
194 newWidget = widgetFactory->createWidget(editor);
195 m_toggleSort->setVisible(widgetFactory->supportsSorting());
196 break;
197 }
198 }
199 }
200
201 if (newWidget != currentWidget()) {
202 // delete old widget
203 if (auto outlineWidget = qobject_cast<IOutlineWidget*>(currentWidget())) {
204 QVariantMap widgetSettings = outlineWidget->settings();
205 for (auto iter = widgetSettings.constBegin(); iter != widgetSettings.constEnd(); ++iter)
206 m_widgetSettings.insert(iter.key(), iter.value());
207 removeWidget(outlineWidget);
208 delete outlineWidget;
209 }
210 if (newWidget) {
211 newWidget->restoreSettings(m_widgetSettings);
212 newWidget->setCursorSynchronization(m_syncWithEditor);
213 m_toggleSort->setChecked(newWidget->isSorted());
214 addWidget(newWidget);
215 setCurrentWidget(newWidget);
216 setFocusProxy(newWidget);
217 }
218
219 updateFilterMenu();
220 }
221 }
222
OutlineFactory()223 OutlineFactory::OutlineFactory()
224 {
225 QTC_CHECK(g_outlineFactory.isNull());
226 g_outlineFactory = this;
227 setDisplayName(tr("Outline"));
228 setId("Outline");
229 setPriority(600);
230 }
231
createWidget()232 Core::NavigationView OutlineFactory::createWidget()
233 {
234 auto placeHolder = new OutlineWidgetStack(this);
235 return {placeHolder, placeHolder->toolButtons()};
236 }
237
saveSettings(Utils::QtcSettings * settings,int position,QWidget * widget)238 void OutlineFactory::saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget)
239 {
240 auto widgetStack = qobject_cast<OutlineWidgetStack *>(widget);
241 Q_ASSERT(widgetStack);
242 widgetStack->saveSettings(settings, position);
243 }
244
restoreSettings(QSettings * settings,int position,QWidget * widget)245 void OutlineFactory::restoreSettings(QSettings *settings, int position, QWidget *widget)
246 {
247 auto widgetStack = qobject_cast<OutlineWidgetStack *>(widget);
248 Q_ASSERT(widgetStack);
249 widgetStack->restoreSettings(settings, position);
250 }
251
252 } // namespace Internal
253 } // namespace TextEditor
254