]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphintabpage.cpp
Remove code for dolphin < 4.14 tab restore
[dolphin.git] / src / dolphintabpage.cpp
1 /*
2 * SPDX-FileCopyrightText: 2014 Emmanuel Pescosta <emmanuelpescosta099@gmail.com>
3 * SPDX-FileCopyrightText: 2020 Felix Ernst <fe.a.ernst@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "dolphintabpage.h"
9
10 #include "dolphin_generalsettings.h"
11 #include "dolphinviewcontainer.h"
12 #include "global.h"
13
14 #include <QVariantAnimation>
15 #include <QSplitter>
16 #include <QGridLayout>
17 #include <QWidgetAction>
18 #include <QStyle>
19
20 DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, QWidget* parent) :
21 QWidget(parent),
22 m_expandingContainer{nullptr},
23 m_primaryViewActive(true),
24 m_splitViewEnabled(false),
25 m_active(true)
26 {
27 QGridLayout *layout = new QGridLayout(this);
28 layout->setSpacing(0);
29 layout->setContentsMargins(0, 0, 0, 0);
30
31 m_splitter = new QSplitter(Qt::Horizontal, this);
32 m_splitter->setChildrenCollapsible(false);
33 connect(m_splitter, &QSplitter::splitterMoved,
34 this, &DolphinTabPage::splitterMoved);
35 layout->addWidget(m_splitter, 1, 0);
36 layout->setRowStretch(1, 1);
37
38 // Create a new primary view
39 m_primaryViewContainer = createViewContainer(primaryUrl);
40 connect(m_primaryViewContainer->view(), &DolphinView::urlChanged,
41 this, &DolphinTabPage::activeViewUrlChanged);
42 connect(m_primaryViewContainer->view(), &DolphinView::redirection,
43 this, &DolphinTabPage::slotViewUrlRedirection);
44
45 m_splitter->addWidget(m_primaryViewContainer);
46 m_primaryViewContainer->show();
47
48 if (secondaryUrl.isValid() || GeneralSettings::splitView()) {
49 // Provide a secondary view, if the given secondary url is valid or if the
50 // startup settings are set this way (use the url of the primary view).
51 m_splitViewEnabled = true;
52 const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl;
53 m_secondaryViewContainer = createViewContainer(url);
54 m_splitter->addWidget(m_secondaryViewContainer);
55 m_secondaryViewContainer->show();
56 }
57
58 m_primaryViewContainer->setActive(true);
59 }
60
61 bool DolphinTabPage::primaryViewActive() const
62 {
63 return m_primaryViewActive;
64 }
65
66 bool DolphinTabPage::splitViewEnabled() const
67 {
68 return m_splitViewEnabled;
69 }
70
71 void DolphinTabPage::setSplitViewEnabled(bool enabled, Animated animated, const QUrl &secondaryUrl)
72 {
73 if (m_splitViewEnabled != enabled) {
74 m_splitViewEnabled = enabled;
75 if (animated == WithAnimation && (
76 style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) < 1 ||
77 GlobalConfig::animationDurationFactor() <= 0.0)) {
78 animated = WithoutAnimation;
79 }
80 if (m_expandViewAnimation) {
81 m_expandViewAnimation->stop(); // deletes because of QAbstractAnimation::DeleteWhenStopped.
82 if (animated == WithoutAnimation) {
83 slotAnimationFinished();
84 }
85 }
86
87 if (enabled) {
88 QList<int> splitterSizes = m_splitter->sizes();
89 const QUrl& url = (secondaryUrl.isEmpty()) ? m_primaryViewContainer->url() : secondaryUrl;
90 m_secondaryViewContainer = createViewContainer(url);
91
92 auto secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
93 if (!secondaryNavigator) {
94 m_navigatorsWidget->createSecondaryUrlNavigator();
95 secondaryNavigator = m_navigatorsWidget->secondaryUrlNavigator();
96 }
97 m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
98 m_navigatorsWidget->setSecondaryNavigatorVisible(true);
99 m_navigatorsWidget->followViewContainersGeometry(m_primaryViewContainer,
100 m_secondaryViewContainer);
101
102 m_splitter->addWidget(m_secondaryViewContainer);
103 m_secondaryViewContainer->setActive(true);
104
105 if (animated == WithAnimation) {
106 m_secondaryViewContainer->setMinimumWidth(1);
107 splitterSizes.append(1);
108 m_splitter->setSizes(splitterSizes);
109 startExpandViewAnimation(m_secondaryViewContainer);
110 }
111 m_secondaryViewContainer->show();
112 } else {
113 m_navigatorsWidget->setSecondaryNavigatorVisible(false);
114 m_secondaryViewContainer->disconnectUrlNavigator();
115
116 DolphinViewContainer* view;
117 if (GeneralSettings::closeActiveSplitView()) {
118 view = activeViewContainer();
119 if (m_primaryViewActive) {
120 m_primaryViewContainer->disconnectUrlNavigator();
121 m_secondaryViewContainer->connectUrlNavigator(
122 m_navigatorsWidget->primaryUrlNavigator());
123
124 // If the primary view is active, we have to swap the pointers
125 // because the secondary view will be the new primary view.
126 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
127 m_primaryViewActive = false;
128 }
129 } else {
130 view = m_primaryViewActive ? m_secondaryViewContainer : m_primaryViewContainer;
131 if (!m_primaryViewActive) {
132 m_primaryViewContainer->disconnectUrlNavigator();
133 m_secondaryViewContainer->connectUrlNavigator(
134 m_navigatorsWidget->primaryUrlNavigator());
135
136 // If the secondary view is active, we have to swap the pointers
137 // because the secondary view will be the new primary view.
138 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
139 m_primaryViewActive = true;
140 }
141 }
142 m_primaryViewContainer->setActive(true);
143 m_navigatorsWidget->followViewContainersGeometry(m_primaryViewContainer, nullptr);
144
145 if (animated == WithoutAnimation) {
146 view->close();
147 view->deleteLater();
148 } else {
149 // Kill it but keep it as a zombie for the closing animation.
150 m_secondaryViewContainer = nullptr;
151 view->blockSignals(true);
152 view->view()->blockSignals(true);
153 view->setDisabled(true);
154 startExpandViewAnimation(m_primaryViewContainer);
155 }
156 }
157 }
158 }
159
160 DolphinViewContainer* DolphinTabPage::primaryViewContainer() const
161 {
162 return m_primaryViewContainer;
163 }
164
165 DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const
166 {
167 return m_secondaryViewContainer;
168 }
169
170 DolphinViewContainer* DolphinTabPage::activeViewContainer() const
171 {
172 return m_primaryViewActive ? m_primaryViewContainer :
173 m_secondaryViewContainer;
174 }
175
176 KFileItemList DolphinTabPage::selectedItems() const
177 {
178 KFileItemList items = m_primaryViewContainer->view()->selectedItems();
179 if (m_splitViewEnabled) {
180 items += m_secondaryViewContainer->view()->selectedItems();
181 }
182 return items;
183 }
184
185 int DolphinTabPage::selectedItemsCount() const
186 {
187 int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount();
188 if (m_splitViewEnabled) {
189 selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount();
190 }
191 return selectedItemsCount;
192 }
193
194 void DolphinTabPage::connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget)
195 {
196 insertNavigatorsWidget(navigatorsWidget);
197 m_navigatorsWidget = navigatorsWidget;
198 auto primaryNavigator = navigatorsWidget->primaryUrlNavigator();
199 m_primaryViewContainer->connectUrlNavigator(primaryNavigator);
200 if (m_splitViewEnabled) {
201 auto secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
202 m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
203 }
204 m_navigatorsWidget->followViewContainersGeometry(m_primaryViewContainer,
205 m_secondaryViewContainer);
206 }
207
208 void DolphinTabPage::disconnectNavigators()
209 {
210 m_navigatorsWidget = nullptr;
211 m_primaryViewContainer->disconnectUrlNavigator();
212 if (m_splitViewEnabled) {
213 m_secondaryViewContainer->disconnectUrlNavigator();
214 }
215 }
216
217 void DolphinTabPage::insertNavigatorsWidget(DolphinNavigatorsWidgetAction* navigatorsWidget)
218 {
219 QGridLayout *gridLayout = static_cast<QGridLayout *>(layout());
220 if (navigatorsWidget->isInToolbar()) {
221 gridLayout->setRowMinimumHeight(0, 0);
222 } else {
223 // We set a row minimum height, so the height does not visibly change whenever
224 // navigatorsWidget is inserted which happens every time the current tab is changed.
225 gridLayout->setRowMinimumHeight(0, navigatorsWidget->primaryUrlNavigator()->height());
226 gridLayout->addWidget(navigatorsWidget->requestWidget(this), 0, 0);
227 }
228 }
229
230 void DolphinTabPage::markUrlsAsSelected(const QList<QUrl>& urls)
231 {
232 m_primaryViewContainer->view()->markUrlsAsSelected(urls);
233 if (m_splitViewEnabled) {
234 m_secondaryViewContainer->view()->markUrlsAsSelected(urls);
235 }
236 }
237
238 void DolphinTabPage::markUrlAsCurrent(const QUrl& url)
239 {
240 m_primaryViewContainer->view()->markUrlAsCurrent(url);
241 if (m_splitViewEnabled) {
242 m_secondaryViewContainer->view()->markUrlAsCurrent(url);
243 }
244 }
245
246 void DolphinTabPage::refreshViews()
247 {
248 m_primaryViewContainer->readSettings();
249 if (m_splitViewEnabled) {
250 m_secondaryViewContainer->readSettings();
251 }
252 }
253
254 QByteArray DolphinTabPage::saveState() const
255 {
256 QByteArray state;
257 QDataStream stream(&state, QIODevice::WriteOnly);
258
259 stream << quint32(2); // Tab state version
260
261 stream << m_splitViewEnabled;
262
263 stream << m_primaryViewContainer->url();
264 stream << m_primaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
265 m_primaryViewContainer->view()->saveState(stream);
266
267 if (m_splitViewEnabled) {
268 stream << m_secondaryViewContainer->url();
269 stream << m_secondaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
270 m_secondaryViewContainer->view()->saveState(stream);
271 }
272
273 stream << m_primaryViewActive;
274 stream << m_splitter->saveState();
275
276 return state;
277 }
278
279 void DolphinTabPage::restoreState(const QByteArray& state)
280 {
281 if (state.isEmpty()) {
282 return;
283 }
284
285 QByteArray sd = state;
286 QDataStream stream(&sd, QIODevice::ReadOnly);
287
288 // Read the version number of the tab state and check if the version is supported.
289 quint32 version = 0;
290 stream >> version;
291 if (version != 2) {
292 // The version of the tab state isn't supported, we can't restore it.
293 return;
294 }
295
296 bool isSplitViewEnabled = false;
297 stream >> isSplitViewEnabled;
298 setSplitViewEnabled(isSplitViewEnabled, WithoutAnimation);
299
300 QUrl primaryUrl;
301 stream >> primaryUrl;
302 m_primaryViewContainer->setUrl(primaryUrl);
303 bool primaryUrlEditable;
304 stream >> primaryUrlEditable;
305 m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable);
306 m_primaryViewContainer->view()->restoreState(stream);
307
308 if (isSplitViewEnabled) {
309 QUrl secondaryUrl;
310 stream >> secondaryUrl;
311 m_secondaryViewContainer->setUrl(secondaryUrl);
312 bool secondaryUrlEditable;
313 stream >> secondaryUrlEditable;
314 m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable);
315 m_secondaryViewContainer->view()->restoreState(stream);
316 }
317
318 stream >> m_primaryViewActive;
319 if (m_primaryViewActive) {
320 m_primaryViewContainer->setActive(true);
321 } else {
322 Q_ASSERT(m_splitViewEnabled);
323 m_secondaryViewContainer->setActive(true);
324 }
325
326 QByteArray splitterState;
327 stream >> splitterState;
328 m_splitter->restoreState(splitterState);
329 }
330
331 void DolphinTabPage::setActive(bool active)
332 {
333 if (active) {
334 m_active = active;
335 } else {
336 // we should bypass changing active view in split mode
337 m_active = !m_splitViewEnabled;
338 }
339 // we want view to fire activated when goes from false to true
340 activeViewContainer()->setActive(active);
341 }
342
343 void DolphinTabPage::slotAnimationFinished()
344 {
345 for (int i = 0; i < m_splitter->count(); ++i) {
346 QWidget *viewContainer = m_splitter->widget(i);
347 if (viewContainer != m_primaryViewContainer &&
348 viewContainer != m_secondaryViewContainer) {
349 viewContainer->close();
350 viewContainer->deleteLater();
351 }
352 }
353 for (int i = 0; i < m_splitter->count(); ++i) {
354 QWidget *viewContainer = m_splitter->widget(i);
355 viewContainer->setMinimumWidth(viewContainer->minimumSizeHint().width());
356 }
357 m_expandingContainer = nullptr;
358 }
359
360 void DolphinTabPage::slotAnimationValueChanged(const QVariant& value)
361 {
362 Q_CHECK_PTR(m_expandingContainer);
363 const int indexOfExpandingContainer = m_splitter->indexOf(m_expandingContainer);
364 int indexOfNonExpandingContainer = -1;
365 if (m_expandingContainer == m_primaryViewContainer) {
366 indexOfNonExpandingContainer = m_splitter->indexOf(m_secondaryViewContainer);
367 } else {
368 indexOfNonExpandingContainer = m_splitter->indexOf(m_primaryViewContainer);
369 }
370 std::vector<QWidget *> widgetsToRemove;
371 const QList<int> oldSplitterSizes = m_splitter->sizes();
372 QList<int> newSplitterSizes{oldSplitterSizes};
373 int expansionWidthNeeded = value.toInt() - oldSplitterSizes.at(indexOfExpandingContainer);
374
375 // Reduce the size of the other widgets to make space for the expandingContainer.
376 for (int i = m_splitter->count() - 1; i >= 0; --i) {
377 if (m_splitter->widget(i) == m_primaryViewContainer ||
378 m_splitter->widget(i) == m_secondaryViewContainer) {
379 continue;
380 }
381 newSplitterSizes[i] = oldSplitterSizes.at(i) - expansionWidthNeeded;
382 expansionWidthNeeded = 0;
383 if (indexOfNonExpandingContainer != -1) {
384 // Make sure every zombie container is at least slightly reduced in size
385 // so it doesn't seem like they are here to stay.
386 newSplitterSizes[i]--;
387 newSplitterSizes[indexOfNonExpandingContainer]++;
388 }
389 if (newSplitterSizes.at(i) <= 0) {
390 expansionWidthNeeded -= newSplitterSizes.at(i);
391 newSplitterSizes[i] = 0;
392 widgetsToRemove.emplace_back(m_splitter->widget(i));
393 }
394 }
395 if (expansionWidthNeeded > 1 && indexOfNonExpandingContainer != -1) {
396 Q_ASSERT(m_splitViewEnabled);
397 newSplitterSizes[indexOfNonExpandingContainer] -= expansionWidthNeeded;
398 }
399 newSplitterSizes[indexOfExpandingContainer] = value.toInt();
400 m_splitter->setSizes(newSplitterSizes);
401 while (!widgetsToRemove.empty()) {
402 widgetsToRemove.back()->close();
403 widgetsToRemove.back()->deleteLater();
404 widgetsToRemove.pop_back();
405 }
406 }
407
408
409 void DolphinTabPage::slotViewActivated()
410 {
411 const DolphinView* oldActiveView = activeViewContainer()->view();
412
413 // Set the view, which was active before, to inactive
414 // and update the active view type, if tab is active
415 if (m_active) {
416 if (m_splitViewEnabled) {
417 activeViewContainer()->setActive(false);
418 m_primaryViewActive = !m_primaryViewActive;
419 } else {
420 m_primaryViewActive = true;
421 if (m_secondaryViewContainer) {
422 m_secondaryViewContainer->setActive(false);
423 }
424 }
425 }
426
427 const DolphinView* newActiveView = activeViewContainer()->view();
428
429 if (newActiveView == oldActiveView) {
430 return;
431 }
432
433 disconnect(oldActiveView, &DolphinView::urlChanged,
434 this, &DolphinTabPage::activeViewUrlChanged);
435 disconnect(oldActiveView, &DolphinView::redirection,
436 this, &DolphinTabPage::slotViewUrlRedirection);
437 connect(newActiveView, &DolphinView::urlChanged,
438 this, &DolphinTabPage::activeViewUrlChanged);
439 connect(newActiveView, &DolphinView::redirection,
440 this, &DolphinTabPage::slotViewUrlRedirection);
441 Q_EMIT activeViewChanged(activeViewContainer());
442 Q_EMIT activeViewUrlChanged(activeViewContainer()->url());
443 }
444
445 void DolphinTabPage::slotViewUrlRedirection(const QUrl& oldUrl, const QUrl& newUrl)
446 {
447 Q_UNUSED(oldUrl)
448
449 Q_EMIT activeViewUrlChanged(newUrl);
450 }
451
452 void DolphinTabPage::switchActiveView()
453 {
454 if (!m_splitViewEnabled) {
455 return;
456 }
457 if (m_primaryViewActive) {
458 m_secondaryViewContainer->setActive(true);
459 } else {
460 m_primaryViewContainer->setActive(true);
461 }
462 }
463
464 DolphinViewContainer* DolphinTabPage::createViewContainer(const QUrl& url) const
465 {
466 DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter);
467 container->setActive(false);
468
469 const DolphinView* view = container->view();
470 connect(view, &DolphinView::activated,
471 this, &DolphinTabPage::slotViewActivated);
472
473 connect(view, &DolphinView::toggleActiveViewRequested,
474 this, &DolphinTabPage::switchActiveView);
475
476 return container;
477 }
478
479 void DolphinTabPage::startExpandViewAnimation(DolphinViewContainer *expandingContainer)
480 {
481 Q_CHECK_PTR(expandingContainer);
482 Q_ASSERT(expandingContainer == m_primaryViewContainer ||
483 expandingContainer == m_secondaryViewContainer);
484 m_expandingContainer = expandingContainer;
485
486 m_expandViewAnimation = new QVariantAnimation(m_splitter);
487 m_expandViewAnimation->setDuration(2 *
488 style()->styleHint(QStyle::SH_Widget_Animation_Duration, nullptr, this) *
489 GlobalConfig::animationDurationFactor());
490 for (int i = 0; i < m_splitter->count(); ++i) {
491 m_splitter->widget(i)->setMinimumWidth(1);
492 }
493 connect(m_expandViewAnimation, &QAbstractAnimation::finished,
494 this, &DolphinTabPage::slotAnimationFinished);
495 connect(m_expandViewAnimation, &QVariantAnimation::valueChanged,
496 this, &DolphinTabPage::slotAnimationValueChanged);
497
498 m_expandViewAnimation->setStartValue(expandingContainer->width());
499 if (m_splitViewEnabled) { // A new viewContainer is being opened.
500 m_expandViewAnimation->setEndValue(m_splitter->width() / 2);
501 m_expandViewAnimation->setEasingCurve(QEasingCurve::OutCubic);
502 } else { // A viewContainer is being closed.
503 m_expandViewAnimation->setEndValue(m_splitter->width());
504 m_expandViewAnimation->setEasingCurve(QEasingCurve::InCubic);
505 }
506 m_expandViewAnimation->start(QAbstractAnimation::DeleteWhenStopped);
507 }