]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphintabpage.cpp
Merge branch 'release/20.12'
[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 <QSplitter>
13 #include <QGridLayout>
14 #include <QWidgetAction>
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 QGridLayout *layout = new QGridLayout(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, 1, 0);
31 layout->setRowStretch(1, 1);
32
33 // Create a new primary view
34 m_primaryViewContainer = createViewContainer(primaryUrl);
35 connect(m_primaryViewContainer->view(), &DolphinView::urlChanged,
36 this, &DolphinTabPage::activeViewUrlChanged);
37 connect(m_primaryViewContainer->view(), &DolphinView::redirection,
38 this, &DolphinTabPage::slotViewUrlRedirection);
39
40 m_splitter->addWidget(m_primaryViewContainer);
41 m_primaryViewContainer->installEventFilter(this);
42 m_primaryViewContainer->show();
43
44 if (secondaryUrl.isValid() || GeneralSettings::splitView()) {
45 // Provide a secondary view, if the given secondary url is valid or if the
46 // startup settings are set this way (use the url of the primary view).
47 m_splitViewEnabled = true;
48 const QUrl& url = secondaryUrl.isValid() ? secondaryUrl : primaryUrl;
49 m_secondaryViewContainer = createViewContainer(url);
50 m_splitter->addWidget(m_secondaryViewContainer);
51 m_secondaryViewContainer->installEventFilter(this);
52 m_secondaryViewContainer->show();
53 }
54
55 m_primaryViewContainer->setActive(true);
56 }
57
58 bool DolphinTabPage::primaryViewActive() const
59 {
60 return m_primaryViewActive;
61 }
62
63 bool DolphinTabPage::splitViewEnabled() const
64 {
65 return m_splitViewEnabled;
66 }
67
68 void DolphinTabPage::setSplitViewEnabled(bool enabled, const QUrl &secondaryUrl)
69 {
70 if (m_splitViewEnabled != enabled) {
71 m_splitViewEnabled = enabled;
72
73 if (enabled) {
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->show();
88 m_secondaryViewContainer->setActive(true);
89 } else {
90 m_navigatorsWidget->setSecondaryNavigatorVisible(false);
91 m_secondaryViewContainer->disconnectUrlNavigator();
92
93 DolphinViewContainer* view;
94 if (GeneralSettings::closeActiveSplitView()) {
95 view = activeViewContainer();
96 if (m_primaryViewActive) {
97 m_primaryViewContainer->disconnectUrlNavigator();
98 m_secondaryViewContainer->connectUrlNavigator(
99 m_navigatorsWidget->primaryUrlNavigator());
100
101 // If the primary view is active, we have to swap the pointers
102 // because the secondary view will be the new primary view.
103 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
104 m_primaryViewActive = false;
105 }
106 } else {
107 view = m_primaryViewActive ? m_secondaryViewContainer : m_primaryViewContainer;
108 if (!m_primaryViewActive) {
109 m_primaryViewContainer->disconnectUrlNavigator();
110 m_secondaryViewContainer->connectUrlNavigator(
111 m_navigatorsWidget->primaryUrlNavigator());
112
113 // If the secondary view is active, we have to swap the pointers
114 // because the secondary view will be the new primary view.
115 qSwap(m_primaryViewContainer, m_secondaryViewContainer);
116 m_primaryViewActive = true;
117 }
118 }
119 m_primaryViewContainer->setActive(true);
120 view->close();
121 view->deleteLater();
122 }
123 }
124 }
125
126 DolphinViewContainer* DolphinTabPage::primaryViewContainer() const
127 {
128 return m_primaryViewContainer;
129 }
130
131 DolphinViewContainer* DolphinTabPage::secondaryViewContainer() const
132 {
133 return m_secondaryViewContainer;
134 }
135
136 DolphinViewContainer* DolphinTabPage::activeViewContainer() const
137 {
138 return m_primaryViewActive ? m_primaryViewContainer :
139 m_secondaryViewContainer;
140 }
141
142 KFileItemList DolphinTabPage::selectedItems() const
143 {
144 KFileItemList items = m_primaryViewContainer->view()->selectedItems();
145 if (m_splitViewEnabled) {
146 items += m_secondaryViewContainer->view()->selectedItems();
147 }
148 return items;
149 }
150
151 int DolphinTabPage::selectedItemsCount() const
152 {
153 int selectedItemsCount = m_primaryViewContainer->view()->selectedItemsCount();
154 if (m_splitViewEnabled) {
155 selectedItemsCount += m_secondaryViewContainer->view()->selectedItemsCount();
156 }
157 return selectedItemsCount;
158 }
159
160 void DolphinTabPage::connectNavigators(DolphinNavigatorsWidgetAction *navigatorsWidget)
161 {
162 insertNavigatorsWidget(navigatorsWidget);
163 m_navigatorsWidget = navigatorsWidget;
164 auto primaryNavigator = navigatorsWidget->primaryUrlNavigator();
165 m_primaryViewContainer->connectUrlNavigator(primaryNavigator);
166 if (m_splitViewEnabled) {
167 auto secondaryNavigator = navigatorsWidget->secondaryUrlNavigator();
168 m_secondaryViewContainer->connectUrlNavigator(secondaryNavigator);
169 }
170 resizeNavigators();
171 }
172
173 void DolphinTabPage::disconnectNavigators()
174 {
175 m_navigatorsWidget = nullptr;
176 m_primaryViewContainer->disconnectUrlNavigator();
177 if (m_splitViewEnabled) {
178 m_secondaryViewContainer->disconnectUrlNavigator();
179 }
180 }
181
182 bool DolphinTabPage::eventFilter(QObject *watched, QEvent *event)
183 {
184 if (event->type() == QEvent::Resize && m_navigatorsWidget) {
185 resizeNavigators();
186 return false;
187 }
188 return QWidget::eventFilter(watched, event);
189 }
190
191 void DolphinTabPage::insertNavigatorsWidget(DolphinNavigatorsWidgetAction* navigatorsWidget)
192 {
193 QGridLayout *gridLayout = static_cast<QGridLayout *>(layout());
194 if (navigatorsWidget->isInToolbar()) {
195 gridLayout->setRowMinimumHeight(0, 0);
196 } else {
197 // We set a row minimum height, so the height does not visibly change whenever
198 // navigatorsWidget is inserted which happens every time the current tab is changed.
199 gridLayout->setRowMinimumHeight(0, navigatorsWidget->primaryUrlNavigator()->height());
200 gridLayout->addWidget(navigatorsWidget->requestWidget(this), 0, 0);
201 }
202 }
203
204
205 void DolphinTabPage::resizeNavigators() const
206 {
207 if (!m_splitViewEnabled) {
208 m_navigatorsWidget->followViewContainerGeometry(
209 m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
210 m_primaryViewContainer->width());
211 } else {
212 m_navigatorsWidget->followViewContainersGeometry(
213 m_primaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
214 m_primaryViewContainer->width(),
215 m_secondaryViewContainer->mapToGlobal(QPoint(0,0)).x(),
216 m_secondaryViewContainer->width());
217 }
218 }
219
220 void DolphinTabPage::markUrlsAsSelected(const QList<QUrl>& urls)
221 {
222 m_primaryViewContainer->view()->markUrlsAsSelected(urls);
223 if (m_splitViewEnabled) {
224 m_secondaryViewContainer->view()->markUrlsAsSelected(urls);
225 }
226 }
227
228 void DolphinTabPage::markUrlAsCurrent(const QUrl& url)
229 {
230 m_primaryViewContainer->view()->markUrlAsCurrent(url);
231 if (m_splitViewEnabled) {
232 m_secondaryViewContainer->view()->markUrlAsCurrent(url);
233 }
234 }
235
236 void DolphinTabPage::refreshViews()
237 {
238 m_primaryViewContainer->readSettings();
239 if (m_splitViewEnabled) {
240 m_secondaryViewContainer->readSettings();
241 }
242 }
243
244 QByteArray DolphinTabPage::saveState() const
245 {
246 QByteArray state;
247 QDataStream stream(&state, QIODevice::WriteOnly);
248
249 stream << quint32(2); // Tab state version
250
251 stream << m_splitViewEnabled;
252
253 stream << m_primaryViewContainer->url();
254 stream << m_primaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
255 m_primaryViewContainer->view()->saveState(stream);
256
257 if (m_splitViewEnabled) {
258 stream << m_secondaryViewContainer->url();
259 stream << m_secondaryViewContainer->urlNavigatorInternalWithHistory()->isUrlEditable();
260 m_secondaryViewContainer->view()->saveState(stream);
261 }
262
263 stream << m_primaryViewActive;
264 stream << m_splitter->saveState();
265
266 return state;
267 }
268
269 void DolphinTabPage::restoreState(const QByteArray& state)
270 {
271 if (state.isEmpty()) {
272 return;
273 }
274
275 QByteArray sd = state;
276 QDataStream stream(&sd, QIODevice::ReadOnly);
277
278 // Read the version number of the tab state and check if the version is supported.
279 quint32 version = 0;
280 stream >> version;
281 if (version != 2) {
282 // The version of the tab state isn't supported, we can't restore it.
283 return;
284 }
285
286 bool isSplitViewEnabled = false;
287 stream >> isSplitViewEnabled;
288 setSplitViewEnabled(isSplitViewEnabled);
289
290 QUrl primaryUrl;
291 stream >> primaryUrl;
292 m_primaryViewContainer->setUrl(primaryUrl);
293 bool primaryUrlEditable;
294 stream >> primaryUrlEditable;
295 m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable);
296 m_primaryViewContainer->view()->restoreState(stream);
297
298 if (isSplitViewEnabled) {
299 QUrl secondaryUrl;
300 stream >> secondaryUrl;
301 m_secondaryViewContainer->setUrl(secondaryUrl);
302 bool secondaryUrlEditable;
303 stream >> secondaryUrlEditable;
304 m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable);
305 m_secondaryViewContainer->view()->restoreState(stream);
306 }
307
308 stream >> m_primaryViewActive;
309 if (m_primaryViewActive) {
310 m_primaryViewContainer->setActive(true);
311 } else {
312 Q_ASSERT(m_splitViewEnabled);
313 m_secondaryViewContainer->setActive(true);
314 }
315
316 QByteArray splitterState;
317 stream >> splitterState;
318 m_splitter->restoreState(splitterState);
319 }
320
321 void DolphinTabPage::restoreStateV1(const QByteArray& state)
322 {
323 if (state.isEmpty()) {
324 return;
325 }
326
327 QByteArray sd = state;
328 QDataStream stream(&sd, QIODevice::ReadOnly);
329
330 bool isSplitViewEnabled = false;
331 stream >> isSplitViewEnabled;
332 setSplitViewEnabled(isSplitViewEnabled);
333
334 QUrl primaryUrl;
335 stream >> primaryUrl;
336 m_primaryViewContainer->setUrl(primaryUrl);
337 bool primaryUrlEditable;
338 stream >> primaryUrlEditable;
339 m_primaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(primaryUrlEditable);
340
341 if (isSplitViewEnabled) {
342 QUrl secondaryUrl;
343 stream >> secondaryUrl;
344 m_secondaryViewContainer->setUrl(secondaryUrl);
345 bool secondaryUrlEditable;
346 stream >> secondaryUrlEditable;
347 m_secondaryViewContainer->urlNavigatorInternalWithHistory()->setUrlEditable(secondaryUrlEditable);
348 }
349
350 stream >> m_primaryViewActive;
351 if (m_primaryViewActive) {
352 m_primaryViewContainer->setActive(true);
353 } else {
354 Q_ASSERT(m_splitViewEnabled);
355 m_secondaryViewContainer->setActive(true);
356 }
357
358 QByteArray splitterState;
359 stream >> splitterState;
360 m_splitter->restoreState(splitterState);
361 }
362
363 void DolphinTabPage::setActive(bool active)
364 {
365 if (active) {
366 m_active = active;
367 } else {
368 // we should bypass changing active view in split mode
369 m_active = !m_splitViewEnabled;
370 }
371 // we want view to fire activated when goes from false to true
372 activeViewContainer()->setActive(active);
373 }
374
375 void DolphinTabPage::slotViewActivated()
376 {
377 const DolphinView* oldActiveView = activeViewContainer()->view();
378
379 // Set the view, which was active before, to inactive
380 // and update the active view type, if tab is active
381 if (m_active) {
382 if (m_splitViewEnabled) {
383 activeViewContainer()->setActive(false);
384 m_primaryViewActive = !m_primaryViewActive;
385 } else {
386 m_primaryViewActive = true;
387 if (m_secondaryViewContainer) {
388 m_secondaryViewContainer->setActive(false);
389 }
390 }
391 }
392
393 const DolphinView* newActiveView = activeViewContainer()->view();
394
395 if (newActiveView == oldActiveView) {
396 return;
397 }
398
399 disconnect(oldActiveView, &DolphinView::urlChanged,
400 this, &DolphinTabPage::activeViewUrlChanged);
401 disconnect(oldActiveView, &DolphinView::redirection,
402 this, &DolphinTabPage::slotViewUrlRedirection);
403 connect(newActiveView, &DolphinView::urlChanged,
404 this, &DolphinTabPage::activeViewUrlChanged);
405 connect(newActiveView, &DolphinView::redirection,
406 this, &DolphinTabPage::slotViewUrlRedirection);
407 Q_EMIT activeViewChanged(activeViewContainer());
408 Q_EMIT activeViewUrlChanged(activeViewContainer()->url());
409 }
410
411 void DolphinTabPage::slotViewUrlRedirection(const QUrl& oldUrl, const QUrl& newUrl)
412 {
413 Q_UNUSED(oldUrl)
414
415 Q_EMIT activeViewUrlChanged(newUrl);
416 }
417
418 void DolphinTabPage::switchActiveView()
419 {
420 if (!m_splitViewEnabled) {
421 return;
422 }
423 if (m_primaryViewActive) {
424 m_secondaryViewContainer->setActive(true);
425 } else {
426 m_primaryViewContainer->setActive(true);
427 }
428 }
429
430 DolphinViewContainer* DolphinTabPage::createViewContainer(const QUrl& url) const
431 {
432 DolphinViewContainer* container = new DolphinViewContainer(url, m_splitter);
433 container->setActive(false);
434
435 const DolphinView* view = container->view();
436 connect(view, &DolphinView::activated,
437 this, &DolphinTabPage::slotViewActivated);
438
439 connect(view, &DolphinView::toggleActiveViewRequested,
440 this, &DolphinTabPage::switchActiveView);
441
442 return container;
443 }