diff --git a/retroshare-gui/src/gui/SoundManager.cpp b/retroshare-gui/src/gui/SoundManager.cpp index 69ba9e64b..f922c02e9 100644 --- a/retroshare-gui/src/gui/SoundManager.cpp +++ b/retroshare-gui/src/gui/SoundManager.cpp @@ -81,10 +81,11 @@ void SoundManager::soundEvents(SoundEvents &events) /* add standard events */ events.addEvent(tr("Friend"), tr("Go Online"), SOUND_USER_ONLINE, QFileInfo(baseDir, "online1.wav").absoluteFilePath()); - events.addEvent(tr("Chatmessage"), tr("New Msg"), SOUND_NEW_CHAT_MESSAGE, QFileInfo(baseDir, "incomingchat.wav").absoluteFilePath()); + events.addEvent(tr("Chat Message"), tr("New Msg"), SOUND_NEW_CHAT_MESSAGE, QFileInfo(baseDir, "incomingchat.wav").absoluteFilePath()); events.addEvent(tr("Message"), tr("Message arrived"), SOUND_MESSAGE_ARRIVED, QFileInfo(baseDir, "receive.wav").absoluteFilePath()); events.addEvent(tr("Download"), tr("Download complete"), SOUND_DOWNLOAD_COMPLETE, QFileInfo(baseDir, "ft_complete.wav").absoluteFilePath()); - events.addEvent(tr("Lobby"), tr("Message arrived"), SOUND_NEW_LOBBY_MESSAGE, QFileInfo(baseDir, "incomingchat.wav").absoluteFilePath()); + events.addEvent(tr("Chat Room"), tr("Message arrived"), SOUND_NEW_LOBBY_MESSAGE, QFileInfo(baseDir, "incomingchat.wav").absoluteFilePath()); + events.addEvent(tr("Chat Room"), tr("Specific User incoming in Chat Room"), SOUND_LOBBY_INCOMING, QFileInfo(baseDir, "incomingchat.wav").absoluteFilePath()); /* add plugin events */ int pluginCount = rsPlugins->nbPlugins(); @@ -116,7 +117,7 @@ QString SoundManager::defaultFilename(const QString &event, bool check) return convertFilename(filename); } - if (QFileInfo(filename).exists()) { + if (QFileInfo::exists(filename)) { return convertFilename(filename); } @@ -132,7 +133,7 @@ void SoundManager::initDefault() foreach (event, events.mEventInfos.keys()) { SoundEvents::SoundEventInfo &eventInfo = events.mEventInfos[event]; - if (QFileInfo(eventInfo.mDefaultFilename).exists()) { + if (QFileInfo::exists(eventInfo.mDefaultFilename)) { setEventFilename(event, convertFilename(eventInfo.mDefaultFilename)); setEventEnabled(event, true); } diff --git a/retroshare-gui/src/gui/SoundManager.h b/retroshare-gui/src/gui/SoundManager.h index 41db55ce4..51d49fba3 100644 --- a/retroshare-gui/src/gui/SoundManager.h +++ b/retroshare-gui/src/gui/SoundManager.h @@ -24,11 +24,12 @@ #include #include -#define SOUND_NEW_CHAT_MESSAGE "NewChatMessage" #define SOUND_USER_ONLINE "User_go_Online" +#define SOUND_NEW_CHAT_MESSAGE "NewChatMessage" #define SOUND_MESSAGE_ARRIVED "MessageArrived" #define SOUND_DOWNLOAD_COMPLETE "DownloadComplete" #define SOUND_NEW_LOBBY_MESSAGE "NewLobbyMessage" +#define SOUND_LOBBY_INCOMING "LobbyIncoming" class SoundEvents { @@ -68,7 +69,7 @@ public: static void create(); #ifdef Q_OS_LINUX - static QString soundDetectPlayer(); + static QString soundDetectPlayer(); #endif static void initDefault(); @@ -88,7 +89,7 @@ public: static QString eventFilename(const QString &event); static void setEventFilename(const QString &event, const QString &filename); - + private: SoundManager(); }; diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp index 3b92883e8..0d56e5938 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp @@ -41,7 +41,9 @@ #include "gui/msgs/MessageComposer.h" #include "gui/settings/RsharePeerSettings.h" #include "gui/settings/rsharesettings.h" +#include "gui/SoundManager.h" #include "util/HandleRichText.h" +#include "util/misc.h" #include "util/QtVersion.h" #include "retroshare/rsnotify.h" @@ -63,17 +65,21 @@ const static uint32_t timeToInactivity2 = 60 * 5; // in seconds /** Default constructor */ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::WindowFlags flags) - : ChatDialog(parent, flags), lobbyId(lid), mWindowedSetted(false), mPCWindow(nullptr), - bullet_red_128(":/icons/bullet_red_128.png"), bullet_grey_128(":/icons/bullet_grey_128.png"), - bullet_green_128(":/icons/bullet_green_128.png"), bullet_yellow_128(":/icons/bullet_yellow_128.png"), - bullet_blue_128(":/icons/bullet_blue_128.png") + : ChatDialog(parent, flags), lobbyId(lid), mWindowedSetted(false), mPCWindow(nullptr) + , bullet_red_128(":/icons/bullet_red_128.png"), bullet_grey_128(":/icons/bullet_grey_128.png") + , bullet_yellow_128(":/icons/bullet_yellow_128.png"), bullet_green_128(":/icons/bullet_green_128.png") + , bullet_blue_128(":/icons/bullet_blue_128.png") + , bullet_red_128_star( misc::mergeIcon(":/icons/bullet_red_128.png" ,":/icons/star_overlay_128.png")) + , bullet_grey_128_star( misc::mergeIcon(":/icons/bullet_grey_128.png" ,":/icons/star_overlay_128.png")) + , bullet_yellow_128_star(misc::mergeIcon(":/icons/bullet_yellow_128.png",":/icons/star_overlay_128.png")) + , bullet_green_128_star( misc::mergeIcon(":/icons/bullet_green_128.png" ,":/icons/star_overlay_128.png")) { /* Invoke Qt Designer generated QObject setup routine */ ui.setupUi(this); lastUpdateListTime = 0; - //connect(ui.actionChangeNickname, SIGNAL(triggered()), this, SLOT(changeNickname())); + //connect(ui.actionChangeNickname, SIGNAL(triggered()), this, SLOT(changeNickname())); connect(ui.participantsList, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(participantsTreeWidgetCustomPopupMenu(QPoint))); connect(ui.participantsList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(participantsTreeWidgetDoubleClicked(QTreeWidgetItem*,int))); @@ -91,6 +97,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi QHeaderView * header = ui.participantsList->header(); QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Stretch); + notifyNewArrivalAct = new QAction(QIcon(), tr("Notify (sound) when come back"), this); muteAct = new QAction(QIcon(), tr("Mute participant"), this); voteNegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-down.png"), tr("Ban this person (Sets negative opinion)"), this); voteNeutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/icons/png/thumbs-neutral.png"), tr("Give neutral opinion"), this); @@ -110,7 +117,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi actionSortByActivity->setChecked(false); actionSortByActivity->setActionGroup(sortgrp); - + connect(notifyNewArrivalAct, SIGNAL(triggered()), this, SLOT(changeNotifyForParticipant())); connect(muteAct, SIGNAL(triggered()), this, SLOT(changeParticipationState())); connect(distantChatAct, SIGNAL(triggered()), this, SLOT(distantChatParticipant())); connect(sendMessageAct, SIGNAL(triggered()), this, SLOT(sendMessage())); @@ -138,7 +145,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi undockButton->setAutoRaise(true); connect(undockButton, SIGNAL(clicked()), this , SLOT(toggleWindowed())); - getChatWidget()->addTitleBarWidget(undockButton) ; + ChatLobbyDialog::getChatWidget()->addTitleBarWidget(undockButton) ; // Add a button to invite friends. // @@ -161,7 +168,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi connect(inviteFriendsButton, SIGNAL(clicked()), this , SLOT(inviteFriends())); - getChatWidget()->addTitleBarWidget(inviteFriendsButton) ; + ChatLobbyDialog::getChatWidget()->addTitleBarWidget(inviteFriendsButton) ; RsGxsId current_id; rsMsgs->getIdentityForChatLobby(lobbyId, current_id); @@ -207,7 +214,7 @@ ChatLobbyDialog::ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent, Qt::Wi connect(unsubscribeButton, SIGNAL(clicked()), this , SLOT(leaveLobby())); - getChatWidget()->addTitleBarWidget(unsubscribeButton) ; + ChatLobbyDialog::getChatWidget()->addTitleBarWidget(unsubscribeButton) ; } void ChatLobbyDialog::leaveLobby() @@ -282,9 +289,12 @@ void ChatLobbyDialog::initParticipantsContextMenu(QMenu *contextMnu, QListsetToolTipsVisible(true); + contextMnu->addAction(distantChatAct); contextMnu->addAction(sendMessageAct); contextMnu->addSeparator(); + contextMnu->addAction(notifyNewArrivalAct); contextMnu->addAction(muteAct); contextMnu->addAction(votePositiveAct); contextMnu->addAction(voteNeutralAct); @@ -293,6 +303,10 @@ void ChatLobbyDialog::initParticipantsContextMenu(QMenu *contextMnu, QListsetEnabled(false); sendMessageAct->setEnabled(false); + notifyNewArrivalAct->setEnabled(false); + notifyNewArrivalAct->setCheckable(true); + notifyNewArrivalAct->setChecked(false); + notifyNewArrivalAct->setToolTip(""); muteAct->setEnabled(false); muteAct->setCheckable(true); muteAct->setChecked(false); @@ -303,6 +317,7 @@ void ChatLobbyDialog::initParticipantsContextMenu(QMenu *contextMnu, QListsetData(QVariant::fromValue(idList)); sendMessageAct->setData(QVariant::fromValue(idList)); + notifyNewArrivalAct->setData(QVariant::fromValue(idList)); muteAct->setData(QVariant::fromValue(idList)); votePositiveAct->setData(QVariant::fromValue(idList)); voteNeutralAct->setData(QVariant::fromValue(idList)); @@ -315,6 +330,17 @@ void ChatLobbyDialog::initParticipantsContextMenu(QMenu *contextMnu, QListsetEnabled(true); sendMessageAct->setEnabled(true); + if (SoundManager::eventEnabled(SOUND_LOBBY_INCOMING)) + { + notifyNewArrivalAct->setEnabled(true); + notifyNewArrivalAct->setToolTip(tr("Play a sound when this user come back.")); + } else { + notifyNewArrivalAct->setEnabled(false); + notifyNewArrivalAct->setToolTip(tr("'Specific User incoming in Chat Room' is unchecked in Sound Preferences page.")); + } + notifyNewArrivalAct->setChecked(isParticipantWaitingArrival(gxsid)); + muteAct->setEnabled(true); + muteAct->setChecked(isParticipantMuted(gxsid)); votePositiveAct->setEnabled( rsReputations->overallReputationLevel(gxsid) != RsReputationLevel::LOCALLY_POSITIVE ); @@ -326,8 +352,6 @@ void ChatLobbyDialog::initParticipantsContextMenu(QMenu *contextMnu, QListsetEnabled( rsReputations->overallReputationLevel(gxsid) != RsReputationLevel::LOCALLY_NEGATIVE ); - muteAct->setEnabled(true); - muteAct->setChecked(isParticipantMuted(gxsid)); } } @@ -411,7 +435,7 @@ void ChatLobbyDialog::init(const ChatId &/*id*/, const QString &/*title*/) if(rsIdentity->getIdDetails(gxs_id,details)) break ; else - rstime::rs_usleep(1000*300) ; + std::this_thread::sleep_for(std::chrono::milliseconds(300)) ; ui.chatWidget->setName(QString::fromUtf8(details.mNickname.c_str())); //ui.chatWidget->addToolsAction(ui.actionChangeNickname); @@ -532,6 +556,13 @@ void ChatLobbyDialog::addChatMsg(const ChatMessage& msg) QString message = QString::fromUtf8(msg.msg.c_str()); RsGxsId gxs_id = msg.lobby_peer_gxs_id ; + if (isParticipantWaitingArrival(gxs_id)) + { + // Who ask a notification for muted? We don't know... + SoundManager::play(SOUND_LOBBY_INCOMING); + idToNotifyWhenComeBack.erase(gxs_id); + } + if(!isParticipantMuted(gxs_id)) { // We could change addChatMsg to display the peers icon, passing a ChatId @@ -562,7 +593,7 @@ void ChatLobbyDialog::addChatMsg(const ChatMessage& msg) time_t now = time(NULL); - QList qlFoundParticipants=ui.participantsList->findItems(QString::fromStdString(gxs_id.toStdString()),Qt::MatchExactly,COLUMN_ID); + QList qlFoundParticipants=ui.participantsList->findItems(QString::fromStdString(gxs_id.toStdString()),Qt::MatchExactly,COLUMN_ID); if (qlFoundParticipants.count()!=0) qlFoundParticipants.at(0)->setText(COLUMN_ACTIVITY,QString::number(now)); if (now > lastUpdateListTime) { @@ -627,30 +658,32 @@ void ChatLobbyDialog::updateParticipantsList() time_t tLastAct=widgetitem->text(COLUMN_ACTIVITY).toInt(); time_t now = time(NULL); - widgetitem->setSizeHint(COLUMN_ICON, QSize(20,20)); + widgetitem->setSizeHint(COLUMN_ICON, QSize(20,20)); - if(isParticipantMuted(it2->first)) - widgetitem->setIcon(COLUMN_ICON, bullet_red_128); - else if (tLastAct + timeToInactivity < now) - widgetitem->setIcon(COLUMN_ICON, bullet_grey_128); - else if (tLastAct + timeToInactivity2 < now) - widgetitem->setIcon(COLUMN_ICON, bullet_yellow_128); - else - widgetitem->setIcon(COLUMN_ICON, bullet_green_128); - RsGxsId gxs_id; rsMsgs->getIdentityForChatLobby(lobbyId, gxs_id); + bool inNotif = isParticipantWaitingArrival(it2->first); - if (RsGxsId(participant.toStdString()) == gxs_id) widgetitem->setIcon(COLUMN_ICON, bullet_blue_128); + if (RsGxsId(participant.toStdString()) == gxs_id) + widgetitem->setIcon(COLUMN_ICON, bullet_blue_128); + else if(isParticipantMuted(it2->first)) + widgetitem->setIcon(COLUMN_ICON, inNotif ? bullet_red_128_star : bullet_red_128); + else if (tLastAct + timeToInactivity < now) + widgetitem->setIcon(COLUMN_ICON, inNotif ? bullet_grey_128_star : bullet_grey_128); + else if (tLastAct + timeToInactivity2 < now) + widgetitem->setIcon(COLUMN_ICON, inNotif ? bullet_yellow_128_star : bullet_yellow_128); + else + widgetitem->setIcon(COLUMN_ICON, inNotif ? bullet_green_128_star : bullet_green_128); - widgetitem->updateBannedState(); + widgetitem->updateBannedState(); QTime qtLastAct=QTime(0,0,0).addSecs(now-tLastAct); widgetitem->setToolTip(COLUMN_ICON,tr("Right click to mute/unmute participants
Double click to address this person
") +tr("This participant is not active since:") +qtLastAct.toString() +tr(" seconds") + + (inNotif ? tr("
A notification (sound) is waiting for this participant come back.") :"") ); } } @@ -659,6 +692,31 @@ void ChatLobbyDialog::updateParticipantsList() filterIds(); } +void ChatLobbyDialog::changeNotifyForParticipant() +{ + QAction *act = dynamic_cast(sender()) ; + if(!act) + { + RS_ERR("No sender! Some bug in the code."); + return ; + } + + QList idList = act->data().value>(); + + for (auto& item : idList) + { + if (act->isChecked()) { + if(!isParticipantWaitingArrival(item)) + idToNotifyWhenComeBack.insert(item); + } else { + if(isParticipantWaitingArrival(item)) + idToNotifyWhenComeBack.erase(item); + } + } + + updateParticipantsList(); +} + /** * Called when a Participant get Clicked / Changed * @@ -813,6 +871,16 @@ bool ChatLobbyDialog::isNicknameInLobby(const RsGxsId& nickname) return clinfo.gxs_ids.find(nickname) != clinfo.gxs_ids.end() ; } +/** + * @brief Is Lobby waiting this participant new arrival to notify. + * @param participant: GxsID to check. + * @return True if in the list. + */ +bool ChatLobbyDialog::isParticipantWaitingArrival(const RsGxsId &participant) +{ + return idToNotifyWhenComeBack.find(participant) != idToNotifyWhenComeBack.end(); +} + /** * Should Messages from this Nickname be muted? * @@ -882,7 +950,7 @@ void ChatLobbyDialog::displayLobbyEvent(int event_type, const RsGxsId& gxs_id, c ui.chatWidget->addChatMsg(true, tr("Chat room management"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), - tr("%1 changed his name to: %2").arg(RsHtml::plainText(name)).arg(RsHtml::plainText(newname)), + tr("%1 changed his name to: %2").arg(RsHtml::plainText(name), RsHtml::plainText(newname)), ChatWidget::MSGTYPE_SYSTEM); // TODO if a user was muted and changed his name, update mute list, but only, when the muted peer, dont change his name to a other peer in your chat lobby @@ -897,15 +965,22 @@ void ChatLobbyDialog::displayLobbyEvent(int event_type, const RsGxsId& gxs_id, c std::cerr << "ChatLobbyDialog::displayLobbyEvent() Unhandled chat room event type " << event_type << std::endl; } - if (!qsParticipant.isNull()) - { - QList qlFoundParticipants=ui.participantsList->findItems(QString::fromStdString(qsParticipant.toStdString()),Qt::MatchExactly,COLUMN_ID); + if (!qsParticipant.isNull()) + { + QList qlFoundParticipants=ui.participantsList->findItems(QString::fromStdString(qsParticipant.toStdString()),Qt::MatchExactly,COLUMN_ID); - if (qlFoundParticipants.count()!=0) - qlFoundParticipants.at(0)->setText(COLUMN_ACTIVITY,QString::number(time(NULL))); - } + if (qlFoundParticipants.count()!=0) + qlFoundParticipants.at(0)->setText(COLUMN_ACTIVITY,QString::number(time(NULL))); - updateParticipantsList() ; + if( (event_type != RS_CHAT_LOBBY_EVENT_PEER_LEFT) + && isParticipantWaitingArrival(gxs_id)) + { + SoundManager::play(SOUND_LOBBY_INCOMING); + idToNotifyWhenComeBack.erase(gxs_id); + } + } + + updateParticipantsList() ; } bool ChatLobbyDialog::canClose() diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h index 065abacee..f0afb5adb 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.h +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.h @@ -47,8 +47,9 @@ public: virtual ChatWidget *getChatWidget(); virtual bool hasPeerStatus() { return false; } virtual bool notifyBlink(); - void setIdentity(const RsGxsId& gxs_id); - bool isParticipantMuted(const RsGxsId &participant); + void setIdentity(const RsGxsId& gxs_id); + bool isParticipantWaitingArrival(const RsGxsId &participant); + bool isParticipantMuted(const RsGxsId &participant); ChatLobbyId id() const { return lobbyId ;} void sortParcipants(); @@ -73,7 +74,7 @@ signals: protected: /** Default constructor */ - ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent = 0, Qt::WindowFlags flags = 0); + ChatLobbyDialog(const ChatLobbyId& lid, QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); /** Default destructor */ virtual ~ChatLobbyDialog(); @@ -85,6 +86,7 @@ protected: protected slots: void changeNickname(); + void changeNotifyForParticipant(); void changeParticipationState(); void distantChatParticipant(); void participantsTreeWidgetDoubleClicked(QTreeWidgetItem *item, int column); @@ -119,22 +121,30 @@ private: Ui::ChatLobbyDialog ui; /** Ignored Users in Chatlobby by nickname until we had implemented Peer Ids in ver 0.6 */ - std::set mutedParticipants; + std::set mutedParticipants; - QAction *muteAct; - QAction *votePositiveAct; - QAction *voteNeutralAct; - QAction *voteNegativeAct; - QAction *distantChatAct; - QAction *actionSortByName; - QAction *actionSortByActivity; - QWidgetAction *checkableAction; - QAction *sendMessageAct; - QAction *showInPeopleAct; + /** + * @brief List of GxsId to notify when they arrive. + * This don't use ChatLobbyUserNotify because no need to save it as GxsId can go away before read notification. + */ + std::set idToNotifyWhenComeBack; - GxsIdChooser *ownIdChooser ; - //icons cache - QIcon bullet_red_128, bullet_grey_128, bullet_green_128, bullet_yellow_128, bullet_blue_128; + QAction *notifyNewArrivalAct; + QAction *muteAct; + QAction *votePositiveAct; + QAction *voteNeutralAct; + QAction *voteNegativeAct; + QAction *distantChatAct; + QAction *actionSortByName; + QAction *actionSortByActivity; + QWidgetAction *checkableAction; + QAction *sendMessageAct; + QAction *showInPeopleAct; + + GxsIdChooser *ownIdChooser ; + //icons cache + QIcon bullet_red_128, bullet_grey_128, bullet_yellow_128, bullet_green_128, bullet_blue_128; + QIcon bullet_red_128_star, bullet_grey_128_star, bullet_yellow_128_star, bullet_green_128_star; }; #endif diff --git a/retroshare-gui/src/util/misc.cpp b/retroshare-gui/src/util/misc.cpp index 84c408de3..fd279fc07 100644 --- a/retroshare-gui/src/util/misc.cpp +++ b/retroshare-gui/src/util/misc.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -411,10 +412,6 @@ QString misc::getExistingDirectory(QWidget *parent, const QString &caption, cons #endif } -/*! - * Clear a Layout content - * \param layout: Layout to Clear - */ void misc::clearLayout(QLayout * layout) { if (! layout) return; @@ -444,3 +441,15 @@ QSizeF misc::getFontSizeFactor(const QString &group, const qreal defaultFactor / return QSizeF(appFontWidth*factor,appFontHeight*factor); } +QIcon misc::mergeIcon(QString iconB, QString iconF) +{ + QIcon icon; + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(iconB); + QPixmap oli = FilesDefs::getPixmapFromQtResourcePath(iconF); + + QPainter painter(&pix); + painter.drawPixmap(0, 0, oli); + + icon.addPixmap(pix); + return icon; +} diff --git a/retroshare-gui/src/util/misc.h b/retroshare-gui/src/util/misc.h index 2863a867c..a7e072af6 100644 --- a/retroshare-gui/src/util/misc.h +++ b/retroshare-gui/src/util/misc.h @@ -25,6 +25,7 @@ #include "gui/settings/rsharesettings.h" #include +#include #include #include #include @@ -185,12 +186,29 @@ class misc : public QObject , const QString &caption = QString() , const QString &dir = QString()); - //Clear QLayout + /*! + * Clear a Layout content + * \param layout: Layout to Clear + */ static void clearLayout(QLayout *layout); + /** + * @brief Get Size depending of application Font Metrics + * @param group: Group name for factor from setting file + * @param defaultFactor: Default factor to use if none is defined + * @return Size factorized font size + */ static QSizeF getFontSizeFactor(const QString &group, const qreal defaultFactor = 1.0); static QSizeF getFontSizeFactor() {return getFontSizeFactor("Main");} + /** + * @brief Merge two icons in one + * @param iconB: Background icon file + * @param iconF: Foreground icon file + * @return Merged icon + */ + static QIcon mergeIcon(QString iconB, QString iconF); + }; // Trick to get a portable sleep() function