1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2013 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2014 Piotr Wójcik <chocimier@tlen.pl>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20
21 #include "SessionsManager.h"
22 #include "Application.h"
23 #include "JsonSettings.h"
24 #include "SessionModel.h"
25 #include "../ui/MainWindow.h"
26 #include "../ui/Window.h"
27
28 #include <QtCore/QDir>
29 #include <QtCore/QJsonArray>
30 #include <QtCore/QJsonObject>
31
32 namespace Otter
33 {
34
35 SessionsManager* SessionsManager::m_instance(nullptr);
36 SessionModel* SessionsManager::m_model(nullptr);
37 QString SessionsManager::m_sessionPath;
38 QString SessionsManager::m_sessionTitle;
39 QString SessionsManager::m_cachePath;
40 QString SessionsManager::m_profilePath;
41 QVector<SessionMainWindow> SessionsManager::m_closedWindows;
42 bool SessionsManager::m_isDirty(false);
43 bool SessionsManager::m_isPrivate(false);
44 bool SessionsManager::m_isReadOnly(false);
45
SessionsManager(QObject * parent)46 SessionsManager::SessionsManager(QObject *parent) : QObject(parent),
47 m_saveTimer(0)
48 {
49 }
50
timerEvent(QTimerEvent * event)51 void SessionsManager::timerEvent(QTimerEvent *event)
52 {
53 if (event->timerId() == m_saveTimer)
54 {
55 m_isDirty = false;
56
57 killTimer(m_saveTimer);
58
59 m_saveTimer = 0;
60
61 if (!m_isPrivate)
62 {
63 saveSession({}, {}, nullptr, false);
64 }
65 }
66 }
67
createInstance(const QString & profilePath,const QString & cachePath,bool isPrivate,bool isReadOnly)68 void SessionsManager::createInstance(const QString &profilePath, const QString &cachePath, bool isPrivate, bool isReadOnly)
69 {
70 if (!m_instance)
71 {
72 m_instance = new SessionsManager(QCoreApplication::instance());
73 m_cachePath = cachePath;
74 m_profilePath = profilePath;
75 m_isPrivate = isPrivate;
76 m_isReadOnly = isReadOnly;
77 }
78 }
79
scheduleSave()80 void SessionsManager::scheduleSave()
81 {
82 if (m_saveTimer == 0 && !m_isPrivate)
83 {
84 m_saveTimer = startTimer(1000);
85 }
86 }
87
clearClosedWindows()88 void SessionsManager::clearClosedWindows()
89 {
90 m_closedWindows.clear();
91
92 emit m_instance->closedWindowsChanged();
93 }
94
storeClosedWindow(MainWindow * mainWindow)95 void SessionsManager::storeClosedWindow(MainWindow *mainWindow)
96 {
97 if (!mainWindow || mainWindow->isPrivate())
98 {
99 return;
100 }
101
102 SessionMainWindow session(mainWindow->getSession());
103 session.geometry = mainWindow->saveGeometry();
104
105 if (!session.windows.isEmpty())
106 {
107 const int limit(SettingsManager::getOption(SettingsManager::History_ClosedTabsLimitAmountOption).toInt());
108
109 m_closedWindows.prepend(session);
110
111 if (m_closedWindows.count() > limit)
112 {
113 m_closedWindows.resize(limit);
114 m_closedWindows.squeeze();
115 }
116
117 emit m_instance->closedWindowsChanged();
118 }
119 }
120
markSessionAsModified()121 void SessionsManager::markSessionAsModified()
122 {
123 if (!m_isPrivate && !m_isDirty && m_sessionPath == QLatin1String("default"))
124 {
125 m_isDirty = true;
126
127 m_instance->scheduleSave();
128 }
129 }
130
removeStoredUrl(const QString & url)131 void SessionsManager::removeStoredUrl(const QString &url)
132 {
133 emit m_instance->requestedRemoveStoredUrl(url);
134 }
135
getInstance()136 SessionsManager* SessionsManager::getInstance()
137 {
138 return m_instance;
139 }
140
getModel()141 SessionModel* SessionsManager::getModel()
142 {
143 if (!m_model)
144 {
145 m_model = new SessionModel(m_instance);
146 }
147
148 return m_model;
149 }
150
getCurrentSession()151 QString SessionsManager::getCurrentSession()
152 {
153 return m_sessionPath;
154 }
155
getCachePath()156 QString SessionsManager::getCachePath()
157 {
158 return (m_isReadOnly ? QString() : m_cachePath);
159 }
160
getProfilePath()161 QString SessionsManager::getProfilePath()
162 {
163 return m_profilePath;
164 }
165
getReadableDataPath(const QString & path,bool forceBundled)166 QString SessionsManager::getReadableDataPath(const QString &path, bool forceBundled)
167 {
168 const QString writablePath(getWritableDataPath(path));
169
170 return ((!forceBundled && QFile::exists(writablePath)) ? writablePath : QLatin1String(":/") + (path.contains(QLatin1Char('/')) ? QString() : QLatin1String("other")) + QDir::separator() + path);
171 }
172
getWritableDataPath(const QString & path)173 QString SessionsManager::getWritableDataPath(const QString &path)
174 {
175 return QDir::toNativeSeparators(m_profilePath + QDir::separator() + path);
176 }
177
getSessionPath(const QString & path,bool isBound)178 QString SessionsManager::getSessionPath(const QString &path, bool isBound)
179 {
180 QString cleanPath(path);
181
182 if (cleanPath.isEmpty())
183 {
184 cleanPath = QLatin1String("default.json");
185 }
186 else
187 {
188 if (!cleanPath.endsWith(QLatin1String(".json")))
189 {
190 cleanPath += QLatin1String(".json");
191 }
192
193 if (isBound)
194 {
195 cleanPath = cleanPath.replace(QLatin1Char('/'), QString()).replace(QLatin1Char('\\'), QString());
196 }
197 else if (QFileInfo(cleanPath).isAbsolute())
198 {
199 return cleanPath;
200 }
201 }
202
203 return QDir::toNativeSeparators(m_profilePath + QLatin1String("/sessions/") + cleanPath);
204 }
205
getSession(const QString & path)206 SessionInformation SessionsManager::getSession(const QString &path)
207 {
208 SessionInformation session;
209 const JsonSettings settings(getSessionPath(path));
210
211 if (settings.isNull())
212 {
213 session.path = path;
214
215 return session;
216 }
217
218 const int defaultZoom(SettingsManager::getOption(SettingsManager::Content_DefaultZoomOption).toInt());
219 const QJsonArray mainWindowsArray(settings.object().value(QLatin1String("windows")).toArray());
220
221 session.path = path;
222 session.title = settings.object().value(QLatin1String("title")).toString((path == QLatin1String("default")) ? tr("Default") : tr("(Untitled)"));
223 session.index = (settings.object().value(QLatin1String("currentIndex")).toInt(1) - 1);
224 session.isClean = settings.object().value(QLatin1String("isClean")).toBool(true);
225
226 for (int i = 0; i < mainWindowsArray.count(); ++i)
227 {
228 const QJsonObject mainWindowObject(mainWindowsArray.at(i).toObject());
229 const QJsonArray windowsArray(mainWindowObject.value(QLatin1String("windows")).toArray());
230 SessionMainWindow sessionMainWindow;
231 sessionMainWindow.geometry = QByteArray::fromBase64(mainWindowObject.value(QLatin1String("geometry")).toString().toLatin1());
232 sessionMainWindow.index = (mainWindowObject.value(QLatin1String("currentIndex")).toInt(1) - 1);
233
234 for (int j = 0; j < windowsArray.count(); ++j)
235 {
236 const QJsonObject windowObject(windowsArray.at(j).toObject());
237 const QJsonArray windowHistoryArray(windowObject.value(QLatin1String("history")).toArray());
238 const QString state(windowObject.value(QLatin1String("state")).toString());
239 WindowState windowState;
240 windowState.geometry = JsonSettings::readRectangle(windowObject.value(QLatin1String("geometry")).toVariant());
241 windowState.state = ((state == QLatin1String("maximized")) ? Qt::WindowMaximized : ((state == QLatin1String("minimized")) ? Qt::WindowMinimized : Qt::WindowNoState));
242
243 SessionWindow sessionWindow;
244 sessionWindow.state = windowState;
245 sessionWindow.historyIndex = (windowObject.value(QLatin1String("currentIndex")).toInt(1) - 1);
246 sessionWindow.isAlwaysOnTop = windowObject.value(QLatin1String("isAlwaysOnTop")).toBool(false);
247 sessionWindow.isPinned = windowObject.value(QLatin1String("isPinned")).toBool(false);
248
249 if (windowObject.contains(QLatin1String("options")))
250 {
251 const QJsonObject optionsObject(windowObject.value(QLatin1String("options")).toObject());
252 QJsonObject::const_iterator iterator;
253
254 for (iterator = optionsObject.constBegin(); iterator != optionsObject.constEnd(); ++iterator)
255 {
256 const int optionIdentifier(SettingsManager::getOptionIdentifier(iterator.key()));
257
258 if (optionIdentifier >= 0)
259 {
260 sessionWindow.options[optionIdentifier] = iterator.value().toVariant();
261 }
262 }
263 }
264
265 for (int k = 0; k < windowHistoryArray.count(); ++k)
266 {
267 const QJsonObject historyEntryObject(windowHistoryArray.at(k).toObject());
268 const QStringList position(historyEntryObject.value(QLatin1String("position")).toString().split(QLatin1Char(',')));
269 WindowHistoryEntry historyEntry;
270 historyEntry.url = historyEntryObject.value(QLatin1String("url")).toString();
271 historyEntry.title = historyEntryObject.value(QLatin1String("title")).toString();
272 historyEntry.position = ((position.count() == 2) ? QPoint(position.at(0).simplified().toInt(), position.at(1).simplified().toInt()) : QPoint(0, 0));
273 historyEntry.zoom = historyEntryObject.value(QLatin1String("zoom")).toInt(defaultZoom);
274
275 sessionWindow.history.append(historyEntry);
276 }
277
278 if (sessionWindow.historyIndex < 0 || sessionWindow.historyIndex >= sessionWindow.history.count())
279 {
280 sessionWindow.historyIndex = (sessionWindow.history.count() - 1);
281 }
282
283 sessionMainWindow.windows.append(sessionWindow);
284 }
285
286 if (sessionMainWindow.index < 0 || sessionMainWindow.index >= sessionMainWindow.windows.count())
287 {
288 sessionMainWindow.index = (sessionMainWindow.windows.count() - 1);
289 }
290
291 if (mainWindowObject.contains(QLatin1String("splitters")))
292 {
293 const QJsonArray splittersArray(mainWindowObject.value(QLatin1String("splitters")).toArray());
294
295 for (int j = 0; j < splittersArray.count(); ++j)
296 {
297 const QJsonObject splitterObject(splittersArray.at(j).toObject());
298 const QVariantList rawSizes(splitterObject.value(QLatin1String("sizes")).toVariant().toList());
299 QVector<int> sizes;
300 sizes.reserve(rawSizes.count());
301
302 for (int k = 0; k < rawSizes.count(); ++k)
303 {
304 sizes.append(rawSizes.at(k).toInt());
305 }
306
307 sessionMainWindow.splitters[splitterObject.value(QLatin1String("identifier")).toString()] = sizes;
308 }
309 }
310
311 if (mainWindowObject.contains(QLatin1String("toolBars")))
312 {
313 const QJsonArray toolBarsArray(mainWindowObject.value(QLatin1String("toolBars")).toArray());
314
315 sessionMainWindow.hasToolBarsState = true;
316 sessionMainWindow.toolBars.reserve(toolBarsArray.count());
317
318 for (int j = 0; j < toolBarsArray.count(); ++j)
319 {
320 const QJsonObject toolBarObject(toolBarsArray.at(j).toObject());
321 ToolBarState toolBarState;
322 toolBarState.identifier = ToolBarsManager::getToolBarIdentifier(toolBarObject.value(QLatin1String("identifier")).toString());
323
324 if (toolBarObject.contains(QLatin1String("location")))
325 {
326 const QString location(toolBarObject.value(QLatin1String("location")).toString());
327
328 if (location == QLatin1String("top"))
329 {
330 toolBarState.location = Qt::TopToolBarArea;
331 }
332 else if (location == QLatin1String("bottom"))
333 {
334 toolBarState.location = Qt::BottomToolBarArea;
335 }
336 else if (location == QLatin1String("left"))
337 {
338 toolBarState.location = Qt::LeftToolBarArea;
339 }
340 else if (location == QLatin1String("right"))
341 {
342 toolBarState.location = Qt::RightToolBarArea;
343 }
344 }
345
346 if (toolBarObject.contains(QLatin1String("normalVisibility")))
347 {
348 toolBarState.normalVisibility = ((toolBarObject.value(QLatin1String("normalVisibility")).toString() == QLatin1String("hidden")) ? ToolBarState::AlwaysHiddenToolBar : ToolBarState::AlwaysVisibleToolBar);
349 }
350
351 if (toolBarObject.contains(QLatin1String("fullScreenVisibility")))
352 {
353 toolBarState.fullScreenVisibility = ((toolBarObject.value(QLatin1String("fullScreenVisibility")).toString() == QLatin1String("hidden")) ? ToolBarState::AlwaysHiddenToolBar : ToolBarState::AlwaysVisibleToolBar);
354 }
355
356 if (toolBarObject.contains(QLatin1String("row")))
357 {
358 toolBarState.row = toolBarObject.value(QLatin1String("row")).toInt(-1);
359 }
360
361 sessionMainWindow.toolBars.append(toolBarState);
362 }
363
364 sessionMainWindow.toolBars.squeeze();
365 }
366
367 session.windows.append(sessionMainWindow);
368 }
369
370 if (session.index < 0 || session.index >= session.windows.count())
371 {
372 session.index = (session.windows.count() - 1);
373 }
374
375 return session;
376 }
377
getClosedWindows()378 QStringList SessionsManager::getClosedWindows()
379 {
380 QStringList closedWindows;
381 closedWindows.reserve(m_closedWindows.count());
382
383 for (int i = 0; i < m_closedWindows.count(); ++i)
384 {
385 const SessionMainWindow window(m_closedWindows.at(i));
386 const QString title(window.windows.value(window.index, SessionWindow()).getTitle());
387
388 closedWindows.append(title.isEmpty() ? tr("(Untitled)") : title);
389 }
390
391 return closedWindows;
392 }
393
getSessions()394 QStringList SessionsManager::getSessions()
395 {
396 const QList<QFileInfo> entries(QDir(m_profilePath + QLatin1String("/sessions/")).entryInfoList({QLatin1String("*.json")}, QDir::Files));
397 QStringList sessions;
398 sessions.reserve(entries.count());
399
400 for (int i = 0; i < entries.count(); ++i)
401 {
402 sessions.append(entries.at(i).completeBaseName());
403 }
404
405 if (!m_sessionPath.isEmpty() && !entries.contains(m_sessionPath))
406 {
407 sessions.append(m_sessionPath);
408 }
409
410 if (!sessions.contains(QLatin1String("default")))
411 {
412 sessions.append(QLatin1String("default"));
413 }
414
415 sessions.sort();
416
417 return sessions;
418 }
419
calculateOpenHints(OpenHints hints,Qt::MouseButton button,Qt::KeyboardModifiers modifiers)420 SessionsManager::OpenHints SessionsManager::calculateOpenHints(OpenHints hints, Qt::MouseButton button, Qt::KeyboardModifiers modifiers)
421 {
422 const bool useNewTab(!hints.testFlag(NewWindowOpen) && SettingsManager::getOption(SettingsManager::Browser_OpenLinksInNewTabOption).toBool());
423
424 if (button == Qt::MiddleButton && modifiers.testFlag(Qt::AltModifier))
425 {
426 return ((useNewTab ? NewTabOpen : NewWindowOpen) | BackgroundOpen | EndOpen);
427 }
428
429 if (modifiers.testFlag(Qt::ControlModifier) || button == Qt::MiddleButton)
430 {
431 return ((useNewTab ? NewTabOpen : NewWindowOpen) | BackgroundOpen);
432 }
433
434 if (modifiers.testFlag(Qt::ShiftModifier))
435 {
436 return (useNewTab ? NewTabOpen : NewWindowOpen);
437 }
438
439 if (hints.testFlag(NewTabOpen) && !hints.testFlag(NewWindowOpen))
440 {
441 return (useNewTab ? NewTabOpen : NewWindowOpen);
442 }
443
444 if (SettingsManager::getOption(SettingsManager::Browser_ReuseCurrentTabOption).toBool())
445 {
446 return CurrentTabOpen;
447 }
448
449 return hints;
450 }
451
calculateOpenHints(OpenHints hints,Qt::MouseButton button)452 SessionsManager::OpenHints SessionsManager::calculateOpenHints(OpenHints hints, Qt::MouseButton button)
453 {
454 return calculateOpenHints(hints, button, QGuiApplication::keyboardModifiers());
455 }
456
calculateOpenHints(const QVariantMap & parameters,bool ignoreModifiers)457 SessionsManager::OpenHints SessionsManager::calculateOpenHints(const QVariantMap ¶meters, bool ignoreModifiers)
458 {
459 if (!parameters.contains(QLatin1String("hints")))
460 {
461 return calculateOpenHints(DefaultOpen, Qt::LeftButton, (ignoreModifiers ? Qt::NoModifier : QGuiApplication::keyboardModifiers()));
462 }
463
464 const QVariant::Type type(parameters[QLatin1String("hints")].type());
465
466 if (type == QVariant::Int || type == QVariant::UInt)
467 {
468 return static_cast<OpenHints>(parameters[QLatin1String("hints")].toInt());
469 }
470
471 if (type != QVariant::List && type != QVariant::StringList)
472 {
473 return DefaultOpen;
474 }
475
476 const QStringList rawHints(parameters[QLatin1String("hints")].toStringList());
477 OpenHints hints(DefaultOpen);
478
479 for (int i = 0; i < rawHints.count(); ++i)
480 {
481 QString hint(rawHints.at(i));
482 hint[0] = hint[0].toUpper();
483
484 if (hint == QLatin1String("Private"))
485 {
486 hints |= PrivateOpen;
487 }
488 else if (hint == QLatin1String("CurrentTab"))
489 {
490 hints |= CurrentTabOpen;
491 }
492 else if (hint == QLatin1String("NewTab"))
493 {
494 hints |= NewTabOpen;
495 }
496 else if (hint == QLatin1String("NewWindow"))
497 {
498 hints |= NewWindowOpen;
499 }
500 else if (hint == QLatin1String("Background"))
501 {
502 hints |= BackgroundOpen;
503 }
504 else if (hint == QLatin1String("End"))
505 {
506 hints |= EndOpen;
507 }
508 }
509
510 return hints;
511 }
512
restoreClosedWindow(int index)513 bool SessionsManager::restoreClosedWindow(int index)
514 {
515 if (index < 0 || index >= m_closedWindows.count())
516 {
517 return false;
518 }
519
520 Application::createWindow({}, m_closedWindows.takeAt(index));
521
522 emit m_instance->closedWindowsChanged();
523
524 return true;
525 }
526
restoreSession(const SessionInformation & session,MainWindow * mainWindow,bool isPrivate)527 bool SessionsManager::restoreSession(const SessionInformation &session, MainWindow *mainWindow, bool isPrivate)
528 {
529 if (session.windows.isEmpty())
530 {
531 if (m_sessionPath.isEmpty() && session.path == QLatin1String("default"))
532 {
533 m_sessionPath = QLatin1String("default");
534 }
535
536 return false;
537 }
538
539 if (m_sessionPath.isEmpty())
540 {
541 m_sessionPath = session.path;
542 m_sessionTitle = session.title;
543 }
544
545 QVariantMap parameters;
546
547 if (isPrivate)
548 {
549 parameters[QLatin1String("hints")] = PrivateOpen;
550 }
551
552 for (int i = 0; i < session.windows.count(); ++i)
553 {
554 if (mainWindow && i == 0)
555 {
556 mainWindow->restoreSession(session.windows.first());
557 }
558 else
559 {
560 Application::createWindow(parameters, session.windows.at(i));
561 }
562 }
563
564 return true;
565 }
566
saveSession(const QString & path,const QString & title,MainWindow * mainWindow,bool isClean)567 bool SessionsManager::saveSession(const QString &path, const QString &title, MainWindow *mainWindow, bool isClean)
568 {
569 if (m_isPrivate && path.isEmpty())
570 {
571 return false;
572 }
573
574 SessionInformation session;
575 session.path = getSessionPath(path);
576 session.title = (title.isEmpty() ? m_sessionTitle : title);
577 session.isClean = isClean;
578
579 QVector<MainWindow*> windows;
580
581 if (mainWindow)
582 {
583 windows.append(mainWindow);
584 }
585 else
586 {
587 windows = Application::getWindows();
588 }
589
590 session.windows.reserve(windows.count());
591
592 for (int i = 0; i < windows.count(); ++i)
593 {
594 if (!windows.at(i)->isPrivate())
595 {
596 session.windows.append(windows.at(i)->getSession());
597 }
598 }
599
600 session.windows.squeeze();
601
602 return saveSession(session);
603 }
604
saveSession(const SessionInformation & session)605 bool SessionsManager::saveSession(const SessionInformation &session)
606 {
607 const QString sessionsPath(m_profilePath + QLatin1String("/sessions/"));
608
609 QDir().mkpath(sessionsPath);
610
611 if (session.windows.isEmpty())
612 {
613 return false;
614 }
615
616 QString path(session.path);
617
618 if (path.isEmpty())
619 {
620 path = sessionsPath + session.title + QLatin1String(".json");
621
622 if (QFile::exists(path))
623 {
624 int i(2);
625
626 do
627 {
628 path = sessionsPath + session.title + QLatin1Char('_') + QString::number(i) + QLatin1String(".json");
629
630 ++i;
631 }
632 while (QFile::exists(path));
633 }
634 }
635
636 const QStringList excludedOptions(SettingsManager::getOption(SettingsManager::Sessions_OptionsExludedFromSavingOption).toStringList());
637 QJsonArray mainWindowsArray;
638 QJsonObject sessionObject({{QLatin1String("title"), session.title}, {QLatin1String("currentIndex"), 1}});
639
640 if (!session.isClean)
641 {
642 sessionObject.insert(QLatin1String("isClean"), false);
643 }
644
645 for (int i = 0; i < session.windows.count(); ++i)
646 {
647 const SessionMainWindow sessionEntry(session.windows.at(i));
648 QJsonObject mainWindowObject({{QLatin1String("currentIndex"), (sessionEntry.index + 1)}, {QLatin1String("geometry"), QString(sessionEntry.geometry.toBase64())}});
649 QJsonArray windowsArray;
650
651 for (int j = 0; j < sessionEntry.windows.count(); ++j)
652 {
653 QJsonObject windowObject({{QLatin1String("currentIndex"), (sessionEntry.windows.at(j).historyIndex + 1)}});
654
655 if (!sessionEntry.windows.at(j).options.isEmpty())
656 {
657 const QHash<int, QVariant> windowOptions(sessionEntry.windows.at(j).options);
658 QHash<int, QVariant>::const_iterator optionsIterator;
659 QJsonObject optionsObject;
660
661 for (optionsIterator = windowOptions.constBegin(); optionsIterator != windowOptions.constEnd(); ++optionsIterator)
662 {
663 const QString optionName(SettingsManager::getOptionName(optionsIterator.key()));
664
665 if (!optionName.isEmpty() && !excludedOptions.contains(optionName))
666 {
667 optionsObject.insert(optionName, QJsonValue::fromVariant(optionsIterator.value()));
668 }
669 }
670
671 windowObject.insert(QLatin1String("options"), optionsObject);
672 }
673
674 switch (sessionEntry.windows.at(j).state.state)
675 {
676 case Qt::WindowMaximized:
677 windowObject.insert(QLatin1String("state"), QLatin1String("maximized"));
678
679 break;
680 case Qt::WindowMinimized:
681 windowObject.insert(QLatin1String("state"), QLatin1String("minimized"));
682
683 break;
684 default:
685 {
686 const QRect geometry(sessionEntry.windows.at(j).state.geometry);
687
688 windowObject.insert(QLatin1String("state"), QLatin1String("normal"));
689
690 if (geometry.isValid())
691 {
692 windowObject.insert(QLatin1String("geometry"), QStringLiteral("%1, %2, %3, %4").arg(geometry.x()).arg(geometry.y()).arg(geometry.width()).arg(geometry.height()));
693 }
694 }
695
696 break;
697 }
698
699 if (sessionEntry.windows.at(j).isAlwaysOnTop)
700 {
701 windowObject.insert(QLatin1String("isAlwaysOnTop"), true);
702 }
703
704 if (sessionEntry.windows.at(j).isPinned)
705 {
706 windowObject.insert(QLatin1String("isPinned"), true);
707 }
708
709 QJsonArray windowHistoryArray;
710
711 for (int k = 0; k < sessionEntry.windows.at(j).history.count(); ++k)
712 {
713 const QPoint position(sessionEntry.windows.at(j).history.at(k).position);
714 QJsonObject historyEntryObject({{QLatin1String("url"), sessionEntry.windows.at(j).history.at(k).url}, {QLatin1String("title"), sessionEntry.windows.at(j).history.at(k).title}, {QLatin1String("zoom"), sessionEntry.windows.at(j).history.at(k).zoom}});
715
716 if (!position.isNull())
717 {
718 historyEntryObject.insert(QLatin1String("position"), QStringLiteral("%1, %2").arg(position.x()).arg(position.y()));
719 }
720
721 windowHistoryArray.append(historyEntryObject);
722 }
723
724 windowObject.insert(QLatin1String("history"), windowHistoryArray);
725
726 windowsArray.append(windowObject);
727 }
728
729 mainWindowObject.insert(QLatin1String("windows"), windowsArray);
730
731 if (sessionEntry.hasToolBarsState)
732 {
733 QJsonArray toolBarsArray;
734
735 for (int j = 0; j < sessionEntry.toolBars.count(); ++j)
736 {
737 const QString identifier(ToolBarsManager::getToolBarName(sessionEntry.toolBars.at(j).identifier));
738
739 if (identifier.isEmpty())
740 {
741 continue;
742 }
743
744 QJsonObject toolBarObject({{QLatin1String("identifier"), identifier}});
745
746 switch (sessionEntry.toolBars.at(j).location)
747 {
748 case Qt::LeftToolBarArea:
749 toolBarObject.insert(QLatin1String("location"), QLatin1String("left"));
750
751 break;
752 case Qt::RightToolBarArea:
753 toolBarObject.insert(QLatin1String("location"), QLatin1String("right"));
754
755 break;
756 case Qt::TopToolBarArea:
757 toolBarObject.insert(QLatin1String("location"), QLatin1String("top"));
758
759 break;
760 case Qt::BottomToolBarArea:
761 toolBarObject.insert(QLatin1String("location"), QLatin1String("bottom"));
762
763 break;
764 default:
765 break;
766 }
767
768 if (sessionEntry.toolBars.at(j).normalVisibility != ToolBarState::UnspecifiedVisibilityToolBar)
769 {
770 toolBarObject.insert(QLatin1String("normalVisibility"), ((sessionEntry.toolBars.at(j).normalVisibility == ToolBarState::AlwaysHiddenToolBar) ? QLatin1String("hidden") : QLatin1String("visible")));
771 }
772
773 if (sessionEntry.toolBars.at(j).fullScreenVisibility != ToolBarState::UnspecifiedVisibilityToolBar)
774 {
775 toolBarObject.insert(QLatin1String("fullScreenVisibility"), ((sessionEntry.toolBars.at(j).fullScreenVisibility == ToolBarState::AlwaysHiddenToolBar) ? QLatin1String("hidden") : QLatin1String("visible")));
776 }
777
778 if (sessionEntry.toolBars.at(j).row >= 0)
779 {
780 toolBarObject.insert(QLatin1String("row"), sessionEntry.toolBars.at(j).row);
781 }
782
783 toolBarsArray.append(toolBarObject);
784 }
785
786 mainWindowObject.insert(QLatin1String("toolBars"), toolBarsArray);
787 }
788
789 if (!sessionEntry.splitters.isEmpty())
790 {
791 QJsonArray splittersArray;
792 QMap<QString, QVector<int> >::const_iterator iterator;
793
794 for (iterator = sessionEntry.splitters.begin(); iterator != sessionEntry.splitters.end(); ++iterator)
795 {
796 QJsonArray sizesArray;
797 const QVector<int> sizes(iterator.value());
798
799 for (int j = 0; j < sizes.count(); ++j)
800 {
801 sizesArray.append(sizes.at(j));
802 }
803
804 splittersArray.append(QJsonObject({{QLatin1String("identifier"), iterator.key()}, {QLatin1String("sizes"), sizesArray}}));
805 }
806
807 mainWindowObject.insert(QLatin1String("splitters"), splittersArray);
808 }
809
810 mainWindowsArray.append(mainWindowObject);
811 }
812
813 sessionObject.insert(QLatin1String("windows"), mainWindowsArray);
814
815 JsonSettings settings;
816 settings.setObject(sessionObject);
817
818 return settings.save(path);
819 }
820
deleteSession(const QString & path)821 bool SessionsManager::deleteSession(const QString &path)
822 {
823 const QString cleanPath(getSessionPath(path, true));
824
825 if (QFile::exists(cleanPath))
826 {
827 return QFile::remove(cleanPath);
828 }
829
830 return false;
831 }
832
isPrivate()833 bool SessionsManager::isPrivate()
834 {
835 return m_isPrivate;
836 }
837
isReadOnly()838 bool SessionsManager::isReadOnly()
839 {
840 return m_isReadOnly;
841 }
842
hasUrl(const QUrl & url,bool activate)843 bool SessionsManager::hasUrl(const QUrl &url, bool activate)
844 {
845 const QVector<MainWindow*> windows(Application::getWindows());
846 QMultiMap<qint64, MainWindow*> map;
847
848 for (int i = 0; i < windows.count(); ++i)
849 {
850 if (windows.at(i)->getActiveWindow())
851 {
852 map.insert(windows.at(i)->getActiveWindow()->getLastActivity().toMSecsSinceEpoch(), windows.at(i));
853 }
854 }
855
856 const QVector<MainWindow*> sortedWindows(map.values().toVector());
857
858 for (int i = (sortedWindows.count() - 1); i >= 0; --i)
859 {
860 if (sortedWindows.at(i)->hasUrl(url, activate))
861 {
862 Application::triggerAction(ActionsManager::ActivateWindowAction, {{QLatin1String("window"), sortedWindows.at(i)->getIdentifier()}}, m_instance);
863
864 return true;
865 }
866 }
867
868 return false;
869 }
870
871 }
872