1 /* GuiUtils.cpp */
2 
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4  *
5  * This file is part of sayonara player
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 /* GuiUtils.cpp */
23 
24 #include "GuiUtils.h"
25 #include "Utils/Logger/Logger.h"
26 
27 #include <QApplication>
28 #include <QDesktopWidget>
29 #include <QDirIterator>
30 #include <QFontMetrics>
31 #include <QIcon>
32 #include <QList>
33 #include <QPalette>
34 #include <QPixmap>
35 #include <QRegExp>
36 #include <QScreen>
37 #include <QSize>
38 #include <QString>
39 #include <QCache>
40 
41 #include <algorithm>
42 
43 namespace
44 {
45 	using IconCache = QCache<QString, QIcon>;
46 	Q_GLOBAL_STATIC_WITH_ARGS(IconCache, iconCache, (50))
47 
getCacheKey(const QString & iconName,Gui::Util::IconTheme theme)48 	QString getCacheKey(const QString& iconName, Gui::Util::IconTheme theme)
49 	{
50 		return QString("%1-%2")
51 			.arg(iconName)
52 			.arg(static_cast<int>(theme));
53 	}
54 
getCacheKey(const QString & iconName,const QString & systemThemeName)55 	QString getCacheKey(const QString& iconName, const QString& systemThemeName)
56 	{
57 		return QString("%1-%2")
58 			.arg(iconName)
59 			.arg(systemThemeName);
60 	}
61 
insertIconIntoCache(const QString & key,QIcon & icon)62 	bool insertIconIntoCache(const QString& key, QIcon& icon)
63 	{
64 		if(!key.isNull() && !icon.isNull() && !iconCache.isDestroyed())
65 		{
66 			auto* iconPtr = new QIcon(std::move(icon));
67 			iconCache->insert(key, iconPtr);
68 			return true;
69 		}
70 
71 		return false;
72 	}
73 
fetchIconFromCache(const QString & cacheKey)74 	QIcon fetchIconFromCache(const QString& cacheKey)
75 	{
76 		return (!iconCache.isDestroyed() && iconCache->object(cacheKey))
77 			? *iconCache->object(cacheKey)
78 			: QIcon();
79 	}
80 
searchInResource(const QString & resourceName,const QString & regex)81 	QStringList searchInResource(const QString& resourceName, const QString& regex)
82 	{
83 		const auto re = QRegExp(regex);
84 		const auto themePath = QString(":/%1").arg(resourceName);
85 
86 		QStringList paths;
87 		auto dirIterator = QDirIterator(themePath);
88 		while(dirIterator.hasNext())
89 		{
90 			const auto path = dirIterator.next();
91 			if(re.indexIn(path) >= 0)
92 			{
93 				paths << path;
94 			}
95 		}
96 
97 		return paths;
98 	}
99 
searchInMintYIcons(const QString & iconName)100 	QStringList searchInMintYIcons(const QString& iconName)
101 	{
102 		const auto themeName = QStringLiteral("MintYIcons");
103 		const auto reString = QString(".*%1/%2-[0-9]+.*")
104 			.arg(themeName)
105 			.arg(iconName);
106 
107 		return searchInResource(themeName, reString);
108 	}
109 
searchInStandardIcons(const QString & iconName)110 	QStringList searchInStandardIcons(const QString& iconName)
111 	{
112 		QStringList paths;
113 
114 		const auto reString = QString(".*/%1(\\.[a-z][a-z][a-z])*$").arg(iconName);
115 		return searchInResource("Icons", reString);
116 	}
117 
iconPaths(const QString & iconName,Gui::Util::IconTheme iconTheme)118 	QStringList iconPaths(const QString& iconName, Gui::Util::IconTheme iconTheme)
119 	{
120 		if(iconName.trimmed().isEmpty())
121 		{
122 			return QStringList();
123 		}
124 
125 		QStringList paths;
126 		if(iconTheme == Gui::Util::MintY)
127 		{
128 			paths = searchInMintYIcons(iconName);
129 		}
130 
131 		if(paths.isEmpty())
132 		{
133 			paths = searchInStandardIcons(iconName);
134 		}
135 
136 		return paths;
137 	}
138 }
139 
140 namespace Gui
141 {
icon(const QString & iconName,IconTheme iconTheme)142 	QIcon Util::icon(const QString& iconName, IconTheme iconTheme)
143 	{
144 		if(iconName.isEmpty())
145 		{
146 			return QIcon();
147 		}
148 
149 		const auto cacheKey = getCacheKey(iconName, iconTheme);
150 		auto icon = fetchIconFromCache(cacheKey);
151 		if(!icon.isNull())
152 		{
153 			return icon;
154 		}
155 
156 		const auto paths = iconPaths(iconName, iconTheme);
157 		for(const auto& path : paths)
158 		{
159 			const auto pixmap = QPixmap(path);
160 			if(!pixmap.isNull())
161 			{
162 				icon.addPixmap(pixmap);
163 			}
164 		}
165 
166 		const auto success = insertIconIntoCache(cacheKey, icon);
167 		return (success)
168 		       ? fetchIconFromCache(cacheKey)
169 		       : icon;
170 	}
171 
systemThemeIcon(const QString & iconName)172 	QIcon Util::systemThemeIcon(const QString& iconName)
173 	{
174 		const auto cacheKey = getCacheKey(iconName, QIcon::themeName());
175 		auto icon = fetchIconFromCache(cacheKey);
176 		if(!icon.isNull())
177 		{
178 			return icon;
179 		}
180 
181 		icon = QIcon::fromTheme(iconName);
182 		const auto success = insertIconIntoCache(cacheKey, icon);
183 		return success
184 		       ? fetchIconFromCache(cacheKey)
185 		       : icon;
186 	}
187 
image(const QString & iconName,IconTheme themeName)188 	QImage Util::image(const QString& iconName, IconTheme themeName)
189 	{
190 		return image(iconName, themeName, QSize(32, 32));
191 	}
192 
image(const QString & iconName,IconTheme themeName,const QSize & size,bool keepAspectRatio)193 	QImage Util::image(const QString& iconName, IconTheme themeName, const QSize& size, bool keepAspectRatio)
194 	{
195 		const auto pixmap = Util::pixmap(iconName, themeName, size, keepAspectRatio);
196 		return (!pixmap.isNull())
197 		       ? pixmap.toImage()
198 		       : QImage();
199 	}
200 
pixmap(const QString & iconName,IconTheme themeName)201 	QPixmap Util::pixmap(const QString& iconName, IconTheme themeName)
202 	{
203 		return pixmap(iconName, themeName, QSize(32, 32));
204 	}
205 
pixmap(const QString & iconName,IconTheme themeName,const QSize & size,bool keepAspectRatio)206 	QPixmap Util::pixmap(const QString& iconName, IconTheme themeName, const QSize& size, bool keepAspectRatio)
207 	{
208 		const auto icon = Util::icon(iconName, themeName);
209 		if(!icon.isNull())
210 		{
211 			const auto pixmap = icon.pixmap(size);
212 			if(!pixmap.isNull())
213 			{
214 				const auto aspect = keepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio;
215 				return pixmap.scaled(size, aspect, Qt::SmoothTransformation);
216 			}
217 		}
218 
219 		return QPixmap();
220 	}
221 
color(QPalette::ColorGroup colorGroup,QPalette::ColorRole colorRole)222 	QColor Util::color(QPalette::ColorGroup colorGroup, QPalette::ColorRole colorRole)
223 	{
224 		return QApplication::palette().color(colorGroup, colorRole);
225 	}
226 
getBiggestScreen()227 	QScreen* Util::getBiggestScreen()
228 	{
229 		const auto screens = QApplication::screens();
230 		const auto it = std::max_element(screens.cbegin(),
231 		                                 screens.cend(),
232 		                                 [](const auto* screen1, const auto* screen2) {
233 			                                 return (screen1->size().height() < screen2->size().height());
234 		                                 });
235 
236 		return (it != screens.end())
237 		       ? *it
238 		       : nullptr;
239 	}
240 
placeInScreenCenter(QWidget * widget,float relativeSizeX,float relativeSizeY)241 	void Util::placeInScreenCenter(QWidget* widget, float relativeSizeX, float relativeSizeY)
242 	{
243 		const auto* screen = getBiggestScreen();
244 		if(!screen)
245 		{
246 			return;
247 		}
248 
249 		const auto width = screen->size().width();
250 		const auto height = screen->size().height();
251 
252 		if(relativeSizeX < 0.1f || relativeSizeY < 0.1f)
253 		{
254 			relativeSizeX = 0.7f;
255 			relativeSizeY = 0.7f;
256 		}
257 
258 		const auto xRemainder = (1.0f - relativeSizeX) / 2.0f;
259 		const auto yRemainder = (1.0f - relativeSizeY) / 2.0f;
260 
261 		const auto xAbs = std::max(0, static_cast<int>(width * xRemainder)) + screen->geometry().x();
262 		const auto yAbs = std::max(0, static_cast<int>(height * yRemainder)) + screen->geometry().y();
263 		auto wAbs = std::max(0, static_cast<int>(width * relativeSizeX));
264 		auto hAbs = std::max(0, static_cast<int>(height * relativeSizeY));
265 
266 		if(wAbs == 0)
267 		{
268 			wAbs = 1200;
269 		}
270 
271 		if(hAbs == 0)
272 		{
273 			hAbs = 800;
274 		}
275 
276 		widget->setGeometry(xAbs, yAbs, wAbs, hAbs);
277 	}
278 
textWidth(const QFontMetrics & fm,const QString & text)279 	int Util::textWidth(const QFontMetrics& fm, const QString& text)
280 	{
281 #if QT_VERSION_MAJOR >= 5 && QT_VERSION_MINOR >= 11
282 		return fm.horizontalAdvance(text);
283 #else
284 		return fm.width(text);
285 #endif
286 	}
287 
textWidth(QWidget * widget,const QString & text)288 	int Util::textWidth(QWidget* widget, const QString& text)
289 	{
290 		return textWidth(widget->fontMetrics(), text);
291 	}
292 
viewRowHeight()293 	int Util::viewRowHeight()
294 	{
295 		return -1;
296 	}
297 }