diff --git a/src/App/FreeCADInit.py b/src/App/FreeCADInit.py index 5479b6f11c..e00275344a 100644 --- a/src/App/FreeCADInit.py +++ b/src/App/FreeCADInit.py @@ -302,6 +302,9 @@ Log = FreeCAD.Console.PrintLog Msg = FreeCAD.Console.PrintMessage Err = FreeCAD.Console.PrintError Wrn = FreeCAD.Console.PrintWarning +Crt = FreeCAD.Console.PrintCritical +Ntf = FreeCAD.Console.PrintNotification +Tnf = FreeCAD.Console.PrintTranslatedNotification test_ascii = lambda s: all(ord(c) < 128 for c in s) #store the cmake variales diff --git a/src/Base/Builder3D.cpp b/src/Base/Builder3D.cpp index e84dfeb9c0..188a7a22de 100644 --- a/src/Base/Builder3D.cpp +++ b/src/Base/Builder3D.cpp @@ -987,7 +987,7 @@ void Builder3D::saveToLog() { ILogger* obs = Base::Console().Get("StatusBar"); if (obs){ - obs->SendLog(result.str().c_str(), Base::LogStyle::Log); + obs->SendLog("Builder3D",result.str().c_str(), Base::LogStyle::Log); } } diff --git a/src/Base/CMakeLists.txt b/src/Base/CMakeLists.txt index e9465dcbc5..38609caf05 100644 --- a/src/Base/CMakeLists.txt +++ b/src/Base/CMakeLists.txt @@ -61,6 +61,7 @@ include_directories( ) list(APPEND FreeCADBase_LIBS ${QtCore_LIBRARIES}) +list(APPEND FreeCADBase_LIBS fmt::fmt) if (BUILD_DYNAMIC_LINK_PYTHON) list(APPEND FreeCADBase_LIBS ${PYTHON_LIBRARIES}) diff --git a/src/Base/Console.cpp b/src/Base/Console.cpp index 2185877e99..c9c0c58321 100644 --- a/src/Base/Console.cpp +++ b/src/Base/Console.cpp @@ -49,10 +49,11 @@ namespace Base { class ConsoleEvent : public QEvent { public: ConsoleSingleton::FreeCAD_ConsoleMsgType msgtype; + std::string notifier; std::string msg; - ConsoleEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& msg) - : QEvent(QEvent::User), msgtype(type), msg(msg) + ConsoleEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifier, const std::string& msg) + : QEvent(QEvent::User), msgtype(type), notifier(notifier),msg(msg) { } ~ConsoleEvent() override = default; @@ -75,18 +76,27 @@ public: if (ev->type() == QEvent::User) { ConsoleEvent* ce = static_cast(ev); switch (ce->msgtype) { - case ConsoleSingleton::MsgType_Txt: - Console().NotifyMessage(ce->msg.c_str()); - break; - case ConsoleSingleton::MsgType_Log: - Console().NotifyLog(ce->msg.c_str()); - break; - case ConsoleSingleton::MsgType_Wrn: - Console().NotifyWarning(ce->msg.c_str()); - break; - case ConsoleSingleton::MsgType_Err: - Console().NotifyError(ce->msg.c_str()); - break; + case ConsoleSingleton::MsgType_Txt: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_Log: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_Wrn: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_Err: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_Critical: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_Notification: + Console().Notify(ce->notifier, ce->msg); + break; + case ConsoleSingleton::MsgType_TranslatedNotification: + Console().Notify(ce->notifier, ce->msg); + break; } } } @@ -189,6 +199,22 @@ ConsoleMsgFlags ConsoleSingleton::SetEnabledMsgType(const char* sObs, ConsoleMsg flags |= MsgType_Log; pObs->bLog = b; } + if ( type&MsgType_Critical ){ + if ( pObs->bCritical != b ) + flags |= MsgType_Critical; + pObs->bCritical = b; + } + if ( type&MsgType_Notification ){ + if ( pObs->bNotification != b ) + flags |= MsgType_Notification; + pObs->bNotification = b; + } + if ( type&MsgType_TranslatedNotification ){ + if ( pObs->bTranslatedNotification != b ) + flags |= MsgType_TranslatedNotification; + pObs->bTranslatedNotification = b; + } + return flags; } else { @@ -209,6 +235,12 @@ bool ConsoleSingleton::IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType return pObs->bWrn; case MsgType_Err: return pObs->bErr; + case MsgType_Critical: + return pObs->bCritical; + case MsgType_Notification: + return pObs->bNotification; + case MsgType_TranslatedNotification: + return pObs->bTranslatedNotification; default: return false; } @@ -227,109 +259,6 @@ void ConsoleSingleton::SetConnectionMode(ConnectionMode mode) } } -/** Prints a Message - * This method issues a Message. - * Messages are used to show some non vital information. That means when - * FreeCAD is running in GUI mode a Message appears on the status bar. - * In console mode a message is printed to the console. - * \par - * You can use a printf like interface like: - * \code - * Console().Message("Doing something important %d times\n",i); - * \endcode - * @see Warning - * @see Error - * @see Log - */ -void ConsoleSingleton::Message( const char *pMsg, ... ) -{ -#define FC_CONSOLE_FMT(_type,_type2) \ - char format[BufferSize];\ - format[sizeof(format)-4] = '.';\ - format[sizeof(format)-3] = '.';\ - format[sizeof(format)-2] = '\n';\ - format[sizeof(format)-1] = 0;\ - const unsigned int format_len = sizeof(format)-4;\ - va_list namelessVars;\ - va_start(namelessVars, pMsg);\ - vsnprintf(format, format_len, pMsg, namelessVars);\ - format[sizeof(format)-5] = '.';\ - va_end(namelessVars);\ - if (connectionMode == Direct)\ - Notify##_type(format);\ - else\ - QCoreApplication::postEvent(ConsoleOutput::getInstance(), new ConsoleEvent(MsgType_##_type2, format)); - - FC_CONSOLE_FMT(Message,Txt); -} - -/** Prints a Message - * This method issues a Warning. - * Messages are used to get the users attention. That means when - * FreeCAD is in GUI mode a Message Box pops up. In console - * mode a colored message is returned to the console! Don't use this carelessly. - * For information purposes the 'Log' or 'Message' methods are more appropriate. - * \par - * You can use a printf like interface like: - * \code - * Console().Warning("Some defects in %s, loading anyway\n",str); - * \endcode - * @see Message - * @see Error - * @see Log - */ -void ConsoleSingleton::Warning( const char *pMsg, ... ) -{ - FC_CONSOLE_FMT(Warning,Wrn); -} - -/** Prints a Message - * This method issues an Error which makes some execution impossible. - * Errors are used to get the users attention. That means when FreeCAD - * is running in GUI mode an Error Message Box pops up. In console - * mode a colored message is printed to the console! Don't use this carelessly. - * For information purposes the 'Log' or 'Message' methods are more appropriate. - * \par - * You can use a printf like interface like: - * \code - * Console().Error("Something really bad in %s happened\n",str); - * \endcode - * @see Message - * @see Warning - * @see Log - */ -void ConsoleSingleton::Error( const char *pMsg, ... ) -{ - FC_CONSOLE_FMT(Error,Err); -} - - -/** Prints a Message - * This method is appropriate for development and tracking purposes. - * It can be used to track execution of algorithms and functions. - * The normal user doesn't need to see it, it's more for developers - * and experienced users. So in normal user mode the logging is switched off. - * \par - * You can use a printf-like interface for example: - * \code - * Console().Log("Execute part %d in algorithm %s\n",i,str); - * \endcode - * @see Message - * @see Warning - * @see Error - */ - - -void ConsoleSingleton::Log( const char *pMsg, ... ) -{ - if (_bVerbose) - { - FC_CONSOLE_FMT(Log,Log); - } -} - - - //************************************************************************** // Observer stuff @@ -357,36 +286,18 @@ void ConsoleSingleton::DetachObserver(ILogger *pcObserver) _aclObservers.erase(pcObserver); } -void ConsoleSingleton::NotifyMessage(const char *sMsg) +void Base::ConsoleSingleton::notifyPrivate(LogStyle category, const std::string& notifiername, const std::string& msg) { for (std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) { - if ((*Iter)->bMsg) - (*Iter)->SendLog(sMsg, LogStyle::Message); // send string to the listener + if ((*Iter)->isActive(category)) { + (*Iter)->SendLog(notifiername, msg, category); // send string to the listener + } } } -void ConsoleSingleton::NotifyWarning(const char *sMsg) +void ConsoleSingleton::postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifiername, const std::string& msg) { - for (std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) { - if ((*Iter)->bWrn) - (*Iter)->SendLog(sMsg, LogStyle::Warning); // send string to the listener - } -} - -void ConsoleSingleton::NotifyError(const char *sMsg) -{ - for (std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) { - if ((*Iter)->bErr) - (*Iter)->SendLog(sMsg, LogStyle::Error); // send string to the listener - } -} - -void ConsoleSingleton::NotifyLog(const char *sMsg) -{ - for (std::set::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) { - if ((*Iter)->bLog) - (*Iter)->SendLog(sMsg, LogStyle::Log); // send string to the listener - } + QCoreApplication::postEvent(ConsoleOutput::getInstance(), new ConsoleEvent(type, notifiername, msg)); } ILogger *ConsoleSingleton::Get(const char *Name) const @@ -464,6 +375,18 @@ PyMethodDef ConsoleSingleton::Methods[] = { "PrintWarning(obj) -> None\n\n" "Print a warning message to the output.\n\n" "obj : object\n The string representation is printed."}, + {"PrintCritical",ConsoleSingleton::sPyCritical, METH_VARARGS, + "PrintCritical(obj) -> None\n\n" + "Print a critical message to the output.\n\n" + "obj : object\n The string representation is printed."}, + {"PrintNotification", ConsoleSingleton::sPyNotification, METH_VARARGS, + "PrintNotification(obj) -> None\n\n" + "Print a user notification to the output.\n\n" + "obj : object\n The string representation is printed."}, + {"PrintTranslatedNotification", ConsoleSingleton::sPyTranslatedNotification, METH_VARARGS, + "PrintTranslatedNotification(obj) -> None\n\n" + "Print an already translated notification to the output.\n\n" + "obj : object\n The string representation is printed."}, {"SetStatus", ConsoleSingleton::sPySetStatus, METH_VARARGS, "SetStatus(observer, type, status) -> None\n\n" "Set the status for either 'Log', 'Msg', 'Wrn' or 'Error' for an observer.\n\n" @@ -483,25 +406,53 @@ PyMethodDef ConsoleSingleton::Methods[] = { }; namespace { -PyObject* FC_PYCONSOLE_MSG(std::function func, PyObject* args) +PyObject* FC_PYCONSOLE_MSG(std::function func, PyObject* args) { PyObject *output; - if (!PyArg_ParseTuple(args, "O", &output)) - return nullptr; - PY_TRY { - const char* string = nullptr; + PyObject *notifier; + + const char* notifierStr = ""; + + auto retrieveString = [] (PyObject* pystr) { PyObject* unicode = nullptr; - if (PyUnicode_Check(output)) { - string = PyUnicode_AsUTF8(output); + + const char* outstr = nullptr; + + if (PyUnicode_Check(pystr)) { + outstr = PyUnicode_AsUTF8(pystr); } else { - unicode = PyObject_Str(output); + unicode = PyObject_Str(pystr); if (unicode) - string = PyUnicode_AsUTF8(unicode); + outstr = PyUnicode_AsUTF8(unicode); } - if (string) - func(string); /*process message*/ + Py_XDECREF(unicode); + + return outstr; + }; + + + if (!PyArg_ParseTuple(args, "OO", ¬ifier, &output)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "O", &output)) { + return nullptr; + } + + } + else { // retrieve notifier + PY_TRY { + notifierStr = retrieveString(notifier); + } + PY_CATCH + } + + PY_TRY { + const char* string = retrieveString(output); + + if (string) + func(notifierStr, string); /*process message*/ + } PY_CATCH Py_Return; @@ -510,29 +461,50 @@ PyObject* FC_PYCONSOLE_MSG(std::function func, PyObject* args PyObject *ConsoleSingleton::sPyMessage(PyObject * /*self*/, PyObject *args) { - return FC_PYCONSOLE_MSG([](const char* msg) { - Instance().Message("%s", msg); + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().Message(notifier, "%s", msg); }, args); } PyObject *ConsoleSingleton::sPyWarning(PyObject * /*self*/, PyObject *args) { - return FC_PYCONSOLE_MSG([](const char* msg) { - Instance().Warning("%s", msg); + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().Warning(notifier, "%s", msg); }, args); } PyObject *ConsoleSingleton::sPyError(PyObject * /*self*/, PyObject *args) { - return FC_PYCONSOLE_MSG([](const char* msg) { - Instance().Error("%s", msg); + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().Error(notifier, "%s", msg); }, args); } PyObject *ConsoleSingleton::sPyLog(PyObject * /*self*/, PyObject *args) { - return FC_PYCONSOLE_MSG([](const char* msg) { - Instance().Log("%s", msg); + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().Log(notifier, "%s", msg); + }, args); +} + +PyObject *ConsoleSingleton::sPyCritical(PyObject * /*self*/, PyObject *args) +{ + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().Critical(notifier, "%s", msg); + }, args); +} + +PyObject *ConsoleSingleton::sPyNotification(PyObject * /*self*/, PyObject *args) +{ + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().UserNotification(notifier, "%s", msg); + }, args); +} + +PyObject *ConsoleSingleton::sPyTranslatedNotification(PyObject * /*self*/, PyObject *args) +{ + return FC_PYCONSOLE_MSG([](const std::string & notifier, const char* msg) { + Instance().UserTranslatedNotification(notifier, "%s", msg); }, args); } @@ -557,8 +529,14 @@ PyObject *ConsoleSingleton::sPyGetStatus(PyObject * /*self*/, PyObject *args) b = pObs->bMsg; else if (strcmp(pstr2,"Err") == 0) b = pObs->bErr; + else if (strcmp(pstr2,"Critical") == 0) + b = pObs->bCritical; + else if (strcmp(pstr2,"Notification") == 0) + b = pObs->bNotification; + else if (strcmp(pstr2,"TranslatedNotification") == 0) + b = pObs->bTranslatedNotification; else - Py_Error(Base::PyExc_FC_GeneralError,"Unknown message type (use 'Log', 'Err', 'Msg' or 'Wrn')"); + Py_Error(Base::PyExc_FC_GeneralError,"Unknown message type (use 'Log', 'Err', 'Wrn', 'Msg', 'Critical', 'Notification' or 'TranslatedNotification')"); return PyBool_FromLong(b ? 1 : 0); } @@ -585,8 +563,14 @@ PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args) pObs->bMsg = status; else if (strcmp(pstr2,"Err") == 0) pObs->bErr = status; + else if (strcmp(pstr2,"Critical") == 0) + pObs->bCritical = status; + else if (strcmp(pstr2,"Notification") == 0) + pObs->bNotification = status; + else if (strcmp(pstr2,"TranslatedNotification") == 0) + pObs->bTranslatedNotification = status; else - Py_Error(Base::PyExc_FC_GeneralError,"Unknown message type (use 'Log', 'Err', 'Msg' or 'Wrn')"); + Py_Error(Base::PyExc_FC_GeneralError,"Unknown message type (use 'Log', 'Err', 'Wrn', 'Msg', 'Critical', 'Notification' or 'TranslatedNotification')"); Py_Return; } diff --git a/src/Base/Console.h b/src/Base/Console.h index f87083dd41..55b20ba5be 100644 --- a/src/Base/Console.h +++ b/src/Base/Console.h @@ -25,6 +25,7 @@ #define BASE_CONSOLE_H // Std. configurations +#include #include #include #include @@ -32,11 +33,13 @@ #include #include +#include + // Python stuff using PyObject = struct _object; using PyMethodDef = struct PyMethodDef; -//FIXME: ISO C++11 requires at least one argument for the "..." in a variadic macro +//FIXME: Even with parameter packs this is necessary for MSYS2 #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" @@ -361,24 +364,24 @@ using PyMethodDef = struct PyMethodDef; _instance.prefix(_str,_file,_line) << _msg;\ if(_instance.add_eol) \ _str<,_msg) +#define FC_WARN(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_WARN,Notify,_msg) +#define FC_ERR(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_ERR,Notify,_msg) +#define FC_LOG(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_LOG,Notify,_msg) +#define FC_TRACE(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_TRACE,Notify,_msg) -#define _FC_MSG(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_MSG,NotifyMessage,_msg,_file,_line) -#define _FC_WARN(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_WARN,NotifyWarning,_msg,_file,_line) -#define _FC_ERR(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_ERR,NotifyError,_msg,_file,_line) -#define _FC_LOG(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_LOG,NotifyLog,_msg,_file,_line) -#define _FC_TRACE(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_TRACE,NotifyLog,_msg,_file,_line) +#define _FC_MSG(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_MSG,Notify,_msg,_file,_line) +#define _FC_WARN(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_WARN,Notify,_msg,_file,_line) +#define _FC_ERR(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_ERR,Notify,_msg,_file,_line) +#define _FC_LOG(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_LOG,Notify,_msg,_file,_line) +#define _FC_TRACE(_file,_line,_msg) __FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_TRACE,Notify,_msg,_file,_line) #define FC_XYZ(_pt) '('<<(_pt).X()<<", " << (_pt).Y()<<", " << (_pt).Z()<<')' #define FC_xy(_pt) '('<<(_pt).x<<", " << (_pt).y<<')' @@ -445,11 +448,6 @@ using PyMethodDef = struct PyMethodDef; #endif //FC_LOG_NO_TIMING -//TODO: Get rid of this forward-declaration -namespace Base { - class ConsoleSingleton; -} // namespace Base - //TODO: Get rid of this typedef using ConsoleMsgFlags = unsigned int; @@ -470,7 +468,10 @@ enum class LogStyle{ Warning, Message, Error, - Log + Log, + Critical, // Special message to mark critical notifications + Notification, // Special message for notifications to the user (e.g. educational) + TranslatedNotification, // Special message for already translated notifications to the user (e.g. educational) }; /** The Logger Interface @@ -484,23 +485,57 @@ class BaseExport ILogger { public: ILogger() - :bErr(true),bMsg(true),bLog(true),bWrn(true){} + :bErr(true), bMsg(true), bLog(true), bWrn(true), bCritical(true), bNotification(false), bTranslatedNotification(false){} virtual ~ILogger() = 0; /** Used to send a Log message at the given level. */ - virtual void SendLog(const std::string& msg, LogStyle level) = 0; + virtual void SendLog(const std::string& notifiername, const std::string& msg, LogStyle level) = 0; + + /** + * Returns whether a LogStyle category is active or not + */ + bool isActive(Base::LogStyle category) { + if(category == Base::LogStyle::Log) { + return bLog; + } + else + if(category == Base::LogStyle::Warning) { + return bWrn; + } + else + if(category == Base::LogStyle::Error) { + return bErr; + } + else + if(category == Base::LogStyle::Message) { + return bMsg; + } + else + if(category == Base::LogStyle::Critical) { + return bCritical; + } + else + if(category == Base::LogStyle::Notification) { + return bNotification; + } + else + if(category == Base::LogStyle::TranslatedNotification) { + return bTranslatedNotification; + } + return false; + } virtual const char *Name(){return nullptr;} - bool bErr,bMsg,bLog,bWrn; + bool bErr, bMsg, bLog, bWrn, bCritical, bNotification, bTranslatedNotification; }; /** The console class * This class manage all the stdio stuff. This includes - * Messages, Warnings, Log entries and Errors. The incoming - * Messages are distributed with the FCConsoleObserver. The - * FCConsole class itself makes no IO, it's more like a manager. + * Messages, Warnings, Log entries, Errors, Criticals, Notifications and + * TranslatedNotifications. The incoming Messages are distributed with the + * FCConsoleObserver. The FCConsole class itself makes no IO, it's more like a manager. * \par * ConsoleSingleton is a singleton! That means you can access the only * instance of the class from every where in c++ by simply using: @@ -515,30 +550,73 @@ public: */ class BaseExport ConsoleSingleton { - public: - static const unsigned int BufferSize = 4024; // exported functions goes here +++++++++++++++++++++++++++++++++++++++ - /// Prints a Message - virtual void Message ( const char * pMsg, ... ); - /// Prints a warning Message - virtual void Warning ( const char * pMsg, ... ); - /// Prints a error Message - virtual void Error ( const char * pMsg, ... ); - /// Prints a log Message - virtual void Log ( const char * pMsg, ... ); - // observer processing - void NotifyMessage(const char *sMsg); - void NotifyWarning(const char *sMsg); - void NotifyError (const char *sMsg); - void NotifyLog (const char *sMsg); + /** Sends a message of type LogStyle (Message, Warning, Error, Log, Critical, Notification or TranslatedNotification). + This function is used by all specific convenience functions (Send(), Message(), Warning(), Error(), Log(), Critical + UserNotification and UserTranslatedNotification, without or without notifier id). + + Notification can be direct or via queue. + */ + template + inline void Send( const std::string & notifiername, const char * pMsg, Args&&... args ); + + /// Prints a Message + template + inline void Message (const char * pMsg, Args&&... args); + /// Prints a warning Message + template + inline void Warning (const char * pMsg, Args&&... args); + /// Prints a error Message + template + inline void Error (const char * pMsg, Args&&... args); + /// Prints a log Message + template + inline void Log (const char * pMsg, Args&&... args); + /// Prints a Critical Message + template + inline void Critical (const char * pMsg, Args&&... args); + /// Sends a User Notification + template + inline void UserNotification( const char * pMsg, Args&&... args ); + /// Sends an already translated User Notification + template + inline void UserTranslatedNotification( const char * pMsg, Args&&... args ); + + + /// Prints a Message with source indication + template + inline void Message (const std::string &, const char * pMsg, Args&&... args); + /// Prints a warning Message with source indication + template + inline void Warning (const std::string &, const char * pMsg, Args&&... args); + /// Prints a error Message with source indication + template + inline void Error (const std::string &, const char * pMsg, Args&&... args); + /// Prints a log Message with source indication + template + inline void Log (const std::string &, const char * pMsg, Args&&... args); + /// Prints a Critical Message with source indication + template + inline void Critical (const std::string &, const char * pMsg, Args&&... args); + /// Sends a User Notification with source indication + template + inline void UserNotification( const std::string & notifier, const char * pMsg, Args&&... args ); + /// Sends an already translated User Notification with source indication + template + inline void UserTranslatedNotification( const std::string & notifier, const char * pMsg, Args&&... args ); + + // Notify a message directly to observers + template + inline void Notify(const std::string & notifiername, const std::string & msg); /// Attaches an Observer to FCConsole void AttachObserver(ILogger *pcObserver); /// Detaches an Observer from FCConsole void DetachObserver(ILogger *pcObserver); - /// enumaration for the console modes + + /// enumeration for the console modes enum ConsoleMode{ Verbose = 1, // suppress Log messages }; @@ -548,10 +626,13 @@ public: }; enum FreeCAD_ConsoleMsgType { - MsgType_Txt = 1, - MsgType_Log = 2, // ConsoleObserverStd sends this and higher to stderr - MsgType_Wrn = 4, - MsgType_Err = 8 + MsgType_Txt = 1, + MsgType_Log = 2, // ConsoleObserverStd sends this and higher to stderr + MsgType_Wrn = 4, + MsgType_Err = 8, + MsgType_Critical = 16, // Special message to notify critical information + MsgType_Notification = 32, // Special message to for notifications to the user + MsgType_TranslatedNotification = 64, // Special message for already translated notifications to the user }; /// Change mode @@ -585,16 +666,21 @@ public: void Refresh(); void EnableRefresh(bool enable); + inline constexpr FreeCAD_ConsoleMsgType getConsoleMsg(Base::LogStyle style); + protected: // python exports goes here +++++++++++++++++++++++++++++++++++++++++++ // static python wrapper of the exported functions - static PyObject *sPyLog (PyObject *self,PyObject *args); - static PyObject *sPyMessage (PyObject *self,PyObject *args); - static PyObject *sPyWarning (PyObject *self,PyObject *args); - static PyObject *sPyError (PyObject *self,PyObject *args); - static PyObject *sPySetStatus(PyObject *self,PyObject *args); - static PyObject *sPyGetStatus(PyObject *self,PyObject *args); - static PyObject *sPyGetObservers(PyObject *self, PyObject *args); + static PyObject *sPyLog (PyObject *self,PyObject *args); + static PyObject *sPyMessage (PyObject *self,PyObject *args); + static PyObject *sPyWarning (PyObject *self,PyObject *args); + static PyObject *sPyError (PyObject *self,PyObject *args); + static PyObject *sPyCritical (PyObject *self,PyObject *args); + static PyObject *sPyNotification (PyObject *self,PyObject *args); + static PyObject *sPyTranslatedNotification (PyObject *self,PyObject *args); + static PyObject *sPySetStatus (PyObject *self,PyObject *args); + static PyObject *sPyGetStatus (PyObject *self,PyObject *args); + static PyObject *sPyGetObservers (PyObject *self,PyObject *args); bool _bVerbose; bool _bCanRefresh; @@ -605,6 +691,9 @@ protected: virtual ~ConsoleSingleton(); private: + void postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifiername, const std::string& msg); + void notifyPrivate(LogStyle category, const std::string& notifiername, const std::string& msg); + // singleton static void Destruct(); static ConsoleSingleton *_pcSingleton; @@ -626,6 +715,21 @@ inline ConsoleSingleton &Console(){ return ConsoleSingleton::Instance(); } +inline constexpr ConsoleSingleton::FreeCAD_ConsoleMsgType ConsoleSingleton::getConsoleMsg(Base::LogStyle style) +{ + constexpr std::array msgTypes { // In order of Base::LogStyle + FreeCAD_ConsoleMsgType::MsgType_Wrn, + FreeCAD_ConsoleMsgType::MsgType_Txt, + FreeCAD_ConsoleMsgType::MsgType_Err, + FreeCAD_ConsoleMsgType::MsgType_Log, + FreeCAD_ConsoleMsgType::MsgType_Critical, + FreeCAD_ConsoleMsgType::MsgType_Notification, + FreeCAD_ConsoleMsgType::MsgType_TranslatedNotification + }; + + return msgTypes.at(static_cast(style)); +} + class BaseExport ConsoleRefreshDisabler { public: ConsoleRefreshDisabler() { @@ -666,10 +770,131 @@ public: std::stringstream &prefix(std::stringstream &str, const char *src, int line); }; - - } // namespace Base +/** Prints a Message + * This method issues a Message. + * Messages are used to show some non vital information. That means when + * FreeCAD is running in GUI mode a Message appears on the status bar. + * In console mode a message is printed to the console. + * \par + * You can use a printf like interface like: + * \code + * Console().Message("Doing something important %d times\n",i); + * \endcode + * @see Warning + * @see Error + * @see Log + * @see Critical + * @see UserNotification + * @see UserTranslatedNotification + */ +template +inline void Base::ConsoleSingleton::Message( const char * pMsg, Args&&... args ) +{ + Message(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Message( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Warning( const char * pMsg, Args&&... args ) +{ + Warning(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Warning( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Error( const char * pMsg, Args&&... args ) +{ + Error(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Error( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Critical( const char * pMsg, Args&&... args ) +{ + Critical(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Critical( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::UserNotification( const char * pMsg, Args&&... args ) +{ + UserNotification(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::UserNotification( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::UserTranslatedNotification( const char * pMsg, Args&&... args ) +{ + UserTranslatedNotification(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::UserTranslatedNotification( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Log( const char * pMsg, Args&&... args ) +{ + Log(std::string(""), pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Log( const std::string & notifier, const char * pMsg, Args&&... args ) +{ + Send(notifier, pMsg, std::forward(args)...); +} + +template +inline void Base::ConsoleSingleton::Send( const std::string & notifiername, const char * pMsg, Args&&... args ) +{ + std::string format = fmt::sprintf(pMsg, args...); + + if (connectionMode == Direct) { + Notify(notifiername,format); + } + else { + + auto type = getConsoleMsg(category); + + postEvent(type, notifiername, format); + } +} + +template +inline void Base::ConsoleSingleton::Notify(const std::string & notifiername, const std::string & msg) +{ + notifyPrivate(category, notifiername, msg); +} + #if defined(__clang__) # pragma clang diagnostic pop #endif diff --git a/src/Base/ConsoleObserver.cpp b/src/Base/ConsoleObserver.cpp index f5a26d9ae8..dcce9091e8 100644 --- a/src/Base/ConsoleObserver.cpp +++ b/src/Base/ConsoleObserver.cpp @@ -58,8 +58,10 @@ ConsoleObserverFile::~ConsoleObserverFile() cFileStream.close(); } -void ConsoleObserverFile::SendLog(const std::string& msg, LogStyle level) +void ConsoleObserverFile::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level) { + (void) notifiername; + std::string prefix; switch(level){ case LogStyle::Warning: @@ -74,6 +76,11 @@ void ConsoleObserverFile::SendLog(const std::string& msg, LogStyle level) case LogStyle::Log: prefix = "Log: "; break; + case LogStyle::Critical: + prefix = "Critical: "; + break; + default: + break; } cFileStream << prefix << msg; @@ -94,8 +101,10 @@ ConsoleObserverStd::ConsoleObserverStd() : ConsoleObserverStd::~ConsoleObserverStd() = default; -void ConsoleObserverStd::SendLog(const std::string& msg, LogStyle level) +void ConsoleObserverStd::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level) { + (void) notifiername; + switch(level){ case LogStyle::Warning: this->Warning(msg.c_str()); @@ -109,6 +118,11 @@ void ConsoleObserverStd::SendLog(const std::string& msg, LogStyle level) case LogStyle::Log: this->Log(msg.c_str()); break; + case LogStyle::Critical: + this->Critical(msg.c_str()); + break; + default: + break; } } @@ -180,6 +194,27 @@ void ConsoleObserverStd::Log (const char *sErr) } } +void ConsoleObserverStd::Critical(const char *sCritical) +{ + if (useColorStderr) { + # if defined(FC_OS_WIN32) + ::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE); + # elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) + fprintf(stderr, "\033[1;33m"); + # endif + } + + fprintf(stderr, "%s", sCritical); + + if (useColorStderr) { + # if defined(FC_OS_WIN32) + ::SetConsoleTextAttribute(::GetStdHandle(STD_ERROR_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE ); + # elif defined(FC_OS_LINUX) || defined(FC_OS_MACOSX) || defined(FC_OS_BSD) + fprintf(stderr, "\033[0m"); + # endif + } +} + RedirectStdOutput::RedirectStdOutput() { buffer.reserve(80); diff --git a/src/Base/ConsoleObserver.h b/src/Base/ConsoleObserver.h index 41191d2f27..4d510664f1 100644 --- a/src/Base/ConsoleObserver.h +++ b/src/Base/ConsoleObserver.h @@ -39,10 +39,10 @@ namespace Base { class BaseExport ConsoleObserverFile : public ILogger { public: - ConsoleObserverFile(const char *sFileName); + explicit ConsoleObserverFile(const char *sFileName); ~ConsoleObserverFile() override; - void SendLog(const std::string& message, LogStyle level) override; + void SendLog(const std::string& notifiername, const std::string& message, LogStyle level) override; const char* Name() override {return "File";} protected: @@ -57,15 +57,16 @@ class BaseExport ConsoleObserverStd: public ILogger public: ConsoleObserverStd(); ~ConsoleObserverStd() override; - void SendLog(const std::string& message, LogStyle level) override; + void SendLog(const std::string& notifiername, const std::string& message, LogStyle level) override; const char* Name() override {return "Console";} protected: bool useColorStderr; private: - void Warning(const char *sWarn); - void Message(const char *sMsg); - void Error (const char *sErr); - void Log (const char *sErr); + void Warning (const char *sWarn); + void Message (const char *sMsg); + void Error (const char *sErr); + void Log (const char *sLog); + void Critical(const char *sCritical); }; /** The ILoggerBlocker class @@ -77,7 +78,8 @@ class BaseExport ILoggerBlocker public: // Constructor that will block message types passed as parameter. By default, all types are blocked. inline explicit ILoggerBlocker(const char* co, ConsoleMsgFlags msgTypes = - ConsoleSingleton::MsgType_Txt | ConsoleSingleton::MsgType_Log | ConsoleSingleton::MsgType_Wrn | ConsoleSingleton::MsgType_Err); + ConsoleSingleton::MsgType_Txt | ConsoleSingleton::MsgType_Log | ConsoleSingleton::MsgType_Wrn | ConsoleSingleton::MsgType_Err | + ConsoleSingleton::MsgType_Critical | ConsoleSingleton::MsgType_Notification | ConsoleSingleton::MsgType_TranslatedNotification); // Disable copy & move constructors ILoggerBlocker(ILoggerBlocker const&) = delete; ILoggerBlocker(ILoggerBlocker const &&) = delete; diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp index 11497b1ce1..5b5197e716 100644 --- a/src/Gui/Application.cpp +++ b/src/Gui/Application.cpp @@ -1117,7 +1117,7 @@ void Application::setActiveDocument(Gui::Document* pcDocument) // May be useful for error detection if (d->activeDocument) { App::Document* doc = d->activeDocument->getDocument(); - Base::Console().Log("Active document is %s (at %p)\n",doc->getName(), doc); + Base::Console().Log("Active document is %s (at %p)\n",doc->getName(), static_cast(doc)); } else { Base::Console().Log("No active document\n"); @@ -1192,7 +1192,7 @@ void Application::viewActivated(MDIView* pcView) #ifdef FC_DEBUG // May be useful for error detection Base::Console().Log("Active view is %s (at %p)\n", - (const char*)pcView->windowTitle().toUtf8(),pcView); + (const char*)pcView->windowTitle().toUtf8(),static_cast(pcView)); #endif signalActivateView(pcView); diff --git a/src/Gui/CommandTest.cpp b/src/Gui/CommandTest.cpp index 959bda5bfb..b70bc59862 100644 --- a/src/Gui/CommandTest.cpp +++ b/src/Gui/CommandTest.cpp @@ -724,11 +724,13 @@ class TestConsoleObserver : public Base::ILogger { QMutex mutex; public: - int matchMsg, matchWrn, matchErr, matchLog; - TestConsoleObserver() : matchMsg(0), matchWrn(0), matchErr(0), matchLog(0) + int matchMsg, matchWrn, matchErr, matchLog, matchCritical; + TestConsoleObserver() : matchMsg(0), matchWrn(0), matchErr(0), matchLog(0), matchCritical(0) { } - void SendLog(const std::string& msg, Base::LogStyle level) override{ + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override{ + + (void) notifiername; QMutexLocker ml(&mutex); @@ -745,6 +747,11 @@ public: case Base::LogStyle::Log: matchLog += strcmp(msg.c_str(), "Write a log to the console output.\n"); break; + case Base::LogStyle::Critical: + matchMsg += strcmp(msg.c_str(), "Write a critical message to the console output.\n"); + break; + default: + break; } } }; @@ -789,6 +796,16 @@ public: } }; +class ConsoleCriticalTask : public QRunnable +{ +public: + void run() override + { + for (int i=0; i<10; i++) + Base::Console().Critical("Write a critical message to the console output.\n"); + } +}; + } void CmdTestConsoleOutput::activated(int iMsg) @@ -800,10 +817,11 @@ void CmdTestConsoleOutput::activated(int iMsg) QThreadPool::globalInstance()->start(new ConsoleWarningTask); QThreadPool::globalInstance()->start(new ConsoleErrorTask); QThreadPool::globalInstance()->start(new ConsoleLogTask); + QThreadPool::globalInstance()->start(new ConsoleCriticalTask); QThreadPool::globalInstance()->waitForDone(); Base::Console().DetachObserver(&obs); - if (obs.matchMsg > 0 || obs.matchWrn > 0 || obs.matchErr > 0 || obs.matchLog > 0) { + if (obs.matchMsg > 0 || obs.matchWrn > 0 || obs.matchErr > 0 || obs.matchLog > 0 || obs.matchCritical > 0) { Base::Console().Error("Race condition in Console class\n"); } } diff --git a/src/Gui/GuiConsole.cpp b/src/Gui/GuiConsole.cpp index 89144e0d3f..3cb10b2882 100644 --- a/src/Gui/GuiConsole.cpp +++ b/src/Gui/GuiConsole.cpp @@ -81,7 +81,7 @@ GUIConsole::~GUIConsole (void) FreeConsole(); } -void GUIConsole::SendLog(const std::string& msg, Base::LogStyle level) +void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) { int color = -1; switch(level){ @@ -97,6 +97,9 @@ void GUIConsole::SendLog(const std::string& msg, Base::LogStyle level) case Base::LogStyle::Log: color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case Base::LogStyle::Critical: + color = FOREGROUND_RED | FOREGROUND_GREEN; + break; } ::SetConsoleTextAttribute(::GetStdHandle(STD_OUTPUT_HANDLE), color); @@ -109,8 +112,10 @@ void GUIConsole::SendLog(const std::string& msg, Base::LogStyle level) // safely ignore GUIConsole::s_nMaxLines and GUIConsole::s_nRefCount GUIConsole::GUIConsole () {} GUIConsole::~GUIConsole () {} -void GUIConsole::SendLog(const std::string& msg, Base::LogStyle level) +void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) { + (void) notifiername; + switch(level){ case Base::LogStyle::Warning: std::cerr << "Warning: " << msg; @@ -124,6 +129,11 @@ void GUIConsole::SendLog(const std::string& msg, Base::LogStyle level) case Base::LogStyle::Log: std::clog << msg; break; + case Base::LogStyle::Critical: + std::cout << "Critical: " << msg; + break; + default: + break; } } diff --git a/src/Gui/GuiConsole.h b/src/Gui/GuiConsole.h index 6e55983529..3633b6a685 100644 --- a/src/Gui/GuiConsole.h +++ b/src/Gui/GuiConsole.h @@ -47,7 +47,7 @@ public: GUIConsole(); /// Destructor ~GUIConsole() override; - void SendLog(const std::string& msg, Base::LogStyle level) override; + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override; const char* Name() override {return "GUIConsole";} protected: diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 3323315d4e..3f1ee3ad7b 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -2146,10 +2146,16 @@ void StatusBarObserver::OnChange(Base::Subject &rCaller, const char unsigned long col = rclGrp.GetUnsigned( sReason ); this->err = format.arg(App::Color::fromPackedRGB(col).name()); } + else if (strcmp(sReason, "colorCritical") == 0) { + unsigned long col = rclGrp.GetUnsigned( sReason ); + this->critical = format.arg(QColor((col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff).name()); + } } -void StatusBarObserver::SendLog(const std::string& msg, Base::LogStyle level) +void StatusBarObserver::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) { + (void) notifiername; + int messageType = -1; switch(level){ case Base::LogStyle::Warning: @@ -2164,6 +2170,11 @@ void StatusBarObserver::SendLog(const std::string& msg, Base::LogStyle level) case Base::LogStyle::Log: messageType = MainWindow::Log; break; + case Base::LogStyle::Critical: + messageType = MainWindow::Critical; + break; + default: + break; } // Send the event to the main window to allow thread-safety. Qt will delete it when done. diff --git a/src/Gui/MainWindow.h b/src/Gui/MainWindow.h index 39bc4f03be..1413480965 100644 --- a/src/Gui/MainWindow.h +++ b/src/Gui/MainWindow.h @@ -201,7 +201,7 @@ public: void updateActions(bool delay = false); - enum StatusType {None, Err, Wrn, Pane, Msg, Log, Tmp}; + enum StatusType {None, Err, Wrn, Pane, Msg, Log, Tmp, Critical}; void showStatus(int type, const QString & message); @@ -371,14 +371,14 @@ public: /** Observes its parameter group. */ void OnChange(Base::Subject &rCaller, const char * sReason) override; - void SendLog(const std::string& msg, Base::LogStyle level) override; + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override; /// name of the observer const char *Name() override {return "StatusBar";} friend class MainWindow; private: - QString msg, wrn, err; + QString msg, wrn, err, critical; }; // ------------------------------------------------------------- diff --git a/src/Gui/ReportView.cpp b/src/Gui/ReportView.cpp index d8f5795910..c3eeb97ac2 100644 --- a/src/Gui/ReportView.cpp +++ b/src/Gui/ReportView.cpp @@ -170,6 +170,9 @@ void ReportHighlighter::highlightBlock (const QString & text) case LogText: setFormat(start, it.length-start, logCol); break; + case Critical: + setFormat(start, it.length-start, criticalCol); + break; default: break; } @@ -203,6 +206,11 @@ void ReportHighlighter::setErrorColor( const QColor& col ) errCol = col; } +void ReportHighlighter::setCriticalColor( const QColor& col ) +{ + criticalCol = col; +} + // ---------------------------------------------------------------------------- namespace Gui { @@ -245,6 +253,15 @@ public: bool show = showOnError(); getGroup()->SetBool("checkShowReportViewOnError", !show); } + static bool showOnCritical() + { + return getGroup()->GetBool("checkShowReportViewOnCritical", false); + } + static void toggleShowOnCritical() + { + bool show = showOnMessage(); + getGroup()->SetBool("checkShowReportViewOnCritical", !show); + } private: static ParameterGrp::handle getGroup() @@ -329,6 +346,11 @@ bool ReportOutputObserver::eventFilter(QObject *obj, QEvent *event) showReportView(); } } + else if (msgType == ReportHighlighter::Critical) { + if (ReportOutputParameter::showOnCritical()) { + showReportView(); + } + } } return false; //true would prevent the messages reaching the report view } @@ -450,8 +472,10 @@ void ReportOutput::restoreFont() setFont(serifFont); } -void ReportOutput::SendLog(const std::string& msg, Base::LogStyle level) +void ReportOutput::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) { + (void) notifiername; + ReportHighlighter::Paragraph style = ReportHighlighter::LogText; switch (level) { case Base::LogStyle::Warning: @@ -466,6 +490,11 @@ void ReportOutput::SendLog(const std::string& msg, Base::LogStyle level) case Base::LogStyle::Log: style = ReportHighlighter::LogText; break; + case Base::LogStyle::Critical: + style = ReportHighlighter::Critical; + break; + default: + break; } QString qMsg = QString::fromUtf8(msg.c_str()); @@ -544,6 +573,7 @@ void ReportOutput::contextMenuEvent ( QContextMenuEvent * e ) bool bShowOnNormal = ReportOutputParameter::showOnMessage(); bool bShowOnWarn = ReportOutputParameter::showOnWarning(); bool bShowOnError = ReportOutputParameter::showOnError(); + bool bShowOnCritical = ReportOutputParameter::showOnCritical(); auto menu = new QMenu(this); auto optionMenu = new QMenu( menu ); @@ -571,6 +601,10 @@ void ReportOutput::contextMenuEvent ( QContextMenuEvent * e ) errAct->setCheckable(true); errAct->setChecked(bErr); + QAction* logCritical = displayMenu->addAction(tr("Critical messages"), this, &ReportOutput::onToggleCritical); + logCritical->setCheckable(true); + logCritical->setChecked(bCritical); + auto showOnMenu = new QMenu (optionMenu); showOnMenu->setTitle(tr("Show Report view on")); optionMenu->addMenu(showOnMenu); @@ -591,6 +625,10 @@ void ReportOutput::contextMenuEvent ( QContextMenuEvent * e ) showErrAct->setCheckable(true); showErrAct->setChecked(bShowOnError); + QAction* showCriticalAct = showOnMenu->addAction(tr("Critical messages"), this, SLOT(onToggleShowReportViewOnCritical())); + showCriticalAct->setCheckable(true); + showCriticalAct->setChecked(bShowOnCritical); + optionMenu->addSeparator(); QAction* stdoutAct = optionMenu->addAction(tr("Redirect Python output"), this, &ReportOutput::onToggleRedirectPythonStdout); @@ -664,6 +702,12 @@ bool ReportOutput::isNormalMessage() const return bMsg; } + +bool ReportOutput::isCritical() const +{ + return bCritical; +} + void ReportOutput::onToggleError() { bErr = bErr ? false : true; @@ -688,6 +732,12 @@ void ReportOutput::onToggleNormalMessage() getWindowParameter()->SetBool( "checkMessage", bMsg ); } +void ReportOutput::onToggleCritical() +{ + bCritical = bCritical ? false : true; + getWindowParameter()->SetBool( "checkCritical", bCritical ); +} + void ReportOutput::onToggleShowReportViewOnWarning() { ReportOutputParameter::toggleShowOnWarning(); @@ -703,6 +753,11 @@ void ReportOutput::onToggleShowReportViewOnNormalMessage() ReportOutputParameter::toggleShowOnMessage(); } +void ReportOutput::onToggleShowReportViewOnCritical() +{ + ReportOutputParameter::toggleShowOnCritical(); +} + void ReportOutput::onToggleShowReportViewOnLogMessage() { ReportOutputParameter::toggleShowOnLogMessage(); @@ -761,10 +816,17 @@ void ReportOutput::OnChange(Base::Subject &rCaller, const char * sR else if (strcmp(sReason, "checkMessage") == 0) { bMsg = rclGrp.GetBool( sReason, bMsg ); } + else if (strcmp(sReason, "checkCritical") == 0) { + bMsg = rclGrp.GetBool( sReason, bMsg ); + } else if (strcmp(sReason, "colorText") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setTextColor(App::Color::fromPackedRGB(col)); } + else if (strcmp(sReason, "colorCriticalText") == 0) { + unsigned long col = rclGrp.GetUnsigned( sReason ); + reportHl->setTextColor( QColor( (col >> 24) & 0xff,(col >> 16) & 0xff,(col >> 8) & 0xff) ); + } else if (strcmp(sReason, "colorLogging") == 0) { unsigned long col = rclGrp.GetUnsigned( sReason ); reportHl->setLogColor(App::Color::fromPackedRGB(col)); diff --git a/src/Gui/ReportView.h b/src/Gui/ReportView.h index 0a726d9d6b..3255558051 100644 --- a/src/Gui/ReportView.h +++ b/src/Gui/ReportView.h @@ -70,10 +70,11 @@ class GuiExport ReportHighlighter : public QSyntaxHighlighter { public: enum Paragraph { - Message = 0, /**< normal text */ - Warning = 1, /**< Warning */ - Error = 2, /**< Error text */ - LogText = 3 /**< Log text */ + Message = 0, /**< normal text */ + Warning = 1, /**< Warning */ + Error = 2, /**< Error text */ + LogText = 3, /**< Log text */ + Critical = 4, /**< critical text */ }; public: @@ -87,6 +88,7 @@ public: * @see ReportOutput::Message * @see ReportOutput::Warning * @see ReportOutput::Error + * @see ReportOutput::Critical */ void setParagraphType(Paragraph); @@ -110,11 +112,16 @@ public: */ void setErrorColor( const QColor& col ); + /** + * Sets the text color to \a col. + */ + void setCriticalColor( const QColor& col ); + private: /** @name for internal use only */ //@{ Paragraph type; - QColor txtCol, logCol, warnCol, errCol; + QColor txtCol, logCol, warnCol, errCol, criticalCol; //@} }; @@ -134,7 +141,7 @@ public: /** Observes its parameter group. */ void OnChange(Base::Subject &rCaller, const char * sReason) override; - void SendLog(const std::string& msg, Base::LogStyle level) override; + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override; /// returns the name for observer handling const char* Name() override {return "ReportOutput";} @@ -150,6 +157,8 @@ public: bool isLogMessage() const; /** Returns true whether normal messages are reported. */ bool isNormalMessage() const; + /** Returns true whether critical messages are reported. */ + bool isCritical() const; protected: /** For internal use only */ @@ -172,12 +181,16 @@ public Q_SLOTS: void onToggleLogMessage(); /** Toggles the report of normal messages. */ void onToggleNormalMessage(); + /** Toggles the report of normal messages. */ + void onToggleCritical(); /** Toggles whether to show report view on warnings*/ void onToggleShowReportViewOnWarning(); /** Toggles whether to show report view on errors*/ void onToggleShowReportViewOnError(); /** Toggles whether to show report view on normal messages*/ void onToggleShowReportViewOnNormalMessage(); + /** Toggles whether to show report view on normal messages*/ + void onToggleShowReportViewOnCritical(); /** Toggles whether to show report view on log messages*/ void onToggleShowReportViewOnLogMessage(); /** Toggles the redirection of Python stdout. */ diff --git a/src/Gui/Splashscreen.cpp b/src/Gui/Splashscreen.cpp index cd09d2b701..20840ca58b 100644 --- a/src/Gui/Splashscreen.cpp +++ b/src/Gui/Splashscreen.cpp @@ -123,8 +123,10 @@ public: { return "SplashObserver"; } - void SendLog(const std::string& msg, Base::LogStyle level) override + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override { + Q_UNUSED(notifiername) + #ifdef FC_DEBUG Log(msg.c_str()); Q_UNUSED(level) diff --git a/src/Gui/Tree.cpp b/src/Gui/Tree.cpp index 2ed813925c..f13015e066 100644 --- a/src/Gui/Tree.cpp +++ b/src/Gui/Tree.cpp @@ -70,11 +70,11 @@ FC_LOG_LEVEL_INIT("Tree", false, true, true) #define _TREE_PRINT(_level,_func,_msg) \ _FC_PRINT(FC_LOG_INSTANCE,_level,_func, '['<,_msg) +#define TREE_WARN(_msg) _TREE_PRINT(FC_LOGLEVEL_WARN,Notify,_msg) +#define TREE_ERR(_msg) _TREE_PRINT(FC_LOGLEVEL_ERR,Notify,_msg) +#define TREE_LOG(_msg) _TREE_PRINT(FC_LOGLEVEL_LOG,Notify,_msg) +#define TREE_TRACE(_msg) _TREE_PRINT(FC_LOGLEVEL_TRACE,Notify,_msg) using namespace Gui; namespace bp = boost::placeholders; diff --git a/src/Mod/Import/App/ImportOCAF2.cpp b/src/Mod/Import/App/ImportOCAF2.cpp index 976da09609..a3a4c251a1 100644 --- a/src/Mod/Import/App/ImportOCAF2.cpp +++ b/src/Mod/Import/App/ImportOCAF2.cpp @@ -147,7 +147,7 @@ static void printLabel(TDF_Label label, Handle(XCAFDoc_ShapeTool) aShapeTool, } ss << std::endl; - Base::Console().NotifyLog(ss.str().c_str()); + Base::Console().Notify("ImportOCAF2",ss.str().c_str()); } static void dumpLabels(TDF_Label label, Handle(XCAFDoc_ShapeTool) aShapeTool, diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 6516013308..e34955eecf 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -568,7 +568,7 @@ void ImpExpDxfWrite::exportShape(const TopoDS_Shape input) } else if (adapt.GetType() == GeomAbs_Line) { exportLine(adapt); } else { - Base::Console().Warning("ImpExpDxf - unknown curve type: %d\n",adapt.GetType()); + Base::Console().Warning("ImpExpDxf - unknown curve type: %d\n", static_cast(adapt.GetType())); } } diff --git a/src/Mod/Part/App/TopoShapePyImp.cpp b/src/Mod/Part/App/TopoShapePyImp.cpp index c3b89671b4..bdabcb9972 100644 --- a/src/Mod/Part/App/TopoShapePyImp.cpp +++ b/src/Mod/Part/App/TopoShapePyImp.cpp @@ -2596,7 +2596,7 @@ PyObject* TopoShapePy::distToShape(PyObject *args) break; default: Base::Console().Message("distToShape: supportType1 is unknown: %d \n", - supportType1); + static_cast(supportType1)); suppType1 = Py::String("Unknown"); suppIndex1 = -1; param1 = Py::None(); @@ -2631,7 +2631,7 @@ PyObject* TopoShapePy::distToShape(PyObject *args) break; default: Base::Console().Message("distToShape: supportType2 is unknown: %d \n", - supportType2); + static_cast(supportType2)); suppType2 = Py::String("Unknown"); suppIndex2 = -1; param2 = Py::None(); diff --git a/src/Mod/Sketcher/App/planegcs/GCS.cpp b/src/Mod/Sketcher/App/planegcs/GCS.cpp index 31310ee212..f80d5e03bc 100644 --- a/src/Mod/Sketcher/App/planegcs/GCS.cpp +++ b/src/Mod/Sketcher/App/planegcs/GCS.cpp @@ -285,10 +285,7 @@ SolverReportingManager& SolverReportingManager::Manager() void SolverReportingManager::LogToConsole(const std::string& str) { - if(str.size() < Base::Console().BufferSize) - Base::Console().Log(str.c_str()); - else - Base::Console().Log("SolverReportingManager - Overly long string suppressed"); + Base::Console().Log(str.c_str()); } void SolverReportingManager::LogToFile(const std::string& str) diff --git a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp index a6aacf81be..37fda52260 100644 --- a/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp +++ b/src/Mod/Sketcher/Gui/ViewProviderSketch.cpp @@ -1088,7 +1088,7 @@ void ViewProviderSketch::editDoubleClicked() Base::Console().Log("double click edge:%d\n",preselection.PreselectCurve); } else if (preselection.isCrossPreselected()) { - Base::Console().Log("double click cross:%d\n",preselection.PreselectCross); + Base::Console().Log("double click cross:%d\n",static_cast(preselection.PreselectCross)); } else if (!preselection.PreselectConstraintSet.empty()) { // Find the constraint diff --git a/src/Mod/TechDraw/App/Cosmetic.cpp b/src/Mod/TechDraw/App/Cosmetic.cpp index 8fc088dce4..76fbefcceb 100644 --- a/src/Mod/TechDraw/App/Cosmetic.cpp +++ b/src/Mod/TechDraw/App/Cosmetic.cpp @@ -461,7 +461,7 @@ void CosmeticEdge::Save(Base::Writer &writer) const TechDraw::AOCPtr aoc = std::static_pointer_cast(m_geometry); aoc->Save(writer); } else { - Base::Console().Warning("CE::Save - unimplemented geomType: %d\n", m_geometry->getGeomType()); + Base::Console().Warning("CE::Save - unimplemented geomType: %d\n", static_cast(m_geometry->getGeomType())); } } @@ -507,7 +507,7 @@ void CosmeticEdge::Restore(Base::XMLReader &reader) permaEnd = aoc->endPnt; permaRadius = aoc->radius; } else { - Base::Console().Warning("CE::Restore - unimplemented geomType: %d\n", gType); + Base::Console().Warning("CE::Restore - unimplemented geomType: %d\n", static_cast(gType)); } } @@ -1262,7 +1262,7 @@ void CenterLine::Save(Base::Writer &writer) const TechDraw::AOCPtr aoc = std::static_pointer_cast(m_geometry); aoc->Save(writer); } else { - Base::Console().Message("CL::Save - unimplemented geomType: %d\n", m_geometry->getGeomType()); + Base::Console().Message("CL::Save - unimplemented geomType: %d\n", static_cast(m_geometry->getGeomType())); } } @@ -1362,7 +1362,7 @@ void CenterLine::Restore(Base::XMLReader &reader) aoc->setOCCEdge(GeometryUtils::edgeFromCircleArc(aoc)); m_geometry = aoc; } else { - Base::Console().Warning("CL::Restore - unimplemented geomType: %d\n", gType); + Base::Console().Warning("CL::Restore - unimplemented geomType: %d\n", static_cast(gType)); } } diff --git a/src/Mod/TechDraw/App/DrawComplexSection.cpp b/src/Mod/TechDraw/App/DrawComplexSection.cpp index a39dc6bb21..db60ad0119 100644 --- a/src/Mod/TechDraw/App/DrawComplexSection.cpp +++ b/src/Mod/TechDraw/App/DrawComplexSection.cpp @@ -820,7 +820,7 @@ TopoDS_Wire DrawComplexSection::makeSectionLineWire() else { //probably can't happen as cut profile has been checked before this Base::Console().Message("DCS::makeSectionLineGeometry - profile is type: %d\n", - sScaled.ShapeType()); + static_cast(sScaled.ShapeType())); return TopoDS_Wire(); } } diff --git a/src/Mod/TechDraw/App/DrawUtil.cpp b/src/Mod/TechDraw/App/DrawUtil.cpp index bb32ee51b8..797449c7ac 100644 --- a/src/Mod/TechDraw/App/DrawUtil.cpp +++ b/src/Mod/TechDraw/App/DrawUtil.cpp @@ -1670,13 +1670,13 @@ void DrawUtil::dumpEdge(const char* label, int i, TopoDS_Edge e) vEnd.X(), vEnd.Y(), vEnd.Z(), - e.Orientation()); + static_cast(e.Orientation())); double edgeLength = GCPnts_AbscissaPoint::Length(adapt, Precision::Confusion()); Base::Console().Message(">>>>>>> length: %.3f distance: %.3f ration: %.3f type: %d\n", edgeLength, vStart.Distance(vEnd), edgeLength / vStart.Distance(vEnd), - adapt.GetType()); + static_cast(adapt.GetType())); } const char* DrawUtil::printBool(bool b) diff --git a/src/Mod/TechDraw/App/GeometryObject.cpp b/src/Mod/TechDraw/App/GeometryObject.cpp index ecb838d8f3..9635890595 100644 --- a/src/Mod/TechDraw/App/GeometryObject.cpp +++ b/src/Mod/TechDraw/App/GeometryObject.cpp @@ -498,7 +498,7 @@ void GeometryObject::extractGeometry(edgeClass category, bool hlrVisible) default: Base::Console().Warning( "GeometryObject::ExtractGeometry - unsupported hlrVisible edgeClass: %d\n", - category); + static_cast(category)); return; } } @@ -522,7 +522,7 @@ void GeometryObject::extractGeometry(edgeClass category, bool hlrVisible) default: Base::Console().Warning( "GeometryObject::ExtractGeometry - unsupported hidden edgeClass: %d\n", - category); + static_cast(category)); return; } } diff --git a/src/Mod/TechDraw/Gui/QGIViewPart.cpp b/src/Mod/TechDraw/Gui/QGIViewPart.cpp index cb54fb0aa4..69f1c76f74 100644 --- a/src/Mod/TechDraw/Gui/QGIViewPart.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewPart.cpp @@ -331,7 +331,7 @@ QPainterPath QGIViewPart::geomToPainterPath(BaseGeomPtr baseGeom, double rot) } break; default: { Base::Console().Error("Error - geomToPainterPath - UNKNOWN geomType: %d\n", - baseGeom->getGeomType()); + static_cast(baseGeom->getGeomType())); } break; }//sb end of switch @@ -1257,8 +1257,8 @@ void QGIViewPart::dumpPath(const char* text, QPainterPath path) typeName = "CurveData"; } Base::Console().Message(">>>>> element %d: type:%d/%s pos(%.3f, %.3f) M:%d L:%d C:%d\n", - iElem, elem.type, typeName, elem.x, elem.y, elem.isMoveTo(), - elem.isLineTo(), elem.isCurveTo()); + iElem, static_cast(elem.type), typeName, elem.x, elem.y, static_cast(elem.isMoveTo()), + static_cast(elem.isLineTo()), static_cast(elem.isCurveTo())); } } diff --git a/src/Mod/Test/Gui/AppTestGui.cpp b/src/Mod/Test/Gui/AppTestGui.cpp index 1c445b99e3..e43a07b4b3 100644 --- a/src/Mod/Test/Gui/AppTestGui.cpp +++ b/src/Mod/Test/Gui/AppTestGui.cpp @@ -40,7 +40,8 @@ public: void flush() {buffer.str("");buffer.clear();} - void SendLog(const std::string& msg, Base::LogStyle level) override{ + void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override{ + (void) notifiername; (void) msg; switch(level){ case Base::LogStyle::Warning: @@ -55,6 +56,11 @@ public: case Base::LogStyle::Log: buffer << "LOG"; break; + case Base::LogStyle::Critical: + buffer << "CMS"; + break; + default: + break; } } @@ -65,44 +71,45 @@ public: Base::Console().Message("MSG"); Base::Console().Warning("WRN"); Base::Console().Error("ERR"); + Base::Console().Critical("CMS"); if (buffer.str() != expectedResult) throw Py::RuntimeError("ILoggerTest: " + buffer.str() + " different from " + expectedResult); } void runTest() { - runSingleTest("Print all message types", "LOGMSGWRNERR"); + runSingleTest("Print all message types", "LOGMSGWRNERRCMS"); { Base::ILoggerBlocker blocker("ILoggerBlockerTest"); runSingleTest("All types blocked", ""); } - runSingleTest("Print all", "LOGMSGWRNERR"); + runSingleTest("Print all", "LOGMSGWRNERRCMS"); { Base::ILoggerBlocker blocker("ILoggerBlockerTest", Base::ConsoleSingleton::MsgType_Err | Base::ConsoleSingleton::MsgType_Wrn); - runSingleTest("Error & Warning blocked", "LOGMSG"); + runSingleTest("Error & Warning blocked", "LOGMSGCMS"); } - runSingleTest("Print all", "LOGMSGWRNERR"); + runSingleTest("Print all", "LOGMSGWRNERRCMS"); { Base::ILoggerBlocker blocker("ILoggerBlockerTest", Base::ConsoleSingleton::MsgType_Log | Base::ConsoleSingleton::MsgType_Txt); - runSingleTest("Log & Message blocked", "WRNERR"); + runSingleTest("Log & Message blocked", "WRNERRCMS"); } - runSingleTest("Print all", "LOGMSGWRNERR"); + runSingleTest("Print all", "LOGMSGWRNERRCMS"); { Base::ILoggerBlocker blocker("ILoggerBlockerTest", Base::ConsoleSingleton::MsgType_Err); - runSingleTest("Nested : Error blocked", "LOGMSGWRN"); + runSingleTest("Nested : Error blocked", "LOGMSGWRNCMS"); { Base::ILoggerBlocker blocker2("ILoggerBlockerTest", Base::ConsoleSingleton::MsgType_Err | Base::ConsoleSingleton::MsgType_Wrn); - runSingleTest("Nested : Warning blocked + Error (from nesting) + Error (redundancy)", "LOGMSG"); + runSingleTest("Nested : Warning blocked + Error (from nesting) + Error (redundancy)", "LOGMSGCMS"); } - runSingleTest("Nested : Error still blocked", "LOGMSGWRN"); + runSingleTest("Nested : Error still blocked", "LOGMSGWRNCMS"); } - runSingleTest("Print all", "LOGMSGWRNERR"); + runSingleTest("Print all", "LOGMSGWRNERRCMS"); { Base::ILoggerBlocker blocker("ILoggerBlockerTest"); Base::Console().SetEnabledMsgType("ILoggerBlockerTest", Base::ConsoleSingleton::MsgType_Log, true); runSingleTest("Log is enabled but a warning is triggered in debug mode", "LOG"); } - runSingleTest("Print all", "LOGMSGWRNERR"); + runSingleTest("Print all", "LOGMSGWRNERRCMS"); } private: