1 /***************************************************************************
2                           icons.cpp
3                              -------------------
4     begin                : Sun Jun 25 2017
5     copyright            : (C) 2017 by Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
6                            (C) 2020 by Dawid Wróbel <me@dawidwrobel.com>
7 
8 ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 #include "icons.h"
20 
21 #include <QHash>
22 #include <QString>
23 #include <QIcon>
24 #include <QPixmapCache>
25 #include <QPainter>
26 #include <QRegularExpression>
27 #include <QDir>
28 #include <QStandardPaths>
29 
30 namespace Icons {
31   QHash<Icon, QString> sStandardIcons;
32 
qHash(const Icon key,uint seed)33   uint qHash(const Icon key, uint seed)
34   {
35     return ::qHash(static_cast<uint>(key), seed);
36   }
37 
qHash(const IconSet key,uint seed)38   uint qHash(const IconSet key, uint seed)
39   {
40     return ::qHash(static_cast<uint>(key), seed);
41   }
42 
43   struct iconDescription {
44       Icon baseIcon;
45       Icon overlayIcon;
46       Qt::Corner corner;
47   };
48 
49   const QHash<Icon, QHash<IconSet, QString> > iconMappings{
50       {Icon::AccountClosed,
51        {{IconSet::Common, QStringLiteral("account-types-closed")},
52         {IconSet::Oxygen, QStringLiteral("dialog-close")},
53         {IconSet::Breeze, QStringLiteral("dialog-close")}}},
54       {Icon::ArrowDown,
55        {{IconSet::Common, QStringLiteral("arrow-down")},
56         {IconSet::Tango, QStringLiteral("go-down")}}},
57       {Icon::ArrowLeft,
58        {{IconSet::Common, QStringLiteral("arrow-left")},
59         {IconSet::Tango, QStringLiteral("go-previous")}}},
60       {Icon::ArrowRight,
61        {{IconSet::Common, QStringLiteral("arrow-right")},
62         {IconSet::Tango, QStringLiteral("go-next")}}},
63       {Icon::ArrowUp,
64        {{IconSet::Common, QStringLiteral("arrow-up")},
65         {IconSet::Tango, QStringLiteral("go-up")}}},
66       {Icon::Budget,
67        {{IconSet::Common, QStringLiteral("budget")},
68         {IconSet::Oxygen, QStringLiteral("view-time-schedule-calculus")},
69         {IconSet::Breeze, QStringLiteral("view-time-schedule-calculus")}}},
70       {Icon::Calculator,
71        {{IconSet::Common, QStringLiteral("accessories-calculator")}}},
72       {Icon::Configure,
73        {{IconSet::Common, QStringLiteral("configure")},
74         {IconSet::Tango, QStringLiteral("preferences-system")}}},
75       {Icon::DialogCancel,
76        {{IconSet::Common, QStringLiteral("dialog-cancel")},
77         {IconSet::Tango, QStringLiteral("stop")}}},
78       {Icon::DialogClose, {{IconSet::Common, QStringLiteral("dialog-close")}}},
79       {Icon::DialogError, {{IconSet::Common, QStringLiteral("dialog-error")}}},
80       {Icon::DialogInformation,
81        {{IconSet::Common, QStringLiteral("dialog-information")}}},
82       {Icon::DialogOK,
83        {{IconSet::Common, QStringLiteral("dialog-ok")},
84         {IconSet::Tango, QStringLiteral("finish")}}},
85       {Icon::DialogOKApply,
86        {{IconSet::Common, QStringLiteral("dialog-ok-apply")}}},
87       {Icon::DialogWarning,
88        {{IconSet::Common, QStringLiteral("dialog-warning")}}},
89       {Icon::DocumentClose,
90        {{IconSet::Common, QStringLiteral("document-close")},
91         {IconSet::Tango, QStringLiteral("stop")}}},
92       {Icon::DocumentEdit,
93        {{IconSet::Common, QStringLiteral("document-edit")},
94         {IconSet::Tango, QStringLiteral("text-editor")}}},
95       {Icon::DocumentExport,
96        {{IconSet::Common, QStringLiteral("format-indent-more")},
97         {IconSet::Oxygen, QStringLiteral("document-export")},
98         {IconSet::Breeze, QStringLiteral("document-export")}}},
99       {Icon::DocumentImport,
100        {{IconSet::Common, QStringLiteral("format-indent-less")},
101         {IconSet::Oxygen, QStringLiteral("document-import")},
102         {IconSet::Breeze, QStringLiteral("document-import")}}},
103       {Icon::DocumentNew, {{IconSet::Common, QStringLiteral("document-new")}}},
104       {Icon::DocumentOpen,
105        {{IconSet::Common, QStringLiteral("document-open")}}},
106       {Icon::DocumentProperties,
107        {{IconSet::Common, QStringLiteral("document-properties")}}},
108       {Icon::DocumentSave,
109        {{IconSet::Common, QStringLiteral("document-save")}}},
110       {Icon::Download,
111        {{IconSet::Breeze, QStringLiteral("edit-download")},
112         {IconSet::Common, QStringLiteral("go-down")},
113         {IconSet::Oxygen, QStringLiteral("download")}}},
114       {Icon::EditClear, {{IconSet::Common, QStringLiteral("edit-clear")}}},
115       {Icon::EditCopy, {{IconSet::Common, QStringLiteral("edit-copy")}}},
116       {Icon::EditDelete, {{IconSet::Common, QStringLiteral("edit-delete")}}},
117       {Icon::Find, {{IconSet::Common, QStringLiteral("edit-find")}}},
118       {Icon::EditRename,
119        {{IconSet::Common, QStringLiteral("edit-rename")},
120         {IconSet::Tango, QStringLiteral("text-editor")}}},
121       {Icon::EditUndo, {{IconSet::Common, QStringLiteral("edit-undo")}}},
122       {Icon::Folder, {{IconSet::Common, QStringLiteral("folder")}}},
123       {Icon::GoTo, {{IconSet::Common, QStringLiteral("go-jump")}}},
124       {Icon::Help, {{IconSet::Common, QStringLiteral("help-contents")}}},
125       {Icon::HideCategories,
126        {{IconSet::Common, QStringLiteral("hide-categories")}}},
127       {Icon::HideReconciled,
128        {{IconSet::Common, QStringLiteral("hide-reconciled")}}},
129       {Icon::KMyMoney, {{IconSet::Common, QStringLiteral("kmymoney")}}},
130       {Icon::KeyEnter,
131        {{IconSet::Common, QStringLiteral("input-keyboard")},
132         {IconSet::Oxygen, QStringLiteral("key-enter")},
133         {IconSet::Breeze, QStringLiteral("key-enter")}}},
134       {Icon::ListAdd, {{IconSet::Common, QStringLiteral("list-add")}}},
135       {Icon::ListAddTag, {{IconSet::Common, QStringLiteral("list-add-tag")}}},
136       {Icon::ListAddUser,
137        {{IconSet::Common, QStringLiteral("list-add-user")}}},
138       {Icon::ListCollapse,
139        {{IconSet::Common, QStringLiteral("zoom-out")},
140         {IconSet::Tango, QStringLiteral("list-remove")}}},
141       {Icon::ListExpand,
142        {{IconSet::Common, QStringLiteral("zoom-in")},
143         {IconSet::Tango, QStringLiteral("list-add")}}},
144       {Icon::ListRemoveTag,
145        {{IconSet::Common, QStringLiteral("list-remove-tag")}}},
146       {Icon::ListRemoveUser,
147        {{IconSet::Common, QStringLiteral("list-remove-user")}}},
148       {Icon::MailMessage,
149        {{IconSet::Common, QStringLiteral("internet-mail")},
150         {IconSet::Oxygen, QStringLiteral("mail-message")},
151         {IconSet::Breeze, QStringLiteral("mail-message")}}},
152       {Icon::MailMessageNew,
153        {{IconSet::Common, QStringLiteral("mail-message-new")}}},
154       {Icon::MailReceive, {{IconSet::Common, QStringLiteral("mail-receive")}}},
155       {Icon::MapOnlineAccount,
156        {{IconSet::Common, QStringLiteral("news-subscribe")}}},
157       {Icon::Merge,
158        {{IconSet::Common, QStringLiteral("reconcile")},
159         {IconSet::Oxygen, QStringLiteral("merge")},
160         {IconSet::Breeze, QStringLiteral("merge")}}},
161       {Icon::NewSchedule,
162        {{IconSet::Common, QStringLiteral("appointment-new")}}},
163       {Icon::OfficeChartLine,
164        {{IconSet::Common, QStringLiteral("account-types-investments")},
165         {IconSet::Oxygen, QStringLiteral("office-chart-line")},
166         {IconSet::Breeze, QStringLiteral("office-chart-line")},
167         {IconSet::Tango, QStringLiteral("report-line")}}},
168       {Icon::OpenDatabase, {{IconSet::Common, QStringLiteral("server-database")}}},
169       {Icon::Pause,
170        {{IconSet::Common, QStringLiteral("media-playback-pause")}}},
171       {Icon::PayeeMerge, {{IconSet::Common, QStringLiteral("merge")}}},
172       {Icon::PayeeRename,
173        {{IconSet::Common, QStringLiteral("user-properties")},
174         {IconSet::Oxygen, QStringLiteral("payee-rename")}}},
175       {Icon::PerformanceTest,
176        {{IconSet::Common, QStringLiteral("fork")},
177         {IconSet::Breeze, QStringLiteral("speedometer")}}},
178       {Icon::PreferencesColors,
179        {{IconSet::Common, QStringLiteral("preferences-desktop-color")}}},
180       {Icon::PreferencesFonts,
181        {{IconSet::Common, QStringLiteral("preferences-desktop-font")}}},
182       {Icon::PreferencesGeneral,
183        {{IconSet::Common, QStringLiteral("system-run")},
184         {IconSet::Tango, QStringLiteral("media-playback-start")}}},
185       {Icon::PreferencesIcons,
186        {{IconSet::Common, QStringLiteral("preferences-desktop-icon")}}},
187       {Icon::PreferencesNetwork,
188        {{IconSet::Common, QStringLiteral("preferences-system-network")}}},
189       {Icon::PreferencesPlugins,
190        {{IconSet::Common, QStringLiteral("network-disconnect")}}},
191       {Icon::Reconcile,
192        {{IconSet::Common, QStringLiteral("reconcile")},
193         {IconSet::Oxygen, QStringLiteral("merge")},
194         {IconSet::Breeze, QStringLiteral("merge")}}},
195       {Icon::Reconciled,
196        {{IconSet::Common, QStringLiteral("reconciled")},
197         {IconSet::Oxygen, QStringLiteral("flag-green")},
198         {IconSet::Breeze, QStringLiteral("flag-green")}}},
199       {Icon::Refresh,
200        {{IconSet::Breeze, QStringLiteral("view-refresh")},
201         {IconSet::Oxygen, QStringLiteral("refresh")}}},
202       {Icon::Report,
203        {{IconSet::Common, QStringLiteral("application-vnd.oasis.opendocument.spreadsheet")}}},
204       {Icon::Reverse, {{IconSet::Common, QStringLiteral("reverse")}}},
205       {Icon::SeekForward,
206        {{IconSet::Common, QStringLiteral("media-seek-forward")}}},
207       {Icon::SkipForward,
208        {{IconSet::Common, QStringLiteral("media-skip-forward")}}},
209       {Icon::SortAscending,
210        {{IconSet::Common, QStringLiteral("go-up")},
211         {IconSet::Oxygen, QStringLiteral("view-sort-ascending")},
212         {IconSet::Breeze, QStringLiteral("view-sort-ascending")}}},
213       {Icon::SortDescending,
214        {{IconSet::Common, QStringLiteral("go-down")},
215         {IconSet::Oxygen, QStringLiteral("view-sort-descending")},
216         {IconSet::Breeze, QStringLiteral("view-sort-descending")}}},
217       {Icon::Split,
218        {{IconSet::Common, QStringLiteral("transaction-split")},
219         {IconSet::Oxygen, QStringLiteral("split")},
220         {IconSet::Breeze, QStringLiteral("split")}}},
221       {Icon::TagRename, {{IconSet::Common, QStringLiteral("tag-rename")}}},
222       {Icon::TaskAccepted,
223        {{IconSet::Common, QStringLiteral("task-accepted")}}},
224       {Icon::TaskComplete,
225        {{IconSet::Common, QStringLiteral("task-complete")}}},
226       {Icon::TaskOngoing, {{IconSet::Common, QStringLiteral("task-ongoing")}}},
227       {Icon::TaskReject, {{IconSet::Common, QStringLiteral("task-reject")}}},
228       {Icon::Warning,
229        {{IconSet::Common, QStringLiteral("dialog-warning")}}},
230       {Icon::Tip,
231        {{IconSet::Common, QStringLiteral("info")},
232         {IconSet::Oxygen, QStringLiteral("ktip")}}},
233       {Icon::Unknown, {{IconSet::Common, QStringLiteral("unknown")}}},
234       {Icon::UnmapOnlineAccount,
235        {{IconSet::Common, QStringLiteral("news-unsubscribe")}}},
236       {Icon::UserProperties,
237        {{IconSet::Common, QStringLiteral("system-users")},
238         {IconSet::Oxygen, QStringLiteral("user-properties")},
239         {IconSet::Breeze, QStringLiteral("user-properties")}}},
240       {Icon::Accounts,
241        {{IconSet::Common, QStringLiteral("account")},
242         {IconSet::Oxygen, QStringLiteral("view-bank-account")},
243         {IconSet::Breeze, QStringLiteral("view-bank-account")}}},
244       {Icon::Asset,
245        {{IconSet::Common, QStringLiteral("account-types-asset")},
246         {IconSet::Oxygen, QStringLiteral("view-bank-account")},
247         {IconSet::Breeze, QStringLiteral("view-bank-account")}}},
248       {Icon::Bank,
249        {{IconSet::Common, QStringLiteral("bank")},
250         {IconSet::Oxygen, QStringLiteral("view-bank")},
251         {IconSet::Breeze, QStringLiteral("view-bank")}}},
252       {Icon::BankAccount,
253        {{IconSet::Common, QStringLiteral("account")},
254         {IconSet::Oxygen, QStringLiteral("view-bank-account")},
255         {IconSet::Breeze, QStringLiteral("view-bank-account")}}},
256       {Icon::Calendar,
257        {{IconSet::Common, QStringLiteral("view-calendar")}}},
258       {Icon::CalendarDay,
259        {{IconSet::Common, QStringLiteral("office-calendar")},
260         {IconSet::Oxygen, QStringLiteral("view-calendar-day")},
261         {IconSet::Breeze, QStringLiteral("view-calendar-day")}}},
262       {Icon::Cash,
263        {{IconSet::Common, QStringLiteral("account-types-cash")}}},
264       {Icon::Checking,
265        {{IconSet::Common, QStringLiteral("account-types-checking")},
266         {IconSet::Oxygen, QStringLiteral("view-bank-account-checking")},
267         {IconSet::Breeze, QStringLiteral("view-bank-account-checking")}}},
268       {Icon::Close, {{IconSet::Common, QStringLiteral("view-close")}}},
269       {Icon::CreditCard,
270        {{IconSet::Breeze, QStringLiteral("skrooge_credit_card")},
271         {IconSet::Common, QStringLiteral("account-types-credit-card")},
272         {IconSet::Oxygen, QStringLiteral("view-credit-card-account")}}},
273       {Icon::Currencies,
274        {{IconSet::Common, QStringLiteral("view-currency-list")}}},
275       {Icon::Equity,
276        {{IconSet::Common, QStringLiteral("account")},
277         {IconSet::Oxygen, QStringLiteral("view-bank-account")},
278         {IconSet::Breeze, QStringLiteral("view-bank-account")}}},
279       {Icon::Expense,
280        {{IconSet::Breeze, QStringLiteral("view-categories-expenditures")},
281         {IconSet::Common, QStringLiteral("account-types-expense")},
282         {IconSet::Oxygen, QStringLiteral("view-expenses-categories")}}},
283       {Icon::Filter, {{IconSet::Common, QStringLiteral("view-filter")}}},
284       {Icon::FinancialCategories,
285        {{IconSet::Common, QStringLiteral("categories")},
286         {IconSet::Oxygen, QStringLiteral("view-categories")},
287         {IconSet::Breeze, QStringLiteral("view-categories")},
288         {IconSet::Oxygen, QStringLiteral("view-financial-categories")}}},
289       {Icon::Ledger,
290        {{IconSet::Common, QStringLiteral("ledger")},
291         {IconSet::Oxygen, QStringLiteral("view-financial-list")}}},
292       {Icon::Transaction,
293        {{IconSet::Common, QStringLiteral("ledger")},
294         {IconSet::Oxygen, QStringLiteral("view-financial-transfer")}}},
295       {Icon::Forecast,
296        {{IconSet::Common, QStringLiteral("forecast")},
297         {IconSet::Oxygen, QStringLiteral("view-financial-forecast")}}},
298       {Icon::Home,
299        {{IconSet::Common, QStringLiteral("home")},
300         {IconSet::Oxygen, QStringLiteral("go-home")},
301         {IconSet::Breeze, QStringLiteral("go-home")}}},
302       {Icon::Income,
303        {{IconSet::Breeze, QStringLiteral("view-categories-incomes")},
304         {IconSet::Common, QStringLiteral("account-types-income")},
305         {IconSet::Oxygen, QStringLiteral("view-income-categories")}}},
306       {Icon::Institution,
307        {{IconSet::Common, QStringLiteral("institution")},
308         {IconSet::Oxygen, QStringLiteral("view-bank")},
309         {IconSet::Breeze, QStringLiteral("view-bank")}}},
310       {Icon::Institutions,
311        {{IconSet::Common, QStringLiteral("institution")},
312         {IconSet::Oxygen, QStringLiteral("view-bank")},
313         {IconSet::Breeze, QStringLiteral("view-bank")}}},
314       {Icon::Investment,
315        {{IconSet::Common, QStringLiteral("investment")},
316         {IconSet::Oxygen, QStringLiteral("view-investment")}}},
317       {Icon::Investments,
318        {{IconSet::Common, QStringLiteral("investment")},
319         {IconSet::Oxygen, QStringLiteral("view-investment")}}},
320       {Icon::Liability,
321        {{IconSet::Common, QStringLiteral("account-types-liability")},
322         {IconSet::Oxygen, QStringLiteral("view-loan")}}},
323       {Icon::Loan,
324        {{IconSet::Common, QStringLiteral("account-types-loan")},
325         {IconSet::Oxygen, QStringLiteral("view-loan")}}},
326       {Icon::LoanAsset,
327        {{IconSet::Common, QStringLiteral("account-types-loan")},
328         {IconSet::Oxygen, QStringLiteral("view-loan-asset")}}},
329       {Icon::OnlineJobOutbox,
330        {{IconSet::Common, QStringLiteral("online-banking")}}},
331       {Icon::Payees,
332        {{IconSet::Common, QStringLiteral("payee")},
333         {IconSet::Oxygen, QStringLiteral("system-users")},
334         {IconSet::Breeze, QStringLiteral("system-users")}}},
335       {Icon::Reports,
336        {{IconSet::Common, QStringLiteral("report")},
337         {IconSet::Oxygen, QStringLiteral("office-chart-bar")},
338         {IconSet::Breeze, QStringLiteral("office-chart-bar")}}},
339       {Icon::Savings,
340        {{IconSet::Common, QStringLiteral("account-types-savings")},
341         {IconSet::Oxygen, QStringLiteral("view-bank-account-savings")},
342         {IconSet::Breeze, QStringLiteral("view-bank-account-savings")}}},
343       {Icon::Schedule,
344        {{IconSet::Common, QStringLiteral("schedule")},
345         {IconSet::Oxygen, QStringLiteral("view-pim-calendar")},
346         {IconSet::Breeze, QStringLiteral("view-pim-calendar")}}},
347       {Icon::Stock,
348        {{IconSet::Common, QStringLiteral("account-types-investments")},
349         {IconSet::Oxygen, QStringLiteral("view-stock-account")}}},
350       {Icon::Tags,
351        {{IconSet::Common, QStringLiteral("bookmark-new")},
352         {IconSet::Oxygen, QStringLiteral("mail-tagged")},
353         {IconSet::Breeze, QStringLiteral("mail-tagged")}}},
354       {Icon::TransactionDetails,
355        {{IconSet::Common, QStringLiteral("edit-find")},
356         {IconSet::Oxygen, QStringLiteral("zoom-in")},
357         {IconSet::Breeze, QStringLiteral("zoom-in")}}},
358       {Icon::UpcomingEvents,
359        {{IconSet::Common, QStringLiteral("view-calendar-upcoming-events")}}},
360       {Icon::ZoomIn, {{IconSet::Common, QStringLiteral("zoom-in")}}},
361       {Icon::ZoomOut, {{IconSet::Common, QStringLiteral("zoom-out")}}},
362       {Icon::Visibility, {{IconSet::Common, QStringLiteral("visibility")}}},
363       {Icon::NoVisibility, {{IconSet::Common, QStringLiteral("hint")}}},
364       {Icon::SelectAll, {{IconSet::Common, QStringLiteral("edit-select-all")}}}
365   };
366 
367   const QHash<Icon, iconDescription> sComposedIcons {
368     {Icon::InstitutionNew,              {Icon::Bank, Icon::ListAdd, Qt::BottomRightCorner}},
369     {Icon::InstitutionEdit,             {Icon::Bank, Icon::DocumentEdit, Qt::BottomRightCorner}},
370     {Icon::InstitutionDelete,           {Icon::Bank, Icon::EditDelete, Qt::BottomRightCorner}},
371     {Icon::AccountNew,                  {Icon::BankAccount, Icon::ListAdd, Qt::TopRightCorner}},
372     {Icon::AccountFinishReconciliation, {Icon::Merge,                    Icon::DialogOK,       Qt::BottomRightCorner}},
373     {Icon::AccountEdit,                 {Icon::BankAccount, Icon::DocumentEdit, Qt::BottomRightCorner}},
374     {Icon::AccountDelete,               {Icon::BankAccount, Icon::EditDelete, Qt::BottomRightCorner}},
375     {Icon::AccountClose,                {Icon::BankAccount, Icon::DialogClose, Qt::BottomRightCorner}},
376     {Icon::AccountReopen,               {Icon::BankAccount, Icon::DialogOK, Qt::BottomRightCorner}},
377     {Icon::AccountUpdate,               {Icon::BankAccount, Icon::Download, Qt::BottomRightCorner}},
378     {Icon::AccountUpdateAll,            {Icon::BankAccount, Icon::Download, Qt::BottomRightCorner}},
379     {Icon::AccountCreditTransfer,       {Icon::BankAccount, Icon::MailMessageNew, Qt::BottomRightCorner}},
380     {Icon::CategoryNew,                 {Icon::FinancialCategories, Icon::ListAdd, Qt::TopRightCorner}},
381     {Icon::CategoryEdit,                {Icon::FinancialCategories, Icon::DocumentEdit, Qt::BottomRightCorner}},
382     {Icon::CategoryDelete,              {Icon::FinancialCategories, Icon::EditDelete, Qt::BottomRightCorner}},
383     {Icon::TransactionNew,              {Icon::Transaction, Icon::ListAdd, Qt::TopRightCorner}},
384     {Icon::TransactionEdit,             {Icon::Transaction, Icon::DocumentEdit, Qt::BottomRightCorner}},
385     {Icon::TransactionMatch,            {Icon::Transaction, Icon::DocumentImport, Qt::BottomRightCorner}},
386     {Icon::TransactionAccept,           {Icon::Transaction, Icon::DialogOKApply, Qt::BottomRightCorner}},
387     {Icon::InvestmentNew,               {Icon::Investment, Icon::ListAdd, Qt::TopRightCorner}},
388     {Icon::InvestmentEdit,              {Icon::Investment, Icon::DocumentEdit, Qt::BottomRightCorner}},
389     {Icon::InvestmentDelete,            {Icon::Investment, Icon::EditDelete, Qt::BottomRightCorner}},
390     {Icon::InvestmentOnlinePrice,       {Icon::Investment, Icon::Download, Qt::BottomRightCorner}},
391     {Icon::InvestmentOnlinePriceAll,    {Icon::Investment, Icon::Download, Qt::BottomRightCorner}},
392     {Icon::BudgetNew,                   {Icon::Budget, Icon::ListAdd, Qt::TopRightCorner}},
393     {Icon::BudgetRename,                {Icon::Budget, Icon::DocumentEdit, Qt::BottomRightCorner}},
394     {Icon::BudgetDelete,                {Icon::Budget, Icon::EditDelete, Qt::BottomRightCorner}},
395     {Icon::BudgetCopy,                  {Icon::Budget, Icon::EditCopy, Qt::BottomRightCorner}},
396     {Icon::PriceUpdate,                 {Icon::Currencies, Icon::Download, Qt::BottomRightCorner}}
397   };
398 
setUpMappings(const QString & themeName)399   KMM_ICONS_EXPORT void setUpMappings(const QString& themeName)
400   {
401     IconSet themeIconSet;
402 
403     if (themeName.contains(QStringLiteral("oxygen"), Qt::CaseInsensitive))
404       themeIconSet = IconSet::Oxygen;
405     else if (themeName.contains(QStringLiteral("tango"), Qt::CaseInsensitive))
406       themeIconSet = IconSet::Tango;
407     // default to breeze mappings
408     else
409       themeIconSet = IconSet::Breeze;
410 
411     for (auto iconDef = iconMappings.cbegin(); iconDef != iconMappings.cend(); ++iconDef) {
412       const auto icon = iconDef.key();
413       auto name = iconDef.value().value(themeIconSet);
414 
415       // get common mapping if theme-specific does not exist
416       if (name.isEmpty())
417         name = iconDef.value().value(IconSet::Common);
418 
419       sStandardIcons.insert(icon, name);
420     }
421   }
422 
423   /**
424     * This method overlays an icon over another one, to get a composite one
425     * eg. an icon to add accounts
426     */
overlayIcon(iconDescription description,const int size=64)427   QIcon overlayIcon(iconDescription description, const int size = 64)
428   {
429     const auto iconName = sStandardIcons[description.baseIcon];
430     const auto overlayName = sStandardIcons[description.overlayIcon];
431     const auto corner = description.corner;
432 
433     QPixmap pxIcon;
434     QString kyIcon = iconName + overlayName;
435 
436     // If found in the cache, return quickly
437     if (QPixmapCache::find(kyIcon, pxIcon))
438       return pxIcon;
439 
440     // try to retrieve the main icon from cache
441     if (!QPixmapCache::find(iconName, pxIcon)) {
442       pxIcon = QIcon::fromTheme(iconName).pixmap(size);
443       QPixmapCache::insert(iconName, pxIcon);
444     }
445 
446     if (overlayName.isEmpty()) // call from MyMoneyAccount::accountPixmap can have no overlay icon, so handle that appropriately
447       return pxIcon;
448 
449     QPainter pixmapPainter(&pxIcon);
450     QPixmap pxOverlay = QIcon::fromTheme(overlayName).pixmap(size);
451 
452     int x, y;
453     switch (corner) {
454     case Qt::TopLeftCorner:
455       x = 0;
456       y = 0;
457       break;
458     case Qt::TopRightCorner:
459       x = pxIcon.width() / 2;
460       y = 0;
461       break;
462     case Qt::BottomLeftCorner:
463       x = 0;
464       y = pxIcon.height() / 2;
465       break;
466     case Qt::BottomRightCorner:
467     default:
468       x = pxIcon.width() / 2;
469       y = pxIcon.height() / 2;
470       break;
471     }
472     pixmapPainter.drawPixmap(x, y, pxIcon.width() / 2, pxIcon.height() / 2, pxOverlay);
473 
474     //save for later use
475     QPixmapCache::insert(kyIcon, pxIcon);
476 
477     return pxIcon;
478   }
479 
get(Icon icon)480   KMM_ICONS_EXPORT QIcon get(Icon icon)
481   {
482     if (sComposedIcons.contains(icon))
483       return overlayIcon(sComposedIcons[icon]);
484 
485     return QIcon::fromTheme(sStandardIcons[icon]);
486   }
487 
iconCacheDir()488   QString iconCacheDir()
489   {
490     const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
491     if (QDir::root().mkpath(cachePath)) {
492       return cachePath;
493     }
494     return QString();
495   }
496 
storeIconInApplicationCache(const QString & name,const QIcon & icon)497   KMM_ICONS_EXPORT bool storeIconInApplicationCache(const QString& name, const QIcon& icon)
498   {
499     // split the icon name from the type
500     QRegularExpression iconPath(QStringLiteral("^(?<type>[a-zA-Z]+):(?<name>.+)"), QRegularExpression::CaseInsensitiveOption);
501     QRegularExpressionMatch matcher = iconPath.match(name);
502     if (matcher.hasMatch()) {
503       if (matcher.captured(QStringLiteral("type")).compare(QLatin1String("enum")) == 0) {
504         return true;
505       } else {
506         const QString cacheDir = iconCacheDir();
507         if (!cacheDir.isEmpty()) {
508           return icon.pixmap(16).save(QString::fromLatin1("%1/%2-%3").arg(cacheDir, matcher.captured(QStringLiteral("type")), matcher.captured(QStringLiteral("name"))), "PNG");
509         }
510       }
511     }
512     return false;
513   }
514 
loadIconFromApplicationCache(const QString & name)515   KMM_ICONS_EXPORT QIcon loadIconFromApplicationCache(const QString& name)
516   {
517     const QHash<QString, Icon> sEnumIcons {
518       { QStringLiteral("Bank"), Icon::Bank },
519     };
520 
521     // split the icon name from the type
522     QRegularExpression iconPath(QStringLiteral("^(?<type>[a-zA-Z]+):(?<name>.+)"), QRegularExpression::CaseInsensitiveOption);
523     QRegularExpressionMatch matcher = iconPath.match(name);
524     if (matcher.hasMatch()) {
525       if (matcher.captured(QStringLiteral("type")).compare(QLatin1String("enum")) == 0) {
526         // type is enum, so we use our own set of icons
527         const QString iconName = matcher.captured(QStringLiteral("name"));
528         if (sEnumIcons.contains(iconName)) {
529           return get(sEnumIcons[iconName]);
530         }
531 
532       } else {
533         // otherwise, we use the type as part of the filename
534         const QString cacheDir = iconCacheDir();
535         if (!cacheDir.isEmpty()) {
536           const QString filename = QString::fromLatin1("%1/%2-%3").arg(cacheDir, matcher.captured(QStringLiteral("type")), matcher.captured(QStringLiteral("name")));
537           if (QFile::exists(filename)) {
538             return QIcon(filename);
539           }
540         }
541       }
542     }
543     return QIcon();
544   }
545 }
546