]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphintabpage.cpp
Make UrlNavigators in the toolbar the only option
[dolphin.git] / src / dolphintabpage.cpp
1 /*
2 * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <emmanuelpescosta099@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "dolphintabpage.h"
8
9 #include "dolphin_generalsettings.h"
10 #include "dolphinviewcontainer.h"
11
12 #include <QPropertyAnimation>
13 #include <QSplitter>
14 #include <QVBoxLayout>
15
16 DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, QWidget* parent) :
17 QWidget(parent),
18 m_primaryViewActive(true),
19 m_splitViewEnabled(false),
20 m_active(true)
21 {
22 QVBoxLayout* layout = new QVBoxLayout(this);
23 layout->setSpacing(0);
24 layout->setContentsMargins(0, 0, 0, 0);
25
26 m_splitter = new QSplitter(Qt::Horizontal, this);
27 m_splitter->setChildrenCollapsible(false);
28 connect(m_splitter, &QSplitter::splitterMoved,
29 this, &DolphinTabPage::splitterMoved);
30 layout->addWidget(m_splitter);
31
32 // Create a new primary view
33 m_primaryViewContainer = createViewContainer(primaryUrl);
34 connect(m_primaryViewContainer->view(), &DolphinView::urlChanged,
35 this, &DolphinTabPage::activeViewUrlChanged);
36 connect(m_primaryViewContainer->view(), &DolphinView::redirection,
37 this, &DolphinTabPage::slotViewUrlRedirection);
38
39 m_splitter->addWidget(m_primaryViewContainer);
40 m_primaryViewContainer->installEventFilter(this);
41 m_primaryViewContainer->show();
42
43 if (secondaryUrl.isValid() || GeneralSettings::splitView()) {
44 // Provide a secondary view, if the given secondary url is valid or if the
45 // startup settings are set this way (use the url of the primary view).
46 m_splitViewEnabled = true;
47 const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl;
48 m_secondaryViewContainer = createViewContainer(url);
49 m_splitter->addWidget(m_secondaryViewContainer);
50 m_secondaryViewContainer->installEventFilter(this);
51 m_secondaryViewContainer->show();
52 }
53
54 m_primaryViewContainer->setActive(true);
55 }
56
57 bool DolphinTabPage::primaryViewActive() const
58 {
59 return m_primaryViewActive;
60 }
61
62 bool DolphinTabPage::splitViewEnabled() const
63 {
64 return m_splitViewEnabled;
65 }
66
67 void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl)
68 {
69 if (m_splitViewEnabled != enabled) {
70 m_splitViewEnabled = enabled;
71
72 if (enabled) {
73 int splitterTotalWidth = m_splitter->width();
74 const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl;
75 m_secondaryViewContainer = createViewContainer(url);
76
77 auto secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
78 if (!secondaryNavigator) {
79 m_navigatorsWidget->createSecondaryUrlNavigator();
80 secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
81 }
82 m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
83 m_navigatorsWidget->setSecondaryNavigatorVisible(true);
84
85 m_splitter->addWidget(m_secondaryViewContainer);
86 m_secondaryViewContainer->installEventFilter(this);
87 m_secondaryViewContainer->setActive(true);
88
89 // opening animation
90 m_splitter->widget(1)->setMinimumWidth(1);
91 const QList<int> splitterSizes = {m_splitter->width(), 0};
92 m_splitter->setSizes(splitterSizes);
93
94 // TODO: This is only here to test the robustness of DolphinNavigatorsWidgetAction! I still have to move it to another merge request!
95 m_splitViewAnimation = new QVariantAnimation(m_splitter);
96 m_splitViewAnimation->setDuration(200); // TODO: where do I get the animation times from again?
97 m_splitViewAnimation->setStartValue(splitterTotalWidth);
98 m_splitViewAnimation->setEndValue(splitterTotalWidth / 2);
99 m_splitViewAnimation->setEasingCurve(QEasingCurve::OutCubic);
100
101 connect(m_splitViewAnimation, &QVariantAnimation::valueChanged, [=]() {
102 if (m_splitter->count() != 2) {
103 return;
104 }
105 int value = m_splitViewAnimation->currentValue().toInt();
106 const QList<int> splitterSizes = {value, m_splitter->width() - value};
107 m_splitter->setSizes(splitterSizes);
108 if (value == m_splitViewAnimation->endValue().toInt()) {
109 m_splitter->widget(1)->setMinimumWidth(m_splitter->widget(1)->minimumSizeHint().width());
110 }
111 });
112 m_splitViewAnimation->start(QAbstractAnimation::DeleteWhenStopped);
113 m_secondaryViewContainer->show();
114 } else {
115 m_navigatorsWidget->setSecondaryNavigatorVisible(false);
116 m_secondaryViewContainer->disconnectUrlNavigator();
117
118 DolphinViewContainer* view;
119 if (GeneralSettings::closeActiveSplitView()) {
120 view = activeViewContainer();
121 if (m_primaryViewActive) {
122 m_primaryViewContainer->disconnectUrlNavigator();
123 m_secondaryViewContainer->connectUrlNavigator(
124 m_navigatorsWidget->primaryUrlNavigator());
125
126 // If the primary view is active, we have to swap the pointers
127 // because the secondary view will be the new primary view.
128 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
129 m_primaryViewActive = false;
130 }
131 } else {
132 view = m_primaryViewActive ? m_secondaryViewContainer : m_primaryViewContainer;
133 if (!m_primaryViewActive) {
134 m_primaryViewContainer->disconnectUrlNavigator();
135 m_secondaryViewContainer->connectUrlNavigator(
136 m_navigatorsWidget->primaryUrlNavigator());
137
138 // If the secondary view is active, we have to swap the pointers
139 // because the secondary view will be the new primary view.
140 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
141 m_primaryViewActive = true;
142 }
143 }
144 m_primaryViewContainer->setActive(true);
145 view->close();
146 view->deleteLater();
147 if (m_splitViewAnimation) {
148 delete m_splitViewAnimation;
149 m_splitter->widget(0)->setMinimumWidth(m_splitter->widget(0)->minimumSizeHint().width());
150 }
151 }
152 }
153 }
154
155 DolphinViewContainer* DolphinTabPage::primaryViewContainer() const
156 {
157 return m_primaryViewContainer;
158 }
159
160 DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const
161 {
162 return m_secondaryViewContainer;
163 }
164
165 DolphinViewContainer* DolphinTabPage::activeViewContainer() const
166 {
167 return m_primaryViewActive ? m_primaryViewContainer :
168 m_secondaryViewContainer;
169 }
170
171 KFileItemList DolphinTabPage::selectedItems() const
172 {
173 KFileItemList items = m_primaryViewContainer->view()->selectedItems();
174 if (m_splitViewEnabled) {
175 items += m_secondaryViewContainer->view()->selectedItems();
176 }
177 return items;
178 }
179
180 int DolphinTabPage::selectedItemsCount() const
181 {
182 int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount();
183 if (m_splitViewEnabled) {
184 selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount();
185 }
186 return selectedItemsCount;
187 }
188
189 void DolphinTabPage::connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget)
190 {
191 m_navigatorsWidget = navigatorsWidget;
192 auto primaryNavigator = navigatorsWidget->primaryUrlNavigator();
193 primaryNavigator->setActive(m_primaryViewActive);
194 m_primaryViewContainer->connectUrlNavigator(primaryNavigator);
195 if (m_splitViewEnabled) {
196 auto secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
197 if (!secondaryNavigator) {
198 navigatorsWidget->createSecondaryUrlNavigator();
199 secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
200 }
201 secondaryNavigator->setActive(!m_primaryViewActive);
202 m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
203 }
204 resizeNavigators();
205 }
206
207 void DolphinTabPage::disconnectNavigators()
208 {
209 m_navigatorsWidget = nullptr;
210 m_primaryViewContainer->disconnectUrlNavigator();
211 if (m_splitViewEnabled) {
212 m_secondaryViewContainer->disconnectUrlNavigator();
213 }
214 }
215
216 bool DolphinTabPage::eventFilter(QObject */* watched */, QEvent *event)
217 {
218 if (event->type() == QEvent::Resize && m_navigatorsWidget) {
219 resizeNavigators();
220 }
221 return false;
222 }
223
224 void DolphinTabPage::resizeNavigators() const
225 {
226 if (!m_splitViewEnabled) {
227 m_navigatorsWidget->followViewContainerGeometry(
228 m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
229 m_primaryViewContainer->width());
230 } else {
231 m_navigatorsWidget->followViewContainersGeometry(
232 m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
233 m_primaryViewContainer->width(),
234 m_secondaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
235 m_secondaryViewContainer->width());
236 }
237 }
238
239 void DolphinTabPage::markUrlsAsSelected(const QList<QUrl>& urls)
240 {
241 m_primaryViewContainer->view()->markUrlsAsSelected(urls);
242 if (m_splitViewEnabled) {
243 m_secondaryViewContainer->view()->markUrlsAsSelected(urls);
244 }
245 }
246
247 void DolphinTabPage::markUrlAsCurrent(const QUrl& url)
248 {
249 m_primaryViewContainer->view()->markUrlAsCurrent(url);
250 if (m_splitViewEnabled) {
251 m_secondaryViewContainer->view()->markUrlAsCurrent(url);
252 }
253 }
254
255 void DolphinTabPage::refreshViews()
256 {
257 m_primaryViewContainer->readSettings();
258 if (m_splitViewEnabled) {
259 m_secondaryViewContainer->readSettings();
260 }
261 }
262
263 QByteArray DolphinTabPage::saveState() const
264 {
265 QByteArray state;
266 QDataStream stream(&state, QIODevice::WriteOnly);
267
268 stream << quint32(2); // Tab state version
269
270 stream << m_splitViewEnabled;
271
272 stream << m_primaryViewContainer->url();
273 stream << m_primaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
274 m_primaryViewContainer->view()->saveState(stream);
275
276 if (m_splitViewEnabled) {
277 stream << m_secondaryViewContainer->url();
278 stream << m_secondaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
279 m_secondaryViewContainer->view()->saveState(stream);
280 }
281
282 stream << m_primaryViewActive;
283 stream << m_splitter->saveState();
284
285 return state;
286 }
287
288 void DolphinTabPage::restoreState(const QByteArray& state)
289 {
290 if (state.isEmpty()) {
291 return;
292 }
293
294 QByteArray sd = state;
295 QDataStream stream(&sd, QIODevice::ReadOnly);
296
297 // Read the version number of the tab state and check if the version is supported.
298 quint32 version = 0;
299 stream >> version;
300 if (version != 2) {
301 // The version of the tab state isn't supported, we can't restore it.
302 return;
303 }
304
305 bool isSplitViewEnabled = false;
306 stream >> isSplitViewEnabled;
307 setSplitViewEnabled(isSplitViewEnabled);
308
309 QUrl primaryUrl;
310 stream >> primaryUrl;
311 m_primaryViewContainer->setUrl(primaryUrl);
312 bool primaryUrlEditable;
313 stream >> primaryUrlEditable;
314 m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable);
315 m_primaryViewContainer->view()->restoreState(stream);
316
317 if (isSplitViewEnabled) {
318 QUrl secondaryUrl;
319 stream >> secondaryUrl;
320 m_secondaryViewContainer->setUrl(secondaryUrl);
321 bool secondaryUrlEditable;
322 stream >> secondaryUrlEditable;
323 m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable);
324 m_secondaryViewContainer->view()->restoreState(stream);
325 }
326
327 stream >> m_primaryViewActive;
328 if (m_primaryViewActive) {
329 m_primaryViewContainer->setActive(true);
330 m_navigatorsWidget->primaryUrlNavigator()->setActive(true);
331 } else {
332 Q_ASSERT(m_splitViewEnabled);
333 m_secondaryViewContainer->setActive(true);
334 m_navigatorsWidget->primaryUrlNavigator()->setActive(false);
335 }
336
337 if (m_splitViewAnimation) {
338 delete m_splitViewAnimation;
339 m_splitter->widget(0)->setMinimumWidth(m_splitter->widget(0)->minimumSizeHint().width());
340 }
341 QByteArray splitterState;
342 stream >> splitterState;
343 m_splitter->restoreState(splitterState);
344 }
345
346 void DolphinTabPage::restoreStateV1(const QByteArray& state)
347 {
348 if (state.isEmpty()) {
349 return;
350 }
351
352 QByteArray sd = state;
353 QDataStream stream(&sd, QIODevice::ReadOnly);
354
355 bool isSplitViewEnabled = false;
356 stream >> isSplitViewEnabled;
357 setSplitViewEnabled(isSplitViewEnabled);
358
359 QUrl primaryUrl;
360 stream >> primaryUrl;
361 m_primaryViewContainer->setUrl(primaryUrl);
362 bool primaryUrlEditable;
363 stream >> primaryUrlEditable;
364 m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable);
365
366 if (isSplitViewEnabled) {
367 QUrl secondaryUrl;
368 stream >> secondaryUrl;
369 m_secondaryViewContainer->setUrl(secondaryUrl);
370 bool secondaryUrlEditable;
371 stream >> secondaryUrlEditable;
372 m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable);
373 }
374
375 stream >> m_primaryViewActive;
376 if (m_primaryViewActive) {
377 m_primaryViewContainer->setActive(true);
378 } else {
379 Q_ASSERT(m_splitViewEnabled);
380 m_secondaryViewContainer->setActive(true);
381 }
382
383 QByteArray splitterState;
384 stream >> splitterState;
385 m_splitter->restoreState(splitterState);
386 }
387
388 void DolphinTabPage::setActive(bool active)
389 {
390 if (active) {
391 m_active = active;
392 } else {
393 // we should bypass changing active view in split mode
394 m_active = !m_splitViewEnabled;
395 }
396 // we want view to fire activated when goes from false to true
397 activeViewContainer()->setActive(active);
398 }
399
400 void DolphinTabPage::slotViewActivated()
401 {
402 const DolphinView* oldActiveView = activeViewContainer()->view();
403
404 // Set the view, which was active before, to inactive
405 // and update the active view type, if tab is active
406 if (m_active) {
407 if (m_splitViewEnabled) {
408 activeViewContainer()->setActive(false);
409 m_primaryViewActive = !m_primaryViewActive;
410 } else {
411 m_primaryViewActive = true;
412 if (m_secondaryViewContainer) {
413 m_secondaryViewContainer->setActive(false);
414 }
415 }
416 }
417
418 const DolphinView* newActiveView = activeViewContainer()->view();
419
420 if (newActiveView == oldActiveView) {
421 return;
422 }
423
424 disconnect(oldActiveView, &DolphinView::urlChanged,
425 this, &DolphinTabPage::activeViewUrlChanged);
426 disconnect(oldActiveView, &DolphinView::redirection,
427 this, &DolphinTabPage::slotViewUrlRedirection);
428 connect(newActiveView, &DolphinView::urlChanged,
429 this, &DolphinTabPage::activeViewUrlChanged);
430 connect(newActiveView, &DolphinView::redirection,
431 this, &DolphinTabPage::slotViewUrlRedirection);
432 Q_EMIT activeViewChanged(activeViewContainer());
433 Q_EMIT activeViewUrlChanged(activeViewContainer()->url());
434 }
435
436 void DolphinTabPage::slotViewUrlRedirection(const QUrl& oldUrl, const QUrl& newUrl)
437 {
438 Q_UNUSED(oldUrl)
439
440 Q_EMIT activeViewUrlChanged(newUrl);
441 }
442
443 void DolphinTabPage::switchActiveView()
444 {
445 if (!m_splitViewEnabled) {
446 return;
447 }
448 if (m_primaryViewActive) {
449 m_secondaryViewContainer->setActive(true);
450 } else {
451 m_primaryViewContainer->setActive(true);
452 }
453 }
454
455 DolphinViewContainer* DolphinTabPage::createViewContainer(const QUrl& url) const
456 {
457 DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter);
458 container->setActive(false);
459
460 const DolphinView* view = container->view();
461 connect(view, &DolphinView::activated,
462 this, &DolphinTabPage::slotViewActivated);
463
464 connect(view, &DolphinView::toggleActiveViewRequested,
465 this, &DolphinTabPage::switchActiveView);
466
467 return container;
468 }