1 /*
2  * %kadu copyright begin%
3  * Copyright 2011 Tomasz Rostanski (rozteck@interia.pl)
4  * Copyright 2009, 2010, 2011 Piotr Galiszewski (piotr.galiszewski@kadu.im)
5  * Copyright 2010, 2011 Przemysław Rudy (prudy1@o2.pl)
6  * Copyright 2010, 2011 Piotr Dąbrowski (ultr@ultr.pl)
7  * Copyright 2010, 2011, 2012, 2013, 2014 Bartosz Brachaczek (b.brachaczek@gmail.com)
8  * Copyright 2009, 2010, 2011, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
9  * %kadu copyright end%
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of
14  * the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 #include <QtGui/QContextMenuEvent>
25 #include <QtWidgets/QMenu>
26 
27 #include "accounts/account-manager.h"
28 #include "buddies/buddy-set.h"
29 #include "buddies/buddy.h"
30 #include "configuration/configuration-api.h"
31 #include "configuration/configuration-manager.h"
32 #include "configuration/configuration.h"
33 #include "configuration/configuration.h"
34 #include "configuration/deprecated-configuration-api.h"
35 #include "core/injected-factory.h"
36 #include "core/session-service.h"
37 #include "gui/actions/action.h"
38 #include "gui/actions/actions.h"
39 #include "gui/configuration/toolbar-configuration-manager.h"
40 #include "gui/widgets/talkable-tree-view.h"
41 #include "gui/widgets/toolbar.h"
42 #include "debug.h"
43 
44 #include "main-window.h"
45 
46 #if defined(Q_OS_UNIX)
47 #include <QtX11Extras/QX11Info>
48 
49 #include "os/x11/x11tools.h" // this should be included as last one,
50 #undef KeyPress
51 #undef Status            // and Status defined by Xlib.h must be undefined
52 #endif
53 
findMainWindow(QWidget * widget)54 MainWindow * MainWindow::findMainWindow(QWidget *widget)
55 {
56 	while (widget)
57 	{
58 		MainWindow *window = qobject_cast<MainWindow *>(widget);
59 		if (window)
60 			return window;
61 		widget = widget->parentWidget();
62 	}
63 
64 	return 0;
65 }
66 
MainWindow(ActionContext * context,const QString & windowName,QWidget * parent)67 MainWindow::MainWindow(ActionContext *context, const QString &windowName, QWidget *parent) :
68 		QMainWindow(parent), DesktopAwareObject(this),  WindowName(windowName), TransparencyEnabled(false), BlurEnabled(false),
69 		Context(context)
70 {
71 }
72 
~MainWindow()73 MainWindow::~MainWindow()
74 {
75 	delete Context;
76 	Context = 0;
77 }
78 
setActions(Actions * actions)79 void MainWindow::setActions(Actions *actions)
80 {
81 	m_actions = actions;
82 }
83 
setConfigurationManager(ConfigurationManager * configurationManager)84 void MainWindow::setConfigurationManager(ConfigurationManager *configurationManager)
85 {
86 	m_configurationManager = configurationManager;
87 }
88 
setConfiguration(Configuration * configuration)89 void MainWindow::setConfiguration(Configuration *configuration)
90 {
91 	m_configuration = configuration;
92 }
93 
configuration() const94 Configuration * MainWindow::configuration() const
95 {
96 	return m_configuration;
97 }
98 
setInjectedFactory(InjectedFactory * injectedFactory)99 void MainWindow::setInjectedFactory(InjectedFactory *injectedFactory)
100 {
101 	m_injectedFactory = injectedFactory;
102 }
103 
setSessionService(SessionService * sessionService)104 void MainWindow::setSessionService(SessionService *sessionService)
105 {
106 	m_sessionService = sessionService;
107 }
108 
init()109 void MainWindow::init()
110 {
111 	connect(m_configurationManager->toolbarConfigurationManager(), SIGNAL(configurationUpdated()),
112 			this, SLOT(refreshToolBars()));
113 	connect(m_actions, SIGNAL(actionLoaded(ActionDescription*)),
114 			this, SLOT(actionLoadedOrUnloaded(ActionDescription*)));
115 	connect(m_actions, SIGNAL(actionUnloaded(ActionDescription*)),
116 			this, SLOT(actionLoadedOrUnloaded(ActionDescription*)));
117 }
118 
injectedFactory() const119 InjectedFactory * MainWindow::injectedFactory() const
120 {
121 	return m_injectedFactory;
122 }
123 
setActionContext(ActionContext * actionContext)124 void MainWindow::setActionContext(ActionContext *actionContext)
125 {
126 	Context = actionContext;
127 }
128 
loadToolBarsFromConfig()129 void MainWindow::loadToolBarsFromConfig()
130 {
131 	// lame, i know
132 
133 	foreach (QObject *object, children())
134 	{
135 		QToolBar *toolBar = qobject_cast<QToolBar *>(object);
136 		if (toolBar)
137 		{
138 			removeToolBar(toolBar);
139 			delete toolBar;
140 		}
141 	}
142 
143 	loadToolBarsFromConfig(Qt::TopToolBarArea);
144 	loadToolBarsFromConfig(Qt::LeftToolBarArea);
145 	loadToolBarsFromConfig(Qt::BottomToolBarArea);
146 	loadToolBarsFromConfig(Qt::RightToolBarArea);
147 }
148 
horizontalToolbarComparator(ToolBar * t1,ToolBar * t2)149 bool horizontalToolbarComparator(ToolBar *t1, ToolBar *t2)
150 {
151 	if (t1->yOffset() < t2->yOffset())
152 		return true;
153 	if (t1->yOffset() > t2->yOffset())
154 		return false;
155 	return t1->xOffset() < t2->xOffset();
156 }
157 
verticalToolbarComparator(ToolBar * t1,ToolBar * t2)158 bool verticalToolbarComparator(ToolBar *t1, ToolBar *t2)
159 {
160 	if (t1->xOffset() < t2->xOffset())
161 		return true;
162 	if (t1->xOffset() > t2->xOffset())
163 		return false;
164 	return t1->yOffset() < t2->yOffset();
165 }
166 
loadToolBarsFromConfigNode(QDomElement dockareaConfig,Qt::ToolBarArea area)167 void MainWindow::loadToolBarsFromConfigNode(QDomElement dockareaConfig, Qt::ToolBarArea area)
168 {
169 	QList<ToolBar *> toolBars;
170 	for (QDomNode n = dockareaConfig.firstChild(); !n.isNull(); n = n.nextSibling())
171 	{
172 		const QDomElement &toolbarConfig = n.toElement();
173 		if (toolbarConfig.isNull())
174 			continue;
175 		if (toolbarConfig.tagName() != "ToolBar")
176 			continue;
177 
178 		ToolBar* toolbar = newToolbar(this);
179 		toolbar->loadFromConfig(toolbarConfig);
180 		toolbar->show();
181 		/* show() resets the WA_NoSystemBackground and AutoFillBackground */
182 		toolbar->setAttribute(Qt::WA_NoSystemBackground, !TransparencyEnabled);
183 		toolbar->setAutoFillBackground(TransparencyEnabled);
184 
185 		toolBars.append(toolbar);
186 	}
187 
188 	int currentLine = 0;
189 	if (area == Qt::LeftToolBarArea || area == Qt::RightToolBarArea)
190 	{
191 		qSort(toolBars.begin(), toolBars.end(), verticalToolbarComparator);
192 		foreach(ToolBar *toolBar, toolBars)
193 		{
194 			if (toolBar->xOffset() != currentLine)
195 				addToolBarBreak(area);
196 
197 			addToolBar(area, toolBar);
198 			currentLine = toolBar->xOffset();
199 		}
200 	}
201 	else
202 	{
203 		qSort(toolBars.begin(), toolBars.end(), horizontalToolbarComparator);
204 		foreach(ToolBar *toolBar, toolBars)
205 		{
206 			if (toolBar->yOffset() != currentLine)
207 				addToolBarBreak(area);
208 
209 			addToolBar(area, toolBar);
210 			currentLine = toolBar->yOffset();
211 		}
212 	}
213 }
214 
loadToolBarsFromConfig(Qt::ToolBarArea area)215 void MainWindow::loadToolBarsFromConfig(Qt::ToolBarArea area)
216 {
217 	QDomElement dockareaConfig = getDockAreaConfigElement(area);
218 	loadToolBarsFromConfigNode(dockareaConfig, area);
219 }
220 
loadOldToolBarsFromConfig(const QString & configName,Qt::ToolBarArea area)221 bool MainWindow::loadOldToolBarsFromConfig(const QString &configName, Qt::ToolBarArea area)
222 {
223 	QDomElement toolbarsConfig = m_configuration->api()->findElement(m_configuration->api()->rootElement(), "Toolbars");
224 
225 	if (toolbarsConfig.isNull())
226 		return false;
227 
228 	QDomElement dockareaConfig = m_configuration->api()->findElementByProperty(toolbarsConfig, "DockArea", "name", configName);
229 	if (dockareaConfig.isNull())
230 		return false;
231 
232 	loadToolBarsFromConfigNode(dockareaConfig, area);
233 
234 	dockareaConfig.parentNode().removeChild(dockareaConfig);
235 	return true;
236 }
237 
getToolbarsConfigElement(Configuration * configuration)238 QDomElement MainWindow::getToolbarsConfigElement(Configuration *configuration)
239 {
240 	QDomElement toolbarsConfig = configuration->api()->findElement(configuration->api()->rootElement(), "Toolbars");
241 	if (toolbarsConfig.isNull())
242 		toolbarsConfig = configuration->api()->createElement(configuration->api()->rootElement(), "Toolbars");
243 
244 	return toolbarsConfig;
245 }
246 
getDockAreaConfigElement(Qt::ToolBarArea area)247 QDomElement MainWindow::getDockAreaConfigElement(Qt::ToolBarArea area)
248 {
249 	QString realPrefix;
250 	if (!WindowName.isEmpty())
251 		realPrefix = WindowName + '_';
252 
253 	QString suffix;
254 
255 	switch (area)
256 	{
257 		case Qt::TopToolBarArea:
258 			suffix = "topDockArea";
259 			break;
260 		case Qt::LeftToolBarArea:
261 			suffix = "leftDockArea";
262 			break;
263 		case Qt::RightToolBarArea:
264 			suffix = "rightDockArea";
265 			break;
266 		case Qt::BottomToolBarArea:
267 			suffix = "bottomDockArea";
268 			break;
269 		default:
270 			return QDomElement();
271 	}
272 
273 	return getDockAreaConfigElement(m_configuration, getToolbarsConfigElement(m_configuration), realPrefix + suffix);
274 }
275 
getDockAreaConfigElement(Configuration * configuration,QDomElement toolbarsConfig,const QString & name)276 QDomElement MainWindow::getDockAreaConfigElement(Configuration *configuration, QDomElement toolbarsConfig, const QString &name)
277 {
278 	QDomElement dockAreaConfig = configuration->api()->findElementByProperty(toolbarsConfig, "DockArea", "name", name);
279 	if (dockAreaConfig.isNull())
280 	{
281 		dockAreaConfig = configuration->api()->createElement(toolbarsConfig, "DockArea");
282 		dockAreaConfig.setAttribute("name", name);
283 	}
284 
285 	return dockAreaConfig;
286 }
287 
addToolButton(Configuration * configuration,QDomElement toolbarConfig,const QString & actionName,Qt::ToolButtonStyle style)288 void MainWindow::addToolButton(Configuration *configuration, QDomElement toolbarConfig, const QString &actionName, Qt::ToolButtonStyle style)
289 {
290 	QDomElement buttonConfig = configuration->api()->findElementByProperty(toolbarConfig, "ToolButton", "action_name", actionName);
291 //don't add element if exists
292 	if (!buttonConfig.isNull())
293 		return;
294 	buttonConfig = configuration->api()->createElement(toolbarConfig, "ToolButton");
295 	buttonConfig.setAttribute("action_name", actionName);
296 	buttonConfig.setAttribute("toolbutton_style", style);
297 }
298 
findExistingToolbarOnArea(Configuration * configuration,const QString & areaName)299 QDomElement MainWindow::findExistingToolbarOnArea(Configuration *configuration, const QString &areaName)
300 {
301 	QDomElement dockAreaConfig = configuration->api()->findElementByProperty(getToolbarsConfigElement(configuration), "DockArea", "name", areaName);
302 	QDomElement nullResult;
303 
304 	if (dockAreaConfig.isNull())
305 		return nullResult;
306 
307 	QDomElement toolbarElement = configuration->api()->findElement(dockAreaConfig, "ToolBar");
308 	if (toolbarElement.isNull())
309 		return nullResult;
310 
311 	return toolbarElement;
312 }
313 
findExistingToolbar(Configuration * configuration,const QString & prefix)314 QDomElement MainWindow::findExistingToolbar(Configuration *configuration, const QString &prefix)
315 {
316 	QString realPrefix;
317 	if (!prefix.isEmpty())
318 		realPrefix = prefix + '_';
319 
320 	QDomElement toolbarElement = findExistingToolbarOnArea(configuration, realPrefix + "topDockArea");
321 	if (!toolbarElement.isNull())
322 		return toolbarElement;
323 
324 	toolbarElement = findExistingToolbarOnArea(configuration, realPrefix + "leftDockArea");
325 	if (!toolbarElement.isNull())
326 		return toolbarElement;
327 
328 	toolbarElement = findExistingToolbarOnArea(configuration, realPrefix + "rightDockArea");
329 	if (!toolbarElement.isNull())
330 		return toolbarElement;
331 
332 	toolbarElement = findExistingToolbarOnArea(configuration, realPrefix + "bottomDockArea");
333 	if (!toolbarElement.isNull())
334 		return toolbarElement;
335 
336 	QDomElement dockAreaConfig = getDockAreaConfigElement(configuration, getToolbarsConfigElement(configuration), realPrefix + "topDockArea");
337 	return configuration->api()->createElement(dockAreaConfig, "ToolBar");
338 }
339 
writeToolBarsToConfig()340 void MainWindow::writeToolBarsToConfig()
341 {
342 	writeToolBarsToConfig(Qt::TopToolBarArea);
343 	writeToolBarsToConfig(Qt::LeftToolBarArea);
344 	writeToolBarsToConfig(Qt::BottomToolBarArea);
345 	writeToolBarsToConfig(Qt::RightToolBarArea);
346 }
347 
writeToolBarsToConfig(Qt::ToolBarArea area)348 void MainWindow::writeToolBarsToConfig(Qt::ToolBarArea area)
349 {
350 	QDomElement dockAreaConfig = getDockAreaConfigElement(area);
351 	m_configuration->api()->removeChildren(dockAreaConfig);
352 
353 	// TODO: laaaaame
354 	foreach(QObject *child, children())
355 	{
356 		ToolBar *toolBar = qobject_cast<ToolBar *>(child);
357 		if (!toolBar)
358 			continue;
359 
360 		if (toolBarArea(toolBar) != area)
361 			continue;
362 
363 		toolBar->writeToConfig(dockAreaConfig);
364 	}
365 }
366 
actionLoadedOrUnloaded(ActionDescription * action)367 void MainWindow::actionLoadedOrUnloaded(ActionDescription *action)
368 {
369 	if (supportsActionType(action->type()))
370 		refreshToolBars();
371 }
372 
refreshToolBars()373 void MainWindow::refreshToolBars()
374 {
375 	// We don't need it when closing.
376 	// BTW, on Mac it caused crashes on exit. TODO: check out why, as there is probably a bug somewhere.
377 	if (m_sessionService->isClosing())
378 		return;
379 
380 	loadToolBarsFromConfig();
381 }
382 
contextMenuEvent(QContextMenuEvent * event)383 void MainWindow::contextMenuEvent(QContextMenuEvent *event)
384 {
385 	if (!ToolBar::isBlockToolbars(m_configuration))
386 	{
387 		QMenu menu;
388 		menu.addAction(tr("Create new toolbar"), this, SLOT(addTopToolbar()));
389 		menu.exec(event->globalPos());
390 	}
391 }
392 
newToolbar(QWidget * parent)393 ToolBar *MainWindow::newToolbar(QWidget *parent)
394 {
395 	auto toolBar = m_injectedFactory->makeInjected<ToolBar>(parent);
396 	toolBar->setAttribute(Qt::WA_NoSystemBackground, !TransparencyEnabled);
397 	toolBar->setAutoFillBackground(TransparencyEnabled);
398 
399 	connect(toolBar, SIGNAL(updated()), this, SLOT(toolbarUpdated()));
400 	connect(toolBar, SIGNAL(removed(ToolBar*)), this, SLOT(toolbarRemoved(ToolBar*)));
401 
402 	return toolBar;
403 }
404 
addTopToolbar()405 void MainWindow::addTopToolbar()
406 {
407 	addToolBar(Qt::TopToolBarArea, newToolbar(this));
408 	toolbarUpdated();
409 }
410 
addBottomToolbar()411 void MainWindow::addBottomToolbar()
412 {
413 	addToolBar(Qt::BottomToolBarArea, newToolbar(this));
414 	toolbarUpdated();
415 }
416 
addLeftToolbar()417 void MainWindow::addLeftToolbar()
418 {
419 	addToolBar(Qt::LeftToolBarArea, newToolbar(this));
420 	toolbarUpdated();
421 }
422 
addRightToolbar()423 void MainWindow::addRightToolbar()
424 {
425 	addToolBar(Qt::RightToolBarArea, newToolbar(this));
426 	toolbarUpdated();
427 }
428 
hasAction(const QString & actionName,ToolBar * exclude)429 bool MainWindow::hasAction(const QString &actionName, ToolBar *exclude)
430 {
431 	foreach (QObject *object, children())
432 	{
433 		ToolBar *toolBar = qobject_cast<ToolBar *>(object);
434 		if (toolBar && toolBar != exclude && toolBar->hasAction(actionName))
435 			return true;
436 	}
437 
438 	return false;
439 }
440 
contact()441 Contact MainWindow::contact()
442 {
443 	ContactSet contactSet = actionContext()->contacts();
444 	return 1 == contactSet.count()
445 			? *contactSet.constBegin()
446 			: Contact::null;
447 }
448 
buddy()449 Buddy MainWindow::buddy()
450 {
451 	BuddySet buddySet = actionContext()->buddies();
452 	return 1 == buddySet.count()
453 			? *buddySet.constBegin()
454 			: Buddy::null;
455 }
456 
setTransparency(bool enable)457 void MainWindow::setTransparency(bool enable)
458 {
459 	/* 1. Do not make MainWindow related to the CompositingAwareObject class
460 	 *    as not every child wants to be compositing aware
461 	 * 2. Allow child to decide if and when to use transparency or not and
462 	 *    provide means to do this.
463 	 * Enabling transparency sets main window background transparent whilst
464 	 * toolbars are opaque, so the central widget can enjoy the transparency.
465 	 */
466 	TransparencyEnabled = enable;
467 	if (TransparencyEnabled)
468 	{
469 		setAttribute(Qt::WA_TranslucentBackground, true);
470 
471 		foreach (QObject *object, children())
472 		{
473 			QToolBar *toolBar = qobject_cast<QToolBar *>(object);
474 			if (toolBar)
475 			{
476 				toolBar->setAttribute(Qt::WA_NoSystemBackground, false);
477 				toolBar->setAutoFillBackground(true);
478 			}
479 		}
480 	}
481 	else
482 	{
483 		foreach (QObject *object, children())
484 		{
485 			QToolBar *toolBar = qobject_cast<QToolBar *>(object);
486 			if (toolBar)
487 				toolBar->setAutoFillBackground(false);
488 		}
489 		setAttribute(Qt::WA_TranslucentBackground, false);
490 		setAttribute(Qt::WA_NoSystemBackground, false);
491 	}
492 }
493 
toolbarUpdated()494 void MainWindow::toolbarUpdated()
495 {
496 	writeToolBarsToConfig();
497 
498 	m_configurationManager->toolbarConfigurationManager()->notifyConfigurationUpdated();
499 }
500 
toolbarRemoved(ToolBar * toolBar)501 void MainWindow::toolbarRemoved(ToolBar *toolBar)
502 {
503 	toolBar->hide();
504 	toolBar->setParent(0); // remove it from this window
505 	toolBar->deleteLater();
506 
507 	toolbarUpdated();
508 }
509 
actionContext()510 ActionContext * MainWindow::actionContext()
511 {
512 	return Context;
513 }
514 
setBlur(bool enable)515 void MainWindow::setBlur(bool enable)
516 {
517 #if !defined(Q_OS_UNIX)
518 	Q_UNUSED(enable);
519 #else
520 	BlurEnabled = enable;
521 	X11_setBlur(QX11Info::display(), winId(), enable);
522 #endif
523 }
524 
525 
showEvent(QShowEvent * event)526 void MainWindow::showEvent(QShowEvent * event)
527 {
528 	if (BlurEnabled)
529 		setBlur(true);
530 	QMainWindow::showEvent(event);
531 }
532 
533 #include "moc_main-window.cpp"
534