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 &parameters, 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