/*************************************************************************** * Copyright (c) 2023 Abdullah Tahiri * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #ifndef GUI_NOTIFICATIONS_H #define GUI_NOTIFICATIONS_H #include #include #include #include #include #include namespace Gui { class ViewProviderDocumentObject; /** Methods to seamlessly provide intrusive or non-intrusive notifications of error, warning, * messages, translated notifications, or untranslated notifications originating in a given * document object. * * They are intended for easy substitution of currently blocking modal dialogs in which the user * may only click 'ok'. * * It produces a blocking modal notification or a non-intrusive non-modal notification depending on * the preference parameter NotificationArea/NonIntrusiveNotificationsEnabled. * * The notifier field can be a string or an App::DocumentObject * object, then the notifier is taken * from the getFullLabel() method of the DocumentObject. * * Translations: * * An attempt is made by NotificationArea to translate the message using the "Notifications" * context, except for messages which are already translated. Those that are untranslatable can only * be send as messages intended for a developer directly using Console.. * * For the former, this may be marked using QT_TRANSLATE_NOOP("Notifications","My message") * * For translated Notification, many modules using blocking notifications have their translations * stored in other contexts, and the translations available at the callee function. This kind of * notification provides a very low entry point to move existing blocking notifications into * non-intrusive respecting the user choice (given by * NotificationArea/NonIntrusiveNotificationsEnabled). * * Example 1: * * QMessageBox::warning(Gui::getMainWindow(), QObject::tr("Wrong selection"), * QObject::tr("Cannot add a constraint between two external geometries.")); * * Can be rewritten as: * * Gui::TranslatedNotification(obj, * QObject::tr("Wrong selection"), * QObject::tr("Cannot add a constraint between two external geometries.")); * * or * * Gui::TranslatedUserWarning(obj, * QObject::tr("Wrong selection"), * QObject::tr("Cannot add a constraint between two external geometries.")); * * or * * Gui::TranslatedUserError(obj, * QObject::tr("Wrong selection"), * QObject::tr("Cannot add a constraint between two external geometries.")); * * * depending on the severity. * * Here obj is a DocumentObject and serves to set the Notifier field for the notification. * If the user preference is to see non-intrusive notifications, no pop-up will be shown and the * notification will be shown in the notifications area. If the user preference is to see intrusive * notifications, the pop-up will be shown (and it won't appear on the notification area as as a * non- intrusive notification). */ /// generic function to send any message provided by Base::LogStyle template< Base::LogStyle, Base::IntendedRecipient = Base::IntendedRecipient::User, Base::ContentType = Base::ContentType::Translated, typename TNotifier, typename TCaption, typename TMessage> inline void Notify(TNotifier&& notifier, TCaption&& caption, TMessage&& message); /** Convenience function to notify warnings * The NotificationArea will attempt to find a translation in the "Notifications" context. * This may be marked using QT_TRANSLATE_NOOP("Notifications","My message") */ template inline void NotifyWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message); template inline void NotifyUserWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message); template inline void TranslatedUserWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message); /** Convenience function to notify errors * The NotificationArea will attempt to find a translation in the "Notifications" context. * This may be marked using QT_TRANSLATE_NOOP("Notifications","My message") */ template inline void NotifyError(TNotifier&& notifier, TCaption&& caption, TMessage&& message); template inline void NotifyUserError(TNotifier&& notifier, TCaption&& caption, TMessage&& message); template inline void TranslatedUserError(TNotifier&& notifier, TCaption&& caption, TMessage&& message); /** Convenience function to send already translated user notifications. * No attempt will be made by the NotificationArea to translate them. */ template inline void TranslatedNotification(TNotifier&& notifier, TCaption&& caption, TMessage&& message); /** Convenience function to send untranslated user notifications. * The NotificationArea will attempt to find a translation in the "Notifications" context. * This may be marked using QT_TRANSLATE_NOOP("Notifications","My message") */ template inline void Notification(TNotifier&& notifier, TCaption&& caption, TMessage&& message); } // namespace Gui template< Base::LogStyle type, Base::IntendedRecipient recipient, Base::ContentType content, typename TNotifier, typename TCaption, typename TMessage> inline void Gui::Notify(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { static_assert( content != Base::ContentType::Untranslatable, "A Gui notification cannot be provided with an untranslatable message." ); static_assert( recipient != Base::IntendedRecipient::Developer, "A Gui notification cannot be intended only for the developer, use Console instead." ); static_assert( !(recipient == Base::IntendedRecipient::All && content == Base::ContentType::Translated), "Information intended for Developers must not be translated. Provide an untranslated " "message instead." ); Base::Reference hGrp = App::GetApplication() .GetUserParameter() .GetGroup("BaseApp") ->GetGroup("Preferences") ->GetGroup("NotificationArea"); bool nonIntrusive = hGrp->GetBool("NonIntrusiveNotificationsEnabled", true); if (!nonIntrusive) { // If Developers are also an intended recipient, notify before creating the blocking pop-up if constexpr (recipient == Base::IntendedRecipient::All) { // Send also to log for developer only auto msg = std::string(message).append("\n"); // use untranslated message if constexpr (std::is_base_of_v< App::DocumentObject, std::remove_pointer_t::type>>) { Base::Console() .send( notifier->getFullLabel(), msg.c_str() ); } else if constexpr (std::is_base_of_v< Gui::ViewProviderDocumentObject, std::remove_pointer_t::type>>) { Base::Console() .send( notifier->getObject()->getFullLabel(), msg.c_str() ); } else if constexpr (std::is_base_of_v< Gui::Document, std::remove_pointer_t::type>>) { Base::Console() .send( notifier->getDocument()->Label.getStrValue(), msg.c_str() ); } else if constexpr (std::is_base_of_v< App::Document, std::remove_pointer_t::type>>) { Base::Console() .send( notifier->Label.getStrValue(), msg.c_str() ); } else { Base::Console() .send( notifier, msg.c_str() ); } } if constexpr (content == Base::ContentType::Untranslated) { if constexpr (type == Base::LogStyle::Warning) { QMessageBox::warning( Gui::getMainWindow(), QCoreApplication::translate("Notifications", caption), QCoreApplication::translate("Notifications", message) ); } else if constexpr (type == Base::LogStyle::Error) { QMessageBox::critical( Gui::getMainWindow(), QCoreApplication::translate("Notifications", caption), QCoreApplication::translate("Notifications", message) ); } else { QMessageBox::information( Gui::getMainWindow(), QCoreApplication::translate("Notifications", caption), QCoreApplication::translate("Notifications", message) ); } } else { if constexpr (type == Base::LogStyle::Warning) { QMessageBox::warning(Gui::getMainWindow(), caption, message); } else if constexpr (type == Base::LogStyle::Error) { QMessageBox::critical(Gui::getMainWindow(), caption, message); } else { QMessageBox::information(Gui::getMainWindow(), caption, message); } } } else { if constexpr (content == Base::ContentType::Translated) { // trailing newline is not necessary as translated messages are not shown in logs auto msg = QStringLiteral("%1. %2").arg(caption).arg(message); // QString if constexpr (std::is_base_of_v< App::DocumentObject, std::remove_pointer_t::type>>) { Base::Console().send(notifier->getFullLabel(), msg.toUtf8()); } else if constexpr (std::is_base_of_v< Gui::ViewProviderDocumentObject, std::remove_pointer_t::type>>) { Base::Console().send( notifier->getObject()->getFullLabel(), msg.toUtf8() ); } else if constexpr (std::is_base_of_v< Gui::Document, std::remove_pointer_t::type>>) { Base::Console().send( notifier->getDocument()->Label.getStrValue(), msg.toUtf8() ); } else if constexpr (std::is_base_of_v< App::Document, std::remove_pointer_t::type>>) { Base::Console().send( notifier->Label.getStrValue(), msg.toUtf8() ); } else { Base::Console().send(notifier, msg.toUtf8()); } } else { // trailing newline is necessary as this may be shown too in a console requiring them // (depending on the configuration). auto msg = std::string(message).append("\n"); if constexpr (std::is_base_of_v< App::DocumentObject, std::remove_pointer_t::type>>) { Base::Console().send(notifier->getFullLabel(), msg.c_str()); } else if constexpr (std::is_base_of_v< Gui::ViewProviderDocumentObject, std::remove_pointer_t::type>>) { Base::Console().send( notifier->getObject()->getFullLabel(), msg.c_str() ); } else if constexpr (std::is_base_of_v< Gui::Document, std::remove_pointer_t::type>>) { Base::Console().send( notifier->getDocument()->Label.getStrValue(), msg.c_str() ); } else if constexpr (std::is_base_of_v< App::Document, std::remove_pointer_t::type>>) { Base::Console().send( notifier->Label.getStrValue(), msg.c_str() ); } else { Base::Console().send(notifier, msg.c_str()); } } } } template inline void Gui::NotifyWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::NotifyUserWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::TranslatedUserWarning(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::NotifyError(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::NotifyUserError(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::TranslatedUserError(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::TranslatedNotification(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } template inline void Gui::Notification(TNotifier&& notifier, TCaption&& caption, TMessage&& message) { Notify( std::forward(notifier), std::forward(caption), std::forward(message) ); } #endif // GUI_NOTIFICATIONS_H